diff options
Diffstat (limited to 'app/assets/javascripts/content_editor/extensions/suggestions.js')
-rw-r--r-- | app/assets/javascripts/content_editor/extensions/suggestions.js | 171 |
1 files changed, 44 insertions, 127 deletions
diff --git a/app/assets/javascripts/content_editor/extensions/suggestions.js b/app/assets/javascripts/content_editor/extensions/suggestions.js index f7ff2fd6647..d309210404a 100644 --- a/app/assets/javascripts/content_editor/extensions/suggestions.js +++ b/app/assets/javascripts/content_editor/extensions/suggestions.js @@ -3,29 +3,20 @@ import { VueRenderer } from '@tiptap/vue-2'; import tippy from 'tippy.js'; import Suggestion from '@tiptap/suggestion'; import { PluginKey } from '@tiptap/pm/state'; -import { isFunction, uniqueId, memoize } from 'lodash'; -import axios from '~/lib/utils/axios_utils'; -import { initEmojiMap, getAllEmoji } from '~/emoji'; +import { uniqueId } from 'lodash'; import SuggestionsDropdown from '../components/suggestions_dropdown.vue'; -function find(haystack, needle) { - return String(haystack).toLocaleLowerCase().includes(String(needle).toLocaleLowerCase()); -} - function createSuggestionPlugin({ editor, char, - dataSource, - search, - limit = 15, + limit = 5, nodeType, - nodeProps = {}, + referenceType, + cache = true, insertionMap = {}, + serializer, + autocompleteHelper, }) { - const fetchData = memoize( - isFunction(dataSource) ? dataSource : async () => (await axios.get(dataSource)).data, - ); - return Suggestion({ editor, char, @@ -42,16 +33,17 @@ function createSuggestionPlugin({ .run(); }, - async items({ query }) { - if (!dataSource) return []; - - try { - const items = await fetchData(); - - return items.filter(search(query)).slice(0, limit); - } catch { - return []; - } + async items({ query, editor: tiptapEditor }) { + const slice = tiptapEditor.state.doc.slice(0, tiptapEditor.state.selection.to); + const markdownLine = serializer.serialize({ doc: slice.content }).split('\n').pop(); + + return autocompleteHelper + .getDataSource(referenceType, { + command: markdownLine.match(/\/\w+/)?.[0], + cache, + limit, + }) + .search(query); }, render: () => { @@ -76,7 +68,7 @@ function createSuggestionPlugin({ ...props, char, nodeType, - nodeProps, + nodeProps: { referenceType }, loading: true, }, editor: props.editor, @@ -132,101 +124,38 @@ export default Node.create({ addOptions() { return { - autocompleteDataSources: {}, + autocompleteHelper: {}, + serializer: null, }; }, addProseMirrorPlugins() { - return [ - createSuggestionPlugin({ - editor: this.editor, - char: '@', - dataSource: this.options.autocompleteDataSources.members, - nodeType: 'reference', - nodeProps: { - referenceType: 'user', - }, - search: (query) => ({ name, username }) => find(name, query) || find(username, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '#', - dataSource: this.options.autocompleteDataSources.issues, - nodeType: 'reference', - nodeProps: { - referenceType: 'issue', - }, - search: (query) => ({ iid, title }) => find(iid, query) || find(title, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '$', - dataSource: this.options.autocompleteDataSources.snippets, - nodeType: 'reference', - nodeProps: { - referenceType: 'snippet', - }, - search: (query) => ({ id, title }) => find(id, query) || find(title, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '~', - dataSource: this.options.autocompleteDataSources.labels, - nodeType: 'referenceLabel', - nodeProps: { - referenceType: 'label', - }, - search: (query) => ({ title }) => find(title, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '&', - dataSource: this.options.autocompleteDataSources.epics, - nodeType: 'reference', - nodeProps: { - referenceType: 'epic', - }, - search: (query) => ({ iid, title }) => find(iid, query) || find(title, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '[vulnerability:', - dataSource: this.options.autocompleteDataSources.vulnerabilities, - nodeType: 'reference', - nodeProps: { - referenceType: 'vulnerability', - }, - search: (query) => ({ id, title }) => find(id, query) || find(title, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '!', - dataSource: this.options.autocompleteDataSources.mergeRequests, - nodeType: 'reference', - nodeProps: { - referenceType: 'merge_request', - }, - search: (query) => ({ iid, title }) => find(iid, query) || find(title, query), - }), - createSuggestionPlugin({ - editor: this.editor, - char: '%', - dataSource: this.options.autocompleteDataSources.milestones, - nodeType: 'reference', - nodeProps: { - referenceType: 'milestone', - }, - search: (query) => ({ iid, title }) => find(iid, query) || find(title, query), - }), + const { serializer, autocompleteHelper } = this.options; + + const createPlugin = (char, nodeType, referenceType, options = {}) => createSuggestionPlugin({ editor: this.editor, - char: '/', - dataSource: this.options.autocompleteDataSources.commands, - nodeType: 'reference', - nodeProps: { - referenceType: 'command', - }, - search: (query) => ({ name }) => find(name, query), + char, + nodeType, + referenceType, + serializer, + autocompleteHelper, + ...options, + }); + + return [ + createPlugin('@', 'reference', 'user', { limit: 10 }), + createPlugin('#', 'reference', 'issue'), + createPlugin('$', 'reference', 'snippet'), + createPlugin('~', 'referenceLabel', 'label', { limit: 20 }), + createPlugin('&', 'reference', 'epic'), + createPlugin('!', 'reference', 'merge_request'), + createPlugin('[vulnerability:', 'reference', 'vulnerability'), + createPlugin('%', 'reference', 'milestone'), + createPlugin(':', 'emoji', 'emoji'), + createPlugin('/', 'reference', 'command', { + cache: false, + limit: 100, insertionMap: { '/label': '~', '/unlabel': '~', @@ -241,18 +170,6 @@ export default Node.create({ '/milestone': '%', }, }), - createSuggestionPlugin({ - editor: this.editor, - char: ':', - dataSource: () => getAllEmoji(), - nodeType: 'emoji', - search: (query) => ({ d, name }) => find(d, query) || find(name, query), - limit: 10, - }), ]; }, - - onCreate() { - initEmojiMap(); - }, }); |