@domternal/vue 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 { 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, refocusEditorAfterCommand, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, defaultBubbleContexts, createBubbleMenuPlugin, Document, Paragraph, Text, BaseKeymap, History, Editor } from '@domternal/core';
2
+ import { positionFloatingOnce, PluginKey, positionFloating, ToolbarController, refocusEditorAfterCommand, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, defaultBubbleContexts, buildBubbleItemMaps, createBubbleShouldShow, createBubbleMenuPlugin, resolveBubbleNames, Document, Paragraph, Text, BaseKeymap, History, Editor, resolveBubbleMenuItems } from '@domternal/core';
3
3
  export { Editor, generateHTML, generateJSON, generateText } from '@domternal/core';
4
4
 
5
5
  // src/useEditor.ts
@@ -33,7 +33,8 @@ function useEditor(options = {}) {
33
33
  extensions: [...defaults, ...extensions],
34
34
  content: initialContent,
35
35
  editable,
36
- autofocus: focus
36
+ autofocus: focus,
37
+ ...options.preset ? { preset: options.preset } : {}
37
38
  });
38
39
  markRaw(ed);
39
40
  wireEvents(ed);
@@ -69,6 +70,7 @@ function useEditor(options = {}) {
69
70
  const mount = editorRef.value;
70
71
  if (mount && ed.view.dom.parentElement !== mount) {
71
72
  mount.appendChild(ed.view.dom);
73
+ ed.adoptPresetClass();
72
74
  }
73
75
  return;
74
76
  }
@@ -279,6 +281,7 @@ function useToolbarController(editor, layout) {
279
281
  const toolbarRef = ref();
280
282
  let cleanupFloating = null;
281
283
  let clickOutsideHandler = null;
284
+ let escapeHandler = null;
282
285
  let dismissOverlayHandler = null;
283
286
  let editorEl = null;
284
287
  let syncStateRaf = 0;
@@ -315,6 +318,17 @@ function useToolbarController(editor, layout) {
315
318
  }
316
319
  };
317
320
  document.addEventListener("mousedown", clickOutsideHandler);
321
+ escapeHandler = (e) => {
322
+ if (!controller?.openDropdown) return;
323
+ if (e.key !== "Escape") return;
324
+ if (toolbarRef.value?.contains(document.activeElement)) return;
325
+ cleanupFloating?.();
326
+ cleanupFloating = null;
327
+ controller.closeDropdown();
328
+ syncState();
329
+ e.preventDefault();
330
+ };
331
+ document.addEventListener("keydown", escapeHandler);
318
332
  editorEl = ed.view.dom.closest(".dm-editor");
