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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-04 15:09:38 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-04 15:09:38 +0300
commitf4c0eed6e59f393302ccc38468063cb5c4698f79 (patch)
treec7b97ac78c073ccc052d611abdd294b6c879a996 /app
parent88741f7df96173e80dea5e67ea9946e766935849 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/content_editor/extensions/code_block_highlight.js9
-rw-r--r--app/assets/javascripts/content_editor/extensions/copy_paste.js30
-rw-r--r--app/assets/javascripts/content_editor/extensions/html_marks.js3
-rw-r--r--app/assets/javascripts/content_editor/extensions/loading.js56
4 files changed, 73 insertions, 25 deletions
diff --git a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
index 8917417e55e..da5ac7eb158 100644
--- a/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
+++ b/app/assets/javascripts/content_editor/extensions/code_block_highlight.js
@@ -81,4 +81,13 @@ export default CodeBlockLowlight.extend({
addNodeView() {
return new VueNodeViewRenderer(CodeBlockWrapper);
},
+
+ addProseMirrorPlugins() {
+ const parentPlugins = this.parent?.() ?? [];
+ // We don't want TipTap's VSCode paste plugin to be loaded since
+ // it conflicts with our CopyPaste plugin.
+ const i = parentPlugins.findIndex((plugin) => plugin.key.includes('VSCode'));
+ if (i >= 0) parentPlugins.splice(i, 1);
+ return parentPlugins;
+ },
}).configure({ lowlight });
diff --git a/app/assets/javascripts/content_editor/extensions/copy_paste.js b/app/assets/javascripts/content_editor/extensions/copy_paste.js
index ab9e5619600..3ae5ef0025d 100644
--- a/app/assets/javascripts/content_editor/extensions/copy_paste.js
+++ b/app/assets/javascripts/content_editor/extensions/copy_paste.js
@@ -11,6 +11,7 @@ import CodeBlockHighlight from './code_block_highlight';
import CodeSuggestion from './code_suggestion';
import Diagram from './diagram';
import Frontmatter from './frontmatter';
+import { loadingPlugin, findLoader } from './loading';
const TEXT_FORMAT = 'text/plain';
const GFM_FORMAT = 'text/x-gfm';
@@ -31,21 +32,6 @@ function parseHTML(schema, html) {
return { document: ProseMirrorDOMParser.fromSchema(schema).parse(body) };
}
-const findLoader = (editor, loaderId) => {
- let position;
-
- editor.view.state.doc.descendants((descendant, pos) => {
- if (descendant.type.name === 'loading' && descendant.attrs.id === loaderId) {
- position = pos;
- return false;
- }
-
- return true;
- });
-
- return position;
-};
-
export default Extension.create({
name: 'copyPaste',
priority: EXTENSION_PRIORITY_HIGHEST,
@@ -74,13 +60,20 @@ export default Extension.create({
Promise.resolve()
.then(() => {
- editor.commands.insertContent({ type: 'loading', attrs: { id: loaderId } });
+ editor
+ .chain()
+ .deleteSelection()
+ .setMeta(loadingPlugin, {
+ add: { loaderId, pos: editor.state.selection.from },
+ })
+ .run();
+
return promise;
})
.then(async ({ document }) => {
if (!document) return;
- const pos = findLoader(editor, loaderId);
+ const pos = findLoader(editor.state, loaderId);
if (!pos) return;
const { firstChild, childCount } = document.content;
@@ -91,7 +84,7 @@ export default Extension.create({
editor
.chain()
- .deleteRange({ from: pos, to: pos + 1 })
+ .setMeta(loadingPlugin, { remove: { loaderId } })
.insertContentAt(pos, toPaste.toJSON(), {
updateSelection: false,
})
@@ -114,6 +107,7 @@ export default Extension.create({
const handleCutAndCopy = (view, event) => {
const slice = view.state.selection.content();
const gfmContent = this.options.serializer.serialize({ doc: slice.content });
+
const documentFragment = DOMSerializer.fromSchema(view.state.schema).serializeFragment(
slice.content,
);
diff --git a/app/assets/javascripts/content_editor/extensions/html_marks.js b/app/assets/javascripts/content_editor/extensions/html_marks.js
index 79fc0eea2c7..58fa2655e25 100644
--- a/app/assets/javascripts/content_editor/extensions/html_marks.js
+++ b/app/assets/javascripts/content_editor/extensions/html_marks.js
@@ -50,7 +50,8 @@ export default marks.map((name) =>
},
parseHTML() {
- return [{ tag: name, priority: PARSE_HTML_PRIORITY_LOWEST }];
+ const tag = name === 'span' ? `${name}:not([data-escaped-char])` : name;
+ return [{ tag, priority: PARSE_HTML_PRIORITY_LOWEST }];
},
renderHTML({ HTMLAttributes }) {
diff --git a/app/assets/javascripts/content_editor/extensions/loading.js b/app/assets/javascripts/content_editor/extensions/loading.js
index 0115fb10d5d..942ac650925 100644
--- a/app/assets/javascripts/content_editor/extensions/loading.js
+++ b/app/assets/javascripts/content_editor/extensions/loading.js
@@ -1,4 +1,52 @@
import { Node } from '@tiptap/core';
+import { Decoration, DecorationSet } from '@tiptap/pm/view';
+import { Plugin } from '@tiptap/pm/state';
+
+const createDotsLoader = () => {
+ const root = document.createElement('span');
+ root.classList.add('gl-display-inline-flex', 'gl-align-items-center');
+ root.innerHTML = '<span class="gl-dots-loader gl-mx-2"><span></span></span>';
+ return root;
+};
+
+export const loadingPlugin = new Plugin({
+ state: {
+ init() {
+ return DecorationSet.empty;
+ },
+ apply(tr, set) {
+ let transformedSet = set.map(tr.mapping, tr.doc);
+ const action = tr.getMeta(this);
+
+ if (action?.add) {
+ const deco = Decoration.widget(action.add.pos, createDotsLoader(), {
+ id: action.add.loaderId,
+ side: -1,
+ });
+ transformedSet = transformedSet.add(tr.doc, [deco]);
+ } else if (action?.remove) {
+ transformedSet = transformedSet.remove(
+ transformedSet.find(null, null, (spec) => spec.id === action.remove.loaderId),
+ );
+ }
+ return transformedSet;
+ },
+ },
+ props: {
+ decorations(state) {
+ return this.getState(state);
+ },
+ },
+});
+
+export const findLoader = (state, loaderId) => {
+ const decos = loadingPlugin.getState(state);
+ const found = decos.find(null, null, (spec) => spec.id === loaderId);
+
+ return found.length ? found[0].from : null;
+};
+
+export const findAllLoaders = (state) => loadingPlugin.getState(state).find();
export default Node.create({
name: 'loading',
@@ -13,11 +61,7 @@ export default Node.create({
};
},
- renderHTML() {
- return [
- 'span',
- { class: 'gl-display-inline-flex gl-align-items-center' },
- ['span', { class: 'gl-dots-loader gl-mx-2' }, ['span']],
- ];
+ addProseMirrorPlugins() {
+ return [loadingPlugin];
},
});