@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/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: { __html: iconHtml },
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: { __html: getCachedItemContent(sub.icon, sub.label) },
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: { __html: getCachedItemContent(sub.icon, sub.label, dropdown.displayMode) },
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: { __html: triggerHtml },
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 itemMap = /* @__PURE__ */ new Map();
950
- const dropdownMap = /* @__PURE__ */ new Map();
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
- if (contexts) {
1086
- updateContextItems(editor, contexts, detectContext, resolveNames, getFormatItems, filterBySchema, bubbleDefaults, setItems);
1087
- } else if (items) {
1088
- setItems(resolveNames(items));
1089
- } else {
1090
- setItems(resolveNames(["bold", "italic", "underline"]));
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
- showColorPickerButton: hasNotionColorPicker,
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
- if (contexts) {
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: { __html: getCachedHtml(btn.icon) },
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
- trailing.showColorPickerButton && !trailing.isNodeSelection && /* @__PURE__ */ jsxs(Fragment$1, { children: [
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
- trailing.showBlockMenuButton && !trailing.isNodeSelection && /* @__PURE__ */ jsxs(Fragment$1, { children: [
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: { __html: getCachedHtml("dotsThree") },
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: { __html: triggerHtml },
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: { __html: subHtml },
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: { __html: iconHtml }
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
- "Activities": "\u26BD",
1922
- "Objects": "\u{1F4A1}",
1923
- "Symbols": "\u{1F523}",
1924
- "Flags": "\u{1F3C1}"
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({ editor: editorProp, emojis }) {
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 = idx;
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((opts = {}) => {
2281
- if (!isOpenRef.current) return;
2282
- setIsOpen(false);
2283
- setStorageOpen2(false);
2284
- if (opts.refocus) {
2285
- editorRef.current?.view.focus();
2286
- }
2287
- setAnchorEl(null);
2288
- }, [setStorageOpen2]);
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
- const extOptions = ext?.options ?? null;
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("mousedown", (e) => {
2334
- const target = e.target;
2335
- if (!target) return;
2336
- if (panelRef.current?.contains(target)) return;
2337
- if (anchorRef.current?.contains(target)) return;
2338
- close({ refocus: false });
2339
- }, { signal });
2340
- document.addEventListener("keydown", (e) => {
2341
- if (e.key === "Escape" && isOpenRef.current) {
2342
- e.preventDefault();
2343
- close({ refocus: true });
2344
- }
2345
- }, { signal });
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((token) => {
2351
- const ed = editorRef.current;
2352
- if (!ed) return;
2353
- ed.commands.setTextColorToken(token);
2354
- syncFromSelection();
2355
- }, [syncFromSelection]);
2356
- const applyBg = useCallback((token) => {
2357
- const ed = editorRef.current;
2358
- if (!ed) return;
2359
- ed.commands.setBackgroundColorToken(token);
2360
- syncFromSelection();
2361
- }, [syncFromSelection]);
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 = idx;
2277
+ let next;
2377
2278
  switch (event.key) {
2378
2279
  case "ArrowRight":
2379
2280
  event.preventDefault();