mirror of
https://github.com/makeplane/plane.git
synced 2026-01-30 10:23:26 -06:00
[WIKI-547] fix: update find suggestion logic for emoji extension (#7411)
* fix: update find suggestion logic * refactor: remove logs * refactor : make logic simpler * feat: check for one char to show suggestion * refactor : import types from extension * refactor: add early return * refactor : put custom suggestion in helper * fix : char * fix: types
This commit is contained in:
@@ -17,6 +17,7 @@ export interface EmojiListProps {
|
||||
items: EmojiItem[];
|
||||
command: (item: { name: string }) => void;
|
||||
editor: Editor;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export interface EmojiListRef {
|
||||
@@ -43,7 +44,7 @@ const updatePosition = (editor: Editor, element: HTMLElement) => {
|
||||
};
|
||||
|
||||
export const EmojiList = forwardRef<EmojiListRef, EmojiListProps>((props, ref) => {
|
||||
const { items, command, editor } = props;
|
||||
const { items, command, editor, query } = props;
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(0);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
@@ -141,6 +142,10 @@ export const EmojiList = forwardRef<EmojiListRef, EmojiListProps>((props, ref) =
|
||||
[handleKeyDown]
|
||||
);
|
||||
|
||||
if (query.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
|
||||
@@ -15,6 +15,8 @@ import { Plugin, PluginKey, Transaction } from "@tiptap/pm/state";
|
||||
import Suggestion, { SuggestionOptions } from "@tiptap/suggestion";
|
||||
import emojiRegex from "emoji-regex";
|
||||
import { isEmojiSupported } from "is-emoji-supported";
|
||||
// helpers
|
||||
import { customFindSuggestionMatch } from "@/helpers/find-suggestion-match";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
@@ -343,6 +345,7 @@ export const Emoji = Node.create<EmojiOptions, EmojiStorage>({
|
||||
return [
|
||||
Suggestion({
|
||||
editor: this.editor,
|
||||
findSuggestionMatch: customFindSuggestionMatch,
|
||||
...this.options.suggestion,
|
||||
}),
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ const emojiSuggestion: EmojiOptions["suggestion"] = {
|
||||
items: props.items,
|
||||
command: props.command,
|
||||
editor: props.editor,
|
||||
query: props.query,
|
||||
},
|
||||
editor: props.editor,
|
||||
});
|
||||
@@ -81,6 +82,7 @@ const emojiSuggestion: EmojiOptions["suggestion"] = {
|
||||
items: props.items,
|
||||
command: props.command,
|
||||
editor: props.editor,
|
||||
query: props.query,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
73
packages/editor/src/core/helpers/find-suggestion-match.ts
Normal file
73
packages/editor/src/core/helpers/find-suggestion-match.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { escapeForRegEx } from "@tiptap/core";
|
||||
import { Trigger, SuggestionMatch } from "@tiptap/suggestion";
|
||||
|
||||
export function customFindSuggestionMatch(config: Trigger): SuggestionMatch | null {
|
||||
const { char, allowSpaces: allowSpacesOption, allowToIncludeChar, allowedPrefixes, startOfLine, $position } = config;
|
||||
|
||||
const allowSpaces = allowSpacesOption && !allowToIncludeChar;
|
||||
|
||||
const escapedChar = escapeForRegEx(char);
|
||||
const suffix = new RegExp(`\\s${escapedChar}$`);
|
||||
const prefix = startOfLine ? "^" : "";
|
||||
const finalEscapedChar = allowToIncludeChar ? "" : escapedChar;
|
||||
const regexp = allowSpaces
|
||||
? new RegExp(`${prefix}${escapedChar}.*?(?=\\s${finalEscapedChar}|$)`, "gm")
|
||||
: new RegExp(`${prefix}(?:^)?${escapedChar}[^\\s${finalEscapedChar}]*`, "gm");
|
||||
|
||||
// Instead of just looking at nodeBefore.text, we need to extract text from the current paragraph
|
||||
// to properly handle text with decorators like bold, italic, etc.
|
||||
const currentParagraph = $position.parent;
|
||||
if (!currentParagraph.isTextblock) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the start position of the current paragraph
|
||||
const paragraphStart = $position.start();
|
||||
// Extract text content using textBetween which handles text across different nodes/marks
|
||||
const text = $position.doc.textBetween(paragraphStart, $position.pos, "\0", "\0");
|
||||
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const textFrom = paragraphStart;
|
||||
const match = Array.from(text.matchAll(regexp)).pop();
|
||||
|
||||
if (!match || match.input === undefined || match.index === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// JavaScript doesn't have lookbehinds. This hacks a check that first character
|
||||
// is a space or the start of the line
|
||||
const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
|
||||
const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes?.join("")}]?$`).test(matchPrefix);
|
||||
|
||||
if (allowedPrefixes && allowedPrefixes.length > 0 && !matchPrefixIsAllowed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The absolute position of the match in the document
|
||||
const from = textFrom + match.index;
|
||||
let to = from + match[0].length;
|
||||
|
||||
// Edge case handling; if spaces are allowed and we're directly in between
|
||||
// two triggers
|
||||
if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
|
||||
match[0] += " ";
|
||||
to += 1;
|
||||
}
|
||||
|
||||
// If the $position is located within the matched substring, return that range
|
||||
if (from < $position.pos && to >= $position.pos) {
|
||||
return {
|
||||
range: {
|
||||
from,
|
||||
to,
|
||||
},
|
||||
query: match[0].slice(char.length),
|
||||
text: match[0],
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user