@domternal/react 1.0.3 → 1.1.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 +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +79 -36
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -19,13 +19,16 @@ and React 19.
|
|
|
19
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` (>=1.
|
|
22
|
+
`react` (>=18), `react-dom` (>=18), and `@domternal/core` (>=1.1.0 <2.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
|
|
26
26
|
@import '@domternal/theme';
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
This package is part of the coordinated Domternal 1.1.1 release.
|
|
30
|
+
Upgrade installed `@domternal/*` packages together.
|
|
31
|
+
|
|
29
32
|
## Usage
|
|
30
33
|
|
|
31
34
|
The recommended pattern uses the composable `Domternal` component. It creates the editor and provides it
|
package/dist/index.d.ts
CHANGED
|
@@ -321,7 +321,7 @@ interface DomternalEditorProps extends Omit<UseEditorOptions, 'outputFormat'> {
|
|
|
321
321
|
value?: Content;
|
|
322
322
|
/** Called when content changes (controlled mode). */
|
|
323
323
|
onChange?: (value: string | JSONContent) => void;
|
|
324
|
-
/** Additional content
|
|
324
|
+
/** Additional content inside the provider, before the dm-editor wrapper as siblings. */
|
|
325
325
|
children?: React.ReactNode;
|
|
326
326
|
}
|
|
327
327
|
interface DomternalEditorRef {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, forwardRef, useImperativeHandle, useRef, useEffect, useState, useMemo,
|
|
1
|
+
import { createContext, forwardRef, useImperativeHandle, useRef, useEffect, useState, useMemo, useSyncExternalStore, useContext, useCallback, Fragment, useLayoutEffect, createElement } from 'react';
|
|
2
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';
|
|
@@ -95,10 +95,7 @@ function useEditor(options = {}, deps) {
|
|
|
95
95
|
const existing = instanceRef.current;
|
|
96
96
|
if (existing && !existing.isDestroyed) {
|
|
97
97
|
const mount = editorRef.current;
|
|
98
|
-
if (mount
|
|
99
|
-
mount.appendChild(existing.view.dom);
|
|
100
|
-
existing.adoptPresetClass();
|
|
101
|
-
}
|
|
98
|
+
if (mount) existing.adoptDom(mount);
|
|
102
99
|
callbacksRef.current.onCreate?.(existing);
|
|
103
100
|
return () => {
|
|
104
101
|
destroyCurrentEditor();
|
|
@@ -222,28 +219,68 @@ function getFullState(editor) {
|
|
|
222
219
|
};
|
|
223
220
|
}
|
|
224
221
|
function useEditorStateSelector(editor, selector) {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
222
|
+
const store = useMemo(() => createEditorStore(editor), [editor]);
|
|
223
|
+
const getSnapshot = useMemo(() => {
|
|
224
|
+
let previous;
|
|
225
|
+
let selected;
|
|
226
|
+
return () => {
|
|
227
|
+
const snapshot = store.getSnapshot();
|
|
228
|
+
if (snapshot !== previous) {
|
|
229
|
+
previous = snapshot;
|
|
230
|
+
selected = snapshot ? selector(snapshot.editor) : void 0;
|
|
231
|
+
}
|
|
232
|
+
return selected;
|
|
233
|
+
};
|
|
234
|
+
}, [store, selector]);
|
|
235
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
236
|
+
}
|
|
237
|
+
function createEditorStore(editor) {
|
|
238
|
+
let snapshot;
|
|
239
|
+
let destroyed = false;
|
|
240
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
241
|
+
const getSnapshot = () => {
|
|
242
|
+
if (!editor || destroyed || editor.isDestroyed) return null;
|
|
243
|
+
const state = editor.state;
|
|
244
|
+
const focused = editor.isFocused;
|
|
245
|
+
const editable = editor.isEditable;
|
|
246
|
+
if (snapshot?.state !== state || snapshot.focused !== focused || snapshot.editable !== editable) {
|
|
247
|
+
snapshot = { editor, state, focused, editable };
|
|
248
|
+
}
|
|
249
|
+
return snapshot;
|
|
250
|
+
};
|
|
251
|
+
const notify = () => {
|
|
252
|
+
snapshot = void 0;
|
|
253
|
+
listeners.forEach((listener) => {
|
|
254
|
+
listener();
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
const onDestroy = () => {
|
|
258
|
+
destroyed = true;
|
|
259
|
+
notify();
|
|
260
|
+
};
|
|
261
|
+
return {
|
|
262
|
+
getSnapshot,
|
|
263
|
+
subscribe(callback) {
|
|
264
|
+
if (!editor || destroyed || editor.isDestroyed) return () => {
|
|
230
265
|
};
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
266
|
+
listeners.add(callback);
|
|
267
|
+
if (listeners.size === 1) {
|
|
268
|
+
editor.on("transaction", notify);
|
|
269
|
+
editor.on("focus", notify);
|
|
270
|
+
editor.on("blur", notify);
|
|
271
|
+
editor.on("destroy", onDestroy);
|
|
272
|
+
}
|
|
234
273
|
return () => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
274
|
+
listeners.delete(callback);
|
|
275
|
+
if (listeners.size === 0) {
|
|
276
|
+
editor.off("transaction", notify);
|
|
277
|
+
editor.off("focus", notify);
|
|
278
|
+
editor.off("blur", notify);
|
|
279
|
+
editor.off("destroy", onDestroy);
|
|
280
|
+
}
|
|
238
281
|
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
);
|
|
242
|
-
const getSnapshot = useCallback(() => {
|
|
243
|
-
if (!editor || editor.isDestroyed) return void 0;
|
|
244
|
-
return selectorRef.current(editor);
|
|
245
|
-
}, [editor]);
|
|
246
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
247
284
|
}
|
|
248
285
|
var EditorContext = createContext({ editor: null });
|
|
249
286
|
function EditorProvider({ editor, children }) {
|
|
@@ -678,6 +715,7 @@ function ToolbarDropdownPanel({
|
|
|
678
715
|
onItemClick
|
|
679
716
|
}) {
|
|
680
717
|
const innerHtml = useInnerHtml();
|
|
718
|
+
const { getTooltip } = useTooltip();
|
|
681
719
|
if (dropdown.layout === "grid") {
|
|
682
720
|
return /* @__PURE__ */ jsx(
|
|
683
721
|
"div",
|
|
@@ -694,7 +732,7 @@ function ToolbarDropdownPanel({
|
|
|
694
732
|
role: "menuitem",
|
|
695
733
|
tabIndex: -1,
|
|
696
734
|
"aria-label": sub.label,
|
|
697
|
-
title: sub
|
|
735
|
+
title: getTooltip(sub),
|
|
698
736
|
style: { backgroundColor: sub.color },
|
|
699
737
|
onMouseDown: (e) => {
|
|
700
738
|
e.preventDefault();
|
|
@@ -712,6 +750,7 @@ function ToolbarDropdownPanel({
|
|
|
712
750
|
role: "menuitem",
|
|
713
751
|
tabIndex: -1,
|
|
714
752
|
"aria-label": sub.label,
|
|
753
|
+
title: getTooltip(sub),
|
|
715
754
|
dangerouslySetInnerHTML: innerHtml(getCachedItemContent(sub.icon, sub.label)),
|
|
716
755
|
onMouseDown: (e) => {
|
|
717
756
|
e.preventDefault();
|
|
@@ -740,6 +779,7 @@ function ToolbarDropdownPanel({
|
|
|
740
779
|
role: "menuitem",
|
|
741
780
|
tabIndex: -1,
|
|
742
781
|
"aria-label": sub.label,
|
|
782
|
+
title: getTooltip(sub),
|
|
743
783
|
ref: (el) => {
|
|
744
784
|
if (el && sub.style) el.setAttribute("style", sub.style);
|
|
745
785
|
},
|
|
@@ -1585,7 +1625,10 @@ function FloatingMenuItemButton({
|
|
|
1585
1625
|
dangerouslySetInnerHTML: innerHtml(iconHtml)
|
|
1586
1626
|
}
|
|
1587
1627
|
),
|
|
1588
|
-
/* @__PURE__ */
|
|
1628
|
+
item.description ? /* @__PURE__ */ jsxs("span", { className: "dm-floating-menu-item-text", children: [
|
|
1629
|
+
/* @__PURE__ */ jsx("span", { className: "dm-floating-menu-item-label", children: item.label }),
|
|
1630
|
+
/* @__PURE__ */ jsx("span", { className: "dm-floating-menu-item-description", children: item.description })
|
|
1631
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "dm-floating-menu-item-label", children: item.label }),
|
|
1589
1632
|
item.shortcut && /* @__PURE__ */ jsx("span", { className: "dm-floating-menu-item-shortcut", "aria-hidden": "true", children: item.shortcut })
|
|
1590
1633
|
]
|
|
1591
1634
|
}
|
|
@@ -1973,11 +2016,12 @@ function DomternalContent({ className }) {
|
|
|
1973
2016
|
useEffect(() => {
|
|
1974
2017
|
const container = containerRef.current;
|
|
1975
2018
|
if (!container || !editor || editor.isDestroyed) return;
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
container
|
|
1979
|
-
|
|
1980
|
-
|
|
2019
|
+
editor.adoptDom(container);
|
|
2020
|
+
return () => {
|
|
2021
|
+
if (!editor.isDestroyed && editor.view.dom.parentElement === container) {
|
|
2022
|
+
editor.adoptDom(document.createElement("div"));
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
1981
2025
|
}, [editor]);
|
|
1982
2026
|
const classes = className ? `dm-editor ${className}` : "dm-editor";
|
|
1983
2027
|
return /* @__PURE__ */ jsx("div", { className: classes, "data-dm-editor-ui": "", children: /* @__PURE__ */ jsx("div", { ref: containerRef }) });
|
|
@@ -2078,12 +2122,11 @@ function EditorContent({ editor, innerRef, ...htmlProps }) {
|
|
|
2078
2122
|
useEffect(() => {
|
|
2079
2123
|
const container = containerRef.current;
|
|
2080
2124
|
if (!container || !editor || editor.isDestroyed) return;
|
|
2081
|
-
|
|
2082
|
-
if (editorDom.parentElement !== container) {
|
|
2083
|
-
container.appendChild(editorDom);
|
|
2084
|
-
editor.adoptPresetClass();
|
|
2085
|
-
}
|
|
2125
|
+
editor.adoptDom(container);
|
|
2086
2126
|
return () => {
|
|
2127
|
+
if (!editor.isDestroyed && editor.view.dom.parentElement === container) {
|
|
2128
|
+
editor.adoptDom(document.createElement("div"));
|
|
2129
|
+
}
|
|
2087
2130
|
};
|
|
2088
2131
|
}, [editor]);
|
|
2089
2132
|
return /* @__PURE__ */ jsx(
|