summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/content_editor/extensions
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-11-18 13:16:36 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-11-18 13:16:36 +0000
commit311b0269b4eb9839fa63f80c8d7a58f32b8138a0 (patch)
tree07e7870bca8aed6d61fdcc810731c50d2c40af47 /app/assets/javascripts/content_editor/extensions
parent27909cef6c4170ed9205afa7426b8d3de47cbb0c (diff)
downloadgitlab-ce-056d9df33865790d1b3c10f77912e00f61193000.tar.gz
Add latest changes from gitlab-org/gitlab@14-5-stable-eev14.5.0-rc42
Diffstat (limited to 'app/assets/javascripts/content_editor/extensions')
-rw-r--r--app/assets/javascripts/content_editor/extensions/blockquote.js12
-rw-r--r--app/assets/javascripts/content_editor/extensions/description_list.js9
-rw-r--r--app/assets/javascripts/content_editor/extensions/details.js9
-rw-r--r--app/assets/javascripts/content_editor/extensions/emoji.js36
-rw-r--r--app/assets/javascripts/content_editor/extensions/frontmatter.js3
-rw-r--r--app/assets/javascripts/content_editor/extensions/horizontal_rule.js6
-rw-r--r--app/assets/javascripts/content_editor/extensions/html_marks.js8
-rw-r--r--app/assets/javascripts/content_editor/extensions/inline_diff.js18
-rw-r--r--app/assets/javascripts/content_editor/extensions/link.js18
-rw-r--r--app/assets/javascripts/content_editor/extensions/math_inline.js6
-rw-r--r--app/assets/javascripts/content_editor/extensions/subscript.js8
-rw-r--r--app/assets/javascripts/content_editor/extensions/superscript.js8
-rw-r--r--app/assets/javascripts/content_editor/extensions/table.js43
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_cell.js3
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_header.js3
-rw-r--r--app/assets/javascripts/content_editor/extensions/table_of_contents.js21
-rw-r--r--app/assets/javascripts/content_editor/extensions/word_break.js6
17 files changed, 149 insertions, 68 deletions
diff --git a/app/assets/javascripts/content_editor/extensions/blockquote.js b/app/assets/javascripts/content_editor/extensions/blockquote.js
index 4512ead44bc..5632bc28592 100644
--- a/app/assets/javascripts/content_editor/extensions/blockquote.js
+++ b/app/assets/javascripts/content_editor/extensions/blockquote.js
@@ -1,10 +1,8 @@
import { Blockquote } from '@tiptap/extension-blockquote';
-import { wrappingInputRule } from 'prosemirror-inputrules';
+import { wrappingInputRule } from '@tiptap/core';
import { getParents } from '~/lib/utils/dom_utils';
import { getMarkdownSource } from '../services/markdown_sourcemap';
-export const multilineInputRegex = /^\s*>>>\s$/gm;
-
export default Blockquote.extend({
addAttributes() {
return {
@@ -25,9 +23,15 @@ export default Blockquote.extend({
},
addInputRules() {
+ const multilineInputRegex = /^\s*>>>\s$/gm;
+
return [
...this.parent?.(),
- wrappingInputRule(multilineInputRegex, this.type, () => ({ multiline: true })),
+ wrappingInputRule({
+ find: multilineInputRegex,
+ type: this.type,
+ getAttributes: () => ({ multiline: true }),
+ }),
];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/description_list.js b/app/assets/javascripts/content_editor/extensions/description_list.js
index a516dfad2b8..8f5b145cfa3 100644
--- a/app/assets/javascripts/content_editor/extensions/description_list.js
+++ b/app/assets/javascripts/content_editor/extensions/description_list.js
@@ -1,7 +1,4 @@
-import { Node, mergeAttributes } from '@tiptap/core';
-import { wrappingInputRule } from 'prosemirror-inputrules';
-
-export const inputRegex = /^\s*(<dl>)$/;
+import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core';
export default Node.create({
name: 'descriptionList',
@@ -18,6 +15,8 @@ export default Node.create({
},
addInputRules() {
- return [wrappingInputRule(inputRegex, this.type)];
+ const inputRegex = /^\s*(<dl>)$/;
+
+ return [wrappingInputRule({ find: inputRegex, type: this.type })];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/details.js b/app/assets/javascripts/content_editor/extensions/details.js
index e3d54ed01fd..46c906d45b1 100644
--- a/app/assets/javascripts/content_editor/extensions/details.js
+++ b/app/assets/javascripts/content_editor/extensions/details.js
@@ -1,10 +1,7 @@
-import { Node } from '@tiptap/core';
+import { Node, wrappingInputRule } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
-import { wrappingInputRule } from 'prosemirror-inputrules';
import DetailsWrapper from '../components/wrappers/details.vue';
-export const inputRegex = /^\s*(<details>)$/;
-
export default Node.create({
name: 'details',
content: 'detailsContent+',
@@ -24,7 +21,9 @@ export default Node.create({
},
addInputRules() {
- return [wrappingInputRule(inputRegex, this.type)];
+ const inputRegex = /^\s*(<details>)$/;
+
+ return [wrappingInputRule({ find: inputRegex, type: this.type })];
},
addCommands() {
diff --git a/app/assets/javascripts/content_editor/extensions/emoji.js b/app/assets/javascripts/content_editor/extensions/emoji.js
index de608c3aaa2..7f8b5da5f46 100644
--- a/app/assets/javascripts/content_editor/extensions/emoji.js
+++ b/app/assets/javascripts/content_editor/extensions/emoji.js
@@ -1,9 +1,6 @@
-import { Node } from '@tiptap/core';
-import { InputRule } from 'prosemirror-inputrules';
+import { Node, InputRule } from '@tiptap/core';
import { initEmojiMap, getAllEmoji } from '~/emoji';
-export const emojiInputRegex = /(?:^|\s)((?::)((?:\w+))(?::))$/;
-
export default Node.create({
name: 'emoji',
@@ -54,23 +51,28 @@ export default Node.create({
},
addInputRules() {
+ const emojiInputRegex = /(?:^|\s)(:(\w+):)$/;
+
return [
- new InputRule(emojiInputRegex, (state, match, start, end) => {
- const [, , name] = match;
- const emojis = getAllEmoji();
- const emoji = emojis[name];
- const { tr } = state;
+ new InputRule({
+ find: emojiInputRegex,
+ handler: ({ state, range: { from, to }, match }) => {
+ const [, , name] = match;
+ const emojis = getAllEmoji();
+ const emoji = emojis[name];
+ const { tr } = state;
- if (emoji) {
- tr.replaceWith(start, end, [
- state.schema.text(' '),
- this.type.create({ name, moji: emoji.e, unicodeVersion: emoji.u, title: emoji.d }),
- ]);
+ if (emoji) {
+ tr.replaceWith(from, to, [
+ state.schema.text(' '),
+ this.type.create({ name, moji: emoji.e, unicodeVersion: emoji.u, title: emoji.d }),
+ ]);
- return tr;
- }
+ return tr;
+ }
- return null;
+ return null;
+ },
}),
];
},
diff --git a/app/assets/javascripts/content_editor/extensions/frontmatter.js b/app/assets/javascripts/content_editor/extensions/frontmatter.js
index 64c84fe046b..c09c10bc524 100644
--- a/app/assets/javascripts/content_editor/extensions/frontmatter.js
+++ b/app/assets/javascripts/content_editor/extensions/frontmatter.js
@@ -17,4 +17,7 @@ export default CodeBlockHighlight.extend({
addNodeView() {
return new VueNodeViewRenderer(FrontmatterWrapper);
},
+ addInputRules() {
+ return [];
+ },
});
diff --git a/app/assets/javascripts/content_editor/extensions/horizontal_rule.js b/app/assets/javascripts/content_editor/extensions/horizontal_rule.js
index c8ec45d835c..c4f31e5f981 100644
--- a/app/assets/javascripts/content_editor/extensions/horizontal_rule.js
+++ b/app/assets/javascripts/content_editor/extensions/horizontal_rule.js
@@ -1,10 +1,10 @@
import { nodeInputRule } from '@tiptap/core';
import { HorizontalRule } from '@tiptap/extension-horizontal-rule';
-export const hrInputRuleRegExp = /^---$/;
-
export default HorizontalRule.extend({
addInputRules() {
- return [nodeInputRule(hrInputRuleRegExp, this.type)];
+ const hrInputRuleRegExp = /^---$/;
+
+ return [nodeInputRule({ find: hrInputRuleRegExp, type: this.type })];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/html_marks.js b/app/assets/javascripts/content_editor/extensions/html_marks.js
index 54adb9efa0c..3abf0e3eee2 100644
--- a/app/assets/javascripts/content_editor/extensions/html_marks.js
+++ b/app/assets/javascripts/content_editor/extensions/html_marks.js
@@ -60,7 +60,13 @@ export default marks.map((name) =>
},
addInputRules() {
- return [markInputRule(markInputRegex(name), this.type, extractMarkAttributesFromMatch)];
+ return [
+ markInputRule({
+ find: markInputRegex(name),
+ type: this.type,
+ getAttributes: extractMarkAttributesFromMatch,
+ }),
+ ];
},
}),
);
diff --git a/app/assets/javascripts/content_editor/extensions/inline_diff.js b/app/assets/javascripts/content_editor/extensions/inline_diff.js
index 3bd328958df..22bb1ac072e 100644
--- a/app/assets/javascripts/content_editor/extensions/inline_diff.js
+++ b/app/assets/javascripts/content_editor/extensions/inline_diff.js
@@ -1,8 +1,5 @@
import { Mark, markInputRule, mergeAttributes } from '@tiptap/core';
-export const inputRegexAddition = /(\{\+(.+?)\+\})$/gm;
-export const inputRegexDeletion = /(\{-(.+?)-\})$/gm;
-
export default Mark.create({
name: 'inlineDiff',
@@ -38,9 +35,20 @@ export default Mark.create({
},
addInputRules() {
+ const inputRegexAddition = /(\{\+(.+?)\+\})$/gm;
+ const inputRegexDeletion = /(\{-(.+?)-\})$/gm;
+
return [
- markInputRule(inputRegexAddition, this.type, () => ({ type: 'addition' })),
- markInputRule(inputRegexDeletion, this.type, () => ({ type: 'deletion' })),
+ markInputRule({
+ find: inputRegexAddition,
+ type: this.type,
+ getAttributes: () => ({ type: 'addition' }),
+ }),
+ markInputRule({
+ find: inputRegexDeletion,
+ type: this.type,
+ getAttributes: () => ({ type: 'deletion' }),
+ }),
];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/link.js b/app/assets/javascripts/content_editor/extensions/link.js
index fc0f38e6935..27bc05dce6f 100644
--- a/app/assets/javascripts/content_editor/extensions/link.js
+++ b/app/assets/javascripts/content_editor/extensions/link.js
@@ -1,9 +1,6 @@
import { markInputRule } from '@tiptap/core';
import { Link } from '@tiptap/extension-link';
-export const markdownLinkSyntaxInputRuleRegExp = /(?:^|\s)\[([\w|\s|-]+)\]\((?<href>.+?)\)$/gm;
-export const urlSyntaxRegExp = /(?:^|\s)(?<href>(?:https?:\/\/|www\.)[\S]+)(?:\s|\n)$/gim;
-
const extractHrefFromMatch = (match) => {
return { href: match.groups.href };
};
@@ -26,9 +23,20 @@ export default Link.extend({
openOnClick: false,
},
addInputRules() {
+ const markdownLinkSyntaxInputRuleRegExp = /(?:^|\s)\[([\w|\s|-]+)\]\((?<href>.+?)\)$/gm;
+ const urlSyntaxRegExp = /(?:^|\s)(?<href>(?:https?:\/\/|www\.)[\S]+)(?:\s|\n)$/gim;
+
return [
- markInputRule(markdownLinkSyntaxInputRuleRegExp, this.type, extractHrefFromMarkdownLink),
- markInputRule(urlSyntaxRegExp, this.type, extractHrefFromMatch),
+ markInputRule({
+ find: markdownLinkSyntaxInputRuleRegExp,
+ type: this.type,
+ getAttributes: extractHrefFromMarkdownLink,
+ }),
+ markInputRule({
+ find: urlSyntaxRegExp,
+ type: this.type,
+ getAttributes: extractHrefFromMatch,
+ }),
];
},
addAttributes() {
diff --git a/app/assets/javascripts/content_editor/extensions/math_inline.js b/app/assets/javascripts/content_editor/extensions/math_inline.js
index 60f5288dcf6..4844f6feb29 100644
--- a/app/assets/javascripts/content_editor/extensions/math_inline.js
+++ b/app/assets/javascripts/content_editor/extensions/math_inline.js
@@ -2,8 +2,6 @@ import { Mark, markInputRule } from '@tiptap/core';
import { __ } from '~/locale';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
-export const inputRegex = /(?:^|\s)\$`([^`]+)`\$$/gm;
-
export default Mark.create({
name: 'mathInline',
@@ -30,6 +28,8 @@ export default Mark.create({
},
addInputRules() {
- return [markInputRule(inputRegex, this.type)];
+ const inputRegex = /(?:^|\s)\$`([^`]+)`\$$/gm;
+
+ return [markInputRule({ find: inputRegex, type: this.type })];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/subscript.js b/app/assets/javascripts/content_editor/extensions/subscript.js
index d0766f42308..a8c087e8bf0 100644
--- a/app/assets/javascripts/content_editor/extensions/subscript.js
+++ b/app/assets/javascripts/content_editor/extensions/subscript.js
@@ -4,6 +4,12 @@ import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark
export default Subscript.extend({
addInputRules() {
- return [markInputRule(markInputRegex('sub'), this.type, extractMarkAttributesFromMatch)];
+ return [
+ markInputRule({
+ find: markInputRegex('sub'),
+ type: this.type,
+ getAttributes: extractMarkAttributesFromMatch,
+ }),
+ ];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/superscript.js b/app/assets/javascripts/content_editor/extensions/superscript.js
index 6cd814977ea..b86906f01f2 100644
--- a/app/assets/javascripts/content_editor/extensions/superscript.js
+++ b/app/assets/javascripts/content_editor/extensions/superscript.js
@@ -4,6 +4,12 @@ import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark
export default Superscript.extend({
addInputRules() {
- return [markInputRule(markInputRegex('sup'), this.type, extractMarkAttributesFromMatch)];
+ return [
+ markInputRule({
+ find: markInputRegex('sup'),
+ type: this.type,
+ getAttributes: extractMarkAttributesFromMatch,
+ }),
+ ];
},
});
diff --git a/app/assets/javascripts/content_editor/extensions/table.js b/app/assets/javascripts/content_editor/extensions/table.js
index 0f0477cba2e..004bb8b815c 100644
--- a/app/assets/javascripts/content_editor/extensions/table.js
+++ b/app/assets/javascripts/content_editor/extensions/table.js
@@ -1 +1,42 @@
-export { Table as default } from '@tiptap/extension-table';
+import { Table } from '@tiptap/extension-table';
+import { debounce } from 'lodash';
+import { __ } from '~/locale';
+import { getMarkdownSource } from '../services/markdown_sourcemap';
+import { shouldRenderHTMLTable } from '../services/serialization_helpers';
+
+let alertShown = false;
+const onUpdate = debounce((editor) => {
+ if (alertShown) return;
+
+ editor.state.doc.descendants((node) => {
+ if (node.type.name === 'table' && node.attrs.isMarkdown && shouldRenderHTMLTable(node)) {
+ editor.emit('alert', {
+ message: __(
+ 'The content editor may change the markdown formatting style of the document, which may not match your original markdown style.',
+ ),
+ variant: 'warning',
+ });
+
+ alertShown = true;
+
+ return false;
+ }
+
+ return true;
+ });
+}, 1000);
+
+export default Table.extend({
+ addAttributes() {
+ return {
+ isMarkdown: {
+ default: null,
+ parseHTML: (element) => Boolean(getMarkdownSource(element)),
+ },
+ };
+ },
+
+ onUpdate({ editor }) {
+ onUpdate(editor);
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/table_cell.js b/app/assets/javascripts/content_editor/extensions/table_cell.js
index befc33e669f..9f437ce066c 100644
--- a/app/assets/javascripts/content_editor/extensions/table_cell.js
+++ b/app/assets/javascripts/content_editor/extensions/table_cell.js
@@ -1,10 +1,9 @@
import { TableCell } from '@tiptap/extension-table-cell';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import TableCellBodyWrapper from '../components/wrappers/table_cell_body.vue';
-import { isBlockTablesFeatureEnabled } from '../services/feature_flags';
export default TableCell.extend({
- content: isBlockTablesFeatureEnabled() ? 'block+' : 'inline*',
+ content: 'block+',
addNodeView() {
return VueNodeViewRenderer(TableCellBodyWrapper);
diff --git a/app/assets/javascripts/content_editor/extensions/table_header.js b/app/assets/javascripts/content_editor/extensions/table_header.js
index 829b06fc14b..045fd03199b 100644
--- a/app/assets/javascripts/content_editor/extensions/table_header.js
+++ b/app/assets/javascripts/content_editor/extensions/table_header.js
@@ -1,10 +1,9 @@
import { TableHeader } from '@tiptap/extension-table-header';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import TableCellHeaderWrapper from '../components/wrappers/table_cell_header.vue';
-import { isBlockTablesFeatureEnabled } from '../services/feature_flags';
export default TableHeader.extend({
- content: isBlockTablesFeatureEnabled() ? 'block+' : 'inline*',
+ content: 'block+',
addNodeView() {
return VueNodeViewRenderer(TableCellHeaderWrapper);
},
diff --git a/app/assets/javascripts/content_editor/extensions/table_of_contents.js b/app/assets/javascripts/content_editor/extensions/table_of_contents.js
index 9e31158837e..a8882f9ede4 100644
--- a/app/assets/javascripts/content_editor/extensions/table_of_contents.js
+++ b/app/assets/javascripts/content_editor/extensions/table_of_contents.js
@@ -1,10 +1,7 @@
-import { Node } from '@tiptap/core';
-import { InputRule } from 'prosemirror-inputrules';
+import { Node, InputRule } from '@tiptap/core';
import { s__ } from '~/locale';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
-export const inputRuleRegExps = [/^\[\[_TOC_\]\]$/, /^\[TOC\]$/];
-
export default Node.create({
name: 'tableOfContents',
@@ -34,17 +31,21 @@ export default Node.create({
addInputRules() {
const { type } = this;
+ const inputRuleRegExps = [/^\[\[_TOC_\]\]$/, /^\[TOC\]$/];
return inputRuleRegExps.map(
(regex) =>
- new InputRule(regex, (state, match, start, end) => {
- const { tr } = state;
+ new InputRule({
+ find: regex,
+ handler: ({ state, range: { from, to }, match }) => {
+ const { tr } = state;
- if (match) {
- tr.replaceWith(start - 1, end, type.create());
- }
+ if (match) {
+ tr.replaceWith(from - 1, to, type.create());
+ }
- return tr;
+ return tr;
+ },
}),
);
},
diff --git a/app/assets/javascripts/content_editor/extensions/word_break.js b/app/assets/javascripts/content_editor/extensions/word_break.js
index 93b42466850..fa7e02f8cc8 100644
--- a/app/assets/javascripts/content_editor/extensions/word_break.js
+++ b/app/assets/javascripts/content_editor/extensions/word_break.js
@@ -1,7 +1,5 @@
import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core';
-export const inputRegex = /^<wbr>$/;
-
export default Node.create({
name: 'wordBreak',
inline: true,
@@ -24,6 +22,8 @@ export default Node.create({
},
addInputRules() {
- return [nodeInputRule(inputRegex, this.type)];
+ const inputRegex = /^<wbr>$/;
+
+ return [nodeInputRule({ find: inputRegex, type: this.type })];
},
});