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:
Diffstat (limited to 'app/assets/javascripts/content_editor/extensions/reference.js')
-rw-r--r--app/assets/javascripts/content_editor/extensions/reference.js84
1 files changed, 83 insertions, 1 deletions
diff --git a/app/assets/javascripts/content_editor/extensions/reference.js b/app/assets/javascripts/content_editor/extensions/reference.js
index b56aa8596a0..ef69b9bbda6 100644
--- a/app/assets/javascripts/content_editor/extensions/reference.js
+++ b/app/assets/javascripts/content_editor/extensions/reference.js
@@ -1,4 +1,4 @@
-import { Node } from '@tiptap/core';
+import { Node, InputRule } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import ReferenceWrapper from '../components/wrappers/reference.vue';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
@@ -8,6 +8,21 @@ const getAnchor = (element) => {
return element.querySelector('a');
};
+const findReference = (editor, reference) => {
+ let position;
+
+ editor.view.state.doc.descendants((descendant, pos) => {
+ if (descendant.isText && descendant.text.includes(reference)) {
+ position = pos + descendant.text.indexOf(reference);
+ return false;
+ }
+
+ return true;
+ });
+
+ return position;
+};
+
export default Node.create({
name: 'reference',
@@ -17,6 +32,12 @@ export default Node.create({
atom: true,
+ addOptions() {
+ return {
+ assetResolver: null,
+ };
+ },
+
addAttributes() {
return {
className: {
@@ -42,6 +63,54 @@ export default Node.create({
};
},
+ addInputRules() {
+ const { editor } = this;
+ const { assetResolver } = this.options;
+ const referenceInputRegex = /(?:^|\s)([\w/]*([!&#])\d+(\+?s?))(?:\s|\n)/m;
+ const referenceTypes = {
+ '#': 'issue',
+ '!': 'merge_request',
+ '&': 'epic',
+ };
+
+ return [
+ new InputRule({
+ find: referenceInputRegex,
+ handler: async ({ match }) => {
+ const [, referenceId, referenceSymbol, expansionType] = match;
+ const referenceType = referenceTypes[referenceSymbol];
+
+ const {
+ href,
+ text,
+ expandedText,
+ fullyExpandedText,
+ } = await assetResolver.resolveReference(referenceId);
+
+ if (!text) return;
+
+ let referenceText = text;
+ if (expansionType === '+') referenceText = expandedText;
+ if (expansionType === '+s') referenceText = fullyExpandedText;
+
+ const position = findReference(editor, referenceId);
+ if (!position) return;
+
+ editor.view.dispatch(
+ editor.state.tr.replaceWith(position, position + referenceId.length, [
+ this.type.create({
+ referenceType,
+ originalText: referenceId,
+ href,
+ text: referenceText,
+ }),
+ ]),
+ );
+ },
+ }),
+ ];
+ },
+
parseHTML() {
return [
{
@@ -51,6 +120,19 @@ export default Node.create({
];
},
+ renderHTML({ node }) {
+ return [
+ 'gl-reference',
+ {
+ 'data-reference-type': node.attrs.referenceType,
+ 'data-original-text': node.attrs.originalText,
+ href: node.attrs.href,
+ text: node.attrs.text,
+ },
+ node.attrs.text,
+ ];
+ },
+
addNodeView() {
return new VueNodeViewRenderer(ReferenceWrapper);
},