From ded986c2976b055265b4922c9be8798e7ea6a0c3 Mon Sep 17 00:00:00 2001 From: iamsivin Date: Tue, 3 Sep 2024 13:16:01 +0530 Subject: [PATCH] chore: Adds comments --- src/mentions/plugin.js | 156 ++++++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 73 deletions(-) diff --git a/src/mentions/plugin.js b/src/mentions/plugin.js index 30f3590..cdc7a7b 100644 --- a/src/mentions/plugin.js +++ b/src/mentions/plugin.js @@ -6,12 +6,24 @@ import { Plugin, PluginKey } from 'prosemirror-state'; import { Decoration, DecorationSet } from 'prosemirror-view'; +/** + * Creates a function to detect if the trigger character followed by a specified number of characters + * has been typed, starting from a new word or after a space. + * @param {string} char - The trigger character to detect. + * @param {number} [minChars=0] - The minimum number of characters that should follow the trigger character. + * @returns {Function} A function that takes a position object and returns true if the condition is met. + */ export const triggerCharacters = (char, minChars = 0) => $position => { + // Regular expression to find occurrences of 'char' followed by at least 'minChars' non-space characters. + // It matches these sequences starting from the beginning of the text or after a space. const regexp = new RegExp(`(^|\\s)(${char}[^\\s${char}]{${minChars},})`, 'g'); + // Get the position before the current cursor position in the document. const textFrom = $position.before(); + // Get the position at the end of the current node. const textTo = $position.end(); + // Get the text between the start of the node and the cursor position. const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0'); let match; @@ -41,96 +53,94 @@ export const suggestionsPlugin = ({ onChange = () => false, onExit = () => false, onKeyDown = () => false, -}) => { - return new Plugin({ - key: new PluginKey('mentions'), +}) => new Plugin({ + key: new PluginKey('mentions'), - view() { - return { - update: (view, prevState) => { - const prev = this.key.getState(prevState); - const next = this.key.getState(view.state); + view() { + return { + update: (view, prevState) => { + const prev = this.key.getState(prevState); + const next = this.key.getState(view.state); - const moved = + const moved = prev.active && next.active && prev.range.from !== next.range.from; - const started = !prev.active && next.active; - const stopped = prev.active && !next.active; - const changed = !started && !stopped && prev.text !== next.text; - - if (stopped || moved) - onExit({ view, range: prev.range, text: prev.text }); - if (changed && !moved) - onChange({ view, range: next.range, text: next.text }); - if (started || moved) - onEnter({ view, range: next.range, text: next.text }); - }, + const started = !prev.active && next.active; + const stopped = prev.active && !next.active; + const changed = !started && !stopped && prev.text !== next.text; + + if (stopped || moved) + onExit({ view, range: prev.range, text: prev.text }); + if (changed && !moved) + onChange({ view, range: next.range, text: next.text }); + if (started || moved) + onEnter({ view, range: next.range, text: next.text }); + }, + }; + }, + + state: { + init() { + return { + active: false, + range: {}, + text: null, }; }, - state: { - init() { - return { - active: false, - range: {}, - text: null, - }; - }, - - apply(tr, prev) { - const { selection } = tr; - const next = { ...prev }; + apply(tr, prev) { + const { selection } = tr; + const next = { ...prev }; - if (selection.from === selection.to) { - if ( - selection.from < prev.range.from || + if (selection.from === selection.to) { + if ( + selection.from < prev.range.from || selection.from > prev.range.to - ) { - next.active = false; - } - - const $position = selection.$from; - const match = matcher($position); - - if (match) { - next.active = true; - next.range = match.range; - next.text = match.text; - } else { - next.active = false; - } - } else { + ) { next.active = false; } - if (!next.active) { - next.range = {}; - next.text = null; + const $position = selection.$from; + const match = matcher($position); + + if (match) { + next.active = true; + next.range = match.range; + next.text = match.text; + } else { + next.active = false; } + } else { + next.active = false; + } - return next; - }, + if (!next.active) { + next.range = {}; + next.text = null; + } + + return next; }, + }, - props: { - handleKeyDown(view, event) { - const { active } = this.getState(view.state); + props: { + handleKeyDown(view, event) { + const { active } = this.getState(view.state); - if (!active) return false; + if (!active) return false; - return onKeyDown({ view, event }); - }, - decorations(editorState) { - const { active, range } = this.getState(editorState); + return onKeyDown({ view, event }); + }, + decorations(editorState) { + const { active, range } = this.getState(editorState); - if (!active) return null; + if (!active) return null; - return DecorationSet.create(editorState.doc, [ - Decoration.inline(range.from, range.to, { - nodeName: 'span', - class: suggestionClass, - }), - ]); - }, + return DecorationSet.create(editorState.doc, [ + Decoration.inline(range.from, range.to, { + nodeName: 'span', + class: suggestionClass, + }), + ]); }, - }); -}; + }, +});