@domternal/react 0.14.0 → 1.0.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 +25 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +142 -241
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, forwardRef, useImperativeHandle, useRef, useEffect, useState, useMemo, useCallback, useSyncExternalStore, useContext, Fragment, useLayoutEffect, createElement } from 'react';
|
|
2
|
-
import { Document, Paragraph, Text, BaseKeymap, History, Editor, refocusEditorAfterCommand, positionFloatingOnce, PluginKey, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, positionFloating, ToolbarController, defaultBubbleContexts, createBubbleMenuPlugin } from '@domternal/core';
|
|
2
|
+
import { Document, Paragraph, Text, BaseKeymap, History, Editor, refocusEditorAfterCommand, positionFloatingOnce, PluginKey, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, positionFloating, ToolbarController, defaultBubbleContexts, buildBubbleItemMaps, createBubbleShouldShow, createBubbleMenuPlugin, resolveBubbleNames, resolveBubbleMenuItems } from '@domternal/core';
|
|
3
3
|
export { Editor, generateHTML, generateJSON, generateText } from '@domternal/core';
|
|
4
4
|
import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
@@ -53,7 +53,8 @@ function useEditor(options = {}, deps) {
|
|
|
53
53
|
extensions: [...defaults, ...extensions],
|
|
54
54
|
content: initialContent,
|
|
55
55
|
editable,
|
|
56
|
-
autofocus: focus
|
|
56
|
+
autofocus: focus,
|
|
57
|
+
...options.preset ? { preset: options.preset } : {}
|
|
57
58
|
});
|
|
58
59
|
wireEvents(ed);
|
|
59
60
|
instanceRef.current = ed;
|
|
@@ -96,6 +97,7 @@ function useEditor(options = {}, deps) {
|
|
|
96
97
|
const mount = editorRef.current;
|
|
97
98
|
if (mount && existing.view.dom.parentElement !== mount) {
|
|
98
99
|
mount.appendChild(existing.view.dom);
|
|
100
|
+
existing.adoptPresetClass();
|
|
99
101
|
}
|
|
100
102
|
callbacksRef.current.onCreate?.(existing);
|
|
101
103
|
return () => {
|
|
@@ -260,6 +262,7 @@ function useToolbarController(editor, layout) {
|
|
|
260
262
|
const toolbarRef = useRef(null);
|
|
261
263
|
const cleanupFloatingRef = useRef(null);
|
|
262
264
|
const clickOutsideRef = useRef(null);
|
|
265
|
+
const escapeKeydownRef = useRef(null);
|
|
263
266
|
const dismissOverlayRef = useRef(null);
|
|
264
267
|
const editorElRef = useRef(null);
|
|
265
268
|
const syncStateRafRef = useRef(0);
|
|
@@ -295,6 +298,18 @@ function useToolbarController(editor, layout) {
|
|
|
295
298
|
};
|
|
296
299
|
clickOutsideRef.current = clickOutside;
|
|
297
300
|
document.addEventListener("mousedown", clickOutside);
|
|
301
|
+
const escapeKeydown = (e) => {
|
|
302
|
+
if (!controller.openDropdown) return;
|
|
303
|
+
if (e.key !== "Escape") return;
|
|
304
|
+
if (toolbarRef.current?.contains(document.activeElement)) return;
|
|
305
|
+
cleanupFloatingRef.current?.();
|
|
306
|
+
cleanupFloatingRef.current = null;
|
|
307
|
+
controller.closeDropdown();
|
|
308
|
+
syncState();
|
|
309
|
+
e.preventDefault();
|
|
310
|
+
};
|
|
311
|
+
escapeKeydownRef.current = escapeKeydown;
|
|
312
|
+
document.addEventListener("keydown", escapeKeydown);
|
|
298
313
|
const editorEl = editor.view.dom.closest(".dm-editor");
|
|
299
314
|
editorElRef.current = editorEl;
|
|
300
315
|
if (editorEl) {
|
|
@@ -317,6 +332,10 @@ function useToolbarController(editor, layout) {
|
|
|
317
332
|
document.removeEventListener("mousedown", clickOutsideRef.current);
|
|
318
333
|
clickOutsideRef.current = null;
|
|
319
334
|
}
|
|
335
|
+
if (escapeKeydownRef.current) {
|
|
336
|
+
document.removeEventListener("keydown", escapeKeydownRef.current);
|
|
337
|
+
escapeKeydownRef.current = null;
|
|
338
|
+
}
|
|
320
339
|
if (dismissOverlayRef.current && editorElRef.current) {
|
|
321
340
|
editorElRef.current.removeEventListener("dm:dismiss-overlays", dismissOverlayRef.current);
|
|
322
341
|
dismissOverlayRef.current = null;
|
|
@@ -603,6 +622,19 @@ function getInlineStyleAtCursor(editor, prop) {
|
|
|
603
622
|
return null;
|
|
604
623
|
}
|
|
605
624
|
}
|
|
625
|
+
function useInnerHtml() {
|
|
626
|
+
const cache = useRef(null);
|
|
627
|
+
cache.current ??= /* @__PURE__ */ new Map();
|
|
628
|
+
return (html) => {
|
|
629
|
+
const store = cache.current;
|
|
630
|
+
let prop = store.get(html);
|
|
631
|
+
if (prop === void 0) {
|
|
632
|
+
prop = { __html: html };
|
|
633
|
+
store.set(html, prop);
|
|
634
|
+
}
|
|
635
|
+
return prop;
|
|
636
|
+
};
|
|
637
|
+
}
|
|
606
638
|
function ToolbarButton({
|
|
607
639
|
item,
|
|
608
640
|
isActive,
|
|
@@ -614,6 +646,7 @@ function ToolbarButton({
|
|
|
614
646
|
onClick,
|
|
615
647
|
onFocus
|
|
616
648
|
}) {
|
|
649
|
+
const innerHtml = useInnerHtml();
|
|
617
650
|
return /* @__PURE__ */ jsx(
|
|
618
651
|
"button",
|
|
619
652
|
{
|
|
@@ -625,7 +658,7 @@ function ToolbarButton({
|
|
|
625
658
|
title: tooltip,
|
|
626
659
|
tabIndex,
|
|
627
660
|
disabled: isDisabled,
|
|
628
|
-
dangerouslySetInnerHTML:
|
|
661
|
+
dangerouslySetInnerHTML: innerHtml(iconHtml),
|
|
629
662
|
onMouseDown: (e) => {
|
|
630
663
|
e.preventDefault();
|
|
631
664
|
},
|
|
@@ -644,6 +677,7 @@ function ToolbarDropdownPanel({
|
|
|
644
677
|
getCachedItemContent,
|
|
645
678
|
onItemClick
|
|
646
679
|
}) {
|
|
680
|
+
const innerHtml = useInnerHtml();
|
|
647
681
|
if (dropdown.layout === "grid") {
|
|
648
682
|
return /* @__PURE__ */ jsx(
|
|
649
683
|
"div",
|
|
@@ -678,7 +712,7 @@ function ToolbarDropdownPanel({
|
|
|
678
712
|
role: "menuitem",
|
|
679
713
|
tabIndex: -1,
|
|
680
714
|
"aria-label": sub.label,
|
|
681
|
-
dangerouslySetInnerHTML:
|
|
715
|
+
dangerouslySetInnerHTML: innerHtml(getCachedItemContent(sub.icon, sub.label)),
|
|
682
716
|
onMouseDown: (e) => {
|
|
683
717
|
e.preventDefault();
|
|
684
718
|
},
|
|
@@ -709,7 +743,7 @@ function ToolbarDropdownPanel({
|
|
|
709
743
|
ref: (el) => {
|
|
710
744
|
if (el && sub.style) el.setAttribute("style", sub.style);
|
|
711
745
|
},
|
|
712
|
-
dangerouslySetInnerHTML:
|
|
746
|
+
dangerouslySetInnerHTML: innerHtml(getCachedItemContent(sub.icon, sub.label, dropdown.displayMode)),
|
|
713
747
|
onMouseDown: (e) => {
|
|
714
748
|
e.preventDefault();
|
|
715
749
|
},
|
|
@@ -735,6 +769,7 @@ function ToolbarDropdown({
|
|
|
735
769
|
onItemClick,
|
|
736
770
|
onFocus
|
|
737
771
|
}) {
|
|
772
|
+
const innerHtml = useInnerHtml();
|
|
738
773
|
return /* @__PURE__ */ jsxs("div", { className: "dm-toolbar-dropdown-wrapper", children: [
|
|
739
774
|
/* @__PURE__ */ jsx(
|
|
740
775
|
"button",
|
|
@@ -748,7 +783,7 @@ function ToolbarDropdown({
|
|
|
748
783
|
tabIndex,
|
|
749
784
|
disabled: isDisabled,
|
|
750
785
|
"data-dropdown": dropdown.name,
|
|
751
|
-
dangerouslySetInnerHTML:
|
|
786
|
+
dangerouslySetInnerHTML: innerHtml(triggerHtml),
|
|
752
787
|
onMouseDown: (e) => {
|
|
753
788
|
e.preventDefault();
|
|
754
789
|
},
|
|
@@ -912,20 +947,6 @@ var INITIAL_TRAILING_STATE = {
|
|
|
912
947
|
currentBgColorVar: null,
|
|
913
948
|
hasAnyColor: false
|
|
914
949
|
};
|
|
915
|
-
function isInsideTableCell($pos) {
|
|
916
|
-
for (let d = $pos.depth; d > 0; d--) {
|
|
917
|
-
const name = $pos.node(d).type.name;
|
|
918
|
-
if (name === "tableCell" || name === "tableHeader") return true;
|
|
919
|
-
}
|
|
920
|
-
return false;
|
|
921
|
-
}
|
|
922
|
-
function findCellNode(pos) {
|
|
923
|
-
for (let d = pos.depth; d > 0; d--) {
|
|
924
|
-
const node = pos.node(d);
|
|
925
|
-
if (node.type.name === "tableCell" || node.type.name === "tableHeader") return node;
|
|
926
|
-
}
|
|
927
|
-
return null;
|
|
928
|
-
}
|
|
929
950
|
function useBubbleMenu(options) {
|
|
930
951
|
const { editor, shouldShow, placement = "top", offset = 8, updateDelay = 0, items, contexts: explicitContexts, icons } = options;
|
|
931
952
|
const contexts = explicitContexts ?? (items ? void 0 : editor ? defaultBubbleContexts(editor) : void 0);
|
|
@@ -936,8 +957,6 @@ function useBubbleMenu(options) {
|
|
|
936
957
|
const [trailing, setTrailing] = useState(INITIAL_TRAILING_STATE);
|
|
937
958
|
const activeMapRef = useRef(/* @__PURE__ */ new Map());
|
|
938
959
|
const disabledMapRef = useRef(/* @__PURE__ */ new Map());
|
|
939
|
-
const itemMapRef = useRef(/* @__PURE__ */ new Map());
|
|
940
|
-
const bubbleDefaultsRef = useRef(/* @__PURE__ */ new Map());
|
|
941
960
|
const resolvedItemsRef = useRef([]);
|
|
942
961
|
const editorRef = useRef(editor);
|
|
943
962
|
editorRef.current = editor;
|
|
@@ -946,127 +965,8 @@ function useBubbleMenu(options) {
|
|
|
946
965
|
const exts = editor.extensionManager.extensions;
|
|
947
966
|
const hasNotionColorPicker = exts.some((e) => e.name === "notionColorPicker");
|
|
948
967
|
const hasBlockContextMenu = exts.some((e) => e.name === "blockContextMenu");
|
|
949
|
-
const
|
|
950
|
-
const
|
|
951
|
-
for (const item of editor.toolbarItems) {
|
|
952
|
-
if (item.type === "button") {
|
|
953
|
-
itemMap.set(item.name, item);
|
|
954
|
-
} else if (item.type === "dropdown") {
|
|
955
|
-
dropdownMap.set(item.name, item);
|
|
956
|
-
for (const sub of item.items) {
|
|
957
|
-
itemMap.set(sub.name, sub);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
itemMapRef.current = itemMap;
|
|
962
|
-
const bubbleDefaults = /* @__PURE__ */ new Map();
|
|
963
|
-
const byCtx = /* @__PURE__ */ new Map();
|
|
964
|
-
const addItem = (btn) => {
|
|
965
|
-
const ctx = btn["bubbleMenu"];
|
|
966
|
-
if (!ctx) return;
|
|
967
|
-
let arr = byCtx.get(ctx);
|
|
968
|
-
if (!arr) {
|
|
969
|
-
arr = [];
|
|
970
|
-
byCtx.set(ctx, arr);
|
|
971
|
-
}
|
|
972
|
-
arr.push(btn);
|
|
973
|
-
};
|
|
974
|
-
for (const item of editor.toolbarItems) {
|
|
975
|
-
if (item.type === "button") addItem(item);
|
|
976
|
-
else if (item.type === "dropdown") {
|
|
977
|
-
for (const sub of item.items) addItem(sub);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
for (const [ctx, ctxItems] of byCtx) {
|
|
981
|
-
ctxItems.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
982
|
-
const result = [];
|
|
983
|
-
let lastGroup;
|
|
984
|
-
let sepIdx = 0;
|
|
985
|
-
for (const item of ctxItems) {
|
|
986
|
-
if (lastGroup !== void 0 && item.group !== lastGroup) {
|
|
987
|
-
result.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
|
|
988
|
-
}
|
|
989
|
-
result.push(item);
|
|
990
|
-
lastGroup = item.group;
|
|
991
|
-
}
|
|
992
|
-
bubbleDefaults.set(ctx, result);
|
|
993
|
-
}
|
|
994
|
-
bubbleDefaultsRef.current = bubbleDefaults;
|
|
995
|
-
const resolveNames = (names) => {
|
|
996
|
-
const result = [];
|
|
997
|
-
let sepIdx = 0;
|
|
998
|
-
for (const name of names) {
|
|
999
|
-
if (name === "|") {
|
|
1000
|
-
result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
|
|
1001
|
-
} else {
|
|
1002
|
-
const dropdown = dropdownMap.get(name);
|
|
1003
|
-
if (dropdown) {
|
|
1004
|
-
result.push(dropdown);
|
|
1005
|
-
continue;
|
|
1006
|
-
}
|
|
1007
|
-
const item = itemMap.get(name);
|
|
1008
|
-
if (item) result.push(item);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
return result;
|
|
1012
|
-
};
|
|
1013
|
-
const getFormatItems = () => {
|
|
1014
|
-
return Array.from(itemMap.values()).filter((item) => item.group === "format").sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
1015
|
-
};
|
|
1016
|
-
const detectContext = (selection, ctxs) => {
|
|
1017
|
-
if ("$anchorCell" in selection) return null;
|
|
1018
|
-
if (selection.node) return selection.node.type.name;
|
|
1019
|
-
if (selection.empty) return null;
|
|
1020
|
-
const fromCell = findCellNode(selection.$from);
|
|
1021
|
-
if (fromCell) {
|
|
1022
|
-
const toCell = findCellNode(selection.$to);
|
|
1023
|
-
if (toCell && fromCell !== toCell) return null;
|
|
1024
|
-
return "table";
|
|
1025
|
-
}
|
|
1026
|
-
const fromName = selection.$from.parent.type.name;
|
|
1027
|
-
if (fromName in ctxs) return fromName;
|
|
1028
|
-
if ("text" in ctxs && selection.$from.parent.type.spec.marks !== "") return "text";
|
|
1029
|
-
const toName = selection.$to.parent.type.name;
|
|
1030
|
-
if (toName in ctxs) return toName;
|
|
1031
|
-
if ("text" in ctxs && selection.$to.parent.type.spec.marks !== "") return "text";
|
|
1032
|
-
return null;
|
|
1033
|
-
};
|
|
1034
|
-
const filterBySchema = (contextName, schemaItems) => {
|
|
1035
|
-
if (contextName === "text" || contextName === "table") return schemaItems;
|
|
1036
|
-
const schema = editor.state.schema;
|
|
1037
|
-
if (!schema) return schemaItems;
|
|
1038
|
-
const nodeType = schema.nodes[contextName];
|
|
1039
|
-
if (!nodeType) return schemaItems;
|
|
1040
|
-
return schemaItems.filter((item) => {
|
|
1041
|
-
const markName = typeof item.isActive === "string" ? item.isActive : null;
|
|
1042
|
-
if (!markName) return true;
|
|
1043
|
-
const markType = schema.marks[markName];
|
|
1044
|
-
if (!markType) return true;
|
|
1045
|
-
return nodeType.allowsMarkType(markType);
|
|
1046
|
-
});
|
|
1047
|
-
};
|
|
1048
|
-
let shouldShowFn = shouldShow;
|
|
1049
|
-
if (!shouldShowFn) {
|
|
1050
|
-
if (contexts) {
|
|
1051
|
-
shouldShowFn = ({ state }) => {
|
|
1052
|
-
const context = detectContext(state.selection, contexts);
|
|
1053
|
-
if (!context) return false;
|
|
1054
|
-
if (context in contexts) {
|
|
1055
|
-
const val = contexts[context];
|
|
1056
|
-
if (val === null) return false;
|
|
1057
|
-
return val === true || Array.isArray(val) && val.length > 0;
|
|
1058
|
-
}
|
|
1059
|
-
return bubbleDefaults.has(context);
|
|
1060
|
-
};
|
|
1061
|
-
} else {
|
|
1062
|
-
shouldShowFn = ({ state }) => {
|
|
1063
|
-
if (state.selection.empty) return false;
|
|
1064
|
-
if (state.selection.node) return bubbleDefaults.has(state.selection.node.type.name);
|
|
1065
|
-
if (isInsideTableCell(state.selection.$from)) return false;
|
|
1066
|
-
return state.selection.$from.parent.type.spec.marks !== "" || state.selection.$to.parent.type.spec.marks !== "";
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
968
|
+
const maps = buildBubbleItemMaps(editor);
|
|
969
|
+
const shouldShowFn = shouldShow ?? createBubbleShouldShow(maps, contexts);
|
|
1070
970
|
const pluginKey = pluginKeyRef.current;
|
|
1071
971
|
const plugin = createBubbleMenuPlugin({
|
|
1072
972
|
pluginKey,
|
|
@@ -1082,13 +982,15 @@ function useBubbleMenu(options) {
|
|
|
1082
982
|
resolvedItemsRef.current = newItems;
|
|
1083
983
|
setResolvedItems(newItems);
|
|
1084
984
|
};
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
985
|
+
const fallbackItems = resolveBubbleNames(
|
|
986
|
+
items ?? ["bold", "italic", "underline"],
|
|
987
|
+
maps.itemMap,
|
|
988
|
+
maps.dropdownMap
|
|
989
|
+
);
|
|
990
|
+
const refreshItems = (ed) => {
|
|
991
|
+
setItems(resolveBubbleMenuItems({ editor: ed, maps, contexts, fallbackItems }));
|
|
992
|
+
};
|
|
993
|
+
refreshItems(editor);
|
|
1092
994
|
const updateStates = (ed) => {
|
|
1093
995
|
let canProxy = null;
|
|
1094
996
|
try {
|
|
@@ -1113,7 +1015,6 @@ function useBubbleMenu(options) {
|
|
|
1113
1015
|
trackButton(item);
|
|
1114
1016
|
}
|
|
1115
1017
|
};
|
|
1116
|
-
const defaultItems = items ? resolveNames(items) : resolveNames(["bold", "italic", "underline"]);
|
|
1117
1018
|
const syncTrailingState = (ed) => {
|
|
1118
1019
|
const sel = ed.state.selection;
|
|
1119
1020
|
const isNode = !!sel.node;
|
|
@@ -1140,7 +1041,8 @@ function useBubbleMenu(options) {
|
|
|
1140
1041
|
}
|
|
1141
1042
|
setTrailing({
|
|
1142
1043
|
isNodeSelection: isNode,
|
|
1143
|
-
|
|
1044
|
+
// Hidden without a live notionColorOpen listener: the trigger would be dead.
|
|
1045
|
+
showColorPickerButton: hasNotionColorPicker && ed.listenerCount("notionColorOpen") > 0,
|
|
1144
1046
|
showBlockMenuButton: hasBlockContextMenu,
|
|
1145
1047
|
blockMenuButtonDisabled: blockMenuDisabled,
|
|
1146
1048
|
currentTextColorVar: textVar,
|
|
@@ -1149,16 +1051,7 @@ function useBubbleMenu(options) {
|
|
|
1149
1051
|
});
|
|
1150
1052
|
};
|
|
1151
1053
|
const transactionHandler = () => {
|
|
1152
|
-
|
|
1153
|
-
updateContextItems(editor, contexts, detectContext, resolveNames, getFormatItems, filterBySchema, bubbleDefaults, setItems);
|
|
1154
|
-
} else {
|
|
1155
|
-
const sel = editor.state.selection;
|
|
1156
|
-
if (sel.node && bubbleDefaults.has(sel.node.type.name)) {
|
|
1157
|
-
setItems(bubbleDefaults.get(sel.node.type.name) ?? []);
|
|
1158
|
-
} else {
|
|
1159
|
-
setItems(defaultItems);
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1054
|
+
refreshItems(editor);
|
|
1162
1055
|
updateStates(editor);
|
|
1163
1056
|
syncTrailingState(editor);
|
|
1164
1057
|
setActiveVersion((v) => v + 1);
|
|
@@ -1173,30 +1066,6 @@ function useBubbleMenu(options) {
|
|
|
1173
1066
|
}
|
|
1174
1067
|
};
|
|
1175
1068
|
}, [editor]);
|
|
1176
|
-
function updateContextItems(ed, ctxs, detectContext, resolveNames, getFormatItems, filterBySchema, defaults, setItems) {
|
|
1177
|
-
const ctx = detectContext(ed.state.selection, ctxs);
|
|
1178
|
-
if (!ctx) {
|
|
1179
|
-
setItems([]);
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
|
-
if (ctx in ctxs) {
|
|
1183
|
-
const val = ctxs[ctx];
|
|
1184
|
-
if (val === null || Array.isArray(val) && val.length === 0) {
|
|
1185
|
-
setItems([]);
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
if (val === true) {
|
|
1189
|
-
setItems(filterBySchema(ctx, getFormatItems()));
|
|
1190
|
-
} else if (Array.isArray(val)) {
|
|
1191
|
-
const resolved = resolveNames(val);
|
|
1192
|
-
const buttons = resolved.filter((i) => i.type !== "separator");
|
|
1193
|
-
const filtered = new Set(filterBySchema(ctx, buttons).map((b) => b.name));
|
|
1194
|
-
setItems(resolved.filter((i) => i.type === "separator" || filtered.has(i.name)));
|
|
1195
|
-
}
|
|
1196
|
-
} else {
|
|
1197
|
-
setItems(defaults.get(ctx) ?? []);
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
1069
|
const isItemActive = (item) => {
|
|
1201
1070
|
return activeMapRef.current.get(item.name) ?? false;
|
|
1202
1071
|
};
|
|
@@ -1297,6 +1166,9 @@ function DomternalBubbleMenu({
|
|
|
1297
1166
|
}, []);
|
|
1298
1167
|
const colorBtnRef = useRef(null);
|
|
1299
1168
|
const blockMenuBtnRef = useRef(null);
|
|
1169
|
+
const innerHtml = useInnerHtml();
|
|
1170
|
+
const showColor = trailing.showColorPickerButton && !trailing.isNodeSelection;
|
|
1171
|
+
const showBlock = trailing.showBlockMenuButton && !trailing.isNodeSelection;
|
|
1300
1172
|
return /* @__PURE__ */ jsxs("div", { ref: menuRef, className: "dm-bubble-menu", role: "toolbar", "aria-label": "Text formatting", children: [
|
|
1301
1173
|
resolvedItems.map((item) => {
|
|
1302
1174
|
if (item.type === "separator") {
|
|
@@ -1335,7 +1207,7 @@ function DomternalBubbleMenu({
|
|
|
1335
1207
|
title: btn.label,
|
|
1336
1208
|
"aria-label": btn.label,
|
|
1337
1209
|
"aria-pressed": active,
|
|
1338
|
-
dangerouslySetInnerHTML:
|
|
1210
|
+
dangerouslySetInnerHTML: innerHtml(getCachedHtml(btn.icon)),
|
|
1339
1211
|
onMouseDown: (e) => {
|
|
1340
1212
|
e.preventDefault();
|
|
1341
1213
|
},
|
|
@@ -1346,8 +1218,8 @@ function DomternalBubbleMenu({
|
|
|
1346
1218
|
btn.name
|
|
1347
1219
|
);
|
|
1348
1220
|
}),
|
|
1349
|
-
|
|
1350
|
-
/* @__PURE__ */ jsx("span", { className: "dm-toolbar-separator", role: "separator" }),
|
|
1221
|
+
showColor && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1222
|
+
resolvedItems.length > 0 && /* @__PURE__ */ jsx("span", { className: "dm-toolbar-separator", role: "separator" }),
|
|
1351
1223
|
/* @__PURE__ */ jsxs(
|
|
1352
1224
|
"button",
|
|
1353
1225
|
{
|
|
@@ -1383,8 +1255,8 @@ function DomternalBubbleMenu({
|
|
|
1383
1255
|
}
|
|
1384
1256
|
)
|
|
1385
1257
|
] }),
|
|
1386
|
-
|
|
1387
|
-
/* @__PURE__ */ jsx("span", { className: "dm-toolbar-separator", role: "separator" }),
|
|
1258
|
+
showBlock && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1259
|
+
(resolvedItems.length > 0 || showColor) && /* @__PURE__ */ jsx("span", { className: "dm-toolbar-separator", role: "separator" }),
|
|
1388
1260
|
/* @__PURE__ */ jsx(
|
|
1389
1261
|
"button",
|
|
1390
1262
|
{
|
|
@@ -1395,7 +1267,7 @@ function DomternalBubbleMenu({
|
|
|
1395
1267
|
title: trailing.blockMenuButtonDisabled ? "Block actions (select within a single block)" : "More options",
|
|
1396
1268
|
"aria-label": "More options",
|
|
1397
1269
|
"aria-haspopup": "menu",
|
|
1398
|
-
dangerouslySetInnerHTML:
|
|
1270
|
+
dangerouslySetInnerHTML: innerHtml(getCachedHtml("dotsThree")),
|
|
1399
1271
|
onMouseDown: (e) => {
|
|
1400
1272
|
e.preventDefault();
|
|
1401
1273
|
},
|
|
@@ -1420,6 +1292,7 @@ function BubbleDropdown({
|
|
|
1420
1292
|
}) {
|
|
1421
1293
|
const triggerRef = useRef(null);
|
|
1422
1294
|
const panelRef = useRef(null);
|
|
1295
|
+
const innerHtml = useInnerHtml();
|
|
1423
1296
|
const dropdownActive = dropdown.items.some((sub) => isItemActive(sub));
|
|
1424
1297
|
const activeChild = dropdown.dynamicIcon ? dropdown.items.find((sub) => isItemActive(sub)) : void 0;
|
|
1425
1298
|
const triggerIcon = activeChild?.icon ?? dropdown.icon;
|
|
@@ -1471,7 +1344,7 @@ function BubbleDropdown({
|
|
|
1471
1344
|
"aria-label": dropdown.label,
|
|
1472
1345
|
title: dropdown.label,
|
|
1473
1346
|
"data-dropdown": dropdown.name,
|
|
1474
|
-
dangerouslySetInnerHTML:
|
|
1347
|
+
dangerouslySetInnerHTML: innerHtml(triggerHtml),
|
|
1475
1348
|
onMouseDown: (e) => {
|
|
1476
1349
|
e.preventDefault();
|
|
1477
1350
|
},
|
|
@@ -1496,7 +1369,7 @@ function BubbleDropdown({
|
|
|
1496
1369
|
className: `dm-toolbar-dropdown-item${subActive ? " dm-toolbar-dropdown-item--active" : ""}`,
|
|
1497
1370
|
role: "menuitem",
|
|
1498
1371
|
"aria-label": sub.label,
|
|
1499
|
-
dangerouslySetInnerHTML:
|
|
1372
|
+
dangerouslySetInnerHTML: innerHtml(subHtml),
|
|
1500
1373
|
onMouseDown: (e) => {
|
|
1501
1374
|
e.preventDefault();
|
|
1502
1375
|
},
|
|
@@ -1682,6 +1555,7 @@ function FloatingMenuItemButton({
|
|
|
1682
1555
|
iconHtml,
|
|
1683
1556
|
onClick
|
|
1684
1557
|
}) {
|
|
1558
|
+
const innerHtml = useInnerHtml();
|
|
1685
1559
|
const handleClick = useCallback(() => {
|
|
1686
1560
|
onClick(item);
|
|
1687
1561
|
}, [item, onClick]);
|
|
@@ -1708,7 +1582,7 @@ function FloatingMenuItemButton({
|
|
|
1708
1582
|
{
|
|
1709
1583
|
className: "dm-floating-menu-item-icon",
|
|
1710
1584
|
"aria-hidden": "true",
|
|
1711
|
-
dangerouslySetInnerHTML:
|
|
1585
|
+
dangerouslySetInnerHTML: innerHtml(iconHtml)
|
|
1712
1586
|
}
|
|
1713
1587
|
),
|
|
1714
1588
|
/* @__PURE__ */ jsx("span", { className: "dm-floating-menu-item-label", children: item.label }),
|
|
@@ -1918,10 +1792,10 @@ var CATEGORY_ICONS = {
|
|
|
1918
1792
|
"Animals & Nature": "\u{1F431}",
|
|
1919
1793
|
"Food & Drink": "\u{1F355}",
|
|
1920
1794
|
"Travel & Places": "\u{1F697}",
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1795
|
+
Activities: "\u26BD",
|
|
1796
|
+
Objects: "\u{1F4A1}",
|
|
1797
|
+
Symbols: "\u{1F523}",
|
|
1798
|
+
Flags: "\u{1F3C1}"
|
|
1925
1799
|
};
|
|
1926
1800
|
function categoryIcon(cat) {
|
|
1927
1801
|
return CATEGORY_ICONS[cat] ?? cat.charAt(0);
|
|
@@ -1929,7 +1803,10 @@ function categoryIcon(cat) {
|
|
|
1929
1803
|
function formatName(name) {
|
|
1930
1804
|
return name.replace(/_/g, " ");
|
|
1931
1805
|
}
|
|
1932
|
-
function DomternalEmojiPicker({
|
|
1806
|
+
function DomternalEmojiPicker({
|
|
1807
|
+
editor: editorProp,
|
|
1808
|
+
emojis
|
|
1809
|
+
}) {
|
|
1933
1810
|
const { editor: contextEditor } = useCurrentEditor();
|
|
1934
1811
|
const editor = editorProp ?? contextEditor;
|
|
1935
1812
|
const {
|
|
@@ -1961,7 +1838,7 @@ function DomternalEmojiPicker({ editor: editorProp, emojis }) {
|
|
|
1961
1838
|
return;
|
|
1962
1839
|
}
|
|
1963
1840
|
const cols = 8;
|
|
1964
|
-
let next
|
|
1841
|
+
let next;
|
|
1965
1842
|
switch (event.key) {
|
|
1966
1843
|
case "ArrowRight":
|
|
1967
1844
|
event.preventDefault();
|
|
@@ -2099,6 +1976,7 @@ function DomternalContent({ className }) {
|
|
|
2099
1976
|
const editorDom = editor.view.dom;
|
|
2100
1977
|
if (editorDom.parentElement !== container) {
|
|
2101
1978
|
container.appendChild(editorDom);
|
|
1979
|
+
editor.adoptPresetClass();
|
|
2102
1980
|
}
|
|
2103
1981
|
}, [editor]);
|
|
2104
1982
|
const classes = className ? `dm-editor ${className}` : "dm-editor";
|
|
@@ -2203,6 +2081,7 @@ function EditorContent({ editor, innerRef, ...htmlProps }) {
|
|
|
2203
2081
|
const editorDom = editor.view.dom;
|
|
2204
2082
|
if (editorDom.parentElement !== container) {
|
|
2205
2083
|
container.appendChild(editorDom);
|
|
2084
|
+
editor.adoptPresetClass();
|
|
2206
2085
|
}
|
|
2207
2086
|
return () => {
|
|
2208
2087
|
};
|
|
@@ -2220,6 +2099,14 @@ function EditorContent({ editor, innerRef, ...htmlProps }) {
|
|
|
2220
2099
|
}
|
|
2221
2100
|
);
|
|
2222
2101
|
}
|
|
2102
|
+
function paletteFromExtensionOptions(options) {
|
|
2103
|
+
if (typeof options !== "object" || options === null || !("palette" in options)) return [];
|
|
2104
|
+
const palette = options.palette;
|
|
2105
|
+
if (!Array.isArray(palette) || !palette.every((token) => typeof token === "string")) {
|
|
2106
|
+
return [];
|
|
2107
|
+
}
|
|
2108
|
+
return [...palette];
|
|
2109
|
+
}
|
|
2223
2110
|
var TOKEN_LABELS = {
|
|
2224
2111
|
gray: "Gray",
|
|
2225
2112
|
brown: "Brown",
|
|
@@ -2277,22 +2164,24 @@ function useNotionColorPicker(options) {
|
|
|
2277
2164
|
setCurrentTextToken(attrs.colorToken ?? null);
|
|
2278
2165
|
setCurrentBgToken(attrs.backgroundColorToken ?? null);
|
|
2279
2166
|
}, []);
|
|
2280
|
-
const close = useCallback(
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2167
|
+
const close = useCallback(
|
|
2168
|
+
(opts = {}) => {
|
|
2169
|
+
if (!isOpenRef.current) return;
|
|
2170
|
+
setIsOpen(false);
|
|
2171
|
+
setStorageOpen2(false);
|
|
2172
|
+
if (opts.refocus) {
|
|
2173
|
+
editorRef.current?.view.focus();
|
|
2174
|
+
}
|
|
2175
|
+
setAnchorEl(null);
|
|
2176
|
+
},
|
|
2177
|
+
[setStorageOpen2]
|
|
2178
|
+
);
|
|
2289
2179
|
useEffect(() => {
|
|
2290
2180
|
if (!editor || editor.isDestroyed) return;
|
|
2291
2181
|
const host = editor.view.dom.closest(".dm-editor") ?? null;
|
|
2292
2182
|
setHostEl(host);
|
|
2293
2183
|
const ext = editor.extensionManager.extensions.find((e) => e.name === "notionColorPicker");
|
|
2294
|
-
|
|
2295
|
-
setPalette(extOptions?.palette ? [...extOptions.palette] : []);
|
|
2184
|
+
setPalette(paletteFromExtensionOptions(ext?.options));
|
|
2296
2185
|
const onOpen = (...args) => {
|
|
2297
2186
|
const detail = args[0];
|
|
2298
2187
|
const incomingAnchor = detail?.anchorElement;
|
|
@@ -2330,35 +2219,49 @@ function useNotionColorPicker(options) {
|
|
|
2330
2219
|
if (!isOpen) return;
|
|
2331
2220
|
const controller = new AbortController();
|
|
2332
2221
|
const { signal } = controller;
|
|
2333
|
-
document.addEventListener(
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2222
|
+
document.addEventListener(
|
|
2223
|
+
"mousedown",
|
|
2224
|
+
(e) => {
|
|
2225
|
+
const target = e.target;
|
|
2226
|
+
if (!target) return;
|
|
2227
|
+
if (panelRef.current?.contains(target)) return;
|
|
2228
|
+
if (anchorRef.current?.contains(target)) return;
|
|
2229
|
+
close({ refocus: false });
|
|
2230
|
+
},
|
|
2231
|
+
{ signal }
|
|
2232
|
+
);
|
|
2233
|
+
document.addEventListener(
|
|
2234
|
+
"keydown",
|
|
2235
|
+
(e) => {
|
|
2236
|
+
if (e.key === "Escape" && isOpenRef.current) {
|
|
2237
|
+
e.preventDefault();
|
|
2238
|
+
close({ refocus: true });
|
|
2239
|
+
}
|
|
2240
|
+
},
|
|
2241
|
+
{ signal }
|
|
2242
|
+
);
|
|
2346
2243
|
return () => {
|
|
2347
2244
|
controller.abort();
|
|
2348
2245
|
};
|
|
2349
2246
|
}, [isOpen, close]);
|
|
2350
|
-
const applyText = useCallback(
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2247
|
+
const applyText = useCallback(
|
|
2248
|
+
(token) => {
|
|
2249
|
+
const ed = editorRef.current;
|
|
2250
|
+
if (!ed) return;
|
|
2251
|
+
ed.commands.setTextColorToken(token);
|
|
2252
|
+
syncFromSelection();
|
|
2253
|
+
},
|
|
2254
|
+
[syncFromSelection]
|
|
2255
|
+
);
|
|
2256
|
+
const applyBg = useCallback(
|
|
2257
|
+
(token) => {
|
|
2258
|
+
const ed = editorRef.current;
|
|
2259
|
+
if (!ed) return;
|
|
2260
|
+
ed.commands.setBackgroundColorToken(token);
|
|
2261
|
+
syncFromSelection();
|
|
2262
|
+
},
|
|
2263
|
+
[syncFromSelection]
|
|
2264
|
+
);
|
|
2362
2265
|
const tokenLabel = useCallback((token) => {
|
|
2363
2266
|
return TOKEN_LABELS[token] ?? token.charAt(0).toUpperCase() + token.slice(1);
|
|
2364
2267
|
}, []);
|
|
@@ -2366,14 +2269,12 @@ function useNotionColorPicker(options) {
|
|
|
2366
2269
|
const cols = 5;
|
|
2367
2270
|
const root = panelRef.current;
|
|
2368
2271
|
if (!root) return;
|
|
2369
|
-
const swatches = Array.from(
|
|
2370
|
-
root.querySelectorAll(".dm-ncp-swatch")
|
|
2371
|
-
);
|
|
2272
|
+
const swatches = Array.from(root.querySelectorAll(".dm-ncp-swatch"));
|
|
2372
2273
|
if (!swatches.length) return;
|
|
2373
2274
|
const active = document.activeElement;
|
|
2374
2275
|
const idx = active ? swatches.indexOf(active) : -1;
|
|
2375
2276
|
if (idx === -1) return;
|
|
2376
|
-
let next
|
|
2277
|
+
let next;
|
|
2377
2278
|
switch (event.key) {
|
|
2378
2279
|
case "ArrowRight":
|
|
2379
2280
|
event.preventDefault();
|