@chatwoot/prosemirror-schema
Advanced tools
Comparing version 1.0.14 to 1.0.15
{ | ||
"name": "@chatwoot/prosemirror-schema", | ||
"version": "1.0.14", | ||
"version": "1.0.15", | ||
"description": "Schema setup for using prosemirror in chatwoot. Based on 👉 https://github.com/ProseMirror/prosemirror-example-setup/", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -11,6 +11,6 @@ /** | ||
* 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. | ||
* has been typed, starting from a new word, after a space, or after a newline. | ||
* @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. | ||
* @returns {Function} A function that takes a position object and returns the match if the condition is met. | ||
*/ | ||
@@ -20,3 +20,3 @@ export const triggerCharacters = (char, minChars = 0) => $position => { | ||
// 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'); | ||
const regexp = new RegExp(`(?:^)?${char}[^\\s${char}]{${minChars},}`, 'g'); | ||
@@ -35,12 +35,17 @@ // Get the position before the current cursor position in the document. | ||
while ((match = regexp.exec(text))) { | ||
const beforeChar = match[1]; // Will be empty at start of text, or a space in the middle | ||
const fullMatch = match[2]; // Includes the trigger character and following text | ||
// Check if the character before the match is a space, start of string, or null character | ||
const prefix = match.input.slice(Math.max(0, match.index - 1), match.index); | ||
if (!/^[\s\0]?$/.test(prefix)) { | ||
// If the prefix is not empty, space, or null, skip this match | ||
// eslint-disable-next-line | ||
continue; | ||
} | ||
const from = match.index + $position.start() + beforeChar.length; | ||
const to = from + fullMatch.length; | ||
const from = match.index + $position.start(); | ||
const to = from + match[0].length; | ||
if (from < $position.pos && to >= $position.pos) { | ||
const trimmedText = fullMatch | ||
? fullMatch.slice(char.length).trim() | ||
: ""; // Remove trigger char and trim | ||
const fullMatch = match[0]; | ||
// Remove trigger char and trim | ||
const trimmedText = fullMatch ? fullMatch.slice(char.length) : ''; | ||
return { range: { from, to }, text: trimmedText }; | ||
@@ -59,94 +64,92 @@ } | ||
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; | ||
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 }); | ||
}, | ||
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; | ||
} | ||
) { | ||
next.active = false; | ||
} | ||
const $position = selection.$from; | ||
const match = matcher($position); | ||
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; | ||
} | ||
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; | ||
} | ||
if (!next.active) { | ||
next.range = {}; | ||
next.text = null; | ||
} | ||
return next; | ||
}, | ||
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, | ||
}), | ||
]); | ||
}, | ||
}); | ||
}; | ||
}, | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
0
184697
35
5180