Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-24 18:15:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-24 18:15:02 +0300
commitc4b4a75c35cb2015c01ef0b60f8ad8baaaf889df (patch)
tree16eabfd63477e1904d7eb5d9f92f3e5a4e4d3e0f /app/assets/javascripts/content_editor
parente40c68997d44209aed2baf3a8ec6be9ae99fb0b5 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/content_editor')
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor.vue19
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor_provider.vue1
-rw-r--r--app/assets/javascripts/content_editor/components/editor_state_observer.vue34
-rw-r--r--app/assets/javascripts/content_editor/constants.js4
-rw-r--r--app/assets/javascripts/content_editor/extensions/attachment.js17
-rw-r--r--app/assets/javascripts/content_editor/services/content_editor.js33
-rw-r--r--app/assets/javascripts/content_editor/services/create_content_editor.js7
-rw-r--r--app/assets/javascripts/content_editor/services/upload_helpers.js14
8 files changed, 78 insertions, 51 deletions
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index a8405fe37c7..7e9380ec553 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -1,7 +1,6 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { EditorContent as TiptapEditorContent } from '@tiptap/vue-2';
-import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } from '../constants';
import { createContentEditor } from '../services/create_content_editor';
import ContentEditorAlert from './content_editor_alert.vue';
import ContentEditorProvider from './content_editor_provider.vue';
@@ -55,17 +54,12 @@ export default {
extensions,
serializerConfig,
});
-
- this.contentEditor.on(LOADING_CONTENT_EVENT, this.displayLoadingIndicator);
- this.contentEditor.on(LOADING_SUCCESS_EVENT, this.hideLoadingIndicator);
- this.contentEditor.on(LOADING_ERROR_EVENT, this.hideLoadingIndicator);
+ },
+ mounted() {
this.$emit('initialized', this.contentEditor);
},
beforeDestroy() {
this.contentEditor.dispose();
- this.contentEditor.off(LOADING_CONTENT_EVENT, this.displayLoadingIndicator);
- this.contentEditor.off(LOADING_SUCCESS_EVENT, this.hideLoadingIndicator);
- this.contentEditor.off(LOADING_ERROR_EVENT, this.hideLoadingIndicator);
},
methods: {
displayLoadingIndicator() {
@@ -91,7 +85,14 @@ export default {
<template>
<content-editor-provider :content-editor="contentEditor">
<div>
- <editor-state-observer @docUpdate="notifyChange" @focus="focus" @blur="blur" />
+ <editor-state-observer
+ @loading="displayLoadingIndicator"
+ @loadingSuccess="hideLoadingIndicator"
+ @loadingError="hideLoadingIndicator"
+ @docUpdate="notifyChange"
+ @focus="focus"
+ @blur="blur"
+ />
<content-editor-alert />
<div
data-testid="content-editor"
diff --git a/app/assets/javascripts/content_editor/components/content_editor_provider.vue b/app/assets/javascripts/content_editor/components/content_editor_provider.vue
index 630aff9858f..cba3b627390 100644
--- a/app/assets/javascripts/content_editor/components/content_editor_provider.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor_provider.vue
@@ -8,6 +8,7 @@ export default {
return {
contentEditor,
+ eventHub: contentEditor.eventHub,
tiptapEditor: contentEditor.tiptapEditor,
};
},
diff --git a/app/assets/javascripts/content_editor/components/editor_state_observer.vue b/app/assets/javascripts/content_editor/components/editor_state_observer.vue
index 0604047a953..02de6470cf2 100644
--- a/app/assets/javascripts/content_editor/components/editor_state_observer.vue
+++ b/app/assets/javascripts/content_editor/components/editor_state_observer.vue
@@ -1,5 +1,11 @@
<script>
import { debounce } from 'lodash';
+import {
+ LOADING_CONTENT_EVENT,
+ LOADING_SUCCESS_EVENT,
+ LOADING_ERROR_EVENT,
+ ALERT_EVENT,
+} from '../constants';
export const tiptapToComponentMap = {
update: 'docUpdate',
@@ -7,30 +13,48 @@ export const tiptapToComponentMap = {
transaction: 'transaction',
focus: 'focus',
blur: 'blur',
- alert: 'alert',
};
+export const eventHubEvents = [
+ ALERT_EVENT,
+ LOADING_CONTENT_EVENT,
+ LOADING_SUCCESS_EVENT,
+ LOADING_ERROR_EVENT,
+];
+
const getComponentEventName = (tiptapEventName) => tiptapToComponentMap[tiptapEventName];
export default {
- inject: ['tiptapEditor'],
+ inject: ['tiptapEditor', 'eventHub'],
created() {
this.disposables = [];
Object.keys(tiptapToComponentMap).forEach((tiptapEvent) => {
- const eventHandler = debounce((params) => this.handleTipTapEvent(tiptapEvent, params), 100);
+ const eventHandler = debounce(
+ (params) => this.bubbleEvent(getComponentEventName(tiptapEvent), params),
+ 100,
+ );
this.tiptapEditor?.on(tiptapEvent, eventHandler);
this.disposables.push(() => this.tiptapEditor?.off(tiptapEvent, eventHandler));
});
+
+ eventHubEvents.forEach((event) => {
+ const handler = (...params) => {
+ this.bubbleEvent(event, ...params);
+ };
+
+ this.eventHub.$on(event, handler);
+ this.disposables.push(() => this.eventHub?.$off(event, handler));
+ });
},
beforeDestroy() {
this.disposables.forEach((dispose) => dispose());
},
methods: {
- handleTipTapEvent(tiptapEvent, params) {
- this.$emit(getComponentEventName(tiptapEvent), params);
+ bubbleEvent(eventHubEvent, params) {
+ this.$emit(eventHubEvent, params);
},
},
render() {
diff --git a/app/assets/javascripts/content_editor/constants.js b/app/assets/javascripts/content_editor/constants.js
index 5e56078df01..a39a243ec6b 100644
--- a/app/assets/javascripts/content_editor/constants.js
+++ b/app/assets/javascripts/content_editor/constants.js
@@ -42,9 +42,10 @@ export const TEXT_STYLE_DROPDOWN_ITEMS = [
},
];
-export const LOADING_CONTENT_EVENT = 'loadingContent';
+export const LOADING_CONTENT_EVENT = 'loading';
export const LOADING_SUCCESS_EVENT = 'loadingSuccess';
export const LOADING_ERROR_EVENT = 'loadingError';
+export const ALERT_EVENT = 'alert';
export const PARSE_HTML_PRIORITY_LOWEST = 1;
export const PARSE_HTML_PRIORITY_DEFAULT = 50;
@@ -56,3 +57,4 @@ export const EXTENSION_PRIORITY_LOWER = 75;
* https://tiptap.dev/guide/custom-extensions/#priority
*/
export const EXTENSION_PRIORITY_DEFAULT = 100;
+export const EXTENSION_PRIORITY_HIGHEST = 200;
diff --git a/app/assets/javascripts/content_editor/extensions/attachment.js b/app/assets/javascripts/content_editor/extensions/attachment.js
index 72df1d071d1..9634730f637 100644
--- a/app/assets/javascripts/content_editor/extensions/attachment.js
+++ b/app/assets/javascripts/content_editor/extensions/attachment.js
@@ -9,15 +9,22 @@ export default Extension.create({
return {
uploadsPath: null,
renderMarkdown: null,
+ eventHub: null,
};
},
addCommands() {
return {
uploadAttachment: ({ file }) => () => {
- const { uploadsPath, renderMarkdown } = this.options;
+ const { uploadsPath, renderMarkdown, eventHub } = this.options;
- return handleFileEvent({ file, uploadsPath, renderMarkdown, editor: this.editor });
+ return handleFileEvent({
+ file,
+ uploadsPath,
+ renderMarkdown,
+ editor: this.editor,
+ eventHub,
+ });
},
};
},
@@ -29,23 +36,25 @@ export default Extension.create({
key: new PluginKey('attachment'),
props: {
handlePaste: (_, event) => {
- const { uploadsPath, renderMarkdown } = this.options;
+ const { uploadsPath, renderMarkdown, eventHub } = this.options;
return handleFileEvent({
editor,
file: event.clipboardData.files[0],
uploadsPath,
renderMarkdown,
+ eventHub,
});
},
handleDrop: (_, event) => {
- const { uploadsPath, renderMarkdown } = this.options;
+ const { uploadsPath, renderMarkdown, eventHub } = this.options;
return handleFileEvent({
editor,
file: event.dataTransfer.files[0],
uploadsPath,
renderMarkdown,
+ eventHub,
});
},
},
diff --git a/app/assets/javascripts/content_editor/services/content_editor.js b/app/assets/javascripts/content_editor/services/content_editor.js
index a387322bff7..1fc05c434ca 100644
--- a/app/assets/javascripts/content_editor/services/content_editor.js
+++ b/app/assets/javascripts/content_editor/services/content_editor.js
@@ -1,17 +1,20 @@
-import eventHubFactory from '~/helpers/event_hub_factory';
import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } from '../constants';
/* eslint-disable no-underscore-dangle */
export class ContentEditor {
- constructor({ tiptapEditor, serializer }) {
+ constructor({ tiptapEditor, serializer, eventHub }) {
this._tiptapEditor = tiptapEditor;
this._serializer = serializer;
- this._eventHub = eventHubFactory();
+ this._eventHub = eventHub;
}
get tiptapEditor() {
return this._tiptapEditor;
}
+ get eventHub() {
+ return this._eventHub;
+ }
+
get empty() {
const doc = this.tiptapEditor?.state.doc;
@@ -23,39 +26,23 @@ export class ContentEditor {
this.tiptapEditor.destroy();
}
- once(type, handler) {
- this._eventHub.$once(type, handler);
- }
-
- on(type, handler) {
- this._eventHub.$on(type, handler);
- }
-
- emit(type, params = {}) {
- this._eventHub.$emit(type, params);
- }
-
- off(type, handler) {
- this._eventHub.$off(type, handler);
- }
-
disposeAllEvents() {
this._eventHub.dispose();
}
async setSerializedContent(serializedContent) {
- const { _tiptapEditor: editor, _serializer: serializer } = this;
+ const { _tiptapEditor: editor, _serializer: serializer, _eventHub: eventHub } = this;
try {
- this._eventHub.$emit(LOADING_CONTENT_EVENT);
+ eventHub.$emit(LOADING_CONTENT_EVENT);
const document = await serializer.deserialize({
schema: editor.schema,
content: serializedContent,
});
editor.commands.setContent(document);
- this._eventHub.$emit(LOADING_SUCCESS_EVENT);
+ eventHub.$emit(LOADING_SUCCESS_EVENT);
} catch (e) {
- this._eventHub.$emit(LOADING_ERROR_EVENT, e);
+ eventHub.$emit(LOADING_ERROR_EVENT, e);
throw e;
}
}
diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js
index f451357e211..e26fc5ca8c9 100644
--- a/app/assets/javascripts/content_editor/services/create_content_editor.js
+++ b/app/assets/javascripts/content_editor/services/create_content_editor.js
@@ -1,5 +1,6 @@
import { Editor } from '@tiptap/vue-2';
import { isFunction } from 'lodash';
+import eventHubFactory from '~/helpers/event_hub_factory';
import { PROVIDE_SERIALIZER_OR_RENDERER_ERROR } from '../constants';
import Attachment from '../extensions/attachment';
import Audio from '../extensions/audio';
@@ -78,8 +79,10 @@ export const createContentEditor = ({
throw new Error(PROVIDE_SERIALIZER_OR_RENDERER_ERROR);
}
+ const eventHub = eventHubFactory();
+
const builtInContentEditorExtensions = [
- Attachment.configure({ uploadsPath, renderMarkdown }),
+ Attachment.configure({ uploadsPath, renderMarkdown, eventHub }),
Audio,
Blockquote,
Bold,
@@ -137,5 +140,5 @@ export const createContentEditor = ({
const tiptapEditor = createTiptapEditor({ extensions: trackedExtensions, ...tiptapOptions });
const serializer = createMarkdownSerializer({ render: renderMarkdown, serializerConfig });
- return new ContentEditor({ tiptapEditor, serializer });
+ return new ContentEditor({ tiptapEditor, serializer, eventHub });
};
diff --git a/app/assets/javascripts/content_editor/services/upload_helpers.js b/app/assets/javascripts/content_editor/services/upload_helpers.js
index f5bf2742748..33e9f455ef8 100644
--- a/app/assets/javascripts/content_editor/services/upload_helpers.js
+++ b/app/assets/javascripts/content_editor/services/upload_helpers.js
@@ -49,7 +49,7 @@ export const uploadFile = async ({ uploadsPath, renderMarkdown, file }) => {
return extractAttachmentLinkUrl(rendered);
};
-const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown }) => {
+const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
const encodedSrc = await readFileAsDataURL(file);
const { view } = editor;
@@ -72,14 +72,14 @@ const uploadImage = async ({ editor, file, uploadsPath, renderMarkdown }) => {
);
} catch (e) {
editor.commands.deleteRange({ from: position, to: position + 1 });
- editor.emit('alert', {
+ eventHub.$emit('alert', {
message: __('An error occurred while uploading the image. Please try again.'),
variant: 'danger',
});
}
};
-const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown }) => {
+const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
await Promise.resolve();
const { view } = editor;
@@ -103,23 +103,23 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown }) =
);
} catch (e) {
editor.commands.deleteRange({ from, to: from + 1 });
- editor.emit('alert', {
+ eventHub.$emit('alert', {
message: __('An error occurred while uploading the file. Please try again.'),
variant: 'danger',
});
}
};
-export const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown }) => {
+export const handleFileEvent = ({ editor, file, uploadsPath, renderMarkdown, eventHub }) => {
if (!file) return false;
if (acceptedMimes.image.includes(file?.type)) {
- uploadImage({ editor, file, uploadsPath, renderMarkdown });
+ uploadImage({ editor, file, uploadsPath, renderMarkdown, eventHub });
return true;
}
- uploadAttachment({ editor, file, uploadsPath, renderMarkdown });
+ uploadAttachment({ editor, file, uploadsPath, renderMarkdown, eventHub });
return true;
};