@domternal/vue 0.6.2 → 0.7.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 +12 -10
- package/dist/Domternal.d.ts +34 -0
- package/dist/Domternal.d.ts.map +1 -0
- package/dist/DomternalEditor.d.ts +224 -0
- package/dist/DomternalEditor.d.ts.map +1 -0
- package/dist/DomternalFloatingMenu.d.ts +94 -0
- package/dist/DomternalFloatingMenu.d.ts.map +1 -0
- package/dist/EditorContent.d.ts +44 -0
- package/dist/EditorContent.d.ts.map +1 -0
- package/dist/EditorContext.d.ts +38 -0
- package/dist/EditorContext.d.ts.map +1 -0
- package/dist/bubble-menu/DomternalBubbleMenu.d.ts +87 -0
- package/dist/bubble-menu/DomternalBubbleMenu.d.ts.map +1 -0
- package/dist/bubble-menu/useBubbleMenu.d.ts +56 -0
- package/dist/bubble-menu/useBubbleMenu.d.ts.map +1 -0
- package/dist/emoji-picker/DomternalEmojiPicker.d.ts +31 -0
- package/dist/emoji-picker/DomternalEmojiPicker.d.ts.map +1 -0
- package/dist/emoji-picker/useEmojiPicker.d.ts +24 -0
- package/dist/emoji-picker/useEmojiPicker.d.ts.map +1 -0
- package/dist/index.d.ts +145 -33
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +962 -103
- package/dist/index.js.map +1 -1
- package/dist/node-views/NodeViewContent.d.ts +30 -0
- package/dist/node-views/NodeViewContent.d.ts.map +1 -0
- package/dist/node-views/NodeViewWrapper.d.ts +29 -0
- package/dist/node-views/NodeViewWrapper.d.ts.map +1 -0
- package/dist/node-views/VueNodeViewContext.d.ts +27 -0
- package/dist/node-views/VueNodeViewContext.d.ts.map +1 -0
- package/dist/node-views/VueNodeViewRenderer.d.ts +88 -0
- package/dist/node-views/VueNodeViewRenderer.d.ts.map +1 -0
- package/dist/notion-color-picker/DomternalNotionColorPicker.d.ts +22 -0
- package/dist/notion-color-picker/DomternalNotionColorPicker.d.ts.map +1 -0
- package/dist/notion-color-picker/index.d.ts +5 -0
- package/dist/notion-color-picker/index.d.ts.map +1 -0
- package/dist/notion-color-picker/useNotionColorPicker.d.ts +35 -0
- package/dist/notion-color-picker/useNotionColorPicker.d.ts.map +1 -0
- package/dist/toolbar/DomternalToolbar.d.ts +41 -0
- package/dist/toolbar/DomternalToolbar.d.ts.map +1 -0
- package/dist/toolbar/ToolbarButton.d.ts +72 -0
- package/dist/toolbar/ToolbarButton.d.ts.map +1 -0
- package/dist/toolbar/ToolbarDropdown.d.ts +76 -0
- package/dist/toolbar/ToolbarDropdown.d.ts.map +1 -0
- package/dist/toolbar/ToolbarDropdownPanel.d.ts +34 -0
- package/dist/toolbar/ToolbarDropdownPanel.d.ts.map +1 -0
- package/dist/toolbar/useComputedStyle.d.ts +12 -0
- package/dist/toolbar/useComputedStyle.d.ts.map +1 -0
- package/dist/toolbar/useKeyboardNav.d.ts +9 -0
- package/dist/toolbar/useKeyboardNav.d.ts.map +1 -0
- package/dist/toolbar/useToolbarController.d.ts +24 -0
- package/dist/toolbar/useToolbarController.d.ts.map +1 -0
- package/dist/toolbar/useToolbarIcons.d.ts +12 -0
- package/dist/toolbar/useToolbarIcons.d.ts.map +1 -0
- package/dist/toolbar/useTooltip.d.ts +5 -0
- package/dist/toolbar/useTooltip.d.ts.map +1 -0
- package/dist/useEditor.d.ts +63 -0
- package/dist/useEditor.d.ts.map +1 -0
- package/dist/useEditorState.d.ts +28 -0
- package/dist/useEditorState.d.ts.map +1 -0
- package/dist/utils.d.ts +39 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { defineComponent, h, computed, Fragment, ref, onMounted,
|
|
2
|
-
import { PluginKey,
|
|
1
|
+
import { defineComponent, h, computed, Fragment, ref, watch, shallowRef, onMounted, nextTick, onScopeDispose, watchEffect, Teleport, inject, provide, getCurrentInstance, customRef, markRaw, shallowReactive, render } from 'vue';
|
|
2
|
+
import { positionFloatingOnce, PluginKey, positionFloating, ToolbarController, FloatingMenuController, defaultIcons, defaultBubbleContexts, createBubbleMenuPlugin, Editor, Document, Paragraph, Text, BaseKeymap, History } from '@domternal/core';
|
|
3
3
|
export { Editor, generateHTML, generateJSON, generateText } from '@domternal/core';
|
|
4
|
+
import { createFloatingMenuPlugin } from '@domternal/extension-block-menu';
|
|
4
5
|
|
|
5
6
|
// src/useEditor.ts
|
|
6
7
|
var DEFAULT_EXTENSIONS = [Document, Paragraph, Text, BaseKeymap, History];
|
|
@@ -46,7 +47,7 @@ function useEditor(options = {}) {
|
|
|
46
47
|
pendingContent = current.getJSON();
|
|
47
48
|
options.onDestroy?.();
|
|
48
49
|
const dom = current.view.dom;
|
|
49
|
-
const parent = dom
|
|
50
|
+
const parent = dom.parentNode;
|
|
50
51
|
if (parent) {
|
|
51
52
|
const clone = dom.cloneNode(true);
|
|
52
53
|
clone.style.pointerEvents = "none";
|
|
@@ -242,7 +243,8 @@ function provideEditor(editor) {
|
|
|
242
243
|
if (instance) {
|
|
243
244
|
const buildCtx = () => {
|
|
244
245
|
const ctx = Object.create(instance.appContext);
|
|
245
|
-
|
|
246
|
+
const instanceWithProvides = instance;
|
|
247
|
+
ctx.provides = instanceWithProvides.provides;
|
|
246
248
|
return ctx;
|
|
247
249
|
};
|
|
248
250
|
pendingAppContextStore.value = buildCtx();
|
|
@@ -344,8 +346,9 @@ function useToolbarController(editor, layout) {
|
|
|
344
346
|
function isDropdownActive(dropdown) {
|
|
345
347
|
if (dropdown.layout === "grid") return false;
|
|
346
348
|
if (dropdown.dynamicLabel) return false;
|
|
347
|
-
|
|
348
|
-
|
|
349
|
+
const ctl = controller;
|
|
350
|
+
if (!ctl) return false;
|
|
351
|
+
return dropdown.items.some((item) => ctl.activeMap.get(item.name) ?? false);
|
|
349
352
|
}
|
|
350
353
|
function getAriaExpanded(item) {
|
|
351
354
|
if (!item.emitEvent) return null;
|
|
@@ -559,7 +562,9 @@ function useKeyboardNav(controllerRef, toolbarRef, closeDropdown) {
|
|
|
559
562
|
const btn = document.activeElement;
|
|
560
563
|
if (btn?.getAttribute("aria-haspopup") && btn.closest(".dm-toolbar")) {
|
|
561
564
|
btn.click();
|
|
562
|
-
requestAnimationFrame(() =>
|
|
565
|
+
requestAnimationFrame(() => {
|
|
566
|
+
focusDropdownItem(0, true);
|
|
567
|
+
});
|
|
563
568
|
}
|
|
564
569
|
}
|
|
565
570
|
break;
|
|
@@ -594,31 +599,25 @@ function useKeyboardNav(controllerRef, toolbarRef, closeDropdown) {
|
|
|
594
599
|
}
|
|
595
600
|
|
|
596
601
|
// src/toolbar/useComputedStyle.ts
|
|
602
|
+
function resolveElementAtCursor(editor) {
|
|
603
|
+
const { from } = editor.state.selection;
|
|
604
|
+
const { node } = editor.view.domAtPos(from);
|
|
605
|
+
return node instanceof HTMLElement ? node : node.parentElement;
|
|
606
|
+
}
|
|
597
607
|
function getComputedStyleAtCursor(editor, prop) {
|
|
598
608
|
try {
|
|
599
|
-
const
|
|
600
|
-
const domAtPos = editor.view.domAtPos(from);
|
|
601
|
-
let node = domAtPos.node;
|
|
602
|
-
if (!(node instanceof HTMLElement)) {
|
|
603
|
-
node = node.parentElement;
|
|
604
|
-
}
|
|
609
|
+
const node = resolveElementAtCursor(editor);
|
|
605
610
|
if (!node) return null;
|
|
606
|
-
const
|
|
607
|
-
const inline = el.style.getPropertyValue(prop);
|
|
611
|
+
const inline = node.style.getPropertyValue(prop);
|
|
608
612
|
if (inline) return inline;
|
|
609
|
-
return window.getComputedStyle(
|
|
613
|
+
return window.getComputedStyle(node).getPropertyValue(prop) || null;
|
|
610
614
|
} catch {
|
|
611
615
|
return null;
|
|
612
616
|
}
|
|
613
617
|
}
|
|
614
618
|
function getInlineStyleAtCursor(editor, prop) {
|
|
615
619
|
try {
|
|
616
|
-
const
|
|
617
|
-
const domAtPos = editor.view.domAtPos(from);
|
|
618
|
-
let node = domAtPos.node;
|
|
619
|
-
if (!(node instanceof HTMLElement)) {
|
|
620
|
-
node = node.parentElement;
|
|
621
|
-
}
|
|
620
|
+
const node = resolveElementAtCursor(editor);
|
|
622
621
|
if (!node) return null;
|
|
623
622
|
return node.style.getPropertyValue(prop) || null;
|
|
624
623
|
} catch {
|
|
@@ -651,9 +650,15 @@ var ToolbarButton = defineComponent({
|
|
|
651
650
|
"aria-expanded": props.ariaExpanded === "true" ? true : void 0,
|
|
652
651
|
"aria-label": props.item.label,
|
|
653
652
|
title: props.tooltip,
|
|
654
|
-
onMousedown: (e) =>
|
|
655
|
-
|
|
656
|
-
|
|
653
|
+
onMousedown: (e) => {
|
|
654
|
+
e.preventDefault();
|
|
655
|
+
},
|
|
656
|
+
onClick: (e) => {
|
|
657
|
+
emit("click", props.item, e);
|
|
658
|
+
},
|
|
659
|
+
onFocus: () => {
|
|
660
|
+
emit("focus", props.item.name);
|
|
661
|
+
}
|
|
657
662
|
});
|
|
658
663
|
}
|
|
659
664
|
});
|
|
@@ -689,8 +694,12 @@ var ToolbarDropdownPanel = defineComponent({
|
|
|
689
694
|
"aria-label": sub.label,
|
|
690
695
|
title: sub.label,
|
|
691
696
|
style: { backgroundColor: sub.color },
|
|
692
|
-
onMousedown: (e) =>
|
|
693
|
-
|
|
697
|
+
onMousedown: (e) => {
|
|
698
|
+
e.preventDefault();
|
|
699
|
+
},
|
|
700
|
+
onClick: (e) => {
|
|
701
|
+
emit("itemClick", sub, e);
|
|
702
|
+
}
|
|
694
703
|
}) : h("button", {
|
|
695
704
|
key: sub.name,
|
|
696
705
|
type: "button",
|
|
@@ -699,8 +708,12 @@ var ToolbarDropdownPanel = defineComponent({
|
|
|
699
708
|
tabindex: -1,
|
|
700
709
|
"aria-label": sub.label,
|
|
701
710
|
innerHTML: getCachedItemContent(sub.icon, sub.label),
|
|
702
|
-
onMousedown: (e) =>
|
|
703
|
-
|
|
711
|
+
onMousedown: (e) => {
|
|
712
|
+
e.preventDefault();
|
|
713
|
+
},
|
|
714
|
+
onClick: (e) => {
|
|
715
|
+
emit("itemClick", sub, e);
|
|
716
|
+
}
|
|
704
717
|
})
|
|
705
718
|
)
|
|
706
719
|
);
|
|
@@ -725,8 +738,12 @@ var ToolbarDropdownPanel = defineComponent({
|
|
|
725
738
|
onVnodeMounted: (vnode) => {
|
|
726
739
|
if (sub.style && vnode.el) vnode.el.setAttribute("style", sub.style);
|
|
727
740
|
},
|
|
728
|
-
onMousedown: (e) =>
|
|
729
|
-
|
|
741
|
+
onMousedown: (e) => {
|
|
742
|
+
e.preventDefault();
|
|
743
|
+
},
|
|
744
|
+
onClick: (e) => {
|
|
745
|
+
emit("itemClick", sub, e);
|
|
746
|
+
}
|
|
730
747
|
})
|
|
731
748
|
)
|
|
732
749
|
);
|
|
@@ -765,9 +782,15 @@ var ToolbarDropdown = defineComponent({
|
|
|
765
782
|
disabled: props.isDisabled,
|
|
766
783
|
"data-dropdown": props.dropdown.name,
|
|
767
784
|
innerHTML: props.triggerHtml,
|
|
768
|
-
onMousedown: (e) =>
|
|
769
|
-
|
|
770
|
-
|
|
785
|
+
onMousedown: (e) => {
|
|
786
|
+
e.preventDefault();
|
|
787
|
+
},
|
|
788
|
+
onClick: () => {
|
|
789
|
+
emit("toggle", props.dropdown);
|
|
790
|
+
},
|
|
791
|
+
onFocus: () => {
|
|
792
|
+
emit("focus", props.dropdown.name);
|
|
793
|
+
}
|
|
771
794
|
})
|
|
772
795
|
];
|
|
773
796
|
if (props.isOpen) {
|
|
@@ -776,7 +799,9 @@ var ToolbarDropdown = defineComponent({
|
|
|
776
799
|
dropdown: props.dropdown,
|
|
777
800
|
isActive: props.isActive,
|
|
778
801
|
getCachedItemContent: props.getCachedItemContent,
|
|
779
|
-
onItemClick: (item, event) =>
|
|
802
|
+
onItemClick: (item, event) => {
|
|
803
|
+
emit("itemClick", item, event);
|
|
804
|
+
}
|
|
780
805
|
})
|
|
781
806
|
);
|
|
782
807
|
}
|
|
@@ -828,20 +853,23 @@ var DomternalToolbar = defineComponent({
|
|
|
828
853
|
closeDropdown();
|
|
829
854
|
}
|
|
830
855
|
if (item.emitEvent) {
|
|
831
|
-
const
|
|
856
|
+
const target = event?.target;
|
|
857
|
+
const anchor = target?.closest(".dm-toolbar-button") ?? target;
|
|
832
858
|
editor.emit(item.emitEvent, { anchorElement: anchor });
|
|
833
859
|
return;
|
|
834
860
|
}
|
|
835
861
|
executeCommand(item);
|
|
836
|
-
requestAnimationFrame(() =>
|
|
862
|
+
requestAnimationFrame(() => {
|
|
863
|
+
editor.view.focus();
|
|
864
|
+
});
|
|
837
865
|
}
|
|
838
866
|
function onDropdownItemClick(item, event) {
|
|
839
867
|
const editor = props.editor ?? contextEditor.value;
|
|
840
868
|
if (!editor) return;
|
|
841
869
|
let anchor;
|
|
842
870
|
if (item.emitEvent) {
|
|
843
|
-
const wrapper = event.target
|
|
844
|
-
anchor = wrapper?.querySelector(".dm-toolbar-dropdown-trigger");
|
|
871
|
+
const wrapper = event.target.closest(".dm-toolbar-dropdown-wrapper");
|
|
872
|
+
anchor = wrapper?.querySelector(".dm-toolbar-dropdown-trigger") ?? void 0;
|
|
845
873
|
}
|
|
846
874
|
closeDropdown();
|
|
847
875
|
if (item.emitEvent) {
|
|
@@ -849,7 +877,9 @@ var DomternalToolbar = defineComponent({
|
|
|
849
877
|
} else {
|
|
850
878
|
executeCommand(item);
|
|
851
879
|
}
|
|
852
|
-
requestAnimationFrame(() =>
|
|
880
|
+
requestAnimationFrame(() => {
|
|
881
|
+
editor.view.focus();
|
|
882
|
+
});
|
|
853
883
|
}
|
|
854
884
|
function onButtonFocus(name) {
|
|
855
885
|
const index = controllerRef.current?.getFlatIndex(name) ?? -1;
|
|
@@ -889,7 +919,9 @@ var DomternalToolbar = defineComponent({
|
|
|
889
919
|
tooltip: getTooltip(btn),
|
|
890
920
|
iconHtml: getCachedIcon(btn.icon),
|
|
891
921
|
ariaExpanded: getAriaExpanded(btn),
|
|
892
|
-
onClick: (clickedItem, event) =>
|
|
922
|
+
onClick: (clickedItem, event) => {
|
|
923
|
+
onButtonClick(clickedItem, event);
|
|
924
|
+
},
|
|
893
925
|
onFocus: onButtonFocus
|
|
894
926
|
});
|
|
895
927
|
}
|
|
@@ -898,18 +930,18 @@ var DomternalToolbar = defineComponent({
|
|
|
898
930
|
const activeItem = dd.items.find((sub) => controllerRef.current?.activeMap.get(sub.name));
|
|
899
931
|
let triggerHtml = getDropdownTriggerHtml(dd, activeItem);
|
|
900
932
|
if (dd.dynamicLabel && !activeItem && dd.computedStyleProperty) {
|
|
901
|
-
let
|
|
933
|
+
let computed7;
|
|
902
934
|
if (dd.computedStyleProperty === "font-family") {
|
|
903
|
-
|
|
904
|
-
if (
|
|
905
|
-
const first =
|
|
906
|
-
|
|
935
|
+
computed7 = getInlineStyleAtCursor(editor, dd.computedStyleProperty);
|
|
936
|
+
if (computed7) {
|
|
937
|
+
const first = computed7.split(",")[0]?.replace(/['"]+/g, "").trim();
|
|
938
|
+
computed7 = first ?? null;
|
|
907
939
|
}
|
|
908
940
|
} else {
|
|
909
|
-
|
|
941
|
+
computed7 = getComputedStyleAtCursor(editor, dd.computedStyleProperty);
|
|
910
942
|
}
|
|
911
|
-
if (
|
|
912
|
-
triggerHtml = `<span class="dm-toolbar-trigger-label">${
|
|
943
|
+
if (computed7) {
|
|
944
|
+
triggerHtml = `<span class="dm-toolbar-trigger-label">${computed7}</span>${DROPDOWN_CARET}`;
|
|
913
945
|
}
|
|
914
946
|
}
|
|
915
947
|
return h(ToolbarDropdown, {
|
|
@@ -936,6 +968,15 @@ var DomternalToolbar = defineComponent({
|
|
|
936
968
|
};
|
|
937
969
|
}
|
|
938
970
|
});
|
|
971
|
+
var INITIAL_TRAILING_STATE = {
|
|
972
|
+
isNodeSelection: false,
|
|
973
|
+
showColorPickerButton: false,
|
|
974
|
+
showBlockMenuButton: false,
|
|
975
|
+
blockMenuButtonDisabled: false,
|
|
976
|
+
currentTextColorVar: null,
|
|
977
|
+
currentBgColorVar: null,
|
|
978
|
+
hasAnyColor: false
|
|
979
|
+
};
|
|
939
980
|
function isInsideTableCell($pos) {
|
|
940
981
|
for (let d = $pos.depth; d > 0; d--) {
|
|
941
982
|
const name = $pos.node(d).type.name;
|
|
@@ -951,26 +992,37 @@ function findCellNode(pos) {
|
|
|
951
992
|
return null;
|
|
952
993
|
}
|
|
953
994
|
function useBubbleMenu(options) {
|
|
954
|
-
const { editor, shouldShow, placement = "top", offset = 8, updateDelay = 0, items, contexts } = options;
|
|
995
|
+
const { editor, shouldShow, placement = "top", offset = 8, updateDelay = 0, items, contexts: explicitContexts, icons: iconsRef } = options;
|
|
955
996
|
const menuRef = ref();
|
|
956
|
-
const
|
|
997
|
+
const cryptoRef = globalThis.crypto;
|
|
998
|
+
const pluginKey = new PluginKey(
|
|
999
|
+
"vueBubbleMenu-" + (cryptoRef?.randomUUID?.().slice(0, 8) ?? Math.random().toString(36).slice(2, 8))
|
|
1000
|
+
);
|
|
957
1001
|
const resolvedItems = shallowRef([]);
|
|
958
1002
|
const activeVersion = useDebouncedRef(0);
|
|
1003
|
+
const trailing = shallowRef(INITIAL_TRAILING_STATE);
|
|
959
1004
|
const activeMapRef = /* @__PURE__ */ new Map();
|
|
960
1005
|
const disabledMapRef = /* @__PURE__ */ new Map();
|
|
961
1006
|
let itemMap;
|
|
1007
|
+
let dropdownMap;
|
|
962
1008
|
let bubbleDefaults;
|
|
963
1009
|
let currentResolvedItems = [];
|
|
964
1010
|
let initialized = false;
|
|
965
1011
|
let stopEditorWatch = null;
|
|
966
1012
|
const doInit = (ed) => {
|
|
967
|
-
if (initialized ||
|
|
1013
|
+
if (initialized || ed.isDestroyed || !menuRef.value) return;
|
|
968
1014
|
initialized = true;
|
|
1015
|
+
const contexts = explicitContexts ?? (items ? void 0 : defaultBubbleContexts(ed));
|
|
1016
|
+
const exts = ed.extensionManager.extensions;
|
|
1017
|
+
const hasNotionColorPicker = exts.some((e) => e.name === "notionColorPicker");
|
|
1018
|
+
const hasBlockContextMenu = exts.some((e) => e.name === "blockContextMenu");
|
|
969
1019
|
itemMap = /* @__PURE__ */ new Map();
|
|
1020
|
+
dropdownMap = /* @__PURE__ */ new Map();
|
|
970
1021
|
for (const item of ed.toolbarItems) {
|
|
971
1022
|
if (item.type === "button") {
|
|
972
1023
|
itemMap.set(item.name, item);
|
|
973
1024
|
} else if (item.type === "dropdown") {
|
|
1025
|
+
dropdownMap.set(item.name, item);
|
|
974
1026
|
for (const sub of item.items) {
|
|
975
1027
|
itemMap.set(sub.name, sub);
|
|
976
1028
|
}
|
|
@@ -1001,7 +1053,7 @@ function useBubbleMenu(options) {
|
|
|
1001
1053
|
let sepIdx = 0;
|
|
1002
1054
|
for (const item of ctxItems) {
|
|
1003
1055
|
if (lastGroup !== void 0 && item.group !== lastGroup) {
|
|
1004
|
-
result.push({ type: "separator", name: `bsep-${sepIdx++}` });
|
|
1056
|
+
result.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
|
|
1005
1057
|
}
|
|
1006
1058
|
result.push(item);
|
|
1007
1059
|
lastGroup = item.group;
|
|
@@ -1013,8 +1065,13 @@ function useBubbleMenu(options) {
|
|
|
1013
1065
|
let sepIdx = 0;
|
|
1014
1066
|
for (const name of names) {
|
|
1015
1067
|
if (name === "|") {
|
|
1016
|
-
result.push({ type: "separator", name: `sep-${sepIdx++}` });
|
|
1068
|
+
result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
|
|
1017
1069
|
} else {
|
|
1070
|
+
const dropdown = dropdownMap.get(name);
|
|
1071
|
+
if (dropdown) {
|
|
1072
|
+
result.push(dropdown);
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1018
1075
|
const item = itemMap.get(name);
|
|
1019
1076
|
if (item) result.push(item);
|
|
1020
1077
|
}
|
|
@@ -1051,7 +1108,7 @@ function useBubbleMenu(options) {
|
|
|
1051
1108
|
return schemaItems.filter((item) => {
|
|
1052
1109
|
const markName = typeof item.isActive === "string" ? item.isActive : null;
|
|
1053
1110
|
if (!markName) return true;
|
|
1054
|
-
const markType = schema.marks
|
|
1111
|
+
const markType = schema.marks[markName];
|
|
1055
1112
|
if (!markType) return true;
|
|
1056
1113
|
return nodeType.allowsMarkType(markType);
|
|
1057
1114
|
});
|
|
@@ -1071,7 +1128,8 @@ function useBubbleMenu(options) {
|
|
|
1071
1128
|
};
|
|
1072
1129
|
} else {
|
|
1073
1130
|
shouldShowFn = ({ state }) => {
|
|
1074
|
-
if (state.selection.empty
|
|
1131
|
+
if (state.selection.empty) return false;
|
|
1132
|
+
if (state.selection.node) return bubbleDefaults.has(state.selection.node.type.name);
|
|
1075
1133
|
if (isInsideTableCell(state.selection.$from)) return false;
|
|
1076
1134
|
return state.selection.$from.parent.type.spec.marks !== "" || state.selection.$to.parent.type.spec.marks !== "";
|
|
1077
1135
|
};
|
|
@@ -1104,26 +1162,77 @@ function useBubbleMenu(options) {
|
|
|
1104
1162
|
canProxy = currentEd.can();
|
|
1105
1163
|
} catch {
|
|
1106
1164
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
activeMapRef.set(item.name, ToolbarController.resolveActive(currentEd, item));
|
|
1165
|
+
const trackButton = (btn) => {
|
|
1166
|
+
activeMapRef.set(btn.name, ToolbarController.resolveActive(currentEd, btn));
|
|
1110
1167
|
try {
|
|
1111
|
-
const canCmd = canProxy?.[
|
|
1112
|
-
disabledMapRef.set(
|
|
1168
|
+
const canCmd = typeof btn.command === "string" ? canProxy?.[btn.command] : void 0;
|
|
1169
|
+
disabledMapRef.set(btn.name, canCmd ? !(btn.commandArgs?.length ? canCmd(...btn.commandArgs) : canCmd()) : false);
|
|
1113
1170
|
} catch {
|
|
1114
|
-
disabledMapRef.set(
|
|
1171
|
+
disabledMapRef.set(btn.name, false);
|
|
1115
1172
|
}
|
|
1173
|
+
};
|
|
1174
|
+
for (const item of currentResolvedItems) {
|
|
1175
|
+
if (item.type === "separator") continue;
|
|
1176
|
+
if (item.type === "dropdown") {
|
|
1177
|
+
for (const sub of item.items) trackButton(sub);
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
trackButton(item);
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
const defaultItems = items ? resolveNames(items) : resolveNames(["bold", "italic", "underline"]);
|
|
1184
|
+
const syncTrailingState = (currentEd) => {
|
|
1185
|
+
const sel = currentEd.state.selection;
|
|
1186
|
+
const isNode = !!sel.node;
|
|
1187
|
+
let blockMenuDisabled = false;
|
|
1188
|
+
if (hasBlockContextMenu) {
|
|
1189
|
+
const { $from, $to } = currentEd.state.selection;
|
|
1190
|
+
if ($from.depth < 1 || $to.depth < 1) {
|
|
1191
|
+
blockMenuDisabled = true;
|
|
1192
|
+
} else {
|
|
1193
|
+
blockMenuDisabled = $from.before(1) !== $to.before(1);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
let textVar = null;
|
|
1197
|
+
let bgVar = null;
|
|
1198
|
+
let hasAny = false;
|
|
1199
|
+
if (hasNotionColorPicker) {
|
|
1200
|
+
const mark = currentEd.state.selection.$from.marks().find((m) => m.type.name === "textStyle");
|
|
1201
|
+
const attrs = mark?.attrs ?? {};
|
|
1202
|
+
const tToken = attrs.colorToken ?? null;
|
|
1203
|
+
const bToken = attrs.backgroundColorToken ?? null;
|
|
1204
|
+
textVar = tToken ? `var(--dm-block-text-${tToken})` : null;
|
|
1205
|
+
bgVar = bToken ? `var(--dm-block-bg-${bToken})` : null;
|
|
1206
|
+
hasAny = tToken !== null || bToken !== null;
|
|
1116
1207
|
}
|
|
1208
|
+
trailing.value = {
|
|
1209
|
+
isNodeSelection: isNode,
|
|
1210
|
+
showColorPickerButton: hasNotionColorPicker,
|
|
1211
|
+
showBlockMenuButton: hasBlockContextMenu,
|
|
1212
|
+
blockMenuButtonDisabled: blockMenuDisabled,
|
|
1213
|
+
currentTextColorVar: textVar,
|
|
1214
|
+
currentBgColorVar: bgVar,
|
|
1215
|
+
hasAnyColor: hasAny
|
|
1216
|
+
};
|
|
1117
1217
|
};
|
|
1118
1218
|
const transactionHandler = () => {
|
|
1119
1219
|
if (contexts) {
|
|
1120
1220
|
updateContextItems(ed, contexts, detectContext, resolveNames, getFormatItems, filterBySchema, bubbleDefaults, setItems);
|
|
1221
|
+
} else {
|
|
1222
|
+
const sel = ed.state.selection;
|
|
1223
|
+
if (sel.node && bubbleDefaults.has(sel.node.type.name)) {
|
|
1224
|
+
setItems(bubbleDefaults.get(sel.node.type.name) ?? []);
|
|
1225
|
+
} else {
|
|
1226
|
+
setItems(defaultItems);
|
|
1227
|
+
}
|
|
1121
1228
|
}
|
|
1122
1229
|
updateStates(ed);
|
|
1230
|
+
syncTrailingState(ed);
|
|
1123
1231
|
activeVersion.value++;
|
|
1124
1232
|
};
|
|
1125
1233
|
ed.on("transaction", transactionHandler);
|
|
1126
1234
|
updateStates(ed);
|
|
1235
|
+
syncTrailingState(ed);
|
|
1127
1236
|
initializedEditor = ed;
|
|
1128
1237
|
initializedHandler = transactionHandler;
|
|
1129
1238
|
};
|
|
@@ -1181,17 +1290,39 @@ function useBubbleMenu(options) {
|
|
|
1181
1290
|
const isItemDisabled = (item) => {
|
|
1182
1291
|
return disabledMapRef.get(item.name) ?? false;
|
|
1183
1292
|
};
|
|
1184
|
-
const executeCommand = (item) => {
|
|
1293
|
+
const executeCommand = (item, event) => {
|
|
1185
1294
|
const ed = editor.value;
|
|
1186
1295
|
if (!ed) return;
|
|
1187
1296
|
if (item.emitEvent) {
|
|
1188
|
-
|
|
1297
|
+
const anchor = event?.currentTarget ?? event?.target ?? null;
|
|
1298
|
+
ed.emit(item.emitEvent, { anchorElement: anchor });
|
|
1189
1299
|
return;
|
|
1190
1300
|
}
|
|
1191
1301
|
ToolbarController.executeItem(ed, item);
|
|
1192
1302
|
};
|
|
1193
1303
|
const getCachedIcon = (name) => {
|
|
1194
|
-
return defaultIcons[name] ?? "";
|
|
1304
|
+
return iconsRef?.value?.[name] ?? defaultIcons[name] ?? "";
|
|
1305
|
+
};
|
|
1306
|
+
const openColorPicker = (anchor) => {
|
|
1307
|
+
const ed = editor.value;
|
|
1308
|
+
if (!ed) return;
|
|
1309
|
+
ed.emit(
|
|
1310
|
+
"notionColorOpen",
|
|
1311
|
+
{ anchorElement: anchor }
|
|
1312
|
+
);
|
|
1313
|
+
};
|
|
1314
|
+
const openBlockContextMenu = (anchor) => {
|
|
1315
|
+
const ed = editor.value;
|
|
1316
|
+
if (!ed) return;
|
|
1317
|
+
const $from = ed.state.selection.$from;
|
|
1318
|
+
if ($from.depth < 1) return;
|
|
1319
|
+
const depth = $from.depth > 1 && $from.node($from.depth - 1).type.name !== "doc" ? $from.depth - 1 : $from.depth;
|
|
1320
|
+
const blockPos = $from.before(depth);
|
|
1321
|
+
const editorEl = ed.view.dom.closest(".dm-editor");
|
|
1322
|
+
editorEl?.dispatchEvent(new CustomEvent("dm:block-context-menu-open", {
|
|
1323
|
+
bubbles: false,
|
|
1324
|
+
detail: { blockPos, anchorElement: anchor }
|
|
1325
|
+
}));
|
|
1195
1326
|
};
|
|
1196
1327
|
return {
|
|
1197
1328
|
menuRef,
|
|
@@ -1200,11 +1331,15 @@ function useBubbleMenu(options) {
|
|
|
1200
1331
|
isItemDisabled,
|
|
1201
1332
|
executeCommand,
|
|
1202
1333
|
activeVersion,
|
|
1203
|
-
getCachedIcon
|
|
1334
|
+
getCachedIcon,
|
|
1335
|
+
trailing,
|
|
1336
|
+
openColorPicker,
|
|
1337
|
+
openBlockContextMenu
|
|
1204
1338
|
};
|
|
1205
1339
|
}
|
|
1206
1340
|
|
|
1207
1341
|
// src/bubble-menu/DomternalBubbleMenu.ts
|
|
1342
|
+
var DROPDOWN_CARET2 = '<svg class="dm-dropdown-caret" width="10" height="10" viewBox="0 0 10 10"><path d="M2 4l3 3 3-3" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
1208
1343
|
var DomternalBubbleMenu = defineComponent({
|
|
1209
1344
|
name: "DomternalBubbleMenu",
|
|
1210
1345
|
props: {
|
|
@@ -1214,10 +1349,13 @@ var DomternalBubbleMenu = defineComponent({
|
|
|
1214
1349
|
offset: { type: Number, default: 8 },
|
|
1215
1350
|
updateDelay: { type: Number, default: 0 },
|
|
1216
1351
|
items: { type: Array, default: void 0 },
|
|
1217
|
-
contexts: { type: Object, default: void 0 }
|
|
1352
|
+
contexts: { type: Object, default: void 0 },
|
|
1353
|
+
icons: { type: Object, default: void 0 }
|
|
1218
1354
|
},
|
|
1219
1355
|
setup(props, { slots }) {
|
|
1220
1356
|
const { editor: contextEditor } = useCurrentEditor();
|
|
1357
|
+
const editorRef = computed(() => props.editor ?? contextEditor.value);
|
|
1358
|
+
const iconsRef = computed(() => props.icons);
|
|
1221
1359
|
const {
|
|
1222
1360
|
menuRef,
|
|
1223
1361
|
resolvedItems,
|
|
@@ -1225,39 +1363,244 @@ var DomternalBubbleMenu = defineComponent({
|
|
|
1225
1363
|
isItemDisabled,
|
|
1226
1364
|
executeCommand,
|
|
1227
1365
|
activeVersion,
|
|
1228
|
-
getCachedIcon
|
|
1366
|
+
getCachedIcon,
|
|
1367
|
+
trailing,
|
|
1368
|
+
openColorPicker,
|
|
1369
|
+
openBlockContextMenu
|
|
1229
1370
|
} = useBubbleMenu({
|
|
1230
|
-
editor:
|
|
1371
|
+
editor: editorRef,
|
|
1231
1372
|
shouldShow: props.shouldShow,
|
|
1232
1373
|
placement: props.placement,
|
|
1233
1374
|
offset: props.offset,
|
|
1234
1375
|
updateDelay: props.updateDelay,
|
|
1235
1376
|
items: props.items,
|
|
1236
|
-
contexts: props.contexts
|
|
1377
|
+
contexts: props.contexts,
|
|
1378
|
+
icons: iconsRef
|
|
1237
1379
|
});
|
|
1380
|
+
const openDropdown = ref(null);
|
|
1381
|
+
const closeDropdown = () => {
|
|
1382
|
+
openDropdown.value = null;
|
|
1383
|
+
};
|
|
1384
|
+
const executeSubItem = (sub) => {
|
|
1385
|
+
closeDropdown();
|
|
1386
|
+
const ed = editorRef.value;
|
|
1387
|
+
if (!ed) return;
|
|
1388
|
+
ToolbarController.executeItem(ed, sub);
|
|
1389
|
+
requestAnimationFrame(() => {
|
|
1390
|
+
ed.view.focus();
|
|
1391
|
+
});
|
|
1392
|
+
};
|
|
1393
|
+
const colorBtnRef = ref();
|
|
1394
|
+
const blockMenuBtnRef = ref();
|
|
1238
1395
|
return () => {
|
|
1239
1396
|
void activeVersion.value;
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1397
|
+
const t = trailing.value;
|
|
1398
|
+
const children = [];
|
|
1399
|
+
for (const item of resolvedItems.value) {
|
|
1400
|
+
if (item.type === "separator") {
|
|
1401
|
+
children.push(h("span", { key: item.name, class: "dm-toolbar-separator", role: "separator" }));
|
|
1402
|
+
continue;
|
|
1403
|
+
}
|
|
1404
|
+
if (item.type === "dropdown") {
|
|
1405
|
+
children.push(
|
|
1406
|
+
h(BubbleDropdown, {
|
|
1407
|
+
key: item.name,
|
|
1408
|
+
dropdown: item,
|
|
1409
|
+
isOpen: openDropdown.value === item.name,
|
|
1410
|
+
isItemActive,
|
|
1411
|
+
getCachedIcon,
|
|
1412
|
+
activeVersion: activeVersion.value,
|
|
1413
|
+
executeSubItem,
|
|
1414
|
+
onToggle: () => {
|
|
1415
|
+
openDropdown.value = openDropdown.value === item.name ? null : item.name;
|
|
1416
|
+
},
|
|
1417
|
+
onClose: closeDropdown
|
|
1418
|
+
})
|
|
1419
|
+
);
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
const btn = item;
|
|
1423
|
+
const active = isItemActive(btn);
|
|
1424
|
+
children.push(h("button", {
|
|
1425
|
+
key: btn.name,
|
|
1426
|
+
type: "button",
|
|
1427
|
+
class: ["dm-toolbar-button", active && "dm-toolbar-button--active"],
|
|
1428
|
+
disabled: isItemDisabled(btn),
|
|
1429
|
+
"aria-label": btn.label,
|
|
1430
|
+
"aria-pressed": active,
|
|
1431
|
+
title: btn.label,
|
|
1432
|
+
innerHTML: getCachedIcon(btn.icon),
|
|
1433
|
+
onMousedown: (e) => {
|
|
1434
|
+
e.preventDefault();
|
|
1435
|
+
},
|
|
1436
|
+
onClick: (e) => {
|
|
1437
|
+
executeCommand(btn, e);
|
|
1438
|
+
}
|
|
1439
|
+
}));
|
|
1440
|
+
}
|
|
1441
|
+
if (t.showColorPickerButton && !t.isNodeSelection) {
|
|
1442
|
+
children.push(
|
|
1443
|
+
h("span", { class: "dm-toolbar-separator", role: "separator" }),
|
|
1444
|
+
h("button", {
|
|
1445
|
+
ref: colorBtnRef,
|
|
1446
|
+
type: "button",
|
|
1447
|
+
class: ["dm-toolbar-button", "dm-ncp-trigger", t.hasAnyColor && "dm-toolbar-button--active"],
|
|
1448
|
+
title: "Text and background color",
|
|
1449
|
+
"aria-label": "Text and background color",
|
|
1450
|
+
"aria-haspopup": "dialog",
|
|
1451
|
+
onMousedown: (e) => {
|
|
1452
|
+
e.preventDefault();
|
|
1453
|
+
},
|
|
1454
|
+
onClick: () => {
|
|
1455
|
+
if (colorBtnRef.value) openColorPicker(colorBtnRef.value);
|
|
1456
|
+
}
|
|
1457
|
+
}, [
|
|
1458
|
+
h("span", {
|
|
1459
|
+
class: "dm-ncp-trigger-glyph",
|
|
1460
|
+
style: t.currentTextColorVar ? { color: t.currentTextColorVar } : void 0
|
|
1461
|
+
}, "A"),
|
|
1462
|
+
h("span", {
|
|
1463
|
+
class: "dm-ncp-trigger-underline",
|
|
1464
|
+
style: t.currentBgColorVar ? { backgroundColor: t.currentBgColorVar } : void 0
|
|
1465
|
+
})
|
|
1466
|
+
])
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
if (t.showBlockMenuButton && !t.isNodeSelection) {
|
|
1470
|
+
children.push(
|
|
1471
|
+
h("span", { class: "dm-toolbar-separator", role: "separator" }),
|
|
1472
|
+
h("button", {
|
|
1473
|
+
ref: blockMenuBtnRef,
|
|
1474
|
+
type: "button",
|
|
1475
|
+
class: "dm-toolbar-button",
|
|
1476
|
+
disabled: t.blockMenuButtonDisabled,
|
|
1477
|
+
title: t.blockMenuButtonDisabled ? "Block actions (select within a single block)" : "More options",
|
|
1478
|
+
"aria-label": "More options",
|
|
1479
|
+
"aria-haspopup": "menu",
|
|
1480
|
+
innerHTML: getCachedIcon("dotsThree"),
|
|
1481
|
+
onMousedown: (e) => {
|
|
1482
|
+
e.preventDefault();
|
|
1483
|
+
},
|
|
1484
|
+
onClick: () => {
|
|
1485
|
+
if (blockMenuBtnRef.value) openBlockContextMenu(blockMenuBtnRef.value);
|
|
1486
|
+
}
|
|
1487
|
+
})
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
const slotContent = slots["default"]?.();
|
|
1491
|
+
if (slotContent) children.push(...slotContent);
|
|
1492
|
+
return h("div", {
|
|
1493
|
+
ref: menuRef,
|
|
1494
|
+
class: "dm-bubble-menu",
|
|
1495
|
+
role: "toolbar",
|
|
1496
|
+
"aria-label": "Text formatting"
|
|
1497
|
+
}, children);
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
var BubbleDropdown = defineComponent({
|
|
1502
|
+
name: "BubbleDropdown",
|
|
1503
|
+
props: {
|
|
1504
|
+
dropdown: { type: Object, required: true },
|
|
1505
|
+
isOpen: { type: Boolean, required: true },
|
|
1506
|
+
isItemActive: { type: Function, required: true },
|
|
1507
|
+
getCachedIcon: { type: Function, required: true },
|
|
1508
|
+
activeVersion: { type: Number, required: true },
|
|
1509
|
+
executeSubItem: { type: Function, required: true }
|
|
1510
|
+
},
|
|
1511
|
+
emits: ["toggle", "close"],
|
|
1512
|
+
setup(props, { emit }) {
|
|
1513
|
+
const triggerRef = ref();
|
|
1514
|
+
const panelRef = ref();
|
|
1515
|
+
watch(
|
|
1516
|
+
() => props.isOpen,
|
|
1517
|
+
(open, _old, onCleanup) => {
|
|
1518
|
+
if (!open) return;
|
|
1519
|
+
const trigger = triggerRef.value;
|
|
1520
|
+
const panel = panelRef.value;
|
|
1521
|
+
if (!trigger || !panel) return;
|
|
1522
|
+
const cleanupFloating = positionFloatingOnce(trigger, panel, {
|
|
1523
|
+
placement: "bottom-start",
|
|
1524
|
+
offsetValue: 4
|
|
1525
|
+
});
|
|
1526
|
+
const controller = new AbortController();
|
|
1527
|
+
const { signal } = controller;
|
|
1528
|
+
document.addEventListener("mousedown", (e) => {
|
|
1529
|
+
const target = e.target;
|
|
1530
|
+
if (!target) return;
|
|
1531
|
+
if (panel.contains(target)) return;
|
|
1532
|
+
if (trigger.contains(target)) return;
|
|
1533
|
+
emit("close");
|
|
1534
|
+
}, { signal });
|
|
1535
|
+
document.addEventListener("keydown", (e) => {
|
|
1536
|
+
if (e.key === "Escape") {
|
|
1537
|
+
e.preventDefault();
|
|
1538
|
+
emit("close");
|
|
1244
1539
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1540
|
+
}, { signal });
|
|
1541
|
+
const editorEl = trigger.closest(".dm-editor");
|
|
1542
|
+
editorEl?.addEventListener("dm:dismiss-overlays", () => {
|
|
1543
|
+
emit("close");
|
|
1544
|
+
}, { signal });
|
|
1545
|
+
onCleanup(() => {
|
|
1546
|
+
controller.abort();
|
|
1547
|
+
cleanupFloating();
|
|
1548
|
+
});
|
|
1549
|
+
},
|
|
1550
|
+
{ flush: "post" }
|
|
1551
|
+
);
|
|
1552
|
+
return () => {
|
|
1553
|
+
void props.activeVersion;
|
|
1554
|
+
const dropdown = props.dropdown;
|
|
1555
|
+
const dropdownActive = dropdown.items.some((sub) => props.isItemActive(sub));
|
|
1556
|
+
const activeChild = dropdown.dynamicIcon ? dropdown.items.find((sub) => props.isItemActive(sub)) : void 0;
|
|
1557
|
+
const triggerIcon = activeChild?.icon ?? dropdown.icon;
|
|
1558
|
+
const triggerHtml = props.getCachedIcon(triggerIcon) + DROPDOWN_CARET2;
|
|
1559
|
+
return h("div", {
|
|
1560
|
+
class: "dm-toolbar-dropdown-wrapper",
|
|
1561
|
+
"data-dropdown-wrapper": dropdown.name
|
|
1562
|
+
}, [
|
|
1563
|
+
h("button", {
|
|
1564
|
+
ref: triggerRef,
|
|
1565
|
+
type: "button",
|
|
1566
|
+
class: ["dm-toolbar-button", "dm-toolbar-dropdown-trigger", dropdownActive && "dm-toolbar-button--active"],
|
|
1567
|
+
"aria-expanded": props.isOpen,
|
|
1568
|
+
"aria-haspopup": "true",
|
|
1569
|
+
"aria-label": dropdown.label,
|
|
1570
|
+
title: dropdown.label,
|
|
1571
|
+
"data-dropdown": dropdown.name,
|
|
1572
|
+
innerHTML: triggerHtml,
|
|
1573
|
+
onMousedown: (e) => {
|
|
1574
|
+
e.preventDefault();
|
|
1575
|
+
},
|
|
1576
|
+
onClick: () => {
|
|
1577
|
+
emit("toggle");
|
|
1578
|
+
}
|
|
1579
|
+
}),
|
|
1580
|
+
props.isOpen ? h("div", {
|
|
1581
|
+
ref: panelRef,
|
|
1582
|
+
class: "dm-toolbar-dropdown-panel",
|
|
1583
|
+
role: "menu",
|
|
1584
|
+
"data-dm-editor-ui": "",
|
|
1585
|
+
"data-dropdown-panel": dropdown.name
|
|
1586
|
+
}, dropdown.items.map((sub) => {
|
|
1587
|
+
const subActive = props.isItemActive(sub);
|
|
1588
|
+
const subHtml = `${props.getCachedIcon(sub.icon)} ${sub.label}`;
|
|
1247
1589
|
return h("button", {
|
|
1248
|
-
key:
|
|
1590
|
+
key: sub.name,
|
|
1249
1591
|
type: "button",
|
|
1250
|
-
class: ["dm-toolbar-
|
|
1251
|
-
|
|
1252
|
-
"aria-label":
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
onClick: () =>
|
|
1592
|
+
class: ["dm-toolbar-dropdown-item", subActive && "dm-toolbar-dropdown-item--active"],
|
|
1593
|
+
role: "menuitem",
|
|
1594
|
+
"aria-label": sub.label,
|
|
1595
|
+
innerHTML: subHtml,
|
|
1596
|
+
onMousedown: (e) => {
|
|
1597
|
+
e.preventDefault();
|
|
1598
|
+
},
|
|
1599
|
+
onClick: () => {
|
|
1600
|
+
props.executeSubItem(sub);
|
|
1601
|
+
}
|
|
1258
1602
|
});
|
|
1259
|
-
})
|
|
1260
|
-
slots["default"]?.()
|
|
1603
|
+
})) : null
|
|
1261
1604
|
]);
|
|
1262
1605
|
};
|
|
1263
1606
|
}
|
|
@@ -1267,25 +1610,53 @@ var DomternalFloatingMenu = defineComponent({
|
|
|
1267
1610
|
props: {
|
|
1268
1611
|
editor: { type: Object, default: void 0 },
|
|
1269
1612
|
shouldShow: { type: Function, default: void 0 },
|
|
1270
|
-
offset: { type: Number, default: 0 }
|
|
1613
|
+
offset: { type: Number, default: 0 },
|
|
1614
|
+
items: {
|
|
1615
|
+
type: [Array, Function],
|
|
1616
|
+
default: void 0
|
|
1617
|
+
},
|
|
1618
|
+
keymap: { type: Object, default: void 0 },
|
|
1619
|
+
icons: { type: Object, default: void 0 },
|
|
1620
|
+
requireExplicitTrigger: { type: Boolean, default: false }
|
|
1271
1621
|
},
|
|
1272
1622
|
setup(props, { slots }) {
|
|
1273
1623
|
const { editor: contextEditor } = useCurrentEditor();
|
|
1274
1624
|
const menuRef = ref();
|
|
1275
|
-
const
|
|
1625
|
+
const cryptoRef = globalThis.crypto;
|
|
1626
|
+
const pluginKey = new PluginKey(
|
|
1627
|
+
"vueFloatingMenu-" + (cryptoRef?.randomUUID?.().slice(0, 8) ?? Math.random().toString(36).slice(2, 8))
|
|
1628
|
+
);
|
|
1629
|
+
const controller = shallowRef(null);
|
|
1630
|
+
const version = ref(0);
|
|
1276
1631
|
let registered = false;
|
|
1277
1632
|
let stopWatch = null;
|
|
1633
|
+
let currentEditor = null;
|
|
1634
|
+
const resolveIcon = (name) => {
|
|
1635
|
+
if (!name) return "";
|
|
1636
|
+
return props.icons?.[name] ?? defaultIcons[name] ?? "";
|
|
1637
|
+
};
|
|
1278
1638
|
const doRegister = (editor) => {
|
|
1279
1639
|
if (registered || editor.isDestroyed || !menuRef.value) return;
|
|
1280
1640
|
registered = true;
|
|
1641
|
+
currentEditor = editor;
|
|
1281
1642
|
const plugin = createFloatingMenuPlugin({
|
|
1282
1643
|
pluginKey,
|
|
1283
1644
|
editor,
|
|
1284
1645
|
element: menuRef.value,
|
|
1285
1646
|
...props.shouldShow && { shouldShow: props.shouldShow },
|
|
1286
|
-
offset: props.offset
|
|
1647
|
+
offset: props.offset,
|
|
1648
|
+
...props.keymap && { keymap: props.keymap },
|
|
1649
|
+
requireExplicitTrigger: props.requireExplicitTrigger
|
|
1287
1650
|
});
|
|
1288
1651
|
editor.registerPlugin(plugin);
|
|
1652
|
+
const hasCustomSlot = Boolean(slots["default"]);
|
|
1653
|
+
if (!hasCustomSlot) {
|
|
1654
|
+
const ctl = new FloatingMenuController(editor, () => {
|
|
1655
|
+
version.value++;
|
|
1656
|
+
}, props.items);
|
|
1657
|
+
ctl.subscribe();
|
|
1658
|
+
controller.value = ctl;
|
|
1659
|
+
}
|
|
1289
1660
|
};
|
|
1290
1661
|
onMounted(() => {
|
|
1291
1662
|
const ed = props.editor ?? contextEditor.value;
|
|
@@ -1304,14 +1675,149 @@ var DomternalFloatingMenu = defineComponent({
|
|
|
1304
1675
|
);
|
|
1305
1676
|
}
|
|
1306
1677
|
});
|
|
1678
|
+
watch(
|
|
1679
|
+
() => [controller.value?.focusedIndex ?? -1, version.value],
|
|
1680
|
+
([focusedIndex]) => {
|
|
1681
|
+
if (focusedIndex < 0 || !menuRef.value) return;
|
|
1682
|
+
void nextTick(() => {
|
|
1683
|
+
const target = menuRef.value?.querySelector(
|
|
1684
|
+
`[data-floating-menu-index="${String(focusedIndex)}"]`
|
|
1685
|
+
);
|
|
1686
|
+
target?.focus();
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
);
|
|
1307
1690
|
onScopeDispose(() => {
|
|
1308
1691
|
stopWatch?.();
|
|
1309
|
-
|
|
1692
|
+
controller.value?.destroy();
|
|
1693
|
+
controller.value = null;
|
|
1694
|
+
const editor = currentEditor ?? props.editor ?? contextEditor.value;
|
|
1310
1695
|
if (editor && !editor.isDestroyed) {
|
|
1311
1696
|
editor.unregisterPlugin(pluginKey);
|
|
1312
1697
|
}
|
|
1313
1698
|
});
|
|
1314
|
-
|
|
1699
|
+
const onItemClick = (item) => {
|
|
1700
|
+
const ctl = controller.value;
|
|
1701
|
+
const ed = currentEditor;
|
|
1702
|
+
if (!ctl || !ed) return;
|
|
1703
|
+
ctl.execute(item);
|
|
1704
|
+
requestAnimationFrame(() => {
|
|
1705
|
+
ed.view.focus();
|
|
1706
|
+
});
|
|
1707
|
+
};
|
|
1708
|
+
const onMenuKeyDown = (e) => {
|
|
1709
|
+
const ctl = controller.value;
|
|
1710
|
+
if (!ctl) return;
|
|
1711
|
+
const focused = ctl.focusedItem();
|
|
1712
|
+
switch (e.key) {
|
|
1713
|
+
case "ArrowDown":
|
|
1714
|
+
e.preventDefault();
|
|
1715
|
+
ctl.next();
|
|
1716
|
+
return;
|
|
1717
|
+
case "ArrowUp":
|
|
1718
|
+
e.preventDefault();
|
|
1719
|
+
ctl.prev();
|
|
1720
|
+
return;
|
|
1721
|
+
case "Home":
|
|
1722
|
+
e.preventDefault();
|
|
1723
|
+
ctl.first();
|
|
1724
|
+
return;
|
|
1725
|
+
case "End":
|
|
1726
|
+
e.preventDefault();
|
|
1727
|
+
ctl.last();
|
|
1728
|
+
return;
|
|
1729
|
+
case "Escape":
|
|
1730
|
+
e.preventDefault();
|
|
1731
|
+
e.stopPropagation();
|
|
1732
|
+
ctl.leaveMenu();
|
|
1733
|
+
currentEditor?.view.focus();
|
|
1734
|
+
return;
|
|
1735
|
+
case "Enter":
|
|
1736
|
+
case " ":
|
|
1737
|
+
if (focused) {
|
|
1738
|
+
e.preventDefault();
|
|
1739
|
+
onItemClick(focused);
|
|
1740
|
+
}
|
|
1741
|
+
return;
|
|
1742
|
+
default:
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
return () => {
|
|
1747
|
+
if (slots["default"]) {
|
|
1748
|
+
return h(
|
|
1749
|
+
"div",
|
|
1750
|
+
{ ref: menuRef, class: "dm-floating-menu", "data-dm-editor-ui": "" },
|
|
1751
|
+
slots["default"]()
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
const ctl = controller.value;
|
|
1755
|
+
void version.value;
|
|
1756
|
+
const groups = ctl?.groups ?? [];
|
|
1757
|
+
const focusedIndex = ctl?.focusedIndex ?? -1;
|
|
1758
|
+
const flatNames = groups.flatMap((g) => g.items.map((i) => i.name));
|
|
1759
|
+
return h(
|
|
1760
|
+
"div",
|
|
1761
|
+
{
|
|
1762
|
+
ref: menuRef,
|
|
1763
|
+
class: "dm-floating-menu",
|
|
1764
|
+
role: "menu",
|
|
1765
|
+
"aria-label": "Insert block",
|
|
1766
|
+
"data-dm-editor-ui": "",
|
|
1767
|
+
onKeydown: onMenuKeyDown
|
|
1768
|
+
},
|
|
1769
|
+
groups.map((group, gi) => {
|
|
1770
|
+
const groupId = `dm-fm-g${String(gi)}`;
|
|
1771
|
+
return h("div", { key: group.name || `__group-${String(gi)}`, class: "dm-floating-menu-group-wrapper" }, [
|
|
1772
|
+
group.name ? h("div", { class: "dm-floating-menu-group-label", id: groupId }, group.name) : null,
|
|
1773
|
+
h(
|
|
1774
|
+
"div",
|
|
1775
|
+
{
|
|
1776
|
+
class: "dm-floating-menu-group",
|
|
1777
|
+
role: "group",
|
|
1778
|
+
...group.name ? { "aria-labelledby": groupId } : {}
|
|
1779
|
+
},
|
|
1780
|
+
group.items.map((item) => {
|
|
1781
|
+
const flatIndex = flatNames.indexOf(item.name);
|
|
1782
|
+
const isFocused = flatIndex === focusedIndex;
|
|
1783
|
+
const disabled = ctl?.isDisabled(item) ?? false;
|
|
1784
|
+
const iconHtml = resolveIcon(item.icon);
|
|
1785
|
+
return h(
|
|
1786
|
+
"button",
|
|
1787
|
+
{
|
|
1788
|
+
key: item.name,
|
|
1789
|
+
type: "button",
|
|
1790
|
+
role: "menuitem",
|
|
1791
|
+
class: "dm-floating-menu-item",
|
|
1792
|
+
"data-floating-menu-item": item.name,
|
|
1793
|
+
"data-floating-menu-index": String(flatIndex),
|
|
1794
|
+
tabindex: isFocused || focusedIndex < 0 && flatIndex === 0 ? 0 : -1,
|
|
1795
|
+
"aria-disabled": disabled ? "true" : void 0,
|
|
1796
|
+
"aria-keyshortcuts": item.shortcut,
|
|
1797
|
+
disabled,
|
|
1798
|
+
onMousedown: (e) => {
|
|
1799
|
+
e.preventDefault();
|
|
1800
|
+
},
|
|
1801
|
+
onClick: () => {
|
|
1802
|
+
onItemClick(item);
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
[
|
|
1806
|
+
iconHtml ? h("span", {
|
|
1807
|
+
class: "dm-floating-menu-item-icon",
|
|
1808
|
+
"aria-hidden": "true",
|
|
1809
|
+
innerHTML: iconHtml
|
|
1810
|
+
}) : null,
|
|
1811
|
+
h("span", { class: "dm-floating-menu-item-label" }, item.label),
|
|
1812
|
+
item.shortcut ? h("span", { class: "dm-floating-menu-item-shortcut", "aria-hidden": "true" }, item.shortcut) : null
|
|
1813
|
+
]
|
|
1814
|
+
);
|
|
1815
|
+
})
|
|
1816
|
+
)
|
|
1817
|
+
]);
|
|
1818
|
+
})
|
|
1819
|
+
);
|
|
1820
|
+
};
|
|
1315
1821
|
}
|
|
1316
1822
|
});
|
|
1317
1823
|
var SCROLL_SETTLE_MS = 50;
|
|
@@ -1350,7 +1856,8 @@ function useEmojiPicker(editor, emojis) {
|
|
|
1350
1856
|
const frequentlyUsed = computed(() => {
|
|
1351
1857
|
if (!isOpen.value) return [];
|
|
1352
1858
|
const storage = getEmojiStorage(editor.value);
|
|
1353
|
-
|
|
1859
|
+
if (!storage) return [];
|
|
1860
|
+
const getFreq = storage["getFrequentlyUsed"];
|
|
1354
1861
|
if (!getFreq) return [];
|
|
1355
1862
|
const names = getFreq();
|
|
1356
1863
|
if (!names.length) return [];
|
|
@@ -1382,7 +1889,9 @@ function useEmojiPicker(editor, emojis) {
|
|
|
1382
1889
|
clickOutsideHandler = (e) => {
|
|
1383
1890
|
const target = e.target;
|
|
1384
1891
|
if (pickerRef.value && !pickerRef.value.contains(target) && target !== anchorEl && !anchorEl?.contains(target)) {
|
|
1385
|
-
requestAnimationFrame(() =>
|
|
1892
|
+
requestAnimationFrame(() => {
|
|
1893
|
+
close();
|
|
1894
|
+
});
|
|
1386
1895
|
}
|
|
1387
1896
|
};
|
|
1388
1897
|
document.addEventListener("mousedown", clickOutsideHandler);
|
|
@@ -1560,7 +2069,7 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1560
2069
|
const swatches = Array.from(grid.querySelectorAll(".dm-emoji-swatch"));
|
|
1561
2070
|
if (!swatches.length) return;
|
|
1562
2071
|
const current = document.activeElement;
|
|
1563
|
-
|
|
2072
|
+
const idx = swatches.indexOf(current);
|
|
1564
2073
|
if (idx === -1) {
|
|
1565
2074
|
if (["ArrowRight", "ArrowDown", "ArrowLeft", "ArrowUp"].includes(event.key)) {
|
|
1566
2075
|
event.preventDefault();
|
|
@@ -1605,8 +2114,12 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1605
2114
|
tabindex: -1,
|
|
1606
2115
|
title: formatName(item.name),
|
|
1607
2116
|
"aria-label": formatName(item.name),
|
|
1608
|
-
onMousedown: (e) =>
|
|
1609
|
-
|
|
2117
|
+
onMousedown: (e) => {
|
|
2118
|
+
e.preventDefault();
|
|
2119
|
+
},
|
|
2120
|
+
onClick: () => {
|
|
2121
|
+
selectEmoji(item);
|
|
2122
|
+
}
|
|
1610
2123
|
}, item.emoji);
|
|
1611
2124
|
}
|
|
1612
2125
|
return () => {
|
|
@@ -1641,8 +2154,12 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1641
2154
|
"aria-selected": activeCategory.value === cat,
|
|
1642
2155
|
title: cat,
|
|
1643
2156
|
"aria-label": cat,
|
|
1644
|
-
onMousedown: (e) =>
|
|
1645
|
-
|
|
2157
|
+
onMousedown: (e) => {
|
|
2158
|
+
e.preventDefault();
|
|
2159
|
+
},
|
|
2160
|
+
onClick: () => {
|
|
2161
|
+
scrollToCategory(cat);
|
|
2162
|
+
}
|
|
1646
2163
|
}, categoryIcon(cat))
|
|
1647
2164
|
)
|
|
1648
2165
|
),
|
|
@@ -1876,6 +2393,348 @@ var EditorContent = defineComponent({
|
|
|
1876
2393
|
});
|
|
1877
2394
|
}
|
|
1878
2395
|
});
|
|
2396
|
+
var TOKEN_LABELS = {
|
|
2397
|
+
gray: "Gray",
|
|
2398
|
+
brown: "Brown",
|
|
2399
|
+
orange: "Orange",
|
|
2400
|
+
yellow: "Yellow",
|
|
2401
|
+
green: "Green",
|
|
2402
|
+
blue: "Blue",
|
|
2403
|
+
purple: "Purple",
|
|
2404
|
+
pink: "Pink",
|
|
2405
|
+
red: "Red"
|
|
2406
|
+
};
|
|
2407
|
+
function useNotionColorPicker(options) {
|
|
2408
|
+
const { editor } = options;
|
|
2409
|
+
const isOpen = ref(false);
|
|
2410
|
+
const anchorEl = shallowRef(null);
|
|
2411
|
+
const hostEl = shallowRef(null);
|
|
2412
|
+
const panelRef = ref();
|
|
2413
|
+
const currentTextToken = ref(null);
|
|
2414
|
+
const currentBgToken = ref(null);
|
|
2415
|
+
const palette = shallowRef([]);
|
|
2416
|
+
const setStorageOpen2 = (open) => {
|
|
2417
|
+
const ed = editor.value;
|
|
2418
|
+
if (!ed) return;
|
|
2419
|
+
const slot = ed.storage["notionColorPicker"];
|
|
2420
|
+
if (slot && typeof slot === "object") {
|
|
2421
|
+
slot.isOpen = open;
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
const syncFromSelection = () => {
|
|
2425
|
+
const ed = editor.value;
|
|
2426
|
+
if (!ed) return;
|
|
2427
|
+
const { selection } = ed.state;
|
|
2428
|
+
let mark = null;
|
|
2429
|
+
if (selection.empty) {
|
|
2430
|
+
mark = selection.$from.marks().find((m) => m.type.name === "textStyle") ?? null;
|
|
2431
|
+
} else {
|
|
2432
|
+
ed.state.doc.nodesBetween(selection.from, selection.to, (node) => {
|
|
2433
|
+
if (mark) return false;
|
|
2434
|
+
if (node.isText) {
|
|
2435
|
+
const found = node.marks.find((m) => m.type.name === "textStyle");
|
|
2436
|
+
if (found) mark = found;
|
|
2437
|
+
}
|
|
2438
|
+
return true;
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
const attrs = mark?.attrs ?? {};
|
|
2442
|
+
currentTextToken.value = attrs.colorToken ?? null;
|
|
2443
|
+
currentBgToken.value = attrs.backgroundColorToken ?? null;
|
|
2444
|
+
};
|
|
2445
|
+
const close = (opts = {}) => {
|
|
2446
|
+
if (!isOpen.value) return;
|
|
2447
|
+
isOpen.value = false;
|
|
2448
|
+
setStorageOpen2(false);
|
|
2449
|
+
if (opts.refocus) {
|
|
2450
|
+
editor.value?.view.focus();
|
|
2451
|
+
}
|
|
2452
|
+
anchorEl.value = null;
|
|
2453
|
+
};
|
|
2454
|
+
watch(
|
|
2455
|
+
editor,
|
|
2456
|
+
(ed, _oldEd, onCleanup) => {
|
|
2457
|
+
if (!ed || ed.isDestroyed) return;
|
|
2458
|
+
hostEl.value = ed.view.dom.closest(".dm-editor");
|
|
2459
|
+
const ext = ed.extensionManager.extensions.find((e) => e.name === "notionColorPicker");
|
|
2460
|
+
const extOptions = ext?.options ?? null;
|
|
2461
|
+
palette.value = extOptions?.palette ? [...extOptions.palette] : [];
|
|
2462
|
+
const onOpen = (...args) => {
|
|
2463
|
+
const detail = args[0];
|
|
2464
|
+
const incomingAnchor = detail?.anchorElement;
|
|
2465
|
+
if (!incomingAnchor) return;
|
|
2466
|
+
if (isOpen.value && anchorEl.value === incomingAnchor) {
|
|
2467
|
+
close({ refocus: true });
|
|
2468
|
+
return;
|
|
2469
|
+
}
|
|
2470
|
+
anchorEl.value = incomingAnchor;
|
|
2471
|
+
syncFromSelection();
|
|
2472
|
+
isOpen.value = true;
|
|
2473
|
+
setStorageOpen2(true);
|
|
2474
|
+
};
|
|
2475
|
+
const onSelectionUpdate = () => {
|
|
2476
|
+
if (!isOpen.value) return;
|
|
2477
|
+
if (!anchorEl.value?.isConnected) {
|
|
2478
|
+
close();
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
if (ed.state.selection.empty) {
|
|
2482
|
+
close();
|
|
2483
|
+
} else {
|
|
2484
|
+
syncFromSelection();
|
|
2485
|
+
}
|
|
2486
|
+
};
|
|
2487
|
+
ed.on("notionColorOpen", onOpen);
|
|
2488
|
+
ed.on("selectionUpdate", onSelectionUpdate);
|
|
2489
|
+
onCleanup(() => {
|
|
2490
|
+
ed.off("notionColorOpen", onOpen);
|
|
2491
|
+
ed.off("selectionUpdate", onSelectionUpdate);
|
|
2492
|
+
if (isOpen.value) setStorageOpen2(false);
|
|
2493
|
+
});
|
|
2494
|
+
},
|
|
2495
|
+
{ immediate: true }
|
|
2496
|
+
);
|
|
2497
|
+
watch(isOpen, (open, _old, onCleanup) => {
|
|
2498
|
+
if (!open) return;
|
|
2499
|
+
const controller = new AbortController();
|
|
2500
|
+
const { signal } = controller;
|
|
2501
|
+
document.addEventListener("mousedown", (e) => {
|
|
2502
|
+
const target = e.target;
|
|
2503
|
+
if (!target) return;
|
|
2504
|
+
if (panelRef.value?.contains(target)) return;
|
|
2505
|
+
if (anchorEl.value?.contains(target)) return;
|
|
2506
|
+
close({ refocus: false });
|
|
2507
|
+
}, { signal });
|
|
2508
|
+
document.addEventListener("keydown", (e) => {
|
|
2509
|
+
if (e.key === "Escape" && isOpen.value) {
|
|
2510
|
+
e.preventDefault();
|
|
2511
|
+
close({ refocus: true });
|
|
2512
|
+
}
|
|
2513
|
+
}, { signal });
|
|
2514
|
+
onCleanup(() => {
|
|
2515
|
+
controller.abort();
|
|
2516
|
+
});
|
|
2517
|
+
});
|
|
2518
|
+
const applyText = (token) => {
|
|
2519
|
+
const ed = editor.value;
|
|
2520
|
+
if (!ed) return;
|
|
2521
|
+
ed.commands.setTextColorToken(token);
|
|
2522
|
+
syncFromSelection();
|
|
2523
|
+
};
|
|
2524
|
+
const applyBg = (token) => {
|
|
2525
|
+
const ed = editor.value;
|
|
2526
|
+
if (!ed) return;
|
|
2527
|
+
ed.commands.setBackgroundColorToken(token);
|
|
2528
|
+
syncFromSelection();
|
|
2529
|
+
};
|
|
2530
|
+
const tokenLabel = (token) => {
|
|
2531
|
+
return TOKEN_LABELS[token] ?? token.charAt(0).toUpperCase() + token.slice(1);
|
|
2532
|
+
};
|
|
2533
|
+
const onPanelKeydown = (event) => {
|
|
2534
|
+
const cols = 5;
|
|
2535
|
+
const root = panelRef.value;
|
|
2536
|
+
if (!root) return;
|
|
2537
|
+
const swatches = Array.from(
|
|
2538
|
+
root.querySelectorAll(".dm-ncp-swatch")
|
|
2539
|
+
);
|
|
2540
|
+
if (!swatches.length) return;
|
|
2541
|
+
const active = document.activeElement;
|
|
2542
|
+
const idx = active ? swatches.indexOf(active) : -1;
|
|
2543
|
+
if (idx === -1) return;
|
|
2544
|
+
let next = idx;
|
|
2545
|
+
switch (event.key) {
|
|
2546
|
+
case "ArrowRight":
|
|
2547
|
+
event.preventDefault();
|
|
2548
|
+
next = Math.min(idx + 1, swatches.length - 1);
|
|
2549
|
+
break;
|
|
2550
|
+
case "ArrowLeft":
|
|
2551
|
+
event.preventDefault();
|
|
2552
|
+
next = Math.max(idx - 1, 0);
|
|
2553
|
+
break;
|
|
2554
|
+
case "ArrowDown":
|
|
2555
|
+
event.preventDefault();
|
|
2556
|
+
next = Math.min(idx + cols, swatches.length - 1);
|
|
2557
|
+
break;
|
|
2558
|
+
case "ArrowUp":
|
|
2559
|
+
event.preventDefault();
|
|
2560
|
+
next = Math.max(idx - cols, 0);
|
|
2561
|
+
break;
|
|
2562
|
+
case "Home":
|
|
2563
|
+
event.preventDefault();
|
|
2564
|
+
next = 0;
|
|
2565
|
+
break;
|
|
2566
|
+
case "End":
|
|
2567
|
+
event.preventDefault();
|
|
2568
|
+
next = swatches.length - 1;
|
|
2569
|
+
break;
|
|
2570
|
+
default:
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
swatches[next]?.focus();
|
|
2574
|
+
};
|
|
2575
|
+
return {
|
|
2576
|
+
isOpen,
|
|
2577
|
+
hostEl,
|
|
2578
|
+
anchorEl,
|
|
2579
|
+
panelRef,
|
|
2580
|
+
currentTextToken,
|
|
2581
|
+
currentBgToken,
|
|
2582
|
+
palette,
|
|
2583
|
+
applyText,
|
|
2584
|
+
applyBg,
|
|
2585
|
+
close,
|
|
2586
|
+
tokenLabel,
|
|
2587
|
+
onPanelKeydown
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
// src/notion-color-picker/DomternalNotionColorPicker.ts
|
|
2592
|
+
var DomternalNotionColorPicker = defineComponent({
|
|
2593
|
+
name: "DomternalNotionColorPicker",
|
|
2594
|
+
props: {
|
|
2595
|
+
editor: { type: Object, default: void 0 }
|
|
2596
|
+
},
|
|
2597
|
+
setup(props, { slots }) {
|
|
2598
|
+
const { editor: contextEditor } = useCurrentEditor();
|
|
2599
|
+
const editorRef = computed(() => props.editor ?? contextEditor.value);
|
|
2600
|
+
const api = useNotionColorPicker({ editor: editorRef });
|
|
2601
|
+
const {
|
|
2602
|
+
isOpen,
|
|
2603
|
+
hostEl,
|
|
2604
|
+
anchorEl,
|
|
2605
|
+
panelRef,
|
|
2606
|
+
currentTextToken,
|
|
2607
|
+
currentBgToken,
|
|
2608
|
+
palette,
|
|
2609
|
+
applyText,
|
|
2610
|
+
applyBg,
|
|
2611
|
+
tokenLabel,
|
|
2612
|
+
onPanelKeydown
|
|
2613
|
+
} = api;
|
|
2614
|
+
watch(
|
|
2615
|
+
[isOpen, anchorEl],
|
|
2616
|
+
([open, anchor], _old, onCleanup) => {
|
|
2617
|
+
if (!open || !anchor || !panelRef.value) return;
|
|
2618
|
+
const panel = panelRef.value;
|
|
2619
|
+
const cleanupFloating = positionFloating(anchor, panel, {
|
|
2620
|
+
placement: "bottom-start",
|
|
2621
|
+
offsetValue: 4
|
|
2622
|
+
});
|
|
2623
|
+
let id2 = 0;
|
|
2624
|
+
const id1 = requestAnimationFrame(() => {
|
|
2625
|
+
id2 = requestAnimationFrame(() => {
|
|
2626
|
+
if (!panel.isConnected) return;
|
|
2627
|
+
const active = panel.querySelector(".dm-ncp-swatch.dm-ncp-active");
|
|
2628
|
+
const fallback = panel.querySelector('.dm-ncp-swatch--text[data-color="null"]');
|
|
2629
|
+
(active ?? fallback)?.focus({ preventScroll: true });
|
|
2630
|
+
});
|
|
2631
|
+
});
|
|
2632
|
+
onCleanup(() => {
|
|
2633
|
+
cancelAnimationFrame(id1);
|
|
2634
|
+
if (id2) cancelAnimationFrame(id2);
|
|
2635
|
+
cleanupFloating();
|
|
2636
|
+
});
|
|
2637
|
+
},
|
|
2638
|
+
{ flush: "post" }
|
|
2639
|
+
);
|
|
2640
|
+
const renderDefaultPanel = () => {
|
|
2641
|
+
const tToken = currentTextToken.value;
|
|
2642
|
+
const bToken = currentBgToken.value;
|
|
2643
|
+
const pal = palette.value;
|
|
2644
|
+
return [
|
|
2645
|
+
h("div", { class: "dm-ncp-section" }, [
|
|
2646
|
+
h("div", { class: "dm-ncp-label" }, "Text color"),
|
|
2647
|
+
h("div", { class: "dm-ncp-grid" }, [
|
|
2648
|
+
h("button", {
|
|
2649
|
+
type: "button",
|
|
2650
|
+
class: ["dm-ncp-swatch", "dm-ncp-swatch--text", tToken === null && "dm-ncp-active"],
|
|
2651
|
+
"aria-pressed": tToken === null,
|
|
2652
|
+
"data-color": "null",
|
|
2653
|
+
title: "Default text color",
|
|
2654
|
+
"aria-label": "Default text color",
|
|
2655
|
+
onMousedown: (e) => {
|
|
2656
|
+
e.preventDefault();
|
|
2657
|
+
},
|
|
2658
|
+
onClick: () => {
|
|
2659
|
+
applyText(null);
|
|
2660
|
+
}
|
|
2661
|
+
}),
|
|
2662
|
+
...pal.map((t) => h("button", {
|
|
2663
|
+
key: t,
|
|
2664
|
+
type: "button",
|
|
2665
|
+
class: ["dm-ncp-swatch", "dm-ncp-swatch--text", tToken === t && "dm-ncp-active"],
|
|
2666
|
+
"aria-pressed": tToken === t,
|
|
2667
|
+
"data-color": t,
|
|
2668
|
+
title: tokenLabel(t),
|
|
2669
|
+
"aria-label": `${tokenLabel(t)} text`,
|
|
2670
|
+
onMousedown: (e) => {
|
|
2671
|
+
e.preventDefault();
|
|
2672
|
+
},
|
|
2673
|
+
onClick: () => {
|
|
2674
|
+
applyText(t);
|
|
2675
|
+
}
|
|
2676
|
+
}))
|
|
2677
|
+
])
|
|
2678
|
+
]),
|
|
2679
|
+
h("div", { class: "dm-ncp-section" }, [
|
|
2680
|
+
h("div", { class: "dm-ncp-label" }, "Background color"),
|
|
2681
|
+
h("div", { class: "dm-ncp-grid" }, [
|
|
2682
|
+
h("button", {
|
|
2683
|
+
type: "button",
|
|
2684
|
+
class: ["dm-ncp-swatch", "dm-ncp-swatch--bg", bToken === null && "dm-ncp-active"],
|
|
2685
|
+
"aria-pressed": bToken === null,
|
|
2686
|
+
"data-color": "null",
|
|
2687
|
+
title: "Default background",
|
|
2688
|
+
"aria-label": "Default background",
|
|
2689
|
+
onMousedown: (e) => {
|
|
2690
|
+
e.preventDefault();
|
|
2691
|
+
},
|
|
2692
|
+
onClick: () => {
|
|
2693
|
+
applyBg(null);
|
|
2694
|
+
}
|
|
2695
|
+
}),
|
|
2696
|
+
...pal.map((t) => h("button", {
|
|
2697
|
+
key: t,
|
|
2698
|
+
type: "button",
|
|
2699
|
+
class: ["dm-ncp-swatch", "dm-ncp-swatch--bg", bToken === t && "dm-ncp-active"],
|
|
2700
|
+
"aria-pressed": bToken === t,
|
|
2701
|
+
"data-color": t,
|
|
2702
|
+
title: `${tokenLabel(t)} background`,
|
|
2703
|
+
"aria-label": `${tokenLabel(t)} background`,
|
|
2704
|
+
onMousedown: (e) => {
|
|
2705
|
+
e.preventDefault();
|
|
2706
|
+
},
|
|
2707
|
+
onClick: () => {
|
|
2708
|
+
applyBg(t);
|
|
2709
|
+
}
|
|
2710
|
+
}))
|
|
2711
|
+
])
|
|
2712
|
+
])
|
|
2713
|
+
];
|
|
2714
|
+
};
|
|
2715
|
+
return () => {
|
|
2716
|
+
if (!isOpen.value || !hostEl.value) return null;
|
|
2717
|
+
const slot = slots["default"];
|
|
2718
|
+
const slotChildren = slot ? slot({ api }) : void 0;
|
|
2719
|
+
const panelChildren = slotChildren ?? renderDefaultPanel();
|
|
2720
|
+
return h(Teleport, { to: hostEl.value }, [
|
|
2721
|
+
h("div", {
|
|
2722
|
+
ref: panelRef,
|
|
2723
|
+
class: "dm-notion-color-picker",
|
|
2724
|
+
"data-show": "",
|
|
2725
|
+
"data-dm-editor-ui": "",
|
|
2726
|
+
role: "dialog",
|
|
2727
|
+
"aria-label": "Text and background color",
|
|
2728
|
+
// Intentional non-modal: the picker doesn't trap focus (outside-click
|
|
2729
|
+
// closes, editor stays interactive). `role="dialog"` defaults to
|
|
2730
|
+
// modal=true for screen readers, so we explicitly opt out.
|
|
2731
|
+
"aria-modal": "false",
|
|
2732
|
+
onKeydown: onPanelKeydown
|
|
2733
|
+
}, panelChildren)
|
|
2734
|
+
]);
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
});
|
|
1879
2738
|
var NODE_VIEW_ON_DRAG_START = /* @__PURE__ */ Symbol("domternal-node-view-drag");
|
|
1880
2739
|
var NODE_VIEW_CONTENT_REF = /* @__PURE__ */ Symbol("domternal-node-view-content");
|
|
1881
2740
|
function useVueNodeView() {
|
|
@@ -1886,7 +2745,7 @@ function useVueNodeView() {
|
|
|
1886
2745
|
|
|
1887
2746
|
// src/node-views/VueNodeViewRenderer.ts
|
|
1888
2747
|
function VueNodeViewRenderer(component, options = {}) {
|
|
1889
|
-
const normalizedComponent = typeof component === "function"
|
|
2748
|
+
const normalizedComponent = typeof component === "function" ? component["__vccOpts"] ?? component : component;
|
|
1890
2749
|
markRaw(normalizedComponent);
|
|
1891
2750
|
const constructor = (node, _view, getPos, decorations) => {
|
|
1892
2751
|
const ctx = constructor.__domternalContext;
|
|
@@ -1899,8 +2758,8 @@ function VueNodeViewRenderer(component, options = {}) {
|
|
|
1899
2758
|
appContextStore.set(editor, appContext);
|
|
1900
2759
|
}
|
|
1901
2760
|
}
|
|
1902
|
-
if (!appContext) {
|
|
1903
|
-
if (
|
|
2761
|
+
if (!appContext || !editor) {
|
|
2762
|
+
if (globalThis.__DEV__ !== false) {
|
|
1904
2763
|
console.warn(
|
|
1905
2764
|
"[VueNodeViewRenderer] appContext not found for editor. Custom Vue node views require provideEditor(editor) to be called, either manually after useEditor() or automatically via <Domternal> root."
|
|
1906
2765
|
);
|
|
@@ -2058,6 +2917,6 @@ var NodeViewContent = defineComponent({
|
|
|
2058
2917
|
}
|
|
2059
2918
|
});
|
|
2060
2919
|
|
|
2061
|
-
export { DEFAULT_EXTENSIONS, Domternal, DomternalBubbleMenu, DomternalEditor, DomternalEmojiPicker, DomternalFloatingMenu, DomternalToolbar, EDITOR_KEY, EditorContent, NodeViewContent, NodeViewWrapper, VueNodeViewRenderer, provideEditor, useCurrentEditor, useEditor, useEditorState, useVueNodeView };
|
|
2920
|
+
export { DEFAULT_EXTENSIONS, Domternal, DomternalBubbleMenu, DomternalEditor, DomternalEmojiPicker, DomternalFloatingMenu, DomternalNotionColorPicker, DomternalToolbar, EDITOR_KEY, EditorContent, NodeViewContent, NodeViewWrapper, VueNodeViewRenderer, provideEditor, useCurrentEditor, useEditor, useEditorState, useNotionColorPicker, useVueNodeView };
|
|
2062
2921
|
//# sourceMappingURL=index.js.map
|
|
2063
2922
|
//# sourceMappingURL=index.js.map
|