@domternal/react 0.7.5 → 0.9.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.d.ts +30 -18
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
- package/dist/Domternal.d.ts.map +0 -1
- package/dist/DomternalEditor.d.ts.map +0 -1
- package/dist/DomternalFloatingMenu.d.ts.map +0 -1
- package/dist/EditorContent.d.ts.map +0 -1
- package/dist/EditorContext.d.ts.map +0 -1
- package/dist/bubble-menu/DomternalBubbleMenu.d.ts.map +0 -1
- package/dist/bubble-menu/useBubbleMenu.d.ts.map +0 -1
- package/dist/emoji-picker/DomternalEmojiPicker.d.ts.map +0 -1
- package/dist/emoji-picker/useEmojiPicker.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/node-views/ReactNodeViewRenderer.d.ts.map +0 -1
- package/dist/notion-color-picker/DomternalNotionColorPicker.d.ts.map +0 -1
- package/dist/notion-color-picker/index.d.ts.map +0 -1
- package/dist/notion-color-picker/useNotionColorPicker.d.ts.map +0 -1
- package/dist/toolbar/DomternalToolbar.d.ts.map +0 -1
- package/dist/toolbar/ToolbarButton.d.ts.map +0 -1
- package/dist/toolbar/ToolbarDropdown.d.ts.map +0 -1
- package/dist/toolbar/ToolbarDropdownPanel.d.ts.map +0 -1
- package/dist/toolbar/useComputedStyle.d.ts.map +0 -1
- package/dist/toolbar/useKeyboardNav.d.ts.map +0 -1
- package/dist/toolbar/useToolbarController.d.ts.map +0 -1
- package/dist/toolbar/useToolbarIcons.d.ts.map +0 -1
- package/dist/toolbar/useTooltip.d.ts.map +0 -1
- package/dist/useEditor.d.ts.map +0 -1
- package/dist/useEditorState.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { DependencyList, ReactNode, HTMLAttributes, Ref, RefObject, KeyboardEvent, ElementType, RefCallback } from 'react';
|
|
3
|
-
import { AnyExtension, Content, FocusPosition, Editor, JSONContent, IconSet, ToolbarLayoutEntry, BubbleMenuOptions, FloatingMenuItemsOverride } from '@domternal/core';
|
|
3
|
+
import { AnyExtension, Content, FocusPosition, Editor, JSONContent, IconSet, ToolbarLayoutEntry, BubbleMenuOptions, FloatingMenuOptions, FloatingMenuItemsOverride, FloatingMenuKeymap } from '@domternal/core';
|
|
4
4
|
export { AnyExtension, Content, Editor, FocusPosition, GenerateHTMLOptions, GenerateJSONOptions, GenerateTextOptions, JSONContent, generateHTML, generateJSON, generateText } from '@domternal/core';
|
|
5
|
-
import { FloatingMenuOptions, FloatingMenuKeymap } from '@domternal/extension-block-menu';
|
|
6
5
|
|
|
7
6
|
declare const DEFAULT_EXTENSIONS: AnyExtension[];
|
|
8
7
|
interface UseEditorOptions {
|
|
@@ -17,10 +16,12 @@ interface UseEditorOptions {
|
|
|
17
16
|
/** Output format for content comparison. @default 'html' */
|
|
18
17
|
outputFormat?: 'html' | 'json';
|
|
19
18
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* Create the editor synchronously during the first render so `editor`
|
|
20
|
+
* is available immediately, with no null flash on first paint. The view
|
|
21
|
+
* starts in a detached element and is adopted into the mount point by
|
|
22
|
+
* the mount effect. Client-only: the default defers creation to a mount
|
|
23
|
+
* effect, which never runs during server-side rendering.
|
|
24
|
+
* @default false
|
|
24
25
|
*/
|
|
25
26
|
immediatelyRender?: boolean;
|
|
26
27
|
/** Called when the editor instance is created. */
|
|
@@ -60,12 +61,12 @@ interface UseEditorOptions {
|
|
|
60
61
|
* return <div className="dm-editor"><div ref={editorRef} /></div>;
|
|
61
62
|
* ```
|
|
62
63
|
*
|
|
63
|
-
* @example
|
|
64
|
+
* @example Editor available on the very first render (client-only apps)
|
|
64
65
|
* ```tsx
|
|
65
66
|
* const { editor, editorRef } = useEditor({
|
|
66
67
|
* extensions,
|
|
67
68
|
* content,
|
|
68
|
-
* immediatelyRender:
|
|
69
|
+
* immediatelyRender: true,
|
|
69
70
|
* });
|
|
70
71
|
* ```
|
|
71
72
|
*
|
|
@@ -238,14 +239,22 @@ interface DomternalProps extends UseEditorOptions {
|
|
|
238
239
|
* </Domternal>
|
|
239
240
|
* ```
|
|
240
241
|
*
|
|
241
|
-
* @example SSR
|
|
242
|
+
* @example Loading state (shown during SSR and until the editor exists)
|
|
242
243
|
* ```tsx
|
|
243
|
-
* <Domternal extensions={extensions}
|
|
244
|
+
* <Domternal extensions={extensions}>
|
|
244
245
|
* <Domternal.Loading>Loading editor...</Domternal.Loading>
|
|
245
246
|
* <Domternal.Toolbar />
|
|
246
247
|
* <Domternal.Content />
|
|
247
248
|
* </Domternal>
|
|
248
249
|
* ```
|
|
250
|
+
*
|
|
251
|
+
* @example Editor on the very first render (client-only apps)
|
|
252
|
+
* ```tsx
|
|
253
|
+
* <Domternal extensions={extensions} immediatelyRender>
|
|
254
|
+
* <Domternal.Toolbar />
|
|
255
|
+
* <Domternal.Content />
|
|
256
|
+
* </Domternal>
|
|
257
|
+
* ```
|
|
249
258
|
*/
|
|
250
259
|
declare function Domternal({ children, deps, ...options }: DomternalProps): ReactNode;
|
|
251
260
|
declare namespace Domternal {
|
|
@@ -448,8 +457,8 @@ interface ReactNodeViewProps {
|
|
|
448
457
|
node: PMNode;
|
|
449
458
|
/** Whether this node is selected via NodeSelection. */
|
|
450
459
|
selected: boolean;
|
|
451
|
-
/** Get the document position of this node. */
|
|
452
|
-
getPos: () => number;
|
|
460
|
+
/** Get the document position of this node. Returns `undefined` once the node is no longer in the document. */
|
|
461
|
+
getPos: () => number | undefined;
|
|
453
462
|
/** Update the node's attributes. */
|
|
454
463
|
updateAttributes: (attrs: Record<string, unknown>) => void;
|
|
455
464
|
/** Delete this node from the document. */
|
|
@@ -460,7 +469,7 @@ interface ReactNodeViewProps {
|
|
|
460
469
|
options: Record<string, unknown>;
|
|
461
470
|
};
|
|
462
471
|
/** ProseMirror decorations applied to this node. */
|
|
463
|
-
decorations: unknown[];
|
|
472
|
+
decorations: readonly unknown[];
|
|
464
473
|
}
|
|
465
474
|
interface ReactNodeViewRendererOptions {
|
|
466
475
|
/** Wrapper element tag. @default 'div' for block, 'span' for inline */
|
|
@@ -493,12 +502,12 @@ interface ReactNodeViewRendererOptions {
|
|
|
493
502
|
* }
|
|
494
503
|
* ```
|
|
495
504
|
*/
|
|
496
|
-
declare function ReactNodeViewRenderer(component: React.ComponentType<ReactNodeViewProps>, options?: ReactNodeViewRendererOptions): (node: PMNode, view: unknown, getPos: () => number, decorations: unknown[]) => ReactNodeView;
|
|
505
|
+
declare function ReactNodeViewRenderer(component: React.ComponentType<ReactNodeViewProps>, options?: ReactNodeViewRendererOptions): (node: PMNode, view: unknown, getPos: () => number | undefined, decorations: readonly unknown[]) => ReactNodeView;
|
|
497
506
|
interface ReactNodeViewInit {
|
|
498
507
|
editor: Editor;
|
|
499
508
|
node: PMNode;
|
|
500
|
-
getPos: () => number;
|
|
501
|
-
decorations: unknown[];
|
|
509
|
+
getPos: () => number | undefined;
|
|
510
|
+
decorations: readonly unknown[];
|
|
502
511
|
extension: {
|
|
503
512
|
name: string;
|
|
504
513
|
options: Record<string, unknown>;
|
|
@@ -517,11 +526,14 @@ declare class ReactNodeView {
|
|
|
517
526
|
private selected;
|
|
518
527
|
constructor(component: React.ComponentType<ReactNodeViewProps>, init: ReactNodeViewInit, options: ReactNodeViewRendererOptions);
|
|
519
528
|
private render;
|
|
520
|
-
update(node: PMNode, decorations: unknown[]): boolean;
|
|
529
|
+
update(node: PMNode, decorations: readonly unknown[]): boolean;
|
|
521
530
|
selectNode(): void;
|
|
522
531
|
deselectNode(): void;
|
|
523
532
|
destroy(): void;
|
|
524
|
-
ignoreMutation(mutation: MutationRecord
|
|
533
|
+
ignoreMutation(mutation: MutationRecord | {
|
|
534
|
+
type: 'selection';
|
|
535
|
+
target: Node;
|
|
536
|
+
}): boolean;
|
|
525
537
|
stopEvent(): boolean;
|
|
526
538
|
}
|
|
527
539
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { createContext, forwardRef, useImperativeHandle, useRef, useEffect, useState, useMemo, useCallback, useSyncExternalStore, useContext, Fragment, useLayoutEffect, createElement } from 'react';
|
|
2
|
-
import { Editor, Document, Paragraph, Text, BaseKeymap, History, positionFloatingOnce, PluginKey, FloatingMenuController, defaultIcons, positionFloating, ToolbarController, defaultBubbleContexts, createBubbleMenuPlugin } from '@domternal/core';
|
|
2
|
+
import { Editor, Document, Paragraph, Text, BaseKeymap, History, positionFloatingOnce, PluginKey, createFloatingMenuPlugin, FloatingMenuController, defaultIcons, positionFloating, ToolbarController, defaultBubbleContexts, createBubbleMenuPlugin } 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
|
-
import { createFloatingMenuPlugin } from '@domternal/extension-block-menu';
|
|
6
5
|
import { createPortal } from 'react-dom';
|
|
7
6
|
import { createRoot } from 'react-dom/client';
|
|
8
7
|
|
|
@@ -14,9 +13,9 @@ function useEditor(options = {}, deps) {
|
|
|
14
13
|
content = "",
|
|
15
14
|
editable = true,
|
|
16
15
|
autofocus = false,
|
|
17
|
-
outputFormat = "html"
|
|
16
|
+
outputFormat = "html",
|
|
17
|
+
immediatelyRender = false
|
|
18
18
|
} = options;
|
|
19
|
-
const [editor, setEditor] = useState(null);
|
|
20
19
|
const editorRef = useRef(null);
|
|
21
20
|
const instanceRef = useRef(null);
|
|
22
21
|
const pendingContentRef = useRef(null);
|
|
@@ -45,7 +44,7 @@ function useEditor(options = {}, deps) {
|
|
|
45
44
|
callbacksRef.current.onBlur?.({ editor: ed, event });
|
|
46
45
|
});
|
|
47
46
|
}
|
|
48
|
-
function
|
|
47
|
+
function buildEditorInstance(element, initialContent, focus) {
|
|
49
48
|
const ed = new Editor({
|
|
50
49
|
element,
|
|
51
50
|
extensions: [...DEFAULT_EXTENSIONS, ...extensions],
|
|
@@ -57,6 +56,10 @@ function useEditor(options = {}, deps) {
|
|
|
57
56
|
instanceRef.current = ed;
|
|
58
57
|
extensionsRef.current = extensions;
|
|
59
58
|
depsRef.current = deps;
|
|
59
|
+
return ed;
|
|
60
|
+
}
|
|
61
|
+
function createEditorInstance(element, initialContent, focus) {
|
|
62
|
+
const ed = buildEditorInstance(element, initialContent, focus);
|
|
60
63
|
setEditor(ed);
|
|
61
64
|
callbacksRef.current.onCreate?.(ed);
|
|
62
65
|
return ed;
|
|
@@ -71,7 +74,30 @@ function useEditor(options = {}, deps) {
|
|
|
71
74
|
instanceRef.current = null;
|
|
72
75
|
setEditor(null);
|
|
73
76
|
}
|
|
77
|
+
const [editor, setEditor] = useState(() => {
|
|
78
|
+
if (!immediatelyRender) return null;
|
|
79
|
+
if (typeof window === "undefined") {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"[@domternal/react] immediatelyRender: true creates the editor during render, which cannot work during server-side rendering. Remove the option for SSR; the editor is then created after mount."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (instanceRef.current && !instanceRef.current.isDestroyed) {
|
|
85
|
+
return instanceRef.current;
|
|
86
|
+
}
|
|
87
|
+
return buildEditorInstance(document.createElement("div"), content, autofocus);
|
|
88
|
+
});
|
|
74
89
|
useEffect(() => {
|
|
90
|
+
const existing = instanceRef.current;
|
|
91
|
+
if (existing && !existing.isDestroyed) {
|
|
92
|
+
const mount = editorRef.current;
|
|
93
|
+
if (mount && existing.view.dom.parentElement !== mount) {
|
|
94
|
+
mount.appendChild(existing.view.dom);
|
|
95
|
+
}
|
|
96
|
+
callbacksRef.current.onCreate?.(existing);
|
|
97
|
+
return () => {
|
|
98
|
+
destroyCurrentEditor();
|
|
99
|
+
};
|
|
100
|
+
}
|
|
75
101
|
const element = editorRef.current ?? document.createElement("div");
|
|
76
102
|
const initialContent = pendingContentRef.current ?? content;
|
|
77
103
|
pendingContentRef.current = null;
|
|
@@ -2621,12 +2647,14 @@ var ReactNodeView = class {
|
|
|
2621
2647
|
decorations: this.decorations,
|
|
2622
2648
|
updateAttributes: (attrs) => {
|
|
2623
2649
|
const pos = this.getPos();
|
|
2650
|
+
if (pos === void 0) return;
|
|
2624
2651
|
const { tr } = this.editor.view.state;
|
|
2625
2652
|
tr.setNodeMarkup(pos, void 0, { ...this.node.attrs, ...attrs });
|
|
2626
2653
|
this.editor.view.dispatch(tr);
|
|
2627
2654
|
},
|
|
2628
2655
|
deleteNode: () => {
|
|
2629
2656
|
const pos = this.getPos();
|
|
2657
|
+
if (pos === void 0) return;
|
|
2630
2658
|
const { tr } = this.editor.view.state;
|
|
2631
2659
|
tr.delete(pos, pos + this.node.nodeSize);
|
|
2632
2660
|
this.editor.view.dispatch(tr);
|