@relevaince/mentions 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -83
- package/dist/index.d.mts +41 -8
- package/dist/index.d.ts +41 -8
- package/dist/index.js +536 -85
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +542 -91
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/components/MentionsInput.tsx
|
|
2
|
-
import { forwardRef, useCallback as useCallback3, useImperativeHandle } from "react";
|
|
2
|
+
import { forwardRef, useCallback as useCallback3, useId, useImperativeHandle } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
3
4
|
import { EditorContent } from "@tiptap/react";
|
|
4
5
|
|
|
5
6
|
// src/hooks/useMentionsEditor.ts
|
|
@@ -25,6 +26,12 @@ var MentionNode = Node.create({
|
|
|
25
26
|
atom: true,
|
|
26
27
|
selectable: true,
|
|
27
28
|
draggable: false,
|
|
29
|
+
addOptions() {
|
|
30
|
+
return {
|
|
31
|
+
onClickRef: void 0,
|
|
32
|
+
onHoverRef: void 0
|
|
33
|
+
};
|
|
34
|
+
},
|
|
28
35
|
addAttributes() {
|
|
29
36
|
return {
|
|
30
37
|
id: {
|
|
@@ -57,11 +64,17 @@ var MentionNode = Node.create({
|
|
|
57
64
|
const label = node.attrs.label;
|
|
58
65
|
const prefix = DEFAULT_PREFIXES[entityType] ?? "@";
|
|
59
66
|
const display = `${prefix}${label}`;
|
|
67
|
+
const hasClick = !!this.options.onClickRef?.current;
|
|
68
|
+
const extraAttrs = {};
|
|
69
|
+
if (hasClick) {
|
|
70
|
+
extraAttrs["data-mention-clickable"] = "";
|
|
71
|
+
}
|
|
60
72
|
return [
|
|
61
73
|
"span",
|
|
62
74
|
mergeAttributes(HTMLAttributes, {
|
|
63
75
|
"data-mention": "",
|
|
64
|
-
class: "mention-chip"
|
|
76
|
+
class: "mention-chip",
|
|
77
|
+
...extraAttrs
|
|
65
78
|
}),
|
|
66
79
|
display
|
|
67
80
|
];
|
|
@@ -72,6 +85,58 @@ var MentionNode = Node.create({
|
|
|
72
85
|
const prefix = DEFAULT_PREFIXES[entityType] ?? "@";
|
|
73
86
|
return `${prefix}${label}`;
|
|
74
87
|
},
|
|
88
|
+
addNodeView() {
|
|
89
|
+
const options = this.options;
|
|
90
|
+
return ({ node, HTMLAttributes }) => {
|
|
91
|
+
const entityType = node.attrs.entityType;
|
|
92
|
+
const label = node.attrs.label;
|
|
93
|
+
const id = node.attrs.id;
|
|
94
|
+
const prefix = DEFAULT_PREFIXES[entityType] ?? "@";
|
|
95
|
+
const dom = document.createElement("span");
|
|
96
|
+
Object.entries(
|
|
97
|
+
mergeAttributes(HTMLAttributes, {
|
|
98
|
+
"data-mention": "",
|
|
99
|
+
"data-type": entityType,
|
|
100
|
+
"data-id": id,
|
|
101
|
+
class: "mention-chip"
|
|
102
|
+
})
|
|
103
|
+
).forEach(([key, val]) => {
|
|
104
|
+
if (val != null && val !== false) dom.setAttribute(key, String(val));
|
|
105
|
+
});
|
|
106
|
+
dom.textContent = `${prefix}${label}`;
|
|
107
|
+
if (options.onClickRef?.current) {
|
|
108
|
+
dom.setAttribute("data-mention-clickable", "");
|
|
109
|
+
dom.style.cursor = "pointer";
|
|
110
|
+
}
|
|
111
|
+
dom.addEventListener("click", (event) => {
|
|
112
|
+
const handler = options.onClickRef?.current;
|
|
113
|
+
if (handler) {
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
event.stopPropagation();
|
|
116
|
+
handler({ id, type: entityType, label }, event);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
let tooltip = null;
|
|
120
|
+
dom.addEventListener("mouseenter", () => {
|
|
121
|
+
const hoverFn = options.onHoverRef?.current;
|
|
122
|
+
if (!hoverFn) return;
|
|
123
|
+
const content = hoverFn({ id, type: entityType, label });
|
|
124
|
+
if (!content) return;
|
|
125
|
+
tooltip = document.createElement("div");
|
|
126
|
+
tooltip.setAttribute("data-mention-tooltip", "");
|
|
127
|
+
tooltip.textContent = typeof content === "string" ? content : "";
|
|
128
|
+
dom.style.position = "relative";
|
|
129
|
+
dom.appendChild(tooltip);
|
|
130
|
+
});
|
|
131
|
+
dom.addEventListener("mouseleave", () => {
|
|
132
|
+
if (tooltip && tooltip.parentNode) {
|
|
133
|
+
tooltip.parentNode.removeChild(tooltip);
|
|
134
|
+
tooltip = null;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return { dom };
|
|
138
|
+
};
|
|
139
|
+
},
|
|
75
140
|
addKeyboardShortcuts() {
|
|
76
141
|
return {
|
|
77
142
|
Backspace: () => this.editor.commands.command(({ tr, state }) => {
|
|
@@ -104,7 +169,13 @@ function detectTrigger(text, cursorPos, docStartPos, triggers) {
|
|
|
104
169
|
if (before.substring(i, i + trigger.length) === trigger) {
|
|
105
170
|
if (i === 0 || /\s/.test(before[i - 1])) {
|
|
106
171
|
const query = before.slice(i + trigger.length);
|
|
107
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
trigger,
|
|
174
|
+
query,
|
|
175
|
+
from: docStartPos + i,
|
|
176
|
+
to: cursorPos,
|
|
177
|
+
textBefore: before.slice(0, i)
|
|
178
|
+
};
|
|
108
179
|
}
|
|
109
180
|
}
|
|
110
181
|
}
|
|
@@ -112,7 +183,7 @@ function detectTrigger(text, cursorPos, docStartPos, triggers) {
|
|
|
112
183
|
return null;
|
|
113
184
|
}
|
|
114
185
|
var suggestionPluginKey = new PluginKey("mentionSuggestion");
|
|
115
|
-
function createSuggestionExtension(triggers, callbacksRef) {
|
|
186
|
+
function createSuggestionExtension(triggers, callbacksRef, allowTriggerRef) {
|
|
116
187
|
return Extension.create({
|
|
117
188
|
name: "mentionSuggestion",
|
|
118
189
|
priority: 200,
|
|
@@ -190,6 +261,20 @@ function createSuggestionExtension(triggers, callbacksRef) {
|
|
|
190
261
|
const cursorPos = $pos.pos;
|
|
191
262
|
const match = detectTrigger(blockText, cursorPos, blockStart, triggers);
|
|
192
263
|
if (match) {
|
|
264
|
+
if (allowTriggerRef?.current) {
|
|
265
|
+
const allowed = allowTriggerRef.current(match.trigger, {
|
|
266
|
+
textBefore: match.textBefore
|
|
267
|
+
});
|
|
268
|
+
if (!allowed) {
|
|
269
|
+
if (active) {
|
|
270
|
+
active = false;
|
|
271
|
+
lastQuery = null;
|
|
272
|
+
lastTrigger = null;
|
|
273
|
+
callbacksRef.current.onExit();
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
193
278
|
const range = { from: match.from, to: match.to };
|
|
194
279
|
const props = {
|
|
195
280
|
query: match.query,
|
|
@@ -359,27 +444,48 @@ function buildOutput(editor) {
|
|
|
359
444
|
plainText: extractPlainText(json)
|
|
360
445
|
};
|
|
361
446
|
}
|
|
362
|
-
function
|
|
447
|
+
function collectMentionTokens(doc) {
|
|
448
|
+
const tokens = [];
|
|
449
|
+
function walk(node) {
|
|
450
|
+
if (node.type === "mention" && node.attrs) {
|
|
451
|
+
tokens.push({
|
|
452
|
+
id: node.attrs.id,
|
|
453
|
+
type: node.attrs.entityType ?? node.attrs.type,
|
|
454
|
+
label: node.attrs.label
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (node.content) {
|
|
458
|
+
for (const child of node.content) walk(child);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
walk(doc);
|
|
462
|
+
return tokens;
|
|
463
|
+
}
|
|
464
|
+
function createSubmitExtension(onSubmitRef, clearOnSubmitRef, submitKeyRef) {
|
|
363
465
|
return Extension2.create({
|
|
364
466
|
name: "submitShortcut",
|
|
365
467
|
priority: 150,
|
|
366
468
|
addKeyboardShortcuts() {
|
|
367
469
|
return {
|
|
368
470
|
"Mod-Enter": () => {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (
|
|
372
|
-
this.editor
|
|
471
|
+
const key = submitKeyRef.current;
|
|
472
|
+
if (key === "mod+enter" || key === "enter") {
|
|
473
|
+
if (onSubmitRef.current) {
|
|
474
|
+
onSubmitRef.current(buildOutput(this.editor));
|
|
475
|
+
if (clearOnSubmitRef.current) {
|
|
476
|
+
this.editor.commands.clearContent(true);
|
|
477
|
+
}
|
|
373
478
|
}
|
|
479
|
+
return true;
|
|
374
480
|
}
|
|
375
|
-
return
|
|
481
|
+
return false;
|
|
376
482
|
}
|
|
377
483
|
};
|
|
378
484
|
}
|
|
379
485
|
});
|
|
380
486
|
}
|
|
381
487
|
var enterSubmitPluginKey = new PluginKey2("enterSubmit");
|
|
382
|
-
function createEnterExtension(onSubmitRef, clearOnSubmitRef) {
|
|
488
|
+
function createEnterExtension(onSubmitRef, clearOnSubmitRef, submitKeyRef) {
|
|
383
489
|
return Extension2.create({
|
|
384
490
|
name: "enterSubmit",
|
|
385
491
|
priority: 150,
|
|
@@ -391,6 +497,11 @@ function createEnterExtension(onSubmitRef, clearOnSubmitRef) {
|
|
|
391
497
|
props: {
|
|
392
498
|
handleKeyDown(_view, event) {
|
|
393
499
|
if (event.key !== "Enter") return false;
|
|
500
|
+
const key = submitKeyRef.current;
|
|
501
|
+
if (key === "none") return false;
|
|
502
|
+
if (key === "mod+enter") {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
394
505
|
if (event.shiftKey) {
|
|
395
506
|
editor.commands.splitBlock();
|
|
396
507
|
return true;
|
|
@@ -410,6 +521,33 @@ function createEnterExtension(onSubmitRef, clearOnSubmitRef) {
|
|
|
410
521
|
}
|
|
411
522
|
});
|
|
412
523
|
}
|
|
524
|
+
var mentionRemovePluginKey = new PluginKey2("mentionRemove");
|
|
525
|
+
function createMentionRemoveExtension(onMentionRemoveRef) {
|
|
526
|
+
return Extension2.create({
|
|
527
|
+
name: "mentionRemoveDetector",
|
|
528
|
+
priority: 100,
|
|
529
|
+
addProseMirrorPlugins() {
|
|
530
|
+
return [
|
|
531
|
+
new Plugin2({
|
|
532
|
+
key: mentionRemovePluginKey,
|
|
533
|
+
appendTransaction(transactions, oldState, newState) {
|
|
534
|
+
if (!onMentionRemoveRef.current) return null;
|
|
535
|
+
const oldMentions = collectMentionTokens(oldState.doc.toJSON());
|
|
536
|
+
const newMentions = collectMentionTokens(newState.doc.toJSON());
|
|
537
|
+
if (oldMentions.length <= newMentions.length) return null;
|
|
538
|
+
const newIds = new Set(newMentions.map((m) => m.id));
|
|
539
|
+
for (const m of oldMentions) {
|
|
540
|
+
if (!newIds.has(m.id)) {
|
|
541
|
+
onMentionRemoveRef.current(m);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
})
|
|
547
|
+
];
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
413
551
|
function useMentionsEditor({
|
|
414
552
|
providers,
|
|
415
553
|
value,
|
|
@@ -419,7 +557,15 @@ function useMentionsEditor({
|
|
|
419
557
|
placeholder,
|
|
420
558
|
autoFocus = false,
|
|
421
559
|
editable = true,
|
|
422
|
-
callbacksRef
|
|
560
|
+
callbacksRef,
|
|
561
|
+
onFocus,
|
|
562
|
+
onBlur,
|
|
563
|
+
submitKey = "enter",
|
|
564
|
+
onMentionRemove,
|
|
565
|
+
onMentionClick,
|
|
566
|
+
onMentionHover,
|
|
567
|
+
allowTrigger,
|
|
568
|
+
validateMention
|
|
423
569
|
}) {
|
|
424
570
|
const onChangeRef = useRef(onChange);
|
|
425
571
|
onChangeRef.current = onChange;
|
|
@@ -427,6 +573,23 @@ function useMentionsEditor({
|
|
|
427
573
|
onSubmitRef.current = onSubmit;
|
|
428
574
|
const clearOnSubmitRef = useRef(clearOnSubmit);
|
|
429
575
|
clearOnSubmitRef.current = clearOnSubmit;
|
|
576
|
+
const onFocusRef = useRef(onFocus);
|
|
577
|
+
onFocusRef.current = onFocus;
|
|
578
|
+
const onBlurRef = useRef(onBlur);
|
|
579
|
+
onBlurRef.current = onBlur;
|
|
580
|
+
const submitKeyRef = useRef(submitKey);
|
|
581
|
+
submitKeyRef.current = submitKey;
|
|
582
|
+
const onMentionRemoveRef = useRef(onMentionRemove);
|
|
583
|
+
onMentionRemoveRef.current = onMentionRemove;
|
|
584
|
+
const onMentionClickRef = useRef(onMentionClick);
|
|
585
|
+
onMentionClickRef.current = onMentionClick;
|
|
586
|
+
const onMentionHoverRef = useRef(onMentionHover);
|
|
587
|
+
onMentionHoverRef.current = onMentionHover;
|
|
588
|
+
const allowTriggerRef = useRef(allowTrigger);
|
|
589
|
+
allowTriggerRef.current = allowTrigger;
|
|
590
|
+
const validateMentionRef = useRef(validateMention);
|
|
591
|
+
validateMentionRef.current = validateMention;
|
|
592
|
+
const internalMarkdownRef = useRef(null);
|
|
430
593
|
const initialContent = useMemo(() => {
|
|
431
594
|
if (!value) return void 0;
|
|
432
595
|
return parseFromMarkdown(value);
|
|
@@ -438,16 +601,27 @@ function useMentionsEditor({
|
|
|
438
601
|
[triggersKey]
|
|
439
602
|
);
|
|
440
603
|
const suggestionExtension = useMemo(
|
|
441
|
-
() => createSuggestionExtension(triggers, callbacksRef),
|
|
604
|
+
() => createSuggestionExtension(triggers, callbacksRef, allowTriggerRef),
|
|
442
605
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
443
606
|
[triggersKey]
|
|
444
607
|
);
|
|
445
608
|
const submitExt = useMemo(
|
|
446
|
-
() => createSubmitExtension(onSubmitRef, clearOnSubmitRef),
|
|
609
|
+
() => createSubmitExtension(onSubmitRef, clearOnSubmitRef, submitKeyRef),
|
|
447
610
|
[]
|
|
448
611
|
);
|
|
449
612
|
const enterExt = useMemo(
|
|
450
|
-
() => createEnterExtension(onSubmitRef, clearOnSubmitRef),
|
|
613
|
+
() => createEnterExtension(onSubmitRef, clearOnSubmitRef, submitKeyRef),
|
|
614
|
+
[]
|
|
615
|
+
);
|
|
616
|
+
const mentionRemoveExt = useMemo(
|
|
617
|
+
() => createMentionRemoveExtension(onMentionRemoveRef),
|
|
618
|
+
[]
|
|
619
|
+
);
|
|
620
|
+
const mentionNodeExt = useMemo(
|
|
621
|
+
() => MentionNode.configure({
|
|
622
|
+
onClickRef: onMentionClickRef,
|
|
623
|
+
onHoverRef: onMentionHoverRef
|
|
624
|
+
}),
|
|
451
625
|
[]
|
|
452
626
|
);
|
|
453
627
|
const editor = useEditor({
|
|
@@ -466,10 +640,11 @@ function useMentionsEditor({
|
|
|
466
640
|
placeholder: ({ editor: editor2 }) => editor2.isEmpty ? placeholder ?? "Type a message..." : "",
|
|
467
641
|
showOnlyCurrent: true
|
|
468
642
|
}),
|
|
469
|
-
|
|
643
|
+
mentionNodeExt,
|
|
470
644
|
suggestionExtension,
|
|
471
645
|
submitExt,
|
|
472
|
-
enterExt
|
|
646
|
+
enterExt,
|
|
647
|
+
mentionRemoveExt
|
|
473
648
|
],
|
|
474
649
|
content: initialContent,
|
|
475
650
|
autofocus: autoFocus ? "end" : false,
|
|
@@ -480,7 +655,15 @@ function useMentionsEditor({
|
|
|
480
655
|
}
|
|
481
656
|
},
|
|
482
657
|
onUpdate: ({ editor: editor2 }) => {
|
|
483
|
-
|
|
658
|
+
const output = buildOutput(editor2);
|
|
659
|
+
internalMarkdownRef.current = output.markdown;
|
|
660
|
+
onChangeRef.current?.(output);
|
|
661
|
+
},
|
|
662
|
+
onFocus: () => {
|
|
663
|
+
onFocusRef.current?.();
|
|
664
|
+
},
|
|
665
|
+
onBlur: () => {
|
|
666
|
+
onBlurRef.current?.();
|
|
484
667
|
}
|
|
485
668
|
});
|
|
486
669
|
useEffect(() => {
|
|
@@ -488,6 +671,36 @@ function useMentionsEditor({
|
|
|
488
671
|
editor.setEditable(editable);
|
|
489
672
|
}
|
|
490
673
|
}, [editor, editable]);
|
|
674
|
+
useEffect(() => {
|
|
675
|
+
if (!editor || value === void 0) return;
|
|
676
|
+
if (value === internalMarkdownRef.current) return;
|
|
677
|
+
const doc = parseFromMarkdown(value);
|
|
678
|
+
editor.commands.setContent(doc);
|
|
679
|
+
internalMarkdownRef.current = value;
|
|
680
|
+
}, [editor, value]);
|
|
681
|
+
useEffect(() => {
|
|
682
|
+
if (!editor || !validateMention) return;
|
|
683
|
+
const runValidation = async () => {
|
|
684
|
+
const doc = editor.getJSON();
|
|
685
|
+
const tokens = collectMentionTokens(doc);
|
|
686
|
+
const invalidIds = /* @__PURE__ */ new Set();
|
|
687
|
+
await Promise.all(
|
|
688
|
+
tokens.map(async (token) => {
|
|
689
|
+
const valid = await validateMention(token);
|
|
690
|
+
if (!valid) invalidIds.add(token.id);
|
|
691
|
+
})
|
|
692
|
+
);
|
|
693
|
+
editor.view.dom.querySelectorAll("[data-mention]").forEach((el) => {
|
|
694
|
+
const id = el.getAttribute("data-id");
|
|
695
|
+
if (id && invalidIds.has(id)) {
|
|
696
|
+
el.setAttribute("data-mention-invalid", "");
|
|
697
|
+
} else {
|
|
698
|
+
el.removeAttribute("data-mention-invalid");
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
};
|
|
702
|
+
runValidation();
|
|
703
|
+
}, [editor, validateMention]);
|
|
491
704
|
const clear = useCallback(() => {
|
|
492
705
|
editor?.commands.clearContent(true);
|
|
493
706
|
}, [editor]);
|
|
@@ -496,6 +709,7 @@ function useMentionsEditor({
|
|
|
496
709
|
if (!editor) return;
|
|
497
710
|
const doc = parseFromMarkdown(markdown);
|
|
498
711
|
editor.commands.setContent(doc);
|
|
712
|
+
internalMarkdownRef.current = markdown;
|
|
499
713
|
},
|
|
500
714
|
[editor]
|
|
501
715
|
);
|
|
@@ -510,7 +724,28 @@ function useMentionsEditor({
|
|
|
510
724
|
}
|
|
511
725
|
|
|
512
726
|
// src/hooks/useSuggestion.ts
|
|
513
|
-
import { useCallback as useCallback2, useRef as useRef2, useState } from "react";
|
|
727
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState } from "react";
|
|
728
|
+
|
|
729
|
+
// src/utils/debounce.ts
|
|
730
|
+
function debounce(fn, ms) {
|
|
731
|
+
let timer = null;
|
|
732
|
+
const debounced = ((...args) => {
|
|
733
|
+
if (timer != null) clearTimeout(timer);
|
|
734
|
+
timer = setTimeout(() => {
|
|
735
|
+
timer = null;
|
|
736
|
+
fn(...args);
|
|
737
|
+
}, ms);
|
|
738
|
+
});
|
|
739
|
+
debounced.cancel = () => {
|
|
740
|
+
if (timer != null) {
|
|
741
|
+
clearTimeout(timer);
|
|
742
|
+
timer = null;
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
return debounced;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/hooks/useSuggestion.ts
|
|
514
749
|
var IDLE_STATE = {
|
|
515
750
|
state: "idle",
|
|
516
751
|
items: [],
|
|
@@ -521,16 +756,24 @@ var IDLE_STATE = {
|
|
|
521
756
|
trigger: null,
|
|
522
757
|
query: ""
|
|
523
758
|
};
|
|
524
|
-
function useSuggestion(providers) {
|
|
759
|
+
function useSuggestion(providers, options = {}) {
|
|
525
760
|
const [uiState, setUIState] = useState(IDLE_STATE);
|
|
526
761
|
const stateRef = useRef2(uiState);
|
|
527
762
|
stateRef.current = uiState;
|
|
528
763
|
const providersRef = useRef2(providers);
|
|
529
764
|
providersRef.current = providers;
|
|
765
|
+
const onMentionAddRef = useRef2(options.onMentionAdd);
|
|
766
|
+
onMentionAddRef.current = options.onMentionAdd;
|
|
530
767
|
const commandRef = useRef2(
|
|
531
768
|
null
|
|
532
769
|
);
|
|
533
770
|
const providerRef = useRef2(null);
|
|
771
|
+
const debouncedFetchRef = useRef2(null);
|
|
772
|
+
useEffect2(() => {
|
|
773
|
+
return () => {
|
|
774
|
+
debouncedFetchRef.current?.cancel();
|
|
775
|
+
};
|
|
776
|
+
}, []);
|
|
534
777
|
const fetchItems = useCallback2(
|
|
535
778
|
async (provider, query, parent, useSearchAll) => {
|
|
536
779
|
setUIState((prev) => ({ ...prev, loading: true, state: "loading" }));
|
|
@@ -561,6 +804,23 @@ function useSuggestion(providers) {
|
|
|
561
804
|
},
|
|
562
805
|
[]
|
|
563
806
|
);
|
|
807
|
+
const scheduleFetch = useCallback2(
|
|
808
|
+
(provider, query, parent, useSearchAll) => {
|
|
809
|
+
debouncedFetchRef.current?.cancel();
|
|
810
|
+
const ms = provider.debounceMs;
|
|
811
|
+
if (ms && ms > 0) {
|
|
812
|
+
setUIState((prev) => ({ ...prev, loading: true }));
|
|
813
|
+
const debouncedFn = debounce(() => {
|
|
814
|
+
fetchItems(provider, query, parent, useSearchAll);
|
|
815
|
+
}, ms);
|
|
816
|
+
debouncedFetchRef.current = debouncedFn;
|
|
817
|
+
debouncedFn();
|
|
818
|
+
} else {
|
|
819
|
+
fetchItems(provider, query, parent, useSearchAll);
|
|
820
|
+
}
|
|
821
|
+
},
|
|
822
|
+
[fetchItems]
|
|
823
|
+
);
|
|
564
824
|
const onStart = useCallback2(
|
|
565
825
|
(props) => {
|
|
566
826
|
const provider = providersRef.current.find(
|
|
@@ -579,13 +839,31 @@ function useSuggestion(providers) {
|
|
|
579
839
|
trigger: props.trigger,
|
|
580
840
|
query: props.query
|
|
581
841
|
});
|
|
842
|
+
if (!props.query.trim() && provider.getRecentItems) {
|
|
843
|
+
provider.getRecentItems().then((recentItems) => {
|
|
844
|
+
const tagged = recentItems.map((item) => ({
|
|
845
|
+
...item,
|
|
846
|
+
group: item.group ?? "Recent"
|
|
847
|
+
}));
|
|
848
|
+
setUIState((prev) => ({
|
|
849
|
+
...prev,
|
|
850
|
+
items: tagged,
|
|
851
|
+
loading: false,
|
|
852
|
+
state: "showing",
|
|
853
|
+
activeIndex: 0
|
|
854
|
+
}));
|
|
855
|
+
}).catch(() => {
|
|
856
|
+
scheduleFetch(provider, props.query);
|
|
857
|
+
});
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
582
860
|
if (props.query.trim() && provider.searchAll) {
|
|
583
|
-
|
|
861
|
+
scheduleFetch(provider, props.query, void 0, true);
|
|
584
862
|
} else {
|
|
585
|
-
|
|
863
|
+
scheduleFetch(provider, props.query);
|
|
586
864
|
}
|
|
587
865
|
},
|
|
588
|
-
[
|
|
866
|
+
[scheduleFetch]
|
|
589
867
|
);
|
|
590
868
|
const onUpdate = useCallback2(
|
|
591
869
|
(props) => {
|
|
@@ -609,14 +887,31 @@ function useSuggestion(providers) {
|
|
|
609
887
|
}));
|
|
610
888
|
}
|
|
611
889
|
if (props.query.trim() && provider.searchAll) {
|
|
612
|
-
|
|
890
|
+
scheduleFetch(provider, props.query, void 0, true);
|
|
891
|
+
} else if (!props.query.trim() && provider.getRecentItems) {
|
|
892
|
+
provider.getRecentItems().then((recentItems) => {
|
|
893
|
+
const tagged = recentItems.map((item) => ({
|
|
894
|
+
...item,
|
|
895
|
+
group: item.group ?? "Recent"
|
|
896
|
+
}));
|
|
897
|
+
setUIState((prev) => ({
|
|
898
|
+
...prev,
|
|
899
|
+
items: tagged,
|
|
900
|
+
loading: false,
|
|
901
|
+
state: "showing",
|
|
902
|
+
activeIndex: 0
|
|
903
|
+
}));
|
|
904
|
+
}).catch(() => {
|
|
905
|
+
scheduleFetch(provider, props.query);
|
|
906
|
+
});
|
|
613
907
|
} else {
|
|
614
|
-
|
|
908
|
+
scheduleFetch(provider, props.query);
|
|
615
909
|
}
|
|
616
910
|
},
|
|
617
|
-
[
|
|
911
|
+
[scheduleFetch]
|
|
618
912
|
);
|
|
619
913
|
const onExit = useCallback2(() => {
|
|
914
|
+
debouncedFetchRef.current?.cancel();
|
|
620
915
|
providerRef.current = null;
|
|
621
916
|
commandRef.current = null;
|
|
622
917
|
setUIState(IDLE_STATE);
|
|
@@ -651,8 +946,8 @@ function useSuggestion(providers) {
|
|
|
651
946
|
fetchItems(provider, "", selected);
|
|
652
947
|
return;
|
|
653
948
|
}
|
|
949
|
+
const rootLabel = current.breadcrumbs.length > 0 ? current.breadcrumbs[0].label : selected.rootLabel ?? null;
|
|
654
950
|
if (commandRef.current) {
|
|
655
|
-
const rootLabel = current.breadcrumbs.length > 0 ? current.breadcrumbs[0].label : selected.rootLabel ?? null;
|
|
656
951
|
commandRef.current({
|
|
657
952
|
id: selected.id,
|
|
658
953
|
label: selected.label,
|
|
@@ -660,6 +955,11 @@ function useSuggestion(providers) {
|
|
|
660
955
|
rootLabel
|
|
661
956
|
});
|
|
662
957
|
}
|
|
958
|
+
onMentionAddRef.current?.({
|
|
959
|
+
id: selected.id,
|
|
960
|
+
type: selected.type,
|
|
961
|
+
label: selected.label
|
|
962
|
+
});
|
|
663
963
|
},
|
|
664
964
|
[fetchItems]
|
|
665
965
|
);
|
|
@@ -684,6 +984,7 @@ function useSuggestion(providers) {
|
|
|
684
984
|
});
|
|
685
985
|
}, [fetchItems]);
|
|
686
986
|
const close = useCallback2(() => {
|
|
987
|
+
debouncedFetchRef.current?.cancel();
|
|
687
988
|
setUIState(IDLE_STATE);
|
|
688
989
|
}, []);
|
|
689
990
|
const searchNested = useCallback2(
|
|
@@ -693,10 +994,10 @@ function useSuggestion(providers) {
|
|
|
693
994
|
const current = stateRef.current;
|
|
694
995
|
const parent = current.breadcrumbs[current.breadcrumbs.length - 1];
|
|
695
996
|
if (parent) {
|
|
696
|
-
|
|
997
|
+
scheduleFetch(provider, query, parent);
|
|
697
998
|
}
|
|
698
999
|
},
|
|
699
|
-
[
|
|
1000
|
+
[scheduleFetch]
|
|
700
1001
|
);
|
|
701
1002
|
const onKeyDown = useCallback2(
|
|
702
1003
|
({ event }) => {
|
|
@@ -719,6 +1020,14 @@ function useSuggestion(providers) {
|
|
|
719
1020
|
}
|
|
720
1021
|
return true;
|
|
721
1022
|
}
|
|
1023
|
+
case "Tab": {
|
|
1024
|
+
event.preventDefault();
|
|
1025
|
+
const selectedItem = current.items[current.activeIndex];
|
|
1026
|
+
if (selectedItem) {
|
|
1027
|
+
select(selectedItem);
|
|
1028
|
+
}
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
722
1031
|
case "ArrowRight": {
|
|
723
1032
|
const activeItem = current.items[current.activeIndex];
|
|
724
1033
|
if (activeItem?.hasChildren) {
|
|
@@ -764,7 +1073,7 @@ function useSuggestion(providers) {
|
|
|
764
1073
|
}
|
|
765
1074
|
|
|
766
1075
|
// src/components/SuggestionList.tsx
|
|
767
|
-
import { useEffect as
|
|
1076
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
|
|
768
1077
|
|
|
769
1078
|
// src/utils/ariaHelpers.ts
|
|
770
1079
|
function comboboxAttrs(expanded, listboxId) {
|
|
@@ -793,13 +1102,13 @@ function optionAttrs(id, selected, index) {
|
|
|
793
1102
|
|
|
794
1103
|
// src/components/SuggestionList.tsx
|
|
795
1104
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
796
|
-
var LISTBOX_ID = "mentions-suggestion-listbox";
|
|
797
1105
|
function SuggestionList({
|
|
798
1106
|
items,
|
|
799
1107
|
activeIndex,
|
|
800
1108
|
breadcrumbs,
|
|
801
1109
|
loading,
|
|
802
1110
|
trigger,
|
|
1111
|
+
query,
|
|
803
1112
|
clientRect,
|
|
804
1113
|
onSelect,
|
|
805
1114
|
onHover,
|
|
@@ -808,7 +1117,12 @@ function SuggestionList({
|
|
|
808
1117
|
onNavigateUp,
|
|
809
1118
|
onNavigateDown,
|
|
810
1119
|
onClose,
|
|
811
|
-
|
|
1120
|
+
onFocusEditor,
|
|
1121
|
+
renderItem,
|
|
1122
|
+
renderEmpty,
|
|
1123
|
+
renderLoading,
|
|
1124
|
+
renderGroupHeader,
|
|
1125
|
+
listboxId
|
|
812
1126
|
}) {
|
|
813
1127
|
const listRef = useRef3(null);
|
|
814
1128
|
const searchInputRef = useRef3(null);
|
|
@@ -816,24 +1130,30 @@ function SuggestionList({
|
|
|
816
1130
|
const [nestedQuery, setNestedQuery] = useState2("");
|
|
817
1131
|
const breadcrumbKey = breadcrumbs.map((b) => b.id).join("/");
|
|
818
1132
|
const prevBreadcrumbKey = useRef3(breadcrumbKey);
|
|
819
|
-
|
|
1133
|
+
useEffect3(() => {
|
|
820
1134
|
if (prevBreadcrumbKey.current !== breadcrumbKey) {
|
|
821
1135
|
setNestedQuery("");
|
|
822
1136
|
prevBreadcrumbKey.current = breadcrumbKey;
|
|
823
1137
|
}
|
|
824
1138
|
}, [breadcrumbKey]);
|
|
825
|
-
|
|
826
|
-
|
|
1139
|
+
const prevBreadcrumbsLen = useRef3(breadcrumbs.length);
|
|
1140
|
+
useEffect3(() => {
|
|
1141
|
+
if (prevBreadcrumbsLen.current > 0 && breadcrumbs.length === 0) {
|
|
1142
|
+
onFocusEditor?.();
|
|
1143
|
+
} else if (breadcrumbs.length > 0 && searchInputRef.current) {
|
|
827
1144
|
requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
828
1145
|
}
|
|
829
|
-
|
|
830
|
-
|
|
1146
|
+
prevBreadcrumbsLen.current = breadcrumbs.length;
|
|
1147
|
+
}, [breadcrumbKey, breadcrumbs.length, onFocusEditor]);
|
|
1148
|
+
useEffect3(() => {
|
|
831
1149
|
if (!listRef.current) return;
|
|
832
1150
|
const active = listRef.current.querySelector('[aria-selected="true"]');
|
|
833
1151
|
active?.scrollIntoView({ block: "nearest" });
|
|
834
1152
|
}, [activeIndex]);
|
|
835
|
-
const style = usePopoverPosition(clientRect);
|
|
836
|
-
|
|
1153
|
+
const { style, position } = usePopoverPosition(clientRect);
|
|
1154
|
+
const activeQuery = breadcrumbs.length > 0 ? nestedQuery : query;
|
|
1155
|
+
const showEmpty = !loading && items.length === 0 && activeQuery.trim().length > 0;
|
|
1156
|
+
if (items.length === 0 && !loading && !showEmpty) return null;
|
|
837
1157
|
const handleSearchKeyDown = (e) => {
|
|
838
1158
|
switch (e.key) {
|
|
839
1159
|
case "ArrowDown":
|
|
@@ -853,8 +1173,23 @@ function SuggestionList({
|
|
|
853
1173
|
}
|
|
854
1174
|
case "Escape":
|
|
855
1175
|
e.preventDefault();
|
|
1176
|
+
onFocusEditor?.();
|
|
856
1177
|
onClose?.();
|
|
857
1178
|
break;
|
|
1179
|
+
case "ArrowLeft":
|
|
1180
|
+
if (nestedQuery === "" || e.currentTarget.selectionStart === 0) {
|
|
1181
|
+
e.preventDefault();
|
|
1182
|
+
onGoBack();
|
|
1183
|
+
}
|
|
1184
|
+
break;
|
|
1185
|
+
case "ArrowRight": {
|
|
1186
|
+
const item = items[activeIndex];
|
|
1187
|
+
if (item?.hasChildren) {
|
|
1188
|
+
e.preventDefault();
|
|
1189
|
+
onSelect(item);
|
|
1190
|
+
}
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
858
1193
|
case "Backspace":
|
|
859
1194
|
if (nestedQuery === "") {
|
|
860
1195
|
e.preventDefault();
|
|
@@ -863,11 +1198,13 @@ function SuggestionList({
|
|
|
863
1198
|
break;
|
|
864
1199
|
}
|
|
865
1200
|
};
|
|
1201
|
+
const hasGroups = items.some((item) => item.group);
|
|
866
1202
|
return /* @__PURE__ */ jsxs(
|
|
867
1203
|
"div",
|
|
868
1204
|
{
|
|
869
1205
|
"data-suggestions": "",
|
|
870
1206
|
"data-trigger": trigger,
|
|
1207
|
+
"data-suggestions-position": position,
|
|
871
1208
|
style,
|
|
872
1209
|
ref: listRef,
|
|
873
1210
|
children: [
|
|
@@ -905,28 +1242,76 @@ function SuggestionList({
|
|
|
905
1242
|
spellCheck: false
|
|
906
1243
|
}
|
|
907
1244
|
) }),
|
|
908
|
-
loading && /* @__PURE__ */ jsx("div", { "data-suggestion-loading": "", children: "Loading..." }),
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
item.id
|
|
924
|
-
);
|
|
925
|
-
}) })
|
|
1245
|
+
loading && /* @__PURE__ */ jsx("div", { "data-suggestion-loading": "", children: renderLoading ? renderLoading() : "Loading..." }),
|
|
1246
|
+
showEmpty && /* @__PURE__ */ jsx("div", { "data-suggestion-empty": "", children: renderEmpty ? renderEmpty(activeQuery) : "No results" }),
|
|
1247
|
+
!loading && items.length > 0 && /* @__PURE__ */ jsx("div", { ...listboxAttrs(listboxId, `${trigger ?? ""} suggestions`), children: hasGroups ? renderGroupedItems(items, activeIndex, depth, onSelect, onHover, renderItem, renderGroupHeader) : items.map((item, index) => /* @__PURE__ */ jsx(
|
|
1248
|
+
SuggestionItem,
|
|
1249
|
+
{
|
|
1250
|
+
item,
|
|
1251
|
+
index,
|
|
1252
|
+
isActive: index === activeIndex,
|
|
1253
|
+
depth,
|
|
1254
|
+
onSelect,
|
|
1255
|
+
onHover,
|
|
1256
|
+
renderItem
|
|
1257
|
+
},
|
|
1258
|
+
item.id
|
|
1259
|
+
)) })
|
|
926
1260
|
]
|
|
927
1261
|
}
|
|
928
1262
|
);
|
|
929
1263
|
}
|
|
1264
|
+
function renderGroupedItems(items, activeIndex, depth, onSelect, onHover, renderItem, renderGroupHeader) {
|
|
1265
|
+
const elements = [];
|
|
1266
|
+
let lastGroup;
|
|
1267
|
+
items.forEach((item, index) => {
|
|
1268
|
+
if (item.group && item.group !== lastGroup) {
|
|
1269
|
+
lastGroup = item.group;
|
|
1270
|
+
elements.push(
|
|
1271
|
+
/* @__PURE__ */ jsx("div", { "data-suggestion-group-header": "", children: renderGroupHeader ? renderGroupHeader(item.group) : item.group }, `group-${item.group}`)
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
elements.push(
|
|
1275
|
+
/* @__PURE__ */ jsx(
|
|
1276
|
+
SuggestionItem,
|
|
1277
|
+
{
|
|
1278
|
+
item,
|
|
1279
|
+
index,
|
|
1280
|
+
isActive: index === activeIndex,
|
|
1281
|
+
depth,
|
|
1282
|
+
onSelect,
|
|
1283
|
+
onHover,
|
|
1284
|
+
renderItem
|
|
1285
|
+
},
|
|
1286
|
+
item.id
|
|
1287
|
+
)
|
|
1288
|
+
);
|
|
1289
|
+
});
|
|
1290
|
+
return elements;
|
|
1291
|
+
}
|
|
1292
|
+
function SuggestionItem({
|
|
1293
|
+
item,
|
|
1294
|
+
index,
|
|
1295
|
+
isActive,
|
|
1296
|
+
depth,
|
|
1297
|
+
onSelect,
|
|
1298
|
+
onHover,
|
|
1299
|
+
renderItem
|
|
1300
|
+
}) {
|
|
1301
|
+
const itemId = `mention-option-${item.id}`;
|
|
1302
|
+
return /* @__PURE__ */ jsx(
|
|
1303
|
+
"div",
|
|
1304
|
+
{
|
|
1305
|
+
...optionAttrs(itemId, isActive, index),
|
|
1306
|
+
"data-suggestion-item": "",
|
|
1307
|
+
"data-suggestion-item-active": isActive ? "" : void 0,
|
|
1308
|
+
"data-has-children": item.hasChildren ? "" : void 0,
|
|
1309
|
+
onMouseEnter: () => onHover(index),
|
|
1310
|
+
onClick: () => onSelect(item),
|
|
1311
|
+
children: renderItem ? renderItem(item, depth) : /* @__PURE__ */ jsx(DefaultSuggestionItem, { item })
|
|
1312
|
+
}
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
930
1315
|
function DefaultSuggestionItem({ item }) {
|
|
931
1316
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
932
1317
|
item.icon && /* @__PURE__ */ jsx("span", { "data-suggestion-item-icon": "", children: item.icon }),
|
|
@@ -935,25 +1320,48 @@ function DefaultSuggestionItem({ item }) {
|
|
|
935
1320
|
item.hasChildren && /* @__PURE__ */ jsx("span", { "data-suggestion-item-chevron": "", "aria-hidden": "true", children: "\u203A" })
|
|
936
1321
|
] });
|
|
937
1322
|
}
|
|
1323
|
+
var POPOVER_HEIGHT_ESTIMATE = 280;
|
|
1324
|
+
var POPOVER_WIDTH_ESTIMATE = 360;
|
|
938
1325
|
function usePopoverPosition(clientRect) {
|
|
939
1326
|
if (!clientRect) {
|
|
940
|
-
return { display: "none" };
|
|
1327
|
+
return { style: { display: "none" }, position: "below" };
|
|
941
1328
|
}
|
|
942
1329
|
const rect = clientRect();
|
|
943
1330
|
if (!rect) {
|
|
944
|
-
return { display: "none" };
|
|
1331
|
+
return { style: { display: "none" }, position: "below" };
|
|
1332
|
+
}
|
|
1333
|
+
const viewportH = typeof window !== "undefined" ? window.innerHeight : 800;
|
|
1334
|
+
const viewportW = typeof window !== "undefined" ? window.innerWidth : 1200;
|
|
1335
|
+
const spaceBelow = viewportH - rect.bottom;
|
|
1336
|
+
const shouldFlip = spaceBelow < POPOVER_HEIGHT_ESTIMATE && rect.top > spaceBelow;
|
|
1337
|
+
let left = rect.left;
|
|
1338
|
+
if (left + POPOVER_WIDTH_ESTIMATE > viewportW) {
|
|
1339
|
+
left = Math.max(0, viewportW - POPOVER_WIDTH_ESTIMATE);
|
|
1340
|
+
}
|
|
1341
|
+
if (shouldFlip) {
|
|
1342
|
+
return {
|
|
1343
|
+
style: {
|
|
1344
|
+
position: "fixed",
|
|
1345
|
+
left: `${left}px`,
|
|
1346
|
+
bottom: `${viewportH - rect.top + 4}px`,
|
|
1347
|
+
zIndex: 50
|
|
1348
|
+
},
|
|
1349
|
+
position: "above"
|
|
1350
|
+
};
|
|
945
1351
|
}
|
|
946
1352
|
return {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1353
|
+
style: {
|
|
1354
|
+
position: "fixed",
|
|
1355
|
+
left: `${left}px`,
|
|
1356
|
+
top: `${rect.bottom + 4}px`,
|
|
1357
|
+
zIndex: 50
|
|
1358
|
+
},
|
|
1359
|
+
position: "below"
|
|
951
1360
|
};
|
|
952
1361
|
}
|
|
953
1362
|
|
|
954
1363
|
// src/components/MentionsInput.tsx
|
|
955
1364
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
956
|
-
var LISTBOX_ID2 = "mentions-suggestion-listbox";
|
|
957
1365
|
var MentionsInput = forwardRef(
|
|
958
1366
|
function MentionsInput2({
|
|
959
1367
|
value,
|
|
@@ -967,10 +1375,29 @@ var MentionsInput = forwardRef(
|
|
|
967
1375
|
clearOnSubmit = true,
|
|
968
1376
|
maxLength,
|
|
969
1377
|
renderItem,
|
|
970
|
-
renderChip
|
|
1378
|
+
renderChip,
|
|
1379
|
+
renderEmpty,
|
|
1380
|
+
renderLoading,
|
|
1381
|
+
renderGroupHeader,
|
|
1382
|
+
onFocus,
|
|
1383
|
+
onBlur,
|
|
1384
|
+
onMentionAdd,
|
|
1385
|
+
onMentionRemove,
|
|
1386
|
+
onMentionClick,
|
|
1387
|
+
onMentionHover,
|
|
1388
|
+
minHeight,
|
|
1389
|
+
maxHeight,
|
|
1390
|
+
submitKey = "enter",
|
|
1391
|
+
allowTrigger,
|
|
1392
|
+
validateMention,
|
|
1393
|
+
portalContainer
|
|
971
1394
|
}, ref) {
|
|
972
|
-
const
|
|
973
|
-
const
|
|
1395
|
+
const instanceId = useId();
|
|
1396
|
+
const listboxId = `mentions-listbox-${instanceId}`;
|
|
1397
|
+
const { uiState, actions, callbacksRef } = useSuggestion(providers, {
|
|
1398
|
+
onMentionAdd
|
|
1399
|
+
});
|
|
1400
|
+
const { editor, getOutput, clear, setContent, focus } = useMentionsEditor({
|
|
974
1401
|
providers,
|
|
975
1402
|
value,
|
|
976
1403
|
onChange,
|
|
@@ -979,46 +1406,70 @@ var MentionsInput = forwardRef(
|
|
|
979
1406
|
placeholder,
|
|
980
1407
|
autoFocus,
|
|
981
1408
|
editable: !disabled,
|
|
982
|
-
callbacksRef
|
|
1409
|
+
callbacksRef,
|
|
1410
|
+
onFocus,
|
|
1411
|
+
onBlur,
|
|
1412
|
+
submitKey,
|
|
1413
|
+
onMentionRemove,
|
|
1414
|
+
onMentionClick,
|
|
1415
|
+
onMentionHover,
|
|
1416
|
+
allowTrigger,
|
|
1417
|
+
validateMention
|
|
983
1418
|
});
|
|
984
1419
|
useImperativeHandle(
|
|
985
1420
|
ref,
|
|
986
|
-
() => ({ clear, setContent, focus }),
|
|
987
|
-
[clear, setContent, focus]
|
|
1421
|
+
() => ({ clear, setContent, focus, getOutput }),
|
|
1422
|
+
[clear, setContent, focus, getOutput]
|
|
988
1423
|
);
|
|
989
1424
|
const isExpanded = uiState.state !== "idle";
|
|
990
1425
|
const handleHover = useCallback3((index) => {
|
|
991
1426
|
void index;
|
|
992
1427
|
}, []);
|
|
1428
|
+
const handleFocusEditor = useCallback3(() => {
|
|
1429
|
+
editor?.commands.focus();
|
|
1430
|
+
}, [editor]);
|
|
1431
|
+
const editorStyle = {};
|
|
1432
|
+
if (minHeight != null) editorStyle.minHeight = `${minHeight}px`;
|
|
1433
|
+
if (maxHeight != null) {
|
|
1434
|
+
editorStyle.maxHeight = `${maxHeight}px`;
|
|
1435
|
+
editorStyle.overflowY = "auto";
|
|
1436
|
+
}
|
|
1437
|
+
const suggestionList = isExpanded ? /* @__PURE__ */ jsx2(
|
|
1438
|
+
SuggestionList,
|
|
1439
|
+
{
|
|
1440
|
+
items: uiState.items,
|
|
1441
|
+
activeIndex: uiState.activeIndex,
|
|
1442
|
+
breadcrumbs: uiState.breadcrumbs,
|
|
1443
|
+
loading: uiState.loading,
|
|
1444
|
+
trigger: uiState.trigger,
|
|
1445
|
+
query: uiState.query,
|
|
1446
|
+
clientRect: uiState.clientRect,
|
|
1447
|
+
onSelect: (item) => actions.select(item),
|
|
1448
|
+
onHover: handleHover,
|
|
1449
|
+
onGoBack: actions.goBack,
|
|
1450
|
+
onSearchNested: actions.searchNested,
|
|
1451
|
+
onNavigateUp: actions.navigateUp,
|
|
1452
|
+
onNavigateDown: actions.navigateDown,
|
|
1453
|
+
onClose: actions.close,
|
|
1454
|
+
onFocusEditor: handleFocusEditor,
|
|
1455
|
+
renderItem,
|
|
1456
|
+
renderEmpty,
|
|
1457
|
+
renderLoading,
|
|
1458
|
+
renderGroupHeader,
|
|
1459
|
+
listboxId
|
|
1460
|
+
}
|
|
1461
|
+
) : null;
|
|
993
1462
|
return /* @__PURE__ */ jsxs2(
|
|
994
1463
|
"div",
|
|
995
1464
|
{
|
|
996
1465
|
className,
|
|
997
1466
|
"data-mentions-input": "",
|
|
998
1467
|
"data-disabled": disabled ? "" : void 0,
|
|
999
|
-
...comboboxAttrs(isExpanded,
|
|
1468
|
+
...comboboxAttrs(isExpanded, listboxId),
|
|
1000
1469
|
"aria-activedescendant": isExpanded && uiState.items[uiState.activeIndex] ? `mention-option-${uiState.items[uiState.activeIndex].id}` : void 0,
|
|
1001
1470
|
children: [
|
|
1002
|
-
/* @__PURE__ */ jsx2(EditorContent, { editor }),
|
|
1003
|
-
|
|
1004
|
-
SuggestionList,
|
|
1005
|
-
{
|
|
1006
|
-
items: uiState.items,
|
|
1007
|
-
activeIndex: uiState.activeIndex,
|
|
1008
|
-
breadcrumbs: uiState.breadcrumbs,
|
|
1009
|
-
loading: uiState.loading,
|
|
1010
|
-
trigger: uiState.trigger,
|
|
1011
|
-
clientRect: uiState.clientRect,
|
|
1012
|
-
onSelect: (item) => actions.select(item),
|
|
1013
|
-
onHover: handleHover,
|
|
1014
|
-
onGoBack: actions.goBack,
|
|
1015
|
-
onSearchNested: actions.searchNested,
|
|
1016
|
-
onNavigateUp: actions.navigateUp,
|
|
1017
|
-
onNavigateDown: actions.navigateDown,
|
|
1018
|
-
onClose: actions.close,
|
|
1019
|
-
renderItem
|
|
1020
|
-
}
|
|
1021
|
-
)
|
|
1471
|
+
/* @__PURE__ */ jsx2("div", { style: editorStyle, children: /* @__PURE__ */ jsx2(EditorContent, { editor }) }),
|
|
1472
|
+
portalContainer ? suggestionList && createPortal(suggestionList, portalContainer) : suggestionList
|
|
1022
1473
|
]
|
|
1023
1474
|
}
|
|
1024
1475
|
);
|