@domternal/vue 0.6.1 → 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 +152 -40
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1178 -302
- 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,
|
|
2
|
-
import { PluginKey, ToolbarController,
|
|
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();
|
|
@@ -259,189 +261,6 @@ function useCurrentEditor() {
|
|
|
259
261
|
const editor = inject(EDITOR_KEY, shallowRef(null));
|
|
260
262
|
return { editor };
|
|
261
263
|
}
|
|
262
|
-
var Domternal = defineComponent({
|
|
263
|
-
name: "Domternal",
|
|
264
|
-
props: {
|
|
265
|
-
extensions: { type: Array, default: void 0 },
|
|
266
|
-
content: { type: [String, Object], default: "" },
|
|
267
|
-
editable: { type: Boolean, default: true },
|
|
268
|
-
autofocus: { type: [Boolean, String, Number], default: false },
|
|
269
|
-
outputFormat: { type: String, default: "html" },
|
|
270
|
-
immediatelyRender: { type: Boolean, default: false },
|
|
271
|
-
onCreate: { type: Function, default: void 0 },
|
|
272
|
-
onUpdate: { type: Function, default: void 0 },
|
|
273
|
-
onSelectionChange: { type: Function, default: void 0 },
|
|
274
|
-
onFocus: { type: Function, default: void 0 },
|
|
275
|
-
onBlur: { type: Function, default: void 0 },
|
|
276
|
-
onDestroy: { type: Function, default: void 0 }
|
|
277
|
-
},
|
|
278
|
-
setup(props, { slots }) {
|
|
279
|
-
const { editor } = useEditor({
|
|
280
|
-
...props.extensions && { extensions: props.extensions },
|
|
281
|
-
content: props.content,
|
|
282
|
-
editable: props.editable,
|
|
283
|
-
autofocus: props.autofocus,
|
|
284
|
-
outputFormat: props.outputFormat,
|
|
285
|
-
immediatelyRender: props.immediatelyRender,
|
|
286
|
-
...props.onCreate && { onCreate: props.onCreate },
|
|
287
|
-
...props.onUpdate && { onUpdate: props.onUpdate },
|
|
288
|
-
...props.onSelectionChange && { onSelectionChange: props.onSelectionChange },
|
|
289
|
-
...props.onFocus && { onFocus: props.onFocus },
|
|
290
|
-
...props.onBlur && { onBlur: props.onBlur },
|
|
291
|
-
...props.onDestroy && { onDestroy: props.onDestroy }
|
|
292
|
-
});
|
|
293
|
-
provideEditor(editor);
|
|
294
|
-
return () => slots["default"]?.();
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
var DomternalContent = defineComponent({
|
|
298
|
-
name: "DomternalContent",
|
|
299
|
-
props: {
|
|
300
|
-
class: { type: String, default: void 0 }
|
|
301
|
-
},
|
|
302
|
-
setup(props) {
|
|
303
|
-
const { editor } = useCurrentEditor();
|
|
304
|
-
const containerRef = ref();
|
|
305
|
-
watchEffect(() => {
|
|
306
|
-
const container = containerRef.value;
|
|
307
|
-
const ed = editor.value;
|
|
308
|
-
if (!container || !ed || ed.isDestroyed) return;
|
|
309
|
-
const editorDom = ed.view.dom;
|
|
310
|
-
if (editorDom.parentElement !== container) {
|
|
311
|
-
container.appendChild(editorDom);
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
return () => {
|
|
315
|
-
const classes = props.class ? `dm-editor ${props.class}` : "dm-editor";
|
|
316
|
-
return h("div", { class: classes, "data-dm-editor-ui": "" }, [h("div", { ref: containerRef })]);
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
var DomternalLoading = defineComponent({
|
|
321
|
-
name: "DomternalLoading",
|
|
322
|
-
setup(_props, { slots }) {
|
|
323
|
-
const { editor } = useCurrentEditor();
|
|
324
|
-
return () => editor.value ? null : slots["default"]?.();
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
Domternal.Content = DomternalContent;
|
|
328
|
-
Domternal.Loading = DomternalLoading;
|
|
329
|
-
var DomternalEditor = defineComponent({
|
|
330
|
-
name: "DomternalEditor",
|
|
331
|
-
props: {
|
|
332
|
-
extensions: { type: Array, default: void 0 },
|
|
333
|
-
content: { type: [String, Object], default: void 0 },
|
|
334
|
-
editable: { type: Boolean, default: true },
|
|
335
|
-
autofocus: { type: [Boolean, String, Number], default: false },
|
|
336
|
-
immediatelyRender: { type: Boolean, default: false },
|
|
337
|
-
outputFormat: { type: String, default: "html" },
|
|
338
|
-
modelValue: { type: [String, Object], default: void 0 },
|
|
339
|
-
class: { type: String, default: void 0 },
|
|
340
|
-
onCreate: { type: Function, default: void 0 },
|
|
341
|
-
onUpdate: { type: Function, default: void 0 },
|
|
342
|
-
onSelectionChange: { type: Function, default: void 0 },
|
|
343
|
-
onFocus: { type: Function, default: void 0 },
|
|
344
|
-
onBlur: { type: Function, default: void 0 },
|
|
345
|
-
onDestroy: { type: Function, default: void 0 }
|
|
346
|
-
},
|
|
347
|
-
emits: {
|
|
348
|
-
"update:modelValue": (_value) => true
|
|
349
|
-
},
|
|
350
|
-
setup(props, { slots, emit, expose }) {
|
|
351
|
-
const { editor, editorRef } = useEditor({
|
|
352
|
-
...props.extensions && { extensions: props.extensions },
|
|
353
|
-
content: props.modelValue ?? props.content ?? "",
|
|
354
|
-
editable: props.editable,
|
|
355
|
-
autofocus: props.autofocus,
|
|
356
|
-
immediatelyRender: props.immediatelyRender,
|
|
357
|
-
outputFormat: props.outputFormat,
|
|
358
|
-
...props.onCreate && { onCreate: props.onCreate },
|
|
359
|
-
...props.onUpdate && { onUpdate: props.onUpdate },
|
|
360
|
-
...props.onSelectionChange && { onSelectionChange: props.onSelectionChange },
|
|
361
|
-
...props.onFocus && { onFocus: props.onFocus },
|
|
362
|
-
...props.onBlur && { onBlur: props.onBlur },
|
|
363
|
-
...props.onDestroy && { onDestroy: props.onDestroy }
|
|
364
|
-
});
|
|
365
|
-
const state = useEditorState(editor);
|
|
366
|
-
expose({
|
|
367
|
-
editor,
|
|
368
|
-
htmlContent: state.htmlContent,
|
|
369
|
-
jsonContent: state.jsonContent,
|
|
370
|
-
isEmpty: state.isEmpty,
|
|
371
|
-
isFocused: state.isFocused
|
|
372
|
-
});
|
|
373
|
-
provideEditor(editor);
|
|
374
|
-
const prevModelValue = ref(props.modelValue);
|
|
375
|
-
watch(
|
|
376
|
-
() => props.modelValue,
|
|
377
|
-
(newValue) => {
|
|
378
|
-
if (newValue === void 0) return;
|
|
379
|
-
const ed = editor.value;
|
|
380
|
-
if (!ed || ed.isDestroyed) return;
|
|
381
|
-
if (newValue === prevModelValue.value) return;
|
|
382
|
-
prevModelValue.value = newValue;
|
|
383
|
-
if (props.outputFormat === "html") {
|
|
384
|
-
if (newValue !== ed.getHTML()) {
|
|
385
|
-
ed.setContent(newValue, false);
|
|
386
|
-
}
|
|
387
|
-
} else {
|
|
388
|
-
if (JSON.stringify(newValue) !== JSON.stringify(ed.getJSON())) {
|
|
389
|
-
ed.setContent(newValue, false);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
{ flush: "post" }
|
|
394
|
-
);
|
|
395
|
-
watch(editor, (ed, _oldEd, onCleanup) => {
|
|
396
|
-
if (!ed || ed.isDestroyed) return;
|
|
397
|
-
const handler = () => {
|
|
398
|
-
const val = props.outputFormat === "html" ? ed.getHTML() : ed.getJSON();
|
|
399
|
-
prevModelValue.value = val;
|
|
400
|
-
emit("update:modelValue", val);
|
|
401
|
-
};
|
|
402
|
-
ed.on("update", handler);
|
|
403
|
-
onCleanup(() => {
|
|
404
|
-
ed.off("update", handler);
|
|
405
|
-
});
|
|
406
|
-
}, { immediate: true });
|
|
407
|
-
return () => {
|
|
408
|
-
const classes = props.class ? `dm-editor ${props.class}` : "dm-editor";
|
|
409
|
-
return [
|
|
410
|
-
h("div", { class: classes, "data-dm-editor-ui": "" }, [h("div", { ref: editorRef })]),
|
|
411
|
-
slots["default"]?.()
|
|
412
|
-
];
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
var EditorContent = defineComponent({
|
|
417
|
-
name: "EditorContent",
|
|
418
|
-
props: {
|
|
419
|
-
editor: {
|
|
420
|
-
type: Object,
|
|
421
|
-
default: null
|
|
422
|
-
},
|
|
423
|
-
class: {
|
|
424
|
-
type: String,
|
|
425
|
-
default: void 0
|
|
426
|
-
}
|
|
427
|
-
},
|
|
428
|
-
setup(props) {
|
|
429
|
-
const containerRef = ref();
|
|
430
|
-
watchEffect(() => {
|
|
431
|
-
const container = containerRef.value;
|
|
432
|
-
const editor = props.editor;
|
|
433
|
-
if (!container || !editor || editor.isDestroyed) return;
|
|
434
|
-
const editorDom = editor.view.dom;
|
|
435
|
-
if (editorDom.parentElement !== container) {
|
|
436
|
-
container.appendChild(editorDom);
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
return () => h("div", {
|
|
440
|
-
ref: containerRef,
|
|
441
|
-
class: props.class
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
264
|
function useToolbarController(editor, layout) {
|
|
446
265
|
const groups = shallowRef([]);
|
|
447
266
|
const focusedIndex = ref(0);
|
|
@@ -527,8 +346,9 @@ function useToolbarController(editor, layout) {
|
|
|
527
346
|
function isDropdownActive(dropdown) {
|
|
528
347
|
if (dropdown.layout === "grid") return false;
|
|
529
348
|
if (dropdown.dynamicLabel) return false;
|
|
530
|
-
|
|
531
|
-
|
|
349
|
+
const ctl = controller;
|
|
350
|
+
if (!ctl) return false;
|
|
351
|
+
return dropdown.items.some((item) => ctl.activeMap.get(item.name) ?? false);
|
|
532
352
|
}
|
|
533
353
|
function getAriaExpanded(item) {
|
|
534
354
|
if (!item.emitEvent) return null;
|
|
@@ -742,7 +562,9 @@ function useKeyboardNav(controllerRef, toolbarRef, closeDropdown) {
|
|
|
742
562
|
const btn = document.activeElement;
|
|
743
563
|
if (btn?.getAttribute("aria-haspopup") && btn.closest(".dm-toolbar")) {
|
|
744
564
|
btn.click();
|
|
745
|
-
requestAnimationFrame(() =>
|
|
565
|
+
requestAnimationFrame(() => {
|
|
566
|
+
focusDropdownItem(0, true);
|
|
567
|
+
});
|
|
746
568
|
}
|
|
747
569
|
}
|
|
748
570
|
break;
|
|
@@ -777,31 +599,25 @@ function useKeyboardNav(controllerRef, toolbarRef, closeDropdown) {
|
|
|
777
599
|
}
|
|
778
600
|
|
|
779
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
|
+
}
|
|
780
607
|
function getComputedStyleAtCursor(editor, prop) {
|
|
781
608
|
try {
|
|
782
|
-
const
|
|
783
|
-
const domAtPos = editor.view.domAtPos(from);
|
|
784
|
-
let node = domAtPos.node;
|
|
785
|
-
if (!(node instanceof HTMLElement)) {
|
|
786
|
-
node = node.parentElement;
|
|
787
|
-
}
|
|
609
|
+
const node = resolveElementAtCursor(editor);
|
|
788
610
|
if (!node) return null;
|
|
789
|
-
const
|
|
790
|
-
const inline = el.style.getPropertyValue(prop);
|
|
611
|
+
const inline = node.style.getPropertyValue(prop);
|
|
791
612
|
if (inline) return inline;
|
|
792
|
-
return window.getComputedStyle(
|
|
613
|
+
return window.getComputedStyle(node).getPropertyValue(prop) || null;
|
|
793
614
|
} catch {
|
|
794
615
|
return null;
|
|
795
616
|
}
|
|
796
617
|
}
|
|
797
618
|
function getInlineStyleAtCursor(editor, prop) {
|
|
798
619
|
try {
|
|
799
|
-
const
|
|
800
|
-
const domAtPos = editor.view.domAtPos(from);
|
|
801
|
-
let node = domAtPos.node;
|
|
802
|
-
if (!(node instanceof HTMLElement)) {
|
|
803
|
-
node = node.parentElement;
|
|
804
|
-
}
|
|
620
|
+
const node = resolveElementAtCursor(editor);
|
|
805
621
|
if (!node) return null;
|
|
806
622
|
return node.style.getPropertyValue(prop) || null;
|
|
807
623
|
} catch {
|
|
@@ -834,9 +650,15 @@ var ToolbarButton = defineComponent({
|
|
|
834
650
|
"aria-expanded": props.ariaExpanded === "true" ? true : void 0,
|
|
835
651
|
"aria-label": props.item.label,
|
|
836
652
|
title: props.tooltip,
|
|
837
|
-
onMousedown: (e) =>
|
|
838
|
-
|
|
839
|
-
|
|
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
|
+
}
|
|
840
662
|
});
|
|
841
663
|
}
|
|
842
664
|
});
|
|
@@ -872,8 +694,12 @@ var ToolbarDropdownPanel = defineComponent({
|
|
|
872
694
|
"aria-label": sub.label,
|
|
873
695
|
title: sub.label,
|
|
874
696
|
style: { backgroundColor: sub.color },
|
|
875
|
-
onMousedown: (e) =>
|
|
876
|
-
|
|
697
|
+
onMousedown: (e) => {
|
|
698
|
+
e.preventDefault();
|
|
699
|
+
},
|
|
700
|
+
onClick: (e) => {
|
|
701
|
+
emit("itemClick", sub, e);
|
|
702
|
+
}
|
|
877
703
|
}) : h("button", {
|
|
878
704
|
key: sub.name,
|
|
879
705
|
type: "button",
|
|
@@ -882,8 +708,12 @@ var ToolbarDropdownPanel = defineComponent({
|
|
|
882
708
|
tabindex: -1,
|
|
883
709
|
"aria-label": sub.label,
|
|
884
710
|
innerHTML: getCachedItemContent(sub.icon, sub.label),
|
|
885
|
-
onMousedown: (e) =>
|
|
886
|
-
|
|
711
|
+
onMousedown: (e) => {
|
|
712
|
+
e.preventDefault();
|
|
713
|
+
},
|
|
714
|
+
onClick: (e) => {
|
|
715
|
+
emit("itemClick", sub, e);
|
|
716
|
+
}
|
|
887
717
|
})
|
|
888
718
|
)
|
|
889
719
|
);
|
|
@@ -908,8 +738,12 @@ var ToolbarDropdownPanel = defineComponent({
|
|
|
908
738
|
onVnodeMounted: (vnode) => {
|
|
909
739
|
if (sub.style && vnode.el) vnode.el.setAttribute("style", sub.style);
|
|
910
740
|
},
|
|
911
|
-
onMousedown: (e) =>
|
|
912
|
-
|
|
741
|
+
onMousedown: (e) => {
|
|
742
|
+
e.preventDefault();
|
|
743
|
+
},
|
|
744
|
+
onClick: (e) => {
|
|
745
|
+
emit("itemClick", sub, e);
|
|
746
|
+
}
|
|
913
747
|
})
|
|
914
748
|
)
|
|
915
749
|
);
|
|
@@ -948,9 +782,15 @@ var ToolbarDropdown = defineComponent({
|
|
|
948
782
|
disabled: props.isDisabled,
|
|
949
783
|
"data-dropdown": props.dropdown.name,
|
|
950
784
|
innerHTML: props.triggerHtml,
|
|
951
|
-
onMousedown: (e) =>
|
|
952
|
-
|
|
953
|
-
|
|
785
|
+
onMousedown: (e) => {
|
|
786
|
+
e.preventDefault();
|
|
787
|
+
},
|
|
788
|
+
onClick: () => {
|
|
789
|
+
emit("toggle", props.dropdown);
|
|
790
|
+
},
|
|
791
|
+
onFocus: () => {
|
|
792
|
+
emit("focus", props.dropdown.name);
|
|
793
|
+
}
|
|
954
794
|
})
|
|
955
795
|
];
|
|
956
796
|
if (props.isOpen) {
|
|
@@ -959,7 +799,9 @@ var ToolbarDropdown = defineComponent({
|
|
|
959
799
|
dropdown: props.dropdown,
|
|
960
800
|
isActive: props.isActive,
|
|
961
801
|
getCachedItemContent: props.getCachedItemContent,
|
|
962
|
-
onItemClick: (item, event) =>
|
|
802
|
+
onItemClick: (item, event) => {
|
|
803
|
+
emit("itemClick", item, event);
|
|
804
|
+
}
|
|
963
805
|
})
|
|
964
806
|
);
|
|
965
807
|
}
|
|
@@ -1011,20 +853,23 @@ var DomternalToolbar = defineComponent({
|
|
|
1011
853
|
closeDropdown();
|
|
1012
854
|
}
|
|
1013
855
|
if (item.emitEvent) {
|
|
1014
|
-
const
|
|
856
|
+
const target = event?.target;
|
|
857
|
+
const anchor = target?.closest(".dm-toolbar-button") ?? target;
|
|
1015
858
|
editor.emit(item.emitEvent, { anchorElement: anchor });
|
|
1016
859
|
return;
|
|
1017
860
|
}
|
|
1018
861
|
executeCommand(item);
|
|
1019
|
-
requestAnimationFrame(() =>
|
|
862
|
+
requestAnimationFrame(() => {
|
|
863
|
+
editor.view.focus();
|
|
864
|
+
});
|
|
1020
865
|
}
|
|
1021
866
|
function onDropdownItemClick(item, event) {
|
|
1022
867
|
const editor = props.editor ?? contextEditor.value;
|
|
1023
868
|
if (!editor) return;
|
|
1024
869
|
let anchor;
|
|
1025
870
|
if (item.emitEvent) {
|
|
1026
|
-
const wrapper = event.target
|
|
1027
|
-
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;
|
|
1028
873
|
}
|
|
1029
874
|
closeDropdown();
|
|
1030
875
|
if (item.emitEvent) {
|
|
@@ -1032,7 +877,9 @@ var DomternalToolbar = defineComponent({
|
|
|
1032
877
|
} else {
|
|
1033
878
|
executeCommand(item);
|
|
1034
879
|
}
|
|
1035
|
-
requestAnimationFrame(() =>
|
|
880
|
+
requestAnimationFrame(() => {
|
|
881
|
+
editor.view.focus();
|
|
882
|
+
});
|
|
1036
883
|
}
|
|
1037
884
|
function onButtonFocus(name) {
|
|
1038
885
|
const index = controllerRef.current?.getFlatIndex(name) ?? -1;
|
|
@@ -1072,7 +919,9 @@ var DomternalToolbar = defineComponent({
|
|
|
1072
919
|
tooltip: getTooltip(btn),
|
|
1073
920
|
iconHtml: getCachedIcon(btn.icon),
|
|
1074
921
|
ariaExpanded: getAriaExpanded(btn),
|
|
1075
|
-
onClick: (clickedItem, event) =>
|
|
922
|
+
onClick: (clickedItem, event) => {
|
|
923
|
+
onButtonClick(clickedItem, event);
|
|
924
|
+
},
|
|
1076
925
|
onFocus: onButtonFocus
|
|
1077
926
|
});
|
|
1078
927
|
}
|
|
@@ -1081,18 +930,18 @@ var DomternalToolbar = defineComponent({
|
|
|
1081
930
|
const activeItem = dd.items.find((sub) => controllerRef.current?.activeMap.get(sub.name));
|
|
1082
931
|
let triggerHtml = getDropdownTriggerHtml(dd, activeItem);
|
|
1083
932
|
if (dd.dynamicLabel && !activeItem && dd.computedStyleProperty) {
|
|
1084
|
-
let
|
|
933
|
+
let computed7;
|
|
1085
934
|
if (dd.computedStyleProperty === "font-family") {
|
|
1086
|
-
|
|
1087
|
-
if (
|
|
1088
|
-
const first =
|
|
1089
|
-
|
|
935
|
+
computed7 = getInlineStyleAtCursor(editor, dd.computedStyleProperty);
|
|
936
|
+
if (computed7) {
|
|
937
|
+
const first = computed7.split(",")[0]?.replace(/['"]+/g, "").trim();
|
|
938
|
+
computed7 = first ?? null;
|
|
1090
939
|
}
|
|
1091
940
|
} else {
|
|
1092
|
-
|
|
941
|
+
computed7 = getComputedStyleAtCursor(editor, dd.computedStyleProperty);
|
|
1093
942
|
}
|
|
1094
|
-
if (
|
|
1095
|
-
triggerHtml = `<span class="dm-toolbar-trigger-label">${
|
|
943
|
+
if (computed7) {
|
|
944
|
+
triggerHtml = `<span class="dm-toolbar-trigger-label">${computed7}</span>${DROPDOWN_CARET}`;
|
|
1096
945
|
}
|
|
1097
946
|
}
|
|
1098
947
|
return h(ToolbarDropdown, {
|
|
@@ -1119,6 +968,15 @@ var DomternalToolbar = defineComponent({
|
|
|
1119
968
|
};
|
|
1120
969
|
}
|
|
1121
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
|
+
};
|
|
1122
980
|
function isInsideTableCell($pos) {
|
|
1123
981
|
for (let d = $pos.depth; d > 0; d--) {
|
|
1124
982
|
const name = $pos.node(d).type.name;
|
|
@@ -1134,26 +992,37 @@ function findCellNode(pos) {
|
|
|
1134
992
|
return null;
|
|
1135
993
|
}
|
|
1136
994
|
function useBubbleMenu(options) {
|
|
1137
|
-
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;
|
|
1138
996
|
const menuRef = ref();
|
|
1139
|
-
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
|
+
);
|
|
1140
1001
|
const resolvedItems = shallowRef([]);
|
|
1141
1002
|
const activeVersion = useDebouncedRef(0);
|
|
1003
|
+
const trailing = shallowRef(INITIAL_TRAILING_STATE);
|
|
1142
1004
|
const activeMapRef = /* @__PURE__ */ new Map();
|
|
1143
1005
|
const disabledMapRef = /* @__PURE__ */ new Map();
|
|
1144
1006
|
let itemMap;
|
|
1007
|
+
let dropdownMap;
|
|
1145
1008
|
let bubbleDefaults;
|
|
1146
1009
|
let currentResolvedItems = [];
|
|
1147
1010
|
let initialized = false;
|
|
1148
1011
|
let stopEditorWatch = null;
|
|
1149
1012
|
const doInit = (ed) => {
|
|
1150
|
-
if (initialized ||
|
|
1013
|
+
if (initialized || ed.isDestroyed || !menuRef.value) return;
|
|
1151
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");
|
|
1152
1019
|
itemMap = /* @__PURE__ */ new Map();
|
|
1020
|
+
dropdownMap = /* @__PURE__ */ new Map();
|
|
1153
1021
|
for (const item of ed.toolbarItems) {
|
|
1154
1022
|
if (item.type === "button") {
|
|
1155
1023
|
itemMap.set(item.name, item);
|
|
1156
1024
|
} else if (item.type === "dropdown") {
|
|
1025
|
+
dropdownMap.set(item.name, item);
|
|
1157
1026
|
for (const sub of item.items) {
|
|
1158
1027
|
itemMap.set(sub.name, sub);
|
|
1159
1028
|
}
|
|
@@ -1184,7 +1053,7 @@ function useBubbleMenu(options) {
|
|
|
1184
1053
|
let sepIdx = 0;
|
|
1185
1054
|
for (const item of ctxItems) {
|
|
1186
1055
|
if (lastGroup !== void 0 && item.group !== lastGroup) {
|
|
1187
|
-
result.push({ type: "separator", name: `bsep-${sepIdx++}` });
|
|
1056
|
+
result.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
|
|
1188
1057
|
}
|
|
1189
1058
|
result.push(item);
|
|
1190
1059
|
lastGroup = item.group;
|
|
@@ -1196,8 +1065,13 @@ function useBubbleMenu(options) {
|
|
|
1196
1065
|
let sepIdx = 0;
|
|
1197
1066
|
for (const name of names) {
|
|
1198
1067
|
if (name === "|") {
|
|
1199
|
-
result.push({ type: "separator", name: `sep-${sepIdx++}` });
|
|
1068
|
+
result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
|
|
1200
1069
|
} else {
|
|
1070
|
+
const dropdown = dropdownMap.get(name);
|
|
1071
|
+
if (dropdown) {
|
|
1072
|
+
result.push(dropdown);
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1201
1075
|
const item = itemMap.get(name);
|
|
1202
1076
|
if (item) result.push(item);
|
|
1203
1077
|
}
|
|
@@ -1234,7 +1108,7 @@ function useBubbleMenu(options) {
|
|
|
1234
1108
|
return schemaItems.filter((item) => {
|
|
1235
1109
|
const markName = typeof item.isActive === "string" ? item.isActive : null;
|
|
1236
1110
|
if (!markName) return true;
|
|
1237
|
-
const markType = schema.marks
|
|
1111
|
+
const markType = schema.marks[markName];
|
|
1238
1112
|
if (!markType) return true;
|
|
1239
1113
|
return nodeType.allowsMarkType(markType);
|
|
1240
1114
|
});
|
|
@@ -1254,7 +1128,8 @@ function useBubbleMenu(options) {
|
|
|
1254
1128
|
};
|
|
1255
1129
|
} else {
|
|
1256
1130
|
shouldShowFn = ({ state }) => {
|
|
1257
|
-
if (state.selection.empty
|
|
1131
|
+
if (state.selection.empty) return false;
|
|
1132
|
+
if (state.selection.node) return bubbleDefaults.has(state.selection.node.type.name);
|
|
1258
1133
|
if (isInsideTableCell(state.selection.$from)) return false;
|
|
1259
1134
|
return state.selection.$from.parent.type.spec.marks !== "" || state.selection.$to.parent.type.spec.marks !== "";
|
|
1260
1135
|
};
|
|
@@ -1287,26 +1162,77 @@ function useBubbleMenu(options) {
|
|
|
1287
1162
|
canProxy = currentEd.can();
|
|
1288
1163
|
} catch {
|
|
1289
1164
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
activeMapRef.set(item.name, ToolbarController.resolveActive(currentEd, item));
|
|
1165
|
+
const trackButton = (btn) => {
|
|
1166
|
+
activeMapRef.set(btn.name, ToolbarController.resolveActive(currentEd, btn));
|
|
1293
1167
|
try {
|
|
1294
|
-
const canCmd = canProxy?.[
|
|
1295
|
-
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);
|
|
1296
1170
|
} catch {
|
|
1297
|
-
disabledMapRef.set(
|
|
1171
|
+
disabledMapRef.set(btn.name, false);
|
|
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);
|
|
1298
1194
|
}
|
|
1299
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;
|
|
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
|
+
};
|
|
1300
1217
|
};
|
|
1301
1218
|
const transactionHandler = () => {
|
|
1302
1219
|
if (contexts) {
|
|
1303
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
|
+
}
|
|
1304
1228
|
}
|
|
1305
1229
|
updateStates(ed);
|
|
1230
|
+
syncTrailingState(ed);
|
|
1306
1231
|
activeVersion.value++;
|
|
1307
1232
|
};
|
|
1308
1233
|
ed.on("transaction", transactionHandler);
|
|
1309
1234
|
updateStates(ed);
|
|
1235
|
+
syncTrailingState(ed);
|
|
1310
1236
|
initializedEditor = ed;
|
|
1311
1237
|
initializedHandler = transactionHandler;
|
|
1312
1238
|
};
|
|
@@ -1364,17 +1290,39 @@ function useBubbleMenu(options) {
|
|
|
1364
1290
|
const isItemDisabled = (item) => {
|
|
1365
1291
|
return disabledMapRef.get(item.name) ?? false;
|
|
1366
1292
|
};
|
|
1367
|
-
const executeCommand = (item) => {
|
|
1293
|
+
const executeCommand = (item, event) => {
|
|
1368
1294
|
const ed = editor.value;
|
|
1369
1295
|
if (!ed) return;
|
|
1370
1296
|
if (item.emitEvent) {
|
|
1371
|
-
|
|
1297
|
+
const anchor = event?.currentTarget ?? event?.target ?? null;
|
|
1298
|
+
ed.emit(item.emitEvent, { anchorElement: anchor });
|
|
1372
1299
|
return;
|
|
1373
1300
|
}
|
|
1374
1301
|
ToolbarController.executeItem(ed, item);
|
|
1375
1302
|
};
|
|
1376
1303
|
const getCachedIcon = (name) => {
|
|
1377
|
-
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
|
+
}));
|
|
1378
1326
|
};
|
|
1379
1327
|
return {
|
|
1380
1328
|
menuRef,
|
|
@@ -1383,11 +1331,15 @@ function useBubbleMenu(options) {
|
|
|
1383
1331
|
isItemDisabled,
|
|
1384
1332
|
executeCommand,
|
|
1385
1333
|
activeVersion,
|
|
1386
|
-
getCachedIcon
|
|
1334
|
+
getCachedIcon,
|
|
1335
|
+
trailing,
|
|
1336
|
+
openColorPicker,
|
|
1337
|
+
openBlockContextMenu
|
|
1387
1338
|
};
|
|
1388
1339
|
}
|
|
1389
1340
|
|
|
1390
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>';
|
|
1391
1343
|
var DomternalBubbleMenu = defineComponent({
|
|
1392
1344
|
name: "DomternalBubbleMenu",
|
|
1393
1345
|
props: {
|
|
@@ -1397,10 +1349,13 @@ var DomternalBubbleMenu = defineComponent({
|
|
|
1397
1349
|
offset: { type: Number, default: 8 },
|
|
1398
1350
|
updateDelay: { type: Number, default: 0 },
|
|
1399
1351
|
items: { type: Array, default: void 0 },
|
|
1400
|
-
contexts: { type: Object, default: void 0 }
|
|
1352
|
+
contexts: { type: Object, default: void 0 },
|
|
1353
|
+
icons: { type: Object, default: void 0 }
|
|
1401
1354
|
},
|
|
1402
1355
|
setup(props, { slots }) {
|
|
1403
1356
|
const { editor: contextEditor } = useCurrentEditor();
|
|
1357
|
+
const editorRef = computed(() => props.editor ?? contextEditor.value);
|
|
1358
|
+
const iconsRef = computed(() => props.icons);
|
|
1404
1359
|
const {
|
|
1405
1360
|
menuRef,
|
|
1406
1361
|
resolvedItems,
|
|
@@ -1408,67 +1363,300 @@ var DomternalBubbleMenu = defineComponent({
|
|
|
1408
1363
|
isItemDisabled,
|
|
1409
1364
|
executeCommand,
|
|
1410
1365
|
activeVersion,
|
|
1411
|
-
getCachedIcon
|
|
1366
|
+
getCachedIcon,
|
|
1367
|
+
trailing,
|
|
1368
|
+
openColorPicker,
|
|
1369
|
+
openBlockContextMenu
|
|
1412
1370
|
} = useBubbleMenu({
|
|
1413
|
-
editor:
|
|
1371
|
+
editor: editorRef,
|
|
1414
1372
|
shouldShow: props.shouldShow,
|
|
1415
1373
|
placement: props.placement,
|
|
1416
1374
|
offset: props.offset,
|
|
1417
1375
|
updateDelay: props.updateDelay,
|
|
1418
1376
|
items: props.items,
|
|
1419
|
-
contexts: props.contexts
|
|
1377
|
+
contexts: props.contexts,
|
|
1378
|
+
icons: iconsRef
|
|
1420
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();
|
|
1421
1395
|
return () => {
|
|
1422
1396
|
void activeVersion.value;
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
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);
|
|
1427
1438
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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,
|
|
1432
1446
|
type: "button",
|
|
1433
|
-
class: ["dm-toolbar-button",
|
|
1434
|
-
|
|
1435
|
-
"aria-label":
|
|
1436
|
-
"aria-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
onClick: () =>
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
})
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
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");
|
|
1539
|
+
}
|
|
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}`;
|
|
1589
|
+
return h("button", {
|
|
1590
|
+
key: sub.name,
|
|
1591
|
+
type: "button",
|
|
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
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
})) : null
|
|
1604
|
+
]);
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
var DomternalFloatingMenu = defineComponent({
|
|
1609
|
+
name: "DomternalFloatingMenu",
|
|
1610
|
+
props: {
|
|
1611
|
+
editor: { type: Object, default: void 0 },
|
|
1612
|
+
shouldShow: { type: Function, default: void 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 }
|
|
1621
|
+
},
|
|
1455
1622
|
setup(props, { slots }) {
|
|
1456
1623
|
const { editor: contextEditor } = useCurrentEditor();
|
|
1457
1624
|
const menuRef = ref();
|
|
1458
|
-
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);
|
|
1459
1631
|
let registered = false;
|
|
1460
1632
|
let stopWatch = null;
|
|
1633
|
+
let currentEditor = null;
|
|
1634
|
+
const resolveIcon = (name) => {
|
|
1635
|
+
if (!name) return "";
|
|
1636
|
+
return props.icons?.[name] ?? defaultIcons[name] ?? "";
|
|
1637
|
+
};
|
|
1461
1638
|
const doRegister = (editor) => {
|
|
1462
1639
|
if (registered || editor.isDestroyed || !menuRef.value) return;
|
|
1463
1640
|
registered = true;
|
|
1641
|
+
currentEditor = editor;
|
|
1464
1642
|
const plugin = createFloatingMenuPlugin({
|
|
1465
1643
|
pluginKey,
|
|
1466
1644
|
editor,
|
|
1467
1645
|
element: menuRef.value,
|
|
1468
1646
|
...props.shouldShow && { shouldShow: props.shouldShow },
|
|
1469
|
-
offset: props.offset
|
|
1647
|
+
offset: props.offset,
|
|
1648
|
+
...props.keymap && { keymap: props.keymap },
|
|
1649
|
+
requireExplicitTrigger: props.requireExplicitTrigger
|
|
1470
1650
|
});
|
|
1471
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
|
+
}
|
|
1472
1660
|
};
|
|
1473
1661
|
onMounted(() => {
|
|
1474
1662
|
const ed = props.editor ?? contextEditor.value;
|
|
@@ -1487,14 +1675,149 @@ var DomternalFloatingMenu = defineComponent({
|
|
|
1487
1675
|
);
|
|
1488
1676
|
}
|
|
1489
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
|
+
);
|
|
1490
1690
|
onScopeDispose(() => {
|
|
1491
1691
|
stopWatch?.();
|
|
1492
|
-
|
|
1692
|
+
controller.value?.destroy();
|
|
1693
|
+
controller.value = null;
|
|
1694
|
+
const editor = currentEditor ?? props.editor ?? contextEditor.value;
|
|
1493
1695
|
if (editor && !editor.isDestroyed) {
|
|
1494
1696
|
editor.unregisterPlugin(pluginKey);
|
|
1495
1697
|
}
|
|
1496
1698
|
});
|
|
1497
|
-
|
|
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
|
+
};
|
|
1498
1821
|
}
|
|
1499
1822
|
});
|
|
1500
1823
|
var SCROLL_SETTLE_MS = 50;
|
|
@@ -1533,7 +1856,8 @@ function useEmojiPicker(editor, emojis) {
|
|
|
1533
1856
|
const frequentlyUsed = computed(() => {
|
|
1534
1857
|
if (!isOpen.value) return [];
|
|
1535
1858
|
const storage = getEmojiStorage(editor.value);
|
|
1536
|
-
|
|
1859
|
+
if (!storage) return [];
|
|
1860
|
+
const getFreq = storage["getFrequentlyUsed"];
|
|
1537
1861
|
if (!getFreq) return [];
|
|
1538
1862
|
const names = getFreq();
|
|
1539
1863
|
if (!names.length) return [];
|
|
@@ -1565,7 +1889,9 @@ function useEmojiPicker(editor, emojis) {
|
|
|
1565
1889
|
clickOutsideHandler = (e) => {
|
|
1566
1890
|
const target = e.target;
|
|
1567
1891
|
if (pickerRef.value && !pickerRef.value.contains(target) && target !== anchorEl && !anchorEl?.contains(target)) {
|
|
1568
|
-
requestAnimationFrame(() =>
|
|
1892
|
+
requestAnimationFrame(() => {
|
|
1893
|
+
close();
|
|
1894
|
+
});
|
|
1569
1895
|
}
|
|
1570
1896
|
};
|
|
1571
1897
|
document.addEventListener("mousedown", clickOutsideHandler);
|
|
@@ -1743,7 +2069,7 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1743
2069
|
const swatches = Array.from(grid.querySelectorAll(".dm-emoji-swatch"));
|
|
1744
2070
|
if (!swatches.length) return;
|
|
1745
2071
|
const current = document.activeElement;
|
|
1746
|
-
|
|
2072
|
+
const idx = swatches.indexOf(current);
|
|
1747
2073
|
if (idx === -1) {
|
|
1748
2074
|
if (["ArrowRight", "ArrowDown", "ArrowLeft", "ArrowUp"].includes(event.key)) {
|
|
1749
2075
|
event.preventDefault();
|
|
@@ -1788,8 +2114,12 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1788
2114
|
tabindex: -1,
|
|
1789
2115
|
title: formatName(item.name),
|
|
1790
2116
|
"aria-label": formatName(item.name),
|
|
1791
|
-
onMousedown: (e) =>
|
|
1792
|
-
|
|
2117
|
+
onMousedown: (e) => {
|
|
2118
|
+
e.preventDefault();
|
|
2119
|
+
},
|
|
2120
|
+
onClick: () => {
|
|
2121
|
+
selectEmoji(item);
|
|
2122
|
+
}
|
|
1793
2123
|
}, item.emoji);
|
|
1794
2124
|
}
|
|
1795
2125
|
return () => {
|
|
@@ -1824,8 +2154,12 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1824
2154
|
"aria-selected": activeCategory.value === cat,
|
|
1825
2155
|
title: cat,
|
|
1826
2156
|
"aria-label": cat,
|
|
1827
|
-
onMousedown: (e) =>
|
|
1828
|
-
|
|
2157
|
+
onMousedown: (e) => {
|
|
2158
|
+
e.preventDefault();
|
|
2159
|
+
},
|
|
2160
|
+
onClick: () => {
|
|
2161
|
+
scrollToCategory(cat);
|
|
2162
|
+
}
|
|
1829
2163
|
}, categoryIcon(cat))
|
|
1830
2164
|
)
|
|
1831
2165
|
),
|
|
@@ -1855,6 +2189,552 @@ var DomternalEmojiPicker = defineComponent({
|
|
|
1855
2189
|
};
|
|
1856
2190
|
}
|
|
1857
2191
|
});
|
|
2192
|
+
|
|
2193
|
+
// src/Domternal.ts
|
|
2194
|
+
var Domternal = defineComponent({
|
|
2195
|
+
name: "Domternal",
|
|
2196
|
+
props: {
|
|
2197
|
+
extensions: { type: Array, default: void 0 },
|
|
2198
|
+
content: { type: [String, Object], default: "" },
|
|
2199
|
+
editable: { type: Boolean, default: true },
|
|
2200
|
+
autofocus: { type: [Boolean, String, Number], default: false },
|
|
2201
|
+
outputFormat: { type: String, default: "html" },
|
|
2202
|
+
immediatelyRender: { type: Boolean, default: false },
|
|
2203
|
+
onCreate: { type: Function, default: void 0 },
|
|
2204
|
+
onUpdate: { type: Function, default: void 0 },
|
|
2205
|
+
onSelectionChange: { type: Function, default: void 0 },
|
|
2206
|
+
onFocus: { type: Function, default: void 0 },
|
|
2207
|
+
onBlur: { type: Function, default: void 0 },
|
|
2208
|
+
onDestroy: { type: Function, default: void 0 }
|
|
2209
|
+
},
|
|
2210
|
+
setup(props, { slots }) {
|
|
2211
|
+
const { editor } = useEditor({
|
|
2212
|
+
...props.extensions && { extensions: props.extensions },
|
|
2213
|
+
content: props.content,
|
|
2214
|
+
editable: props.editable,
|
|
2215
|
+
autofocus: props.autofocus,
|
|
2216
|
+
outputFormat: props.outputFormat,
|
|
2217
|
+
immediatelyRender: props.immediatelyRender,
|
|
2218
|
+
...props.onCreate && { onCreate: props.onCreate },
|
|
2219
|
+
...props.onUpdate && { onUpdate: props.onUpdate },
|
|
2220
|
+
...props.onSelectionChange && { onSelectionChange: props.onSelectionChange },
|
|
2221
|
+
...props.onFocus && { onFocus: props.onFocus },
|
|
2222
|
+
...props.onBlur && { onBlur: props.onBlur },
|
|
2223
|
+
...props.onDestroy && { onDestroy: props.onDestroy }
|
|
2224
|
+
});
|
|
2225
|
+
provideEditor(editor);
|
|
2226
|
+
watch(
|
|
2227
|
+
() => props.editable,
|
|
2228
|
+
(newEditable) => {
|
|
2229
|
+
const ed = editor.value;
|
|
2230
|
+
if (ed && !ed.isDestroyed) ed.setEditable(newEditable);
|
|
2231
|
+
}
|
|
2232
|
+
);
|
|
2233
|
+
return () => slots["default"]?.();
|
|
2234
|
+
}
|
|
2235
|
+
});
|
|
2236
|
+
var DomternalContent = defineComponent({
|
|
2237
|
+
name: "DomternalContent",
|
|
2238
|
+
props: {
|
|
2239
|
+
class: { type: String, default: void 0 }
|
|
2240
|
+
},
|
|
2241
|
+
setup(props) {
|
|
2242
|
+
const { editor } = useCurrentEditor();
|
|
2243
|
+
const containerRef = ref();
|
|
2244
|
+
watchEffect(() => {
|
|
2245
|
+
const container = containerRef.value;
|
|
2246
|
+
const ed = editor.value;
|
|
2247
|
+
if (!container || !ed || ed.isDestroyed) return;
|
|
2248
|
+
const editorDom = ed.view.dom;
|
|
2249
|
+
if (editorDom.parentElement !== container) {
|
|
2250
|
+
container.appendChild(editorDom);
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2253
|
+
return () => {
|
|
2254
|
+
const classes = props.class ? `dm-editor ${props.class}` : "dm-editor";
|
|
2255
|
+
return h("div", { class: classes, "data-dm-editor-ui": "" }, [h("div", { ref: containerRef })]);
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
});
|
|
2259
|
+
var DomternalLoading = defineComponent({
|
|
2260
|
+
name: "DomternalLoading",
|
|
2261
|
+
setup(_props, { slots }) {
|
|
2262
|
+
const { editor } = useCurrentEditor();
|
|
2263
|
+
return () => editor.value ? null : slots["default"]?.();
|
|
2264
|
+
}
|
|
2265
|
+
});
|
|
2266
|
+
Domternal.Content = DomternalContent;
|
|
2267
|
+
Domternal.Loading = DomternalLoading;
|
|
2268
|
+
Domternal.Toolbar = DomternalToolbar;
|
|
2269
|
+
Domternal.BubbleMenu = DomternalBubbleMenu;
|
|
2270
|
+
Domternal.FloatingMenu = DomternalFloatingMenu;
|
|
2271
|
+
Domternal.EmojiPicker = DomternalEmojiPicker;
|
|
2272
|
+
var DomternalEditor = defineComponent({
|
|
2273
|
+
name: "DomternalEditor",
|
|
2274
|
+
props: {
|
|
2275
|
+
extensions: { type: Array, default: void 0 },
|
|
2276
|
+
content: { type: [String, Object], default: void 0 },
|
|
2277
|
+
editable: { type: Boolean, default: true },
|
|
2278
|
+
autofocus: { type: [Boolean, String, Number], default: false },
|
|
2279
|
+
immediatelyRender: { type: Boolean, default: false },
|
|
2280
|
+
outputFormat: { type: String, default: "html" },
|
|
2281
|
+
modelValue: { type: [String, Object], default: void 0 },
|
|
2282
|
+
class: { type: String, default: void 0 },
|
|
2283
|
+
onCreate: { type: Function, default: void 0 },
|
|
2284
|
+
onUpdate: { type: Function, default: void 0 },
|
|
2285
|
+
onSelectionChange: { type: Function, default: void 0 },
|
|
2286
|
+
onFocus: { type: Function, default: void 0 },
|
|
2287
|
+
onBlur: { type: Function, default: void 0 },
|
|
2288
|
+
onDestroy: { type: Function, default: void 0 }
|
|
2289
|
+
},
|
|
2290
|
+
emits: {
|
|
2291
|
+
"update:modelValue": (_value) => true
|
|
2292
|
+
},
|
|
2293
|
+
setup(props, { slots, emit, expose }) {
|
|
2294
|
+
const { editor, editorRef } = useEditor({
|
|
2295
|
+
...props.extensions && { extensions: props.extensions },
|
|
2296
|
+
content: props.modelValue ?? props.content ?? "",
|
|
2297
|
+
editable: props.editable,
|
|
2298
|
+
autofocus: props.autofocus,
|
|
2299
|
+
immediatelyRender: props.immediatelyRender,
|
|
2300
|
+
outputFormat: props.outputFormat,
|
|
2301
|
+
...props.onCreate && { onCreate: props.onCreate },
|
|
2302
|
+
...props.onUpdate && { onUpdate: props.onUpdate },
|
|
2303
|
+
...props.onSelectionChange && { onSelectionChange: props.onSelectionChange },
|
|
2304
|
+
...props.onFocus && { onFocus: props.onFocus },
|
|
2305
|
+
...props.onBlur && { onBlur: props.onBlur },
|
|
2306
|
+
...props.onDestroy && { onDestroy: props.onDestroy }
|
|
2307
|
+
});
|
|
2308
|
+
const state = useEditorState(editor);
|
|
2309
|
+
expose({
|
|
2310
|
+
editor,
|
|
2311
|
+
htmlContent: state.htmlContent,
|
|
2312
|
+
jsonContent: state.jsonContent,
|
|
2313
|
+
isEmpty: state.isEmpty,
|
|
2314
|
+
isFocused: state.isFocused
|
|
2315
|
+
});
|
|
2316
|
+
provideEditor(editor);
|
|
2317
|
+
watch(
|
|
2318
|
+
() => props.editable,
|
|
2319
|
+
(newEditable) => {
|
|
2320
|
+
const ed = editor.value;
|
|
2321
|
+
if (ed && !ed.isDestroyed) ed.setEditable(newEditable);
|
|
2322
|
+
}
|
|
2323
|
+
);
|
|
2324
|
+
const prevModelValue = ref(props.modelValue);
|
|
2325
|
+
watch(
|
|
2326
|
+
() => props.modelValue,
|
|
2327
|
+
(newValue) => {
|
|
2328
|
+
if (newValue === void 0) return;
|
|
2329
|
+
const ed = editor.value;
|
|
2330
|
+
if (!ed || ed.isDestroyed) return;
|
|
2331
|
+
if (newValue === prevModelValue.value) return;
|
|
2332
|
+
prevModelValue.value = newValue;
|
|
2333
|
+
if (props.outputFormat === "html") {
|
|
2334
|
+
if (newValue !== ed.getHTML()) {
|
|
2335
|
+
ed.setContent(newValue, false);
|
|
2336
|
+
}
|
|
2337
|
+
} else {
|
|
2338
|
+
if (JSON.stringify(newValue) !== JSON.stringify(ed.getJSON())) {
|
|
2339
|
+
ed.setContent(newValue, false);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
},
|
|
2343
|
+
{ flush: "post" }
|
|
2344
|
+
);
|
|
2345
|
+
watch(editor, (ed, _oldEd, onCleanup) => {
|
|
2346
|
+
if (!ed || ed.isDestroyed) return;
|
|
2347
|
+
const handler = () => {
|
|
2348
|
+
const val = props.outputFormat === "html" ? ed.getHTML() : ed.getJSON();
|
|
2349
|
+
prevModelValue.value = val;
|
|
2350
|
+
emit("update:modelValue", val);
|
|
2351
|
+
};
|
|
2352
|
+
ed.on("update", handler);
|
|
2353
|
+
onCleanup(() => {
|
|
2354
|
+
ed.off("update", handler);
|
|
2355
|
+
});
|
|
2356
|
+
}, { immediate: true });
|
|
2357
|
+
return () => {
|
|
2358
|
+
const classes = props.class ? `dm-editor ${props.class}` : "dm-editor";
|
|
2359
|
+
return [
|
|
2360
|
+
h("div", { class: classes, "data-dm-editor-ui": "" }, [h("div", { ref: editorRef })]),
|
|
2361
|
+
slots["default"]?.()
|
|
2362
|
+
];
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
});
|
|
2366
|
+
var EditorContent = defineComponent({
|
|
2367
|
+
name: "EditorContent",
|
|
2368
|
+
props: {
|
|
2369
|
+
editor: {
|
|
2370
|
+
type: Object,
|
|
2371
|
+
default: null
|
|
2372
|
+
},
|
|
2373
|
+
class: {
|
|
2374
|
+
type: String,
|
|
2375
|
+
default: void 0
|
|
2376
|
+
}
|
|
2377
|
+
},
|
|
2378
|
+
setup(props) {
|
|
2379
|
+
const containerRef = ref();
|
|
2380
|
+
watchEffect(() => {
|
|
2381
|
+
const container = containerRef.value;
|
|
2382
|
+
const editor = props.editor;
|
|
2383
|
+
if (!container || !editor || editor.isDestroyed) return;
|
|
2384
|
+
const editorDom = editor.view.dom;
|
|
2385
|
+
if (editorDom.parentElement !== container) {
|
|
2386
|
+
container.appendChild(editorDom);
|
|
2387
|
+
}
|
|
2388
|
+
});
|
|
2389
|
+
return () => h("div", {
|
|
2390
|
+
ref: containerRef,
|
|
2391
|
+
class: props.class,
|
|
2392
|
+
"data-dm-editor-ui": ""
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
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
|
+
});
|
|
1858
2738
|
var NODE_VIEW_ON_DRAG_START = /* @__PURE__ */ Symbol("domternal-node-view-drag");
|
|
1859
2739
|
var NODE_VIEW_CONTENT_REF = /* @__PURE__ */ Symbol("domternal-node-view-content");
|
|
1860
2740
|
function useVueNodeView() {
|
|
@@ -1865,7 +2745,7 @@ function useVueNodeView() {
|
|
|
1865
2745
|
|
|
1866
2746
|
// src/node-views/VueNodeViewRenderer.ts
|
|
1867
2747
|
function VueNodeViewRenderer(component, options = {}) {
|
|
1868
|
-
const normalizedComponent = typeof component === "function"
|
|
2748
|
+
const normalizedComponent = typeof component === "function" ? component["__vccOpts"] ?? component : component;
|
|
1869
2749
|
markRaw(normalizedComponent);
|
|
1870
2750
|
const constructor = (node, _view, getPos, decorations) => {
|
|
1871
2751
|
const ctx = constructor.__domternalContext;
|
|
@@ -1878,8 +2758,8 @@ function VueNodeViewRenderer(component, options = {}) {
|
|
|
1878
2758
|
appContextStore.set(editor, appContext);
|
|
1879
2759
|
}
|
|
1880
2760
|
}
|
|
1881
|
-
if (!appContext) {
|
|
1882
|
-
if (
|
|
2761
|
+
if (!appContext || !editor) {
|
|
2762
|
+
if (globalThis.__DEV__ !== false) {
|
|
1883
2763
|
console.warn(
|
|
1884
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."
|
|
1885
2765
|
);
|
|
@@ -2036,11 +2916,7 @@ var NodeViewContent = defineComponent({
|
|
|
2036
2916
|
};
|
|
2037
2917
|
}
|
|
2038
2918
|
});
|
|
2039
|
-
Domternal.Toolbar = DomternalToolbar;
|
|
2040
|
-
Domternal.BubbleMenu = DomternalBubbleMenu;
|
|
2041
|
-
Domternal.FloatingMenu = DomternalFloatingMenu;
|
|
2042
|
-
Domternal.EmojiPicker = DomternalEmojiPicker;
|
|
2043
2919
|
|
|
2044
|
-
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 };
|
|
2045
2921
|
//# sourceMappingURL=index.js.map
|
|
2046
2922
|
//# sourceMappingURL=index.js.map
|