@relevaince/mentions 0.2.2 → 0.3.1
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 +41 -13
- package/dist/index.d.mts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +164 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +164 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { useEditor } from "@tiptap/react";
|
|
|
8
8
|
import StarterKit from "@tiptap/starter-kit";
|
|
9
9
|
import Placeholder from "@tiptap/extension-placeholder";
|
|
10
10
|
import { Extension as Extension2 } from "@tiptap/core";
|
|
11
|
+
import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state";
|
|
11
12
|
|
|
12
13
|
// src/core/mentionExtension.ts
|
|
13
14
|
import { mergeAttributes, Node } from "@tiptap/core";
|
|
@@ -114,6 +115,7 @@ var suggestionPluginKey = new PluginKey("mentionSuggestion");
|
|
|
114
115
|
function createSuggestionExtension(triggers, callbacksRef) {
|
|
115
116
|
return Extension.create({
|
|
116
117
|
name: "mentionSuggestion",
|
|
118
|
+
priority: 200,
|
|
117
119
|
addProseMirrorPlugins() {
|
|
118
120
|
const editor = this.editor;
|
|
119
121
|
let active = false;
|
|
@@ -297,10 +299,10 @@ function extractParagraphText(node) {
|
|
|
297
299
|
var MENTION_RE = /@(?:([^\[]+)\[)?\[([^\]]+)\]\((?:([^:)]+):)?([^)]+)\)/g;
|
|
298
300
|
function parseFromMarkdown(markdown) {
|
|
299
301
|
const lines = markdown.split("\n");
|
|
300
|
-
const content = lines.map((line) =>
|
|
301
|
-
|
|
302
|
-
content:
|
|
303
|
-
})
|
|
302
|
+
const content = lines.map((line) => {
|
|
303
|
+
const children = parseLine(line);
|
|
304
|
+
return children.length > 0 ? { type: "paragraph", content: children } : { type: "paragraph" };
|
|
305
|
+
});
|
|
304
306
|
return { type: "doc", content };
|
|
305
307
|
}
|
|
306
308
|
function parseLine(line) {
|
|
@@ -337,11 +339,15 @@ function parseLine(line) {
|
|
|
337
339
|
text: line.slice(lastIndex)
|
|
338
340
|
});
|
|
339
341
|
}
|
|
340
|
-
if (nodes.length === 0) {
|
|
341
|
-
nodes.push({ type: "text", text: "" });
|
|
342
|
-
}
|
|
343
342
|
return nodes;
|
|
344
343
|
}
|
|
344
|
+
function extractFromMarkdown(markdown) {
|
|
345
|
+
const doc = parseFromMarkdown(markdown);
|
|
346
|
+
return {
|
|
347
|
+
tokens: extractTokens(doc),
|
|
348
|
+
plainText: extractPlainText(doc)
|
|
349
|
+
};
|
|
350
|
+
}
|
|
345
351
|
|
|
346
352
|
// src/hooks/useMentionsEditor.ts
|
|
347
353
|
function buildOutput(editor) {
|
|
@@ -355,6 +361,7 @@ function buildOutput(editor) {
|
|
|
355
361
|
function createSubmitExtension(onSubmitRef, clearOnSubmitRef) {
|
|
356
362
|
return Extension2.create({
|
|
357
363
|
name: "submitShortcut",
|
|
364
|
+
priority: 150,
|
|
358
365
|
addKeyboardShortcuts() {
|
|
359
366
|
return {
|
|
360
367
|
"Mod-Enter": () => {
|
|
@@ -370,22 +377,35 @@ function createSubmitExtension(onSubmitRef, clearOnSubmitRef) {
|
|
|
370
377
|
}
|
|
371
378
|
});
|
|
372
379
|
}
|
|
380
|
+
var enterSubmitPluginKey = new PluginKey2("enterSubmit");
|
|
373
381
|
function createEnterExtension(onSubmitRef, clearOnSubmitRef) {
|
|
374
382
|
return Extension2.create({
|
|
375
383
|
name: "enterSubmit",
|
|
376
|
-
priority:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
+
priority: 150,
|
|
385
|
+
addProseMirrorPlugins() {
|
|
386
|
+
const editor = this.editor;
|
|
387
|
+
return [
|
|
388
|
+
new Plugin2({
|
|
389
|
+
key: enterSubmitPluginKey,
|
|
390
|
+
props: {
|
|
391
|
+
handleKeyDown(_view, event) {
|
|
392
|
+
if (event.key !== "Enter") return false;
|
|
393
|
+
if (event.shiftKey) {
|
|
394
|
+
editor.commands.splitBlock();
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
if (event.metaKey || event.ctrlKey) return false;
|
|
398
|
+
if (onSubmitRef.current) {
|
|
399
|
+
onSubmitRef.current(buildOutput(editor));
|
|
400
|
+
if (clearOnSubmitRef.current) {
|
|
401
|
+
editor.commands.clearContent(true);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return true;
|
|
384
405
|
}
|
|
385
406
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
};
|
|
407
|
+
})
|
|
408
|
+
];
|
|
389
409
|
}
|
|
390
410
|
});
|
|
391
411
|
}
|
|
@@ -438,10 +458,12 @@ function useMentionsEditor({
|
|
|
438
458
|
bulletList: false,
|
|
439
459
|
orderedList: false,
|
|
440
460
|
listItem: false,
|
|
441
|
-
horizontalRule: false
|
|
461
|
+
horizontalRule: false,
|
|
462
|
+
hardBreak: false
|
|
442
463
|
}),
|
|
443
464
|
Placeholder.configure({
|
|
444
|
-
placeholder: placeholder ?? "Type a message..."
|
|
465
|
+
placeholder: placeholder ?? "Type a message...",
|
|
466
|
+
showOnlyCurrent: false
|
|
445
467
|
}),
|
|
446
468
|
MentionNode,
|
|
447
469
|
suggestionExtension,
|
|
@@ -509,10 +531,17 @@ function useSuggestion(providers) {
|
|
|
509
531
|
);
|
|
510
532
|
const providerRef = useRef2(null);
|
|
511
533
|
const fetchItems = useCallback2(
|
|
512
|
-
async (provider, query, parent) => {
|
|
534
|
+
async (provider, query, parent, useSearchAll) => {
|
|
513
535
|
setUIState((prev) => ({ ...prev, loading: true, state: "loading" }));
|
|
514
536
|
try {
|
|
515
|
-
|
|
537
|
+
let items;
|
|
538
|
+
if (useSearchAll && provider.searchAll) {
|
|
539
|
+
items = await provider.searchAll(query);
|
|
540
|
+
} else if (parent && provider.getChildren) {
|
|
541
|
+
items = await provider.getChildren(parent, query);
|
|
542
|
+
} else {
|
|
543
|
+
items = await provider.getRootItems(query);
|
|
544
|
+
}
|
|
516
545
|
setUIState((prev) => ({
|
|
517
546
|
...prev,
|
|
518
547
|
items,
|
|
@@ -549,7 +578,11 @@ function useSuggestion(providers) {
|
|
|
549
578
|
trigger: props.trigger,
|
|
550
579
|
query: props.query
|
|
551
580
|
});
|
|
552
|
-
|
|
581
|
+
if (props.query.trim() && provider.searchAll) {
|
|
582
|
+
fetchItems(provider, props.query, void 0, true);
|
|
583
|
+
} else {
|
|
584
|
+
fetchItems(provider, props.query);
|
|
585
|
+
}
|
|
553
586
|
},
|
|
554
587
|
[fetchItems]
|
|
555
588
|
);
|
|
@@ -558,12 +591,25 @@ function useSuggestion(providers) {
|
|
|
558
591
|
const provider = providerRef.current;
|
|
559
592
|
if (!provider) return;
|
|
560
593
|
commandRef.current = props.command;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
594
|
+
const current = stateRef.current;
|
|
595
|
+
if (current.breadcrumbs.length > 0) {
|
|
596
|
+
setUIState((prev) => ({
|
|
597
|
+
...prev,
|
|
598
|
+
breadcrumbs: [],
|
|
599
|
+
clientRect: props.clientRect,
|
|
600
|
+
query: props.query,
|
|
601
|
+
activeIndex: 0
|
|
602
|
+
}));
|
|
603
|
+
} else {
|
|
604
|
+
setUIState((prev) => ({
|
|
605
|
+
...prev,
|
|
606
|
+
clientRect: props.clientRect,
|
|
607
|
+
query: props.query
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
610
|
+
if (props.query.trim() && provider.searchAll) {
|
|
611
|
+
fetchItems(provider, props.query, void 0, true);
|
|
612
|
+
} else {
|
|
567
613
|
fetchItems(provider, props.query);
|
|
568
614
|
}
|
|
569
615
|
},
|
|
@@ -605,7 +651,7 @@ function useSuggestion(providers) {
|
|
|
605
651
|
return;
|
|
606
652
|
}
|
|
607
653
|
if (commandRef.current) {
|
|
608
|
-
const rootLabel = current.breadcrumbs.length > 0 ? current.breadcrumbs[0].label : null;
|
|
654
|
+
const rootLabel = current.breadcrumbs.length > 0 ? current.breadcrumbs[0].label : selected.rootLabel ?? null;
|
|
609
655
|
commandRef.current({
|
|
610
656
|
id: selected.id,
|
|
611
657
|
label: selected.label,
|
|
@@ -639,6 +685,18 @@ function useSuggestion(providers) {
|
|
|
639
685
|
const close = useCallback2(() => {
|
|
640
686
|
setUIState(IDLE_STATE);
|
|
641
687
|
}, []);
|
|
688
|
+
const searchNested = useCallback2(
|
|
689
|
+
(query) => {
|
|
690
|
+
const provider = providerRef.current;
|
|
691
|
+
if (!provider) return;
|
|
692
|
+
const current = stateRef.current;
|
|
693
|
+
const parent = current.breadcrumbs[current.breadcrumbs.length - 1];
|
|
694
|
+
if (parent) {
|
|
695
|
+
fetchItems(provider, query, parent);
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
[fetchItems]
|
|
699
|
+
);
|
|
642
700
|
const onKeyDown = useCallback2(
|
|
643
701
|
({ event }) => {
|
|
644
702
|
const current = stateRef.current;
|
|
@@ -698,13 +756,14 @@ function useSuggestion(providers) {
|
|
|
698
756
|
navigateDown,
|
|
699
757
|
select,
|
|
700
758
|
goBack,
|
|
701
|
-
close
|
|
759
|
+
close,
|
|
760
|
+
searchNested
|
|
702
761
|
};
|
|
703
762
|
return { uiState, actions, callbacksRef };
|
|
704
763
|
}
|
|
705
764
|
|
|
706
765
|
// src/components/SuggestionList.tsx
|
|
707
|
-
import { useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
766
|
+
import { useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
708
767
|
|
|
709
768
|
// src/utils/ariaHelpers.ts
|
|
710
769
|
function comboboxAttrs(expanded, listboxId) {
|
|
@@ -744,10 +803,29 @@ function SuggestionList({
|
|
|
744
803
|
onSelect,
|
|
745
804
|
onHover,
|
|
746
805
|
onGoBack,
|
|
806
|
+
onSearchNested,
|
|
807
|
+
onNavigateUp,
|
|
808
|
+
onNavigateDown,
|
|
809
|
+
onClose,
|
|
747
810
|
renderItem
|
|
748
811
|
}) {
|
|
749
812
|
const listRef = useRef3(null);
|
|
813
|
+
const searchInputRef = useRef3(null);
|
|
750
814
|
const depth = breadcrumbs.length;
|
|
815
|
+
const [nestedQuery, setNestedQuery] = useState2("");
|
|
816
|
+
const breadcrumbKey = breadcrumbs.map((b) => b.id).join("/");
|
|
817
|
+
const prevBreadcrumbKey = useRef3(breadcrumbKey);
|
|
818
|
+
useEffect2(() => {
|
|
819
|
+
if (prevBreadcrumbKey.current !== breadcrumbKey) {
|
|
820
|
+
setNestedQuery("");
|
|
821
|
+
prevBreadcrumbKey.current = breadcrumbKey;
|
|
822
|
+
}
|
|
823
|
+
}, [breadcrumbKey]);
|
|
824
|
+
useEffect2(() => {
|
|
825
|
+
if (breadcrumbs.length > 0 && searchInputRef.current) {
|
|
826
|
+
requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
827
|
+
}
|
|
828
|
+
}, [breadcrumbKey, breadcrumbs.length]);
|
|
751
829
|
useEffect2(() => {
|
|
752
830
|
if (!listRef.current) return;
|
|
753
831
|
const active = listRef.current.querySelector('[aria-selected="true"]');
|
|
@@ -755,6 +833,35 @@ function SuggestionList({
|
|
|
755
833
|
}, [activeIndex]);
|
|
756
834
|
const style = usePopoverPosition(clientRect);
|
|
757
835
|
if (items.length === 0 && !loading) return null;
|
|
836
|
+
const handleSearchKeyDown = (e) => {
|
|
837
|
+
switch (e.key) {
|
|
838
|
+
case "ArrowDown":
|
|
839
|
+
e.preventDefault();
|
|
840
|
+
onNavigateDown?.();
|
|
841
|
+
break;
|
|
842
|
+
case "ArrowUp":
|
|
843
|
+
e.preventDefault();
|
|
844
|
+
onNavigateUp?.();
|
|
845
|
+
break;
|
|
846
|
+
case "Enter": {
|
|
847
|
+
e.preventDefault();
|
|
848
|
+
e.stopPropagation();
|
|
849
|
+
const selectedItem = items[activeIndex];
|
|
850
|
+
if (selectedItem) onSelect(selectedItem);
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
case "Escape":
|
|
854
|
+
e.preventDefault();
|
|
855
|
+
onClose?.();
|
|
856
|
+
break;
|
|
857
|
+
case "Backspace":
|
|
858
|
+
if (nestedQuery === "") {
|
|
859
|
+
e.preventDefault();
|
|
860
|
+
onGoBack();
|
|
861
|
+
}
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
};
|
|
758
865
|
return /* @__PURE__ */ jsxs(
|
|
759
866
|
"div",
|
|
760
867
|
{
|
|
@@ -779,6 +886,24 @@ function SuggestionList({
|
|
|
779
886
|
crumb.label
|
|
780
887
|
] }, crumb.id))
|
|
781
888
|
] }),
|
|
889
|
+
breadcrumbs.length > 0 && /* @__PURE__ */ jsx("div", { "data-suggestion-search": "", children: /* @__PURE__ */ jsx(
|
|
890
|
+
"input",
|
|
891
|
+
{
|
|
892
|
+
ref: searchInputRef,
|
|
893
|
+
type: "text",
|
|
894
|
+
"data-suggestion-search-input": "",
|
|
895
|
+
placeholder: "Search...",
|
|
896
|
+
value: nestedQuery,
|
|
897
|
+
onChange: (e) => {
|
|
898
|
+
const q = e.target.value;
|
|
899
|
+
setNestedQuery(q);
|
|
900
|
+
onSearchNested?.(q);
|
|
901
|
+
},
|
|
902
|
+
onKeyDown: handleSearchKeyDown,
|
|
903
|
+
autoComplete: "off",
|
|
904
|
+
spellCheck: false
|
|
905
|
+
}
|
|
906
|
+
) }),
|
|
782
907
|
loading && /* @__PURE__ */ jsx("div", { "data-suggestion-loading": "", children: "Loading..." }),
|
|
783
908
|
!loading && /* @__PURE__ */ jsx("div", { ...listboxAttrs(LISTBOX_ID, `${trigger ?? ""} suggestions`), children: items.map((item, index) => {
|
|
784
909
|
const isActive = index === activeIndex;
|
|
@@ -861,7 +986,8 @@ var MentionsInput = forwardRef(
|
|
|
861
986
|
[clear, setContent, focus]
|
|
862
987
|
);
|
|
863
988
|
const isExpanded = uiState.state !== "idle";
|
|
864
|
-
const handleHover = useCallback3((
|
|
989
|
+
const handleHover = useCallback3((index) => {
|
|
990
|
+
void index;
|
|
865
991
|
}, []);
|
|
866
992
|
return /* @__PURE__ */ jsxs2(
|
|
867
993
|
"div",
|
|
@@ -885,6 +1011,10 @@ var MentionsInput = forwardRef(
|
|
|
885
1011
|
onSelect: (item) => actions.select(item),
|
|
886
1012
|
onHover: handleHover,
|
|
887
1013
|
onGoBack: actions.goBack,
|
|
1014
|
+
onSearchNested: actions.searchNested,
|
|
1015
|
+
onNavigateUp: actions.navigateUp,
|
|
1016
|
+
onNavigateDown: actions.navigateDown,
|
|
1017
|
+
onClose: actions.close,
|
|
888
1018
|
renderItem
|
|
889
1019
|
}
|
|
890
1020
|
)
|
|
@@ -895,6 +1025,7 @@ var MentionsInput = forwardRef(
|
|
|
895
1025
|
);
|
|
896
1026
|
export {
|
|
897
1027
|
MentionsInput,
|
|
1028
|
+
extractFromMarkdown,
|
|
898
1029
|
parseFromMarkdown,
|
|
899
1030
|
serializeToMarkdown
|
|
900
1031
|
};
|