319
333
  if (editorEl) {
320
334
  dismissOverlayHandler = () => {
@@ -338,6 +352,10 @@ function useToolbarController(editor, layout) {
338
352
  document.removeEventListener("mousedown", clickOutsideHandler);
339
353
  clickOutsideHandler = null;
340
354
  }
355
+ if (escapeHandler) {
356
+ document.removeEventListener("keydown", escapeHandler);
357
+ escapeHandler = null;
358
+ }
341
359
  if (dismissOverlayHandler && editorEl) {
342
360
  editorEl.removeEventListener("dm:dismiss-overlays", dismissOverlayHandler);
343
361
  dismissOverlayHandler = null;
@@ -848,11 +866,9 @@ var DomternalToolbar = defineComponent({
848
866
  computed(() => props.editor ?? contextEditor.value),
849
867
  props.layout
850
868
  );
851
- const {
852
- getCachedIcon,
853
- getCachedItemContent,
854
- getDropdownTriggerHtml
855
- } = useToolbarIcons(props.icons);
869
+ const { getCachedIcon, getCachedItemContent, getDropdownTriggerHtml } = useToolbarIcons(
870
+ props.icons
871
+ );
856
872
  const { getTooltip } = useTooltip();
857
873
  const { onKeyDown } = useKeyboardNav(controllerRef, toolbarRef, closeDropdown);
858
874
  function onButtonClick(item, event) {
@@ -932,7 +948,9 @@ var DomternalToolbar = defineComponent({
932
948
  }
933
949
  if (item.type === "dropdown") {
934
950
  const dd = item;
935
- const activeItem = dd.items.find((sub) => controllerRef.current?.activeMap.get(sub.name));
951
+ const activeItem = dd.items.find(
952
+ (sub) => controllerRef.current?.activeMap.get(sub.name)
953
+ );
936
954
  let triggerHtml = getDropdownTriggerHtml(dd, activeItem);
937
955
  if (dd.dynamicLabel && !activeItem && dd.computedStyleProperty) {
938
956
  let computed7;
@@ -982,20 +1000,6 @@ var INITIAL_TRAILING_STATE = {
982
1000
  currentBgColorVar: null,
983
1001
  hasAnyColor: false
984
1002
  };
985
- function isInsideTableCell($pos) {
986
- for (let d = $pos.depth; d > 0; d--) {
987
- const name = $pos.node(d).type.name;
988
- if (name === "tableCell" || name === "tableHeader") return true;
989
- }
990
- return false;
991
- }
992
- function findCellNode(pos) {
993
- for (let d = pos.depth; d > 0; d--) {
994
- const node = pos.node(d);
995
- if (node.type.name === "tableCell" || node.type.name === "tableHeader") return node;
996
- }
997
- return null;
998
- }
999
1003
  function useBubbleMenu(options) {
1000
1004
  const { editor, shouldShow, placement = "top", offset = 8, updateDelay = 0, items, contexts: explicitContexts, icons: iconsRef } = options;
1001
1005
  const menuRef = ref();
@@ -1008,9 +1012,7 @@ function useBubbleMenu(options) {
1008
1012
  const trailing = shallowRef(INITIAL_TRAILING_STATE);
1009
1013
  const activeMapRef = /* @__PURE__ */ new Map();
1010
1014
  const disabledMapRef = /* @__PURE__ */ new Map();
1011
- let itemMap;
1012
- let dropdownMap;
1013
- let bubbleDefaults;
1015
+ let maps;
1014
1016
  let currentResolvedItems = [];
1015
1017
  let initialized = false;
1016
1018
  let stopEditorWatch = null;
@@ -1021,125 +1023,8 @@ function useBubbleMenu(options) {
1021
1023
  const exts = ed.extensionManager.extensions;
1022
1024
  const hasNotionColorPicker = exts.some((e) => e.name === "notionColorPicker");
1023
1025
  const hasBlockContextMenu = exts.some((e) => e.name === "blockContextMenu");
1024
- itemMap = /* @__PURE__ */ new Map();
1025
- dropdownMap = /* @__PURE__ */ new Map();
1026
- for (const item of ed.toolbarItems) {
1027
- if (item.type === "button") {
1028
- itemMap.set(item.name, item);
1029
- } else if (item.type === "dropdown") {
1030
- dropdownMap.set(item.name, item);
1031
- for (const sub of item.items) {
1032
- itemMap.set(sub.name, sub);
1033
- }
1034
- }
1035
- }
1036
- bubbleDefaults = /* @__PURE__ */ new Map();
1037
- const byCtx = /* @__PURE__ */ new Map();
1038
- const addItem = (btn) => {
1039
- const ctx = btn["bubbleMenu"];
1040
- if (!ctx) return;
1041
- let arr = byCtx.get(ctx);
1042
- if (!arr) {
1043
- arr = [];
1044
- byCtx.set(ctx, arr);
1045
- }
1046
- arr.push(btn);
1047
- };
1048
- for (const item of ed.toolbarItems) {
1049
- if (item.type === "button") addItem(item);
1050
- else if (item.type === "dropdown") {
1051
- for (const sub of item.items) addItem(sub);
1052
- }
1053
- }
1054
- for (const [ctx, ctxItems] of byCtx) {
1055
- ctxItems.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
1056
- const result = [];
1057
- let lastGroup;
1058
- let sepIdx = 0;
1059
- for (const item of ctxItems) {
1060
- if (lastGroup !== void 0 && item.group !== lastGroup) {
1061
- result.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
1062
- }
1063
- result.push(item);
1064
- lastGroup = item.group;
1065
- }
1066
- bubbleDefaults.set(ctx, result);
1067
- }
1068
- const resolveNames = (names) => {
1069
- const result = [];
1070
- let sepIdx = 0;
1071
- for (const name of names) {
1072
- if (name === "|") {
1073
- result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
1074
- } else {
1075
- const dropdown = dropdownMap.get(name);
1076
- if (dropdown) {
1077
- result.push(dropdown);
1078
- continue;
1079
- }
1080
- const item = itemMap.get(name);
1081
- if (item) result.push(item);
1082
- }
1083
- }
1084
- return result;
1085
- };
1086
- const getFormatItems = () => {
1087
- return Array.from(itemMap.values()).filter((item) => item.group === "format").sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
1088
- };
1089
- const detectContext = (selection, ctxs) => {
1090
- if ("$anchorCell" in selection) return null;
1091
- if (selection.node) return selection.node.type.name;
1092
- if (selection.empty) return null;
1093
- const fromCell = findCellNode(selection.$from);
1094
- if (fromCell) {
1095
- const toCell = findCellNode(selection.$to);
1096
- if (toCell && fromCell !== toCell) return null;
1097
- return "table";
1098
- }
1099
- const fromName = selection.$from.parent.type.name;
1100
- if (fromName in ctxs) return fromName;
1101
- if ("text" in ctxs && selection.$from.parent.type.spec.marks !== "") return "text";
1102
- const toName = selection.$to.parent.type.name;
1103
- if (toName in ctxs) return toName;
1104
- if ("text" in ctxs && selection.$to.parent.type.spec.marks !== "") return "text";
1105
- return null;
1106
- };
1107
- const filterBySchema = (contextName, schemaItems) => {
1108
- if (contextName === "text" || contextName === "table") return schemaItems;
1109
- const schema = ed.state.schema;
1110
- if (!schema) return schemaItems;
1111
- const nodeType = schema.nodes[contextName];
1112
- if (!nodeType) return schemaItems;
1113
- return schemaItems.filter((item) => {
1114
- const markName = typeof item.isActive === "string" ? item.isActive : null;
1115
- if (!markName) return true;
1116
- const markType = schema.marks[markName];
1117
- if (!markType) return true;
1118
- return nodeType.allowsMarkType(markType);
1119
- });
1120
- };
1121
- let shouldShowFn = shouldShow;
1122
- if (!shouldShowFn) {
1123
- if (contexts) {
1124
- shouldShowFn = ({ state }) => {
1125
- const context = detectContext(state.selection, contexts);
1126
- if (!context) return false;
1127
- if (context in contexts) {
1128
- const val = contexts[context];
1129
- if (val === null) return false;
1130
- return val === true || Array.isArray(val) && val.length > 0;
1131
- }
1132
- return bubbleDefaults.has(context);
1133
- };
1134
- } else {
1135
- shouldShowFn = ({ state }) => {
1136
- if (state.selection.empty) return false;
1137
- if (state.selection.node) return bubbleDefaults.has(state.selection.node.type.name);
1138
- if (isInsideTableCell(state.selection.$from)) return false;
1139
- return state.selection.$from.parent.type.spec.marks !== "" || state.selection.$to.parent.type.spec.marks !== "";
1140
- };
1141
- }
1142
- }
1026
+ maps = buildBubbleItemMaps(ed);
1027
+ const shouldShowFn = shouldShow ?? createBubbleShouldShow(maps, contexts);
1143
1028
  const plugin = createBubbleMenuPlugin({
1144
1029
  pluginKey,
1145
1030
  editor: ed,
@@ -1154,13 +1039,15 @@ function useBubbleMenu(options) {
1154
1039
  currentResolvedItems = newItems;
1155
1040
  resolvedItems.value = newItems;
1156
1041
  };
1157
- if (contexts) {
1158
- updateContextItems(ed, contexts, detectContext, resolveNames, getFormatItems, filterBySchema, bubbleDefaults, setItems);
1159
- } else if (items) {
1160
- setItems(resolveNames(items));
1161
- } else {
1162
- setItems(resolveNames(["bold", "italic", "underline"]));
1163
- }
1042
+ const fallbackItems = resolveBubbleNames(
1043
+ items ?? ["bold", "italic", "underline"],
1044
+ maps.itemMap,
1045
+ maps.dropdownMap
1046
+ );
1047
+ const refreshItems = () => {
1048
+ setItems(resolveBubbleMenuItems({ editor: ed, maps, contexts, fallbackItems }));
1049
+ };
1050
+ refreshItems();
1164
1051
  const updateStates = (currentEd) => {
1165
1052
  let canProxy = null;
1166
1053
  try {
@@ -1185,7 +1072,6 @@ function useBubbleMenu(options) {
1185
1072
  trackButton(item);
1186
1073
  }
1187
1074
  };
1188
- const defaultItems = items ? resolveNames(items) : resolveNames(["bold", "italic", "underline"]);
1189
1075
  const syncTrailingState = (currentEd) => {
1190
1076
  const sel = currentEd.state.selection;
1191
1077
  const isNode = !!sel.node;
@@ -1212,7 +1098,8 @@ function useBubbleMenu(options) {
1212
1098
  }
1213
1099
  trailing.value = {
1214
1100
  isNodeSelection: isNode,
1215
- showColorPickerButton: hasNotionColorPicker,
1101
+ // Hidden without a live notionColorOpen listener: the trigger would be dead.
1102
+ showColorPickerButton: hasNotionColorPicker && currentEd.listenerCount("notionColorOpen") > 0,
1216
1103
  showBlockMenuButton: hasBlockContextMenu,
1217
1104
  blockMenuButtonDisabled: blockMenuDisabled,
1218
1105
  currentTextColorVar: textVar,
@@ -1221,16 +1108,7 @@ function useBubbleMenu(options) {
1221
1108
  };
1222
1109
  };
1223
1110
  const transactionHandler = () => {
1224
- if (contexts) {
1225
- updateContextItems(ed, contexts, detectContext, resolveNames, getFormatItems, filterBySchema, bubbleDefaults, setItems);
1226
- } else {
1227
- const sel = ed.state.selection;
1228
- if (sel.node && bubbleDefaults.has(sel.node.type.name)) {
1229
- setItems(bubbleDefaults.get(sel.node.type.name) ?? []);
1230
- } else {
1231
- setItems(defaultItems);
1232
- }
1233
- }
1111
+ refreshItems();
1234
1112
  updateStates(ed);
1235
1113
  syncTrailingState(ed);
1236
1114
  activeVersion.value++;
@@ -1265,30 +1143,6 @@ function useBubbleMenu(options) {
1265
1143
  }
1266
1144
  }
1267
1145
  });
1268
- function updateContextItems(ed, ctxs, detectContext, resolveNames, getFormatItems, filterBySchema, defaults, setItems) {
1269
- const ctx = detectContext(ed.state.selection, ctxs);
1270
- if (!ctx) {
1271
- setItems([]);
1272
- return;
1273
- }
1274
- if (ctx in ctxs) {
1275
- const val = ctxs[ctx];
1276
- if (val === null || Array.isArray(val) && val.length === 0) {
1277
- setItems([]);
1278
- return;
1279
- }
1280
- if (val === true) {
1281
- setItems(filterBySchema(ctx, getFormatItems()));
1282
- } else if (Array.isArray(val)) {
1283
- const resolved = resolveNames(val);
1284
- const buttons = resolved.filter((i) => i.type !== "separator");
1285
- const filtered = new Set(filterBySchema(ctx, buttons).map((b) => b.name));
1286
- setItems(resolved.filter((i) => i.type === "separator" || filtered.has(i.name)));
1287
- }
1288
- } else {
1289
- setItems(defaults.get(ctx) ?? []);
1290
- }
1291
- }
1292
1146
  const isItemActive = (item) => {
1293
1147
  return activeMapRef.get(item.name) ?? false;
1294
1148
  };
@@ -1441,9 +1295,13 @@ var DomternalBubbleMenu = defineComponent({
1441
1295
  }
1442
1296
  }));
1443
1297
  }
1444
- if (t.showColorPickerButton && !t.isNodeSelection) {
1298
+ const showColor = t.showColorPickerButton && !t.isNodeSelection;
1299
+ const showBlock = t.showBlockMenuButton && !t.isNodeSelection;
1300
+ if (showColor) {
1301
+ if (children.length > 0) {
1302
+ children.push(h("span", { class: "dm-toolbar-separator", role: "separator" }));
1303
+ }
1445
1304
  children.push(
1446
- h("span", { class: "dm-toolbar-separator", role: "separator" }),
1447
1305
  h("button", {
1448
1306
  ref: colorBtnRef,
1449
1307
  type: "button",
@@ -1469,9 +1327,11 @@ var DomternalBubbleMenu = defineComponent({
1469
1327
  ])
1470
1328
  );
1471
1329
  }
1472
- if (t.showBlockMenuButton && !t.isNodeSelection) {
1330
+ if (showBlock) {
1331
+ if (children.length > 0) {
1332
+ children.push(h("span", { class: "dm-toolbar-separator", role: "separator" }));
1333
+ }
1473
1334
  children.push(
1474
- h("span", { class: "dm-toolbar-separator", role: "separator" }),
1475
1335
  h("button", {
1476
1336
  ref: blockMenuBtnRef,
1477
1337
  type: "button",
@@ -2028,10 +1888,10 @@ var CATEGORY_ICONS = {
2028
1888
  "Animals & Nature": "\u{1F431}",
2029
1889
  "Food & Drink": "\u{1F355}",
2030
1890
  "Travel & Places": "\u{1F697}",
2031
- "Activities": "\u26BD",
2032
- "Objects": "\u{1F4A1}",
2033
- "Symbols": "\u{1F523}",
2034
- "Flags": "\u{1F3C1}"
1891
+ Activities: "\u26BD",
1892
+ Objects: "\u{1F4A1}",
1893
+ Symbols: "\u{1F523}",
1894
+ Flags: "\u{1F3C1}"
2035
1895
  };
2036
1896
  function categoryIcon(cat) {
2037
1897
  return CATEGORY_ICONS[cat] ?? cat.charAt(0);
@@ -2079,7 +1939,7 @@ var DomternalEmojiPicker = defineComponent({
2079
1939
  return;
2080
1940
  }
2081
1941
  const cols = 8;
2082
- let next = idx;
1942
+ let next;
2083
1943
  switch (event.key) {
2084
1944
  case "ArrowRight":
2085
1945
  event.preventDefault();
@@ -2108,20 +1968,24 @@ var DomternalEmojiPicker = defineComponent({
2108
1968
  swatches[next]?.focus();
2109
1969
  }
2110
1970
  function renderEmojiButton(item) {
2111
- return h("button", {
2112
- key: item.name,
2113
- type: "button",
2114
- class: "dm-emoji-swatch",
2115
- tabindex: -1,
2116
- title: formatName(item.name),
2117
- "aria-label": formatName(item.name),
2118
- onMousedown: (e) => {
2119
- e.preventDefault();
1971
+ return h(
1972
+ "button",
1973
+ {
1974
+ key: item.name,
1975
+ type: "button",
1976
+ class: "dm-emoji-swatch",
1977
+ tabindex: -1,
1978
+ title: formatName(item.name),
1979
+ "aria-label": formatName(item.name),
1980
+ onMousedown: (e) => {
1981
+ e.preventDefault();
1982
+ },
1983
+ onClick: () => {
1984
+ selectEmoji(item);
1985
+ }
2120
1986
  },
2121
- onClick: () => {
2122
- selectEmoji(item);
2123
- }
2124
- }, item.emoji);
1987
+ item.emoji
1988
+ );
2125
1989
  }
2126
1990
  return () => {
2127
1991
  if (!isOpen.value) {
@@ -2147,21 +2011,28 @@ var DomternalEmojiPicker = defineComponent({
2147
2011
  "div",
2148
2012
  { class: "dm-emoji-picker-tabs", role: "tablist" },
2149
2013
  categoryNames.value.map(
2150
- (cat) => h("button", {
2151
- key: cat,
2152
- type: "button",
2153
- class: ["dm-emoji-picker-tab", activeCategory.value === cat && "dm-emoji-picker-tab--active"],
2154
- role: "tab",
2155
- "aria-selected": activeCategory.value === cat,
2156
- title: cat,
2157
- "aria-label": cat,
2158
- onMousedown: (e) => {
2159
- e.preventDefault();
2014
+ (cat) => h(
2015
+ "button",
2016
+ {
2017
+ key: cat,
2018
+ type: "button",
2019
+ class: [
2020
+ "dm-emoji-picker-tab",
2021
+ activeCategory.value === cat && "dm-emoji-picker-tab--active"
2022
+ ],
2023
+ role: "tab",
2024
+ "aria-selected": activeCategory.value === cat,
2025
+ title: cat,
2026
+ "aria-label": cat,
2027
+ onMousedown: (e) => {
2028
+ e.preventDefault();
2029
+ },
2030
+ onClick: () => {
2031
+ scrollToCategory(cat);
2032
+ }
2160
2033
  },
2161
- onClick: () => {
2162
- scrollToCategory(cat);
2163
- }
2164
- }, categoryIcon(cat))
2034
+ categoryIcon(cat)
2035
+ )
2165
2036
  )
2166
2037
  ),
2167
2038
  // Grid
@@ -2176,11 +2047,15 @@ var DomternalEmojiPicker = defineComponent({
2176
2047
  ] : [],
2177
2048
  // All categories
2178
2049
  ...categoryNames.value.flatMap((cat) => [
2179
- h("div", {
2180
- key: `label-${cat}`,
2181
- class: "dm-emoji-picker-category-label",
2182
- "data-category": cat
2183
- }, cat),
2050
+ h(
2051
+ "div",
2052
+ {
2053
+ key: `label-${cat}`,
2054
+ class: "dm-emoji-picker-category-label",
2055
+ "data-category": cat
2056
+ },
2057
+ cat
2058
+ ),
2184
2059
  ...(categories.value.get(cat) ?? []).map(renderEmojiButton)
2185
2060
  ])
2186
2061
  ]
@@ -2198,6 +2073,7 @@ var Domternal = defineComponent({
2198
2073
  extensions: { type: Array, default: void 0 },
2199
2074
  content: { type: [String, Object], default: "" },
2200
2075
  editable: { type: Boolean, default: true },
2076
+ preset: { type: String, default: void 0 },
2201
2077
  autofocus: { type: [Boolean, String, Number], default: false },
2202
2078
  outputFormat: { type: String, default: "html" },
2203
2079
  immediatelyRender: { type: Boolean, default: false },
@@ -2213,6 +2089,7 @@ var Domternal = defineComponent({
2213
2089
  ...props.extensions && { extensions: props.extensions },
2214
2090
  content: props.content,
2215
2091
  editable: props.editable,
2092
+ ...props.preset && { preset: props.preset },
2216
2093
  autofocus: props.autofocus,
2217
2094
  outputFormat: props.outputFormat,
2218
2095
  immediatelyRender: props.immediatelyRender,
@@ -2249,6 +2126,7 @@ var DomternalContent = defineComponent({
2249
2126
  const editorDom = ed.view.dom;
2250
2127
  if (editorDom.parentElement !== container) {
2251
2128
  container.appendChild(editorDom);
2129
+ ed.adoptPresetClass();
2252
2130
  }
2253
2131
  });
2254
2132
  return () => {
@@ -2276,6 +2154,7 @@ var DomternalEditor = defineComponent({
2276
2154
  extensions: { type: Array, default: void 0 },
2277
2155
  content: { type: [String, Object], default: void 0 },
2278
2156
  editable: { type: Boolean, default: true },
2157
+ preset: { type: String, default: void 0 },
2279
2158
  autofocus: { type: [Boolean, String, Number], default: false },
2280
2159
  immediatelyRender: { type: Boolean, default: false },
2281
2160
  outputFormat: { type: String, default: "html" },
@@ -2296,6 +2175,7 @@ var DomternalEditor = defineComponent({
2296
2175
  ...props.extensions && { extensions: props.extensions },
2297
2176
  content: props.modelValue ?? props.content ?? "",
2298
2177
  editable: props.editable,
2178
+ ...props.preset && { preset: props.preset },
2299
2179
  autofocus: props.autofocus,
2300
2180
  immediatelyRender: props.immediatelyRender,
2301
2181
  outputFormat: props.outputFormat,
@@ -2385,6 +2265,7 @@ var EditorContent = defineComponent({
2385
2265
  const editorDom = editor.view.dom;
2386
2266
  if (editorDom.parentElement !== container) {
2387
2267
  container.appendChild(editorDom);
2268
+ editor.adoptPresetClass();
2388
2269
  }
2389
2270
  });
2390
2271
  return () => h("div", {
@@ -2394,6 +2275,14 @@ var EditorContent = defineComponent({
2394
2275
  });
2395
2276
  }
2396
2277
  });
2278
+ function paletteFromExtensionOptions(options) {
2279
+ if (typeof options !== "object" || options === null || !("palette" in options)) return [];
2280
+ const palette = options.palette;
2281
+ if (!Array.isArray(palette) || !palette.every((token) => typeof token === "string")) {
2282
+ return [];
2283
+ }
2284
+ return [...palette];
2285
+ }
2397
2286
  var TOKEN_LABELS = {
2398
2287
  gray: "Gray",
2399
2288
  brown: "Brown",
@@ -2458,8 +2347,7 @@ function useNotionColorPicker(options) {
2458
2347
  if (!ed || ed.isDestroyed) return;
2459
2348
  hostEl.value = ed.view.dom.closest(".dm-editor");
2460
2349
  const ext = ed.extensionManager.extensions.find((e) => e.name === "notionColorPicker");
2461
- const extOptions = ext?.options ?? null;
2462
- palette.value = extOptions?.palette ? [...extOptions.palette] : [];
2350
+ palette.value = paletteFromExtensionOptions(ext?.options);
2463
2351
  const onOpen = (...args) => {
2464
2352
  const detail = args[0];
2465
2353
  const incomingAnchor = detail?.anchorElement;
@@ -2499,19 +2387,27 @@ function useNotionColorPicker(options) {
2499
2387
  if (!open) return;
2500
2388
  const controller = new AbortController();
2501
2389
  const { signal } = controller;
2502
- document.addEventListener("mousedown", (e) => {
2503
- const target = e.target;
2504
- if (!target) return;
2505
- if (panelRef.value?.contains(target)) return;
2506
- if (anchorEl.value?.contains(target)) return;
2507
- close({ refocus: false });
2508
- }, { signal });
2509
- document.addEventListener("keydown", (e) => {
2510
- if (e.key === "Escape" && isOpen.value) {
2511
- e.preventDefault();
2512
- close({ refocus: true });
2513
- }
2514
- }, { signal });
2390
+ document.addEventListener(
2391
+ "mousedown",
2392
+ (e) => {
2393
+ const target = e.target;
2394
+ if (!target) return;
2395
+ if (panelRef.value?.contains(target)) return;
2396
+ if (anchorEl.value?.contains(target)) return;
2397
+ close({ refocus: false });
2398
+ },
2399
+ { signal }
2400
+ );
2401
+ document.addEventListener(
2402
+ "keydown",
2403
+ (e) => {
2404
+ if (e.key === "Escape" && isOpen.value) {
2405
+ e.preventDefault();
2406
+ close({ refocus: true });
2407
+ }
2408
+ },
2409
+ { signal }
2410
+ );
2515
2411
  onCleanup(() => {
2516
2412
  controller.abort();
2517
2413
  });
@@ -2535,14 +2431,12 @@ function useNotionColorPicker(options) {
2535
2431
  const cols = 5;
2536
2432
  const root = panelRef.value;
2537
2433
  if (!root) return;
2538
- const swatches = Array.from(
2539
- root.querySelectorAll(".dm-ncp-swatch")
2540
- );
2434
+ const swatches = Array.from(root.querySelectorAll(".dm-ncp-swatch"));
2541
2435
  if (!swatches.length) return;
2542
2436
  const active = document.activeElement;
2543
2437
  const idx = active ? swatches.indexOf(active) : -1;
2544
2438
  if (idx === -1) return;
2545
- let next = idx;
2439
+ let next;
2546
2440
  switch (event.key) {
2547
2441
  case "ArrowRight":
2548
2442
  event.preventDefault();
@@ -2597,7 +2491,9 @@ var DomternalNotionColorPicker = defineComponent({
2597
2491
  },
2598
2492
  setup(props, { slots }) {
2599
2493
  const { editor: contextEditor } = useCurrentEditor();
2600
- const editorRef = computed(() => props.editor ?? contextEditor.value);
2494
+ const editorRef = computed(
2495
+ () => props.editor ?? contextEditor.value
2496
+ );
2601
2497
  const api = useNotionColorPicker({ editor: editorRef });
2602
2498
  const {
2603
2499
  isOpen,
@@ -2626,7 +2522,9 @@ var DomternalNotionColorPicker = defineComponent({
2626
2522
  id2 = requestAnimationFrame(() => {
2627
2523
  if (!panel.isConnected) return;
2628
2524
  const active = panel.querySelector(".dm-ncp-swatch.dm-ncp-active");
2629
- const fallback = panel.querySelector('.dm-ncp-swatch--text[data-color="null"]');
2525
+ const fallback = panel.querySelector(
2526
+ '.dm-ncp-swatch--text[data-color="null"]'
2527
+ );
2630
2528
  (active ?? fallback)?.focus({ preventScroll: true });
2631
2529
  });
2632
2530
  });
@@ -2660,21 +2558,23 @@ var DomternalNotionColorPicker = defineComponent({
2660
2558
  applyText(null);
2661
2559
  }
2662
2560
  }),
2663
- ...pal.map((t) => h("button", {
2664
- key: t,
2665
- type: "button",
2666
- class: ["dm-ncp-swatch", "dm-ncp-swatch--text", tToken === t && "dm-ncp-active"],
2667
- "aria-pressed": tToken === t,
2668
- "data-color": t,
2669
- title: tokenLabel(t),
2670
- "aria-label": `${tokenLabel(t)} text`,
2671
- onMousedown: (e) => {
2672
- e.preventDefault();
2673
- },
2674
- onClick: () => {
2675
- applyText(t);
2676
- }
2677
- }))
2561
+ ...pal.map(
2562
+ (t) => h("button", {
2563
+ key: t,
2564
+ type: "button",
2565
+ class: ["dm-ncp-swatch", "dm-ncp-swatch--text", tToken === t && "dm-ncp-active"],
2566
+ "aria-pressed": tToken === t,
2567
+ "data-color": t,
2568
+ title: tokenLabel(t),
2569
+ "aria-label": `${tokenLabel(t)} text`,
2570
+ onMousedown: (e) => {
2571
+ e.preventDefault();
2572
+ },
2573
+ onClick: () => {
2574
+ applyText(t);
2575
+ }
2576
+ })
2577
+ )
2678
2578
  ])
2679
2579
  ]),
2680
2580
  h("div", { class: "dm-ncp-section" }, [
@@ -2694,21 +2594,23 @@ var DomternalNotionColorPicker = defineComponent({
2694
2594
  applyBg(null);
2695
2595
  }
2696
2596
  }),
2697
- ...pal.map((t) => h("button", {
2698
- key: t,
2699
- type: "button",
2700
- class: ["dm-ncp-swatch", "dm-ncp-swatch--bg", bToken === t && "dm-ncp-active"],
2701
- "aria-pressed": bToken === t,
2702
- "data-color": t,
2703
- title: `${tokenLabel(t)} background`,
2704
- "aria-label": `${tokenLabel(t)} background`,
2705
- onMousedown: (e) => {
2706
- e.preventDefault();
2707
- },
2708
- onClick: () => {
2709
- applyBg(t);
2710
- }
2711
- }))
2597
+ ...pal.map(
2598
+ (t) => h("button", {
2599
+ key: t,
2600
+ type: "button",
2601
+ class: ["dm-ncp-swatch", "dm-ncp-swatch--bg", bToken === t && "dm-ncp-active"],
2602
+ "aria-pressed": bToken === t,
2603
+ "data-color": t,
2604
+ title: `${tokenLabel(t)} background`,
2605
+ "aria-label": `${tokenLabel(t)} background`,
2606
+ onMousedown: (e) => {
2607
+ e.preventDefault();
2608
+ },
2609
+ onClick: () => {
2610
+ applyBg(t);
2611
+ }
2612
+ })
2613
+ )
2712
2614
  ])
2713
2615
  ])
2714
2616
  ];
@@ -2719,19 +2621,23 @@ var DomternalNotionColorPicker = defineComponent({
2719
2621
  const slotChildren = slot ? slot({ api }) : void 0;
2720
2622
  const panelChildren = slotChildren ?? renderDefaultPanel();
2721
2623
  return h(Teleport, { to: hostEl.value }, [
2722
- h("div", {
2723
- ref: panelRef,
2724
- class: "dm-notion-color-picker",
2725
- "data-show": "",
2726
- "data-dm-editor-ui": "",
2727
- role: "dialog",
2728
- "aria-label": "Text and background color",
2729
- // Intentional non-modal: the picker doesn't trap focus (outside-click
2730
- // closes, editor stays interactive). `role="dialog"` defaults to
2731
- // modal=true for screen readers, so we explicitly opt out.
2732
- "aria-modal": "false",
2733
- onKeydown: onPanelKeydown
2734
- }, panelChildren)
2624
+ h(
2625
+ "div",
2626
+ {
2627
+ ref: panelRef,
2628
+ class: "dm-notion-color-picker",
2629
+ "data-show": "",
2630
+ "data-dm-editor-ui": "",
2631
+ role: "dialog",
2632
+ "aria-label": "Text and background color",
2633
+ // Intentional non-modal: the picker doesn't trap focus (outside-click
2634
+ // closes, editor stays interactive). `role="dialog"` defaults to
2635
+ // modal=true for screen readers, so we explicitly opt out.
2636
+ "aria-modal": "false",
2637
+ onKeydown: onPanelKeydown
2638
+ },
2639
+ panelChildren
2640
+ )
2735
2641
  ]);
2736
2642
  };
2737
2643
  }