@domternal/react 0.15.0 → 1.0.1
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 +3 -3
- package/dist/index.js +90 -230
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -16,10 +16,10 @@ and React 19.
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
pnpm add @domternal/react @domternal/core react react-dom
|
|
19
|
+
pnpm add @domternal/react @domternal/core @domternal/theme react react-dom
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
`react` (>=18), `react-dom` (>=18), and `@domternal/core` (>=0.
|
|
22
|
+
`react` (>=18), `react-dom` (>=18), and `@domternal/core` (>=1.0.0) are peer dependencies. Import the theme
|
|
23
23
|
([`@domternal/theme`](https://www.npmjs.com/package/@domternal/theme)) in your entry CSS for default styling:
|
|
24
24
|
|
|
25
25
|
```css
|
|
@@ -73,13 +73,35 @@ export default function Editor() {
|
|
|
73
73
|
}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
## Options
|
|
77
|
+
|
|
78
|
+
`useEditor(options, deps?)` and `<Domternal>` take the same options:
|
|
79
|
+
|
|
80
|
+
| Option | Type | Default | Description |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| `extensions` | `AnyExtension[]` | `[]` | Extensions to load on top of `DEFAULT_EXTENSIONS`. |
|
|
83
|
+
| `history` | `boolean` | `true` | Whether the built-in History extension is loaded. Turn it off when an extension brings its own undo. |
|
|
84
|
+
| `content` | `Content` | `''` | Initial content, HTML string or JSON. Changing it syncs the editor. |
|
|
85
|
+
| `editable` | `boolean` | `true` | Whether the editor is editable. |
|
|
86
|
+
| `preset` | `'classic' \| 'notion'` | `'classic'` | `'notion'` paints `dm-notion-mode` on the `.dm-editor` host and switches preset-aware extensions to their Notion behavior. Create-time only. |
|
|
87
|
+
| `autofocus` | `FocusPosition` | `false` | Where to place the caret on mount. |
|
|
88
|
+
| `outputFormat` | `'html' \| 'json'` | `'html'` | Format used when comparing the `content` prop against the document. |
|
|
89
|
+
| `immediatelyRender` | `boolean` | `false` | Create the editor during the first render, so `editor` is never null. Throws in SSR. |
|
|
90
|
+
|
|
91
|
+
`onCreate`, `onUpdate`, `onSelectionChange`, `onFocus`, `onBlur`, and `onDestroy` callbacks are
|
|
92
|
+
accepted alongside them. `<Domternal>` additionally takes `deps`, the dependency array
|
|
93
|
+
`useEditor` reads as its second argument: change a value in it and the editor is rebuilt.
|
|
94
|
+
|
|
76
95
|
## Exports
|
|
77
96
|
|
|
78
|
-
- **Hooks**: `useEditor`, `useEditorState` (full state or a granular selector), `useCurrentEditor
|
|
97
|
+
- **Hooks**: `useEditor`, `useEditorState` (full state or a granular selector), `useCurrentEditor`,
|
|
98
|
+
`useNotionColorPicker`
|
|
79
99
|
- **Constants**: `DEFAULT_EXTENSIONS`
|
|
80
100
|
- **Composable component**: `Domternal` with `Domternal.Toolbar`, `Domternal.Content`, `Domternal.BubbleMenu`,
|
|
81
101
|
`Domternal.FloatingMenu`, `Domternal.EmojiPicker`, `Domternal.Loading`
|
|
82
102
|
- **Components**: `DomternalEditor`, `EditorContent`, `DomternalToolbar`, `DomternalBubbleMenu`,
|
|
83
103
|
`DomternalFloatingMenu`, `DomternalEmojiPicker`, `DomternalNotionColorPicker`, `EditorProvider`
|
|
84
104
|
- **Node views**: `ReactNodeViewRenderer`, `NodeViewWrapper`, `NodeViewContent`, `useReactNodeView`
|
|
105
|
+
- **Core re-exports**: the `Editor` class plus the `Content`, `AnyExtension`, `FocusPosition`,
|
|
106
|
+
and `JSONContent` types
|
|
85
107
|
- **SSR helpers** (re-exported from core, work without an editor instance): `generateHTML`, `generateJSON`, `generateText`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { DependencyList, ReactNode, HTMLAttributes, Ref, RefObject, KeyboardEvent, ElementType, RefCallback } from 'react';
|
|
3
|
-
import { AnyExtension, Content, EditorPreset, FocusPosition, Editor, JSONContent, IconSet, ToolbarLayoutEntry, BubbleMenuOptions, FloatingMenuOptions, FloatingMenuItemsOverride, FloatingMenuKeymap } from '@domternal/core';
|
|
3
|
+
import { AnyExtension, Content, EditorPreset, FocusPosition, Editor, JSONContent, IconSet, ToolbarLayoutEntry, BubbleMenuOptions, BubbleContexts, FloatingMenuOptions, FloatingMenuItemsOverride, FloatingMenuKeymap } from '@domternal/core';
|
|
4
4
|
export { AnyExtension, Content, Editor, FocusPosition, GenerateHTMLOptions, GenerateJSONOptions, GenerateTextOptions, JSONContent, generateHTML, generateJSON, generateText } from '@domternal/core';
|
|
5
5
|
|
|
6
6
|
declare const DEFAULT_EXTENSIONS: AnyExtension[];
|
|
@@ -177,7 +177,7 @@ interface DomternalBubbleMenuProps {
|
|
|
177
177
|
/** Fixed item names, e.g. ['bold', 'italic', 'code']. */
|
|
178
178
|
items?: string[];
|
|
179
179
|
/** Context-aware: map context names to item arrays, true for all, or null to disable. */
|
|
180
|
-
contexts?:
|
|
180
|
+
contexts?: BubbleContexts;
|
|
181
181
|
/** Custom icon overrides. Falls back to default Phosphor icons for unmapped keys. */
|
|
182
182
|
icons?: IconSet;
|
|
183
183
|
/** Additional content rendered after buttons. */
|
|
@@ -230,7 +230,7 @@ interface DomternalEmojiPickerProps {
|
|
|
230
230
|
/** Array of emoji items with emoji, name, and group. */
|
|
231
231
|
emojis: EmojiPickerItem[];
|
|
232
232
|
}
|
|
233
|
-
declare function DomternalEmojiPicker({ editor: editorProp, emojis }: DomternalEmojiPickerProps): ReactNode;
|
|
233
|
+
declare function DomternalEmojiPicker({ editor: editorProp, emojis, }: DomternalEmojiPickerProps): ReactNode;
|
|
234
234
|
|
|
235
235
|
interface DomternalProps extends UseEditorOptions {
|
|
236
236
|
/** Optional dependency array for forced editor recreation. */
|
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';
|
|
@@ -947,20 +947,6 @@ var INITIAL_TRAILING_STATE = {
|
|
|
947
947
|
currentBgColorVar: null,
|
|
948
948
|
hasAnyColor: false
|
|
949
949
|
};
|
|
950
|
-
function isInsideTableCell($pos) {
|
|
951
|
-
for (let d = $pos.depth; d > 0; d--) {
|
|
952
|
-
const name = $pos.node(d).type.name;
|
|
953
|
-
if (name === "tableCell" || name === "tableHeader") return true;
|
|
954
|
-
}
|
|
955
|
-
return false;
|
|
956
|
-
}
|
|
957
|
-
function findCellNode(pos) {
|
|
958
|
-
for (let d = pos.depth; d > 0; d--) {
|
|
959
|
-
const node = pos.node(d);
|
|
960
|
-
if (node.type.name === "tableCell" || node.type.name === "tableHeader") return node;
|
|
961
|
-
}
|
|
962
|
-
return null;
|
|
963
|
-
}
|
|
964
950
|
function useBubbleMenu(options) {
|
|
965
951
|
const { editor, shouldShow, placement = "top", offset = 8, updateDelay = 0, items, contexts: explicitContexts, icons } = options;
|
|
966
952
|
const contexts = explicitContexts ?? (items ? void 0 : editor ? defaultBubbleContexts(editor) : void 0);
|
|
@@ -971,8 +957,6 @@ function useBubbleMenu(options) {
|
|
|
971
957
|
const [trailing, setTrailing] = useState(INITIAL_TRAILING_STATE);
|
|
972
958
|
const activeMapRef = useRef(/* @__PURE__ */ new Map());
|
|
973
959
|
const disabledMapRef = useRef(/* @__PURE__ */ new Map());
|
|
974
|
-
const itemMapRef = useRef(/* @__PURE__ */ new Map());
|
|
975
|
-
const bubbleDefaultsRef = useRef(/* @__PURE__ */ new Map());
|
|
976
960
|
const resolvedItemsRef = useRef([]);
|
|
977
961
|
const editorRef = useRef(editor);
|
|
978
962
|
editorRef.current = editor;
|
|
@@ -981,127 +965,8 @@ function useBubbleMenu(options) {
|
|
|
981
965
|
const exts = editor.extensionManager.extensions;
|
|
982
966
|
const hasNotionColorPicker = exts.some((e) => e.name === "notionColorPicker");
|
|
983
967
|
const hasBlockContextMenu = exts.some((e) => e.name === "blockContextMenu");
|
|
984
|
-
const
|
|
985
|
-
const
|
|
986
|
-
for (const item of editor.toolbarItems) {
|
|
987
|
-
if (item.type === "button") {
|
|
988
|
-
itemMap.set(item.name, item);
|
|
989
|
-
} else if (item.type === "dropdown") {
|
|
990
|
-
dropdownMap.set(item.name, item);
|
|
991
|
-
for (const sub of item.items) {
|
|
992
|
-
itemMap.set(sub.name, sub);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
itemMapRef.current = itemMap;
|
|
997
|
-
const bubbleDefaults = /* @__PURE__ */ new Map();
|
|
998
|
-
const byCtx = /* @__PURE__ */ new Map();
|
|
999
|
-
const addItem = (btn) => {
|
|
1000
|
-
const ctx = btn["bubbleMenu"];
|
|
1001
|
-
if (!ctx) return;
|
|
1002
|
-
let arr = byCtx.get(ctx);
|
|
1003
|
-
if (!arr) {
|
|
1004
|
-
arr = [];
|
|
1005
|
-
byCtx.set(ctx, arr);
|
|
1006
|
-
}
|
|
1007
|
-
arr.push(btn);
|
|
1008
|
-
};
|
|
1009
|
-
for (const item of editor.toolbarItems) {
|
|
1010
|
-
if (item.type === "button") addItem(item);
|
|
1011
|
-
else if (item.type === "dropdown") {
|
|
1012
|
-
for (const sub of item.items) addItem(sub);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
for (const [ctx, ctxItems] of byCtx) {
|
|
1016
|
-
ctxItems.sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
1017
|
-
const result = [];
|
|
1018
|
-
let lastGroup;
|
|
1019
|
-
let sepIdx = 0;
|
|
1020
|
-
for (const item of ctxItems) {
|
|
1021
|
-
if (lastGroup !== void 0 && item.group !== lastGroup) {
|
|
1022
|
-
result.push({ type: "separator", name: `bsep-${String(sepIdx++)}` });
|
|
1023
|
-
}
|
|
1024
|
-
result.push(item);
|
|
1025
|
-
lastGroup = item.group;
|
|
1026
|
-
}
|
|
1027
|
-
bubbleDefaults.set(ctx, result);
|
|
1028
|
-
}
|
|
1029
|
-
bubbleDefaultsRef.current = bubbleDefaults;
|
|
1030
|
-
const resolveNames = (names) => {
|
|
1031
|
-
const result = [];
|
|
1032
|
-
let sepIdx = 0;
|
|
1033
|
-
for (const name of names) {
|
|
1034
|
-
if (name === "|") {
|
|
1035
|
-
result.push({ type: "separator", name: `sep-${String(sepIdx++)}` });
|
|
1036
|
-
} else {
|
|
1037
|
-
const dropdown = dropdownMap.get(name);
|
|
1038
|
-
if (dropdown) {
|
|
1039
|
-
result.push(dropdown);
|
|
1040
|
-
continue;
|
|
1041
|
-
}
|
|
1042
|
-
const item = itemMap.get(name);
|
|
1043
|
-
if (item) result.push(item);
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
return result;
|
|
1047
|
-
};
|
|
1048
|
-
const getFormatItems = () => {
|
|
1049
|
-
return Array.from(itemMap.values()).filter((item) => item.group === "format").sort((a, b) => (b.priority ?? 100) - (a.priority ?? 100));
|
|
1050
|
-
};
|
|
1051
|
-
const detectContext = (selection, ctxs) => {
|
|
1052
|
-
if ("$anchorCell" in selection) return null;
|
|
1053
|
-
if (selection.node) return selection.node.type.name;
|
|
1054
|
-
if (selection.empty) return null;
|
|
1055
|
-
const fromCell = findCellNode(selection.$from);
|
|
1056
|
-
if (fromCell) {
|
|
1057
|
-
const toCell = findCellNode(selection.$to);
|
|
1058
|
-
if (toCell && fromCell !== toCell) return null;
|
|
1059
|
-
return "table";
|
|
1060
|
-
}
|
|
1061
|
-
const fromName = selection.$from.parent.type.name;
|
|
1062
|
-
if (fromName in ctxs) return fromName;
|
|
1063
|
-
if ("text" in ctxs && selection.$from.parent.type.spec.marks !== "") return "text";
|
|
1064
|
-
const toName = selection.$to.parent.type.name;
|
|
1065
|
-
if (toName in ctxs) return toName;
|
|
1066
|
-
if ("text" in ctxs && selection.$to.parent.type.spec.marks !== "") return "text";
|
|
1067
|
-
return null;
|
|
1068
|
-
};
|
|
1069
|
-
const filterBySchema = (contextName, schemaItems) => {
|
|
1070
|
-
if (contextName === "text" || contextName === "table") return schemaItems;
|
|
1071
|
-
const schema = editor.state.schema;
|
|
1072
|
-
if (!schema) return schemaItems;
|
|
1073
|
-
const nodeType = schema.nodes[contextName];
|
|
1074
|
-
if (!nodeType) return schemaItems;
|
|
1075
|
-
return schemaItems.filter((item) => {
|
|
1076
|
-
const markName = typeof item.isActive === "string" ? item.isActive : null;
|
|
1077
|
-
if (!markName) return true;
|
|
1078
|
-
const markType = schema.marks[markName];
|
|
1079
|
-
if (!markType) return true;
|
|
1080
|
-
return nodeType.allowsMarkType(markType);
|
|
1081
|
-
});
|
|
1082
|
-
};
|
|
1083
|
-
let shouldShowFn = shouldShow;
|
|
1084
|
-
if (!shouldShowFn) {
|
|
1085
|
-
if (contexts) {
|
|
1086
|
-
shouldShowFn = ({ state }) => {
|
|
1087
|
-
const context = detectContext(state.selection, contexts);
|
|
1088
|
-
if (!context) return false;
|
|
1089
|
-
if (context in contexts) {
|
|
1090
|
-
const val = contexts[context];
|
|
1091
|
-
if (val === null) return false;
|
|
1092
|
-
return val === true || Array.isArray(val) && val.length > 0;
|
|
1093
|
-
}
|
|
1094
|
-
return bubbleDefaults.has(context);
|
|
1095
|
-
};
|
|
1096
|
-
} else {
|
|
1097
|
-
shouldShowFn = ({ state }) => {
|
|
1098
|
-
if (state.selection.empty) return false;
|
|
1099
|
-
if (state.selection.node) return bubbleDefaults.has(state.selection.node.type.name);
|
|
1100
|
-
if (isInsideTableCell(state.selection.$from)) return false;
|
|
1101
|
-
return state.selection.$from.parent.type.spec.marks !== "" || state.selection.$to.parent.type.spec.marks !== "";
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
968
|
+
const maps = buildBubbleItemMaps(editor);
|
|
969
|
+
const shouldShowFn = shouldShow ?? createBubbleShouldShow(maps, contexts);
|
|
1105
970
|
const pluginKey = pluginKeyRef.current;
|
|
1106
971
|
const plugin = createBubbleMenuPlugin({
|
|
1107
972
|
pluginKey,
|
|
@@ -1117,13 +982,15 @@ function useBubbleMenu(options) {
|
|
|
1117
982
|
resolvedItemsRef.current = newItems;
|
|
1118
983
|
setResolvedItems(newItems);
|
|
1119
984
|
};
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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);
|
|
1127
994
|
const updateStates = (ed) => {
|
|
1128
995
|
let canProxy = null;
|
|
1129
996
|
try {
|
|
@@ -1148,7 +1015,6 @@ function useBubbleMenu(options) {
|
|
|
1148
1015
|
trackButton(item);
|
|
1149
1016
|
}
|
|
1150
1017
|
};
|
|
1151
|
-
const defaultItems = items ? resolveNames(items) : resolveNames(["bold", "italic", "underline"]);
|
|
1152
1018
|
const syncTrailingState = (ed) => {
|
|
1153
1019
|
const sel = ed.state.selection;
|
|
1154
1020
|
const isNode = !!sel.node;
|
|
@@ -1185,16 +1051,7 @@ function useBubbleMenu(options) {
|
|
|
1185
1051
|
});
|
|
1186
1052
|
};
|
|
1187
1053
|
const transactionHandler = () => {
|
|
1188
|
-
|
|
1189
|
-
updateContextItems(editor, contexts, detectContext, resolveNames, getFormatItems, filterBySchema, bubbleDefaults, setItems);
|
|
1190
|
-
} else {
|
|
1191
|
-
const sel = editor.state.selection;
|
|
1192
|
-
if (sel.node && bubbleDefaults.has(sel.node.type.name)) {
|
|
1193
|
-
setItems(bubbleDefaults.get(sel.node.type.name) ?? []);
|
|
1194
|
-
} else {
|
|
1195
|
-
setItems(defaultItems);
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1054
|
+
refreshItems(editor);
|
|
1198
1055
|
updateStates(editor);
|
|
1199
1056
|
syncTrailingState(editor);
|
|
1200
1057
|
setActiveVersion((v) => v + 1);
|
|
@@ -1209,30 +1066,6 @@ function useBubbleMenu(options) {
|
|
|
1209
1066
|
}
|
|
1210
1067
|
};
|
|
1211
1068
|
}, [editor]);
|
|
1212
|
-
function updateContextItems(ed, ctxs, detectContext, resolveNames, getFormatItems, filterBySchema, defaults, setItems) {
|
|
1213
|
-
const ctx = detectContext(ed.state.selection, ctxs);
|
|
1214
|
-
if (!ctx) {
|
|
1215
|
-
setItems([]);
|
|
1216
|
-
return;
|
|
1217
|
-
}
|
|
1218
|
-
if (ctx in ctxs) {
|
|
1219
|
-
const val = ctxs[ctx];
|
|
1220
|
-
if (val === null || Array.isArray(val) && val.length === 0) {
|
|
1221
|
-
setItems([]);
|
|
1222
|
-
return;
|
|
1223
|
-
}
|
|
1224
|
-
if (val === true) {
|
|
1225
|
-
setItems(filterBySchema(ctx, getFormatItems()));
|
|
1226
|
-
} else if (Array.isArray(val)) {
|
|
1227
|
-
const resolved = resolveNames(val);
|
|
1228
|
-
const buttons = resolved.filter((i) => i.type !== "separator");
|
|
1229
|
-
const filtered = new Set(filterBySchema(ctx, buttons).map((b) => b.name));
|
|
1230
|
-
setItems(resolved.filter((i) => i.type === "separator" || filtered.has(i.name)));
|
|
1231
|
-
}
|
|
1232
|
-
} else {
|
|
1233
|
-
setItems(defaults.get(ctx) ?? []);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
1069
|
const isItemActive = (item) => {
|
|
1237
1070
|
return activeMapRef.current.get(item.name) ?? false;
|
|
1238
1071
|
};
|
|
@@ -1334,6 +1167,8 @@ function DomternalBubbleMenu({
|
|
|
1334
1167
|
const colorBtnRef = useRef(null);
|
|
1335
1168
|
const blockMenuBtnRef = useRef(null);
|
|
1336
1169
|
const innerHtml = useInnerHtml();
|
|
1170
|
+
const showColor = trailing.showColorPickerButton && !trailing.isNodeSelection;
|
|
1171
|
+
const showBlock = trailing.showBlockMenuButton && !trailing.isNodeSelection;
|
|
1337
1172
|
return /* @__PURE__ */ jsxs("div", { ref: menuRef, className: "dm-bubble-menu", role: "toolbar", "aria-label": "Text formatting", children: [
|
|
1338
1173
|
resolvedItems.map((item) => {
|
|
1339
1174
|
if (item.type === "separator") {
|
|
@@ -1383,8 +1218,8 @@ function DomternalBubbleMenu({
|
|
|
1383
1218
|
btn.name
|
|
1384
1219
|
);
|
|
1385
1220
|
}),
|
|
1386
|
-
|
|
1387
|
-
/* @__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" }),
|
|
1388
1223
|
/* @__PURE__ */ jsxs(
|
|
1389
1224
|
"button",
|
|
1390
1225
|
{
|
|
@@ -1420,8 +1255,8 @@ function DomternalBubbleMenu({
|
|
|
1420
1255
|
}
|
|
1421
1256
|
)
|
|
1422
1257
|
] }),
|
|
1423
|
-
|
|
1424
|
-
/* @__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" }),
|
|
1425
1260
|
/* @__PURE__ */ jsx(
|
|
1426
1261
|
"button",
|
|
1427
1262
|
{
|
|
@@ -1957,10 +1792,10 @@ var CATEGORY_ICONS = {
|
|
|
1957
1792
|
"Animals & Nature": "\u{1F431}",
|
|
1958
1793
|
"Food & Drink": "\u{1F355}",
|
|
1959
1794
|
"Travel & Places": "\u{1F697}",
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1795
|
+
Activities: "\u26BD",
|
|
1796
|
+
Objects: "\u{1F4A1}",
|
|
1797
|
+
Symbols: "\u{1F523}",
|
|
1798
|
+
Flags: "\u{1F3C1}"
|
|
1964
1799
|
};
|
|
1965
1800
|
function categoryIcon(cat) {
|
|
1966
1801
|
return CATEGORY_ICONS[cat] ?? cat.charAt(0);
|
|
@@ -1968,7 +1803,10 @@ function categoryIcon(cat) {
|
|
|
1968
1803
|
function formatName(name) {
|
|
1969
1804
|
return name.replace(/_/g, " ");
|
|
1970
1805
|
}
|
|
1971
|
-
function DomternalEmojiPicker({
|
|
1806
|
+
function DomternalEmojiPicker({
|
|
1807
|
+
editor: editorProp,
|
|
1808
|
+
emojis
|
|
1809
|
+
}) {
|
|
1972
1810
|
const { editor: contextEditor } = useCurrentEditor();
|
|
1973
1811
|
const editor = editorProp ?? contextEditor;
|
|
1974
1812
|
const {
|
|
@@ -2000,7 +1838,7 @@ function DomternalEmojiPicker({ editor: editorProp, emojis }) {
|
|
|
2000
1838
|
return;
|
|
2001
1839
|
}
|
|
2002
1840
|
const cols = 8;
|
|
2003
|
-
let next
|
|
1841
|
+
let next;
|
|
2004
1842
|
switch (event.key) {
|
|
2005
1843
|
case "ArrowRight":
|
|
2006
1844
|
event.preventDefault();
|
|
@@ -2261,6 +2099,14 @@ function EditorContent({ editor, innerRef, ...htmlProps }) {
|
|
|
2261
2099
|
}
|
|
2262
2100
|
);
|
|
2263
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
|
+
}
|
|
2264
2110
|
var TOKEN_LABELS = {
|
|
2265
2111
|
gray: "Gray",
|
|
2266
2112
|
brown: "Brown",
|
|
@@ -2318,22 +2164,24 @@ function useNotionColorPicker(options) {
|
|
|
2318
2164
|
setCurrentTextToken(attrs.colorToken ?? null);
|
|
2319
2165
|
setCurrentBgToken(attrs.backgroundColorToken ?? null);
|
|
2320
2166
|
}, []);
|
|
2321
|
-
const close = useCallback(
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
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
|
+
);
|
|
2330
2179
|
useEffect(() => {
|
|
2331
2180
|
if (!editor || editor.isDestroyed) return;
|
|
2332
2181
|
const host = editor.view.dom.closest(".dm-editor") ?? null;
|
|
2333
2182
|
setHostEl(host);
|
|
2334
2183
|
const ext = editor.extensionManager.extensions.find((e) => e.name === "notionColorPicker");
|
|
2335
|
-
|
|
2336
|
-
setPalette(extOptions?.palette ? [...extOptions.palette] : []);
|
|
2184
|
+
setPalette(paletteFromExtensionOptions(ext?.options));
|
|
2337
2185
|
const onOpen = (...args) => {
|
|
2338
2186
|
const detail = args[0];
|
|
2339
2187
|
const incomingAnchor = detail?.anchorElement;
|
|
@@ -2371,35 +2219,49 @@ function useNotionColorPicker(options) {
|
|
|
2371
2219
|
if (!isOpen) return;
|
|
2372
2220
|
const controller = new AbortController();
|
|
2373
2221
|
const { signal } = controller;
|
|
2374
|
-
document.addEventListener(
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
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
|
+
);
|
|
2387
2243
|
return () => {
|
|
2388
2244
|
controller.abort();
|
|
2389
2245
|
};
|
|
2390
2246
|
}, [isOpen, close]);
|
|
2391
|
-
const applyText = useCallback(
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
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
|
+
);
|
|
2403
2265
|
const tokenLabel = useCallback((token) => {
|
|
2404
2266
|
return TOKEN_LABELS[token] ?? token.charAt(0).toUpperCase() + token.slice(1);
|
|
2405
2267
|
}, []);
|
|
@@ -2407,14 +2269,12 @@ function useNotionColorPicker(options) {
|
|
|
2407
2269
|
const cols = 5;
|
|
2408
2270
|
const root = panelRef.current;
|
|
2409
2271
|
if (!root) return;
|
|
2410
|
-
const swatches = Array.from(
|
|
2411
|
-
root.querySelectorAll(".dm-ncp-swatch")
|
|
2412
|
-
);
|
|
2272
|
+
const swatches = Array.from(root.querySelectorAll(".dm-ncp-swatch"));
|
|
2413
2273
|
if (!swatches.length) return;
|
|
2414
2274
|
const active = document.activeElement;
|
|
2415
2275
|
const idx = active ? swatches.indexOf(active) : -1;
|
|
2416
2276
|
if (idx === -1) return;
|
|
2417
|
-
let next
|
|
2277
|
+
let next;
|
|
2418
2278
|
switch (event.key) {
|
|
2419
2279
|
case "ArrowRight":
|
|
2420
2280
|
event.preventDefault();
|