@openeditor/react 0.0.27 → 0.0.28
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 +24 -7
- package/dist/index.d.ts +19 -26
- package/dist/index.js +124 -40
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Pass `attachmentRuntime` to `useOpenEditorController` for editable attachments a
|
|
|
9
9
|
- `useOpenEditorController` for editor state, commands, document exports, and selection state
|
|
10
10
|
- `OpenEditorContent` for the editable Tiptap-backed content surface
|
|
11
11
|
- `OpenEditorViewer` for read-only React rendering
|
|
12
|
+
- `OpenEditorPageHeader` for host-backed page title and icon editing
|
|
12
13
|
- `getDefaultBlockPickerItems` for host-owned block rails, pickers, and command palettes
|
|
13
14
|
- `getDefaultSlashMenuItems` for UI packages that want the built-in insert actions
|
|
14
15
|
- stable-identity block targeting and transaction-backed copy, duplicate, move, and delete commands
|
|
@@ -120,12 +121,24 @@ themeable SVG rendering and retain their canonical Mermaid source in the documen
|
|
|
120
121
|
import { OpenEditorContent, useOpenEditorController } from "@openeditor/react";
|
|
121
122
|
|
|
122
123
|
export function Example() {
|
|
123
|
-
const controller = useOpenEditorController();
|
|
124
|
+
const controller = useOpenEditorController({ initialDocument });
|
|
124
125
|
|
|
125
126
|
return <OpenEditorContent controller={controller} />;
|
|
126
127
|
}
|
|
127
128
|
```
|
|
128
129
|
|
|
130
|
+
Editors are uncontrolled on both web and native. `initialDocument` is read once;
|
|
131
|
+
use `controller.setContent(document)` for an undoable programmatic replacement.
|
|
132
|
+
It emits `onChange` and preserves the current selection where possible. Runtime
|
|
133
|
+
changes to callbacks, `editable`, and `placeholder` are applied without remounting.
|
|
134
|
+
|
|
135
|
+
Exports are lazy. `controller.export("html")` returns publishing-safe built-in
|
|
136
|
+
HTML. `controller.exportUnsafe("html")` opts into trusted custom extension HTML.
|
|
137
|
+
|
|
138
|
+
Pass `enabledBlocks` to restrict authoring without removing nodes from the
|
|
139
|
+
schema. Existing disabled blocks remain readable. Runtime-dependent insertion
|
|
140
|
+
is automatically disabled unless page creation or attachment upload is wired.
|
|
141
|
+
|
|
129
142
|
## Custom blocks
|
|
130
143
|
|
|
131
144
|
Custom blocks belong to the consuming product. OpenEditor hosts them without
|
|
@@ -215,8 +228,9 @@ export function ProductEditor() {
|
|
|
215
228
|
}
|
|
216
229
|
```
|
|
217
230
|
|
|
218
|
-
Pass the same extension array to `OpenEditorViewer`. The controller
|
|
219
|
-
|
|
231
|
+
Pass the same extension array to `OpenEditorViewer`. The controller uses
|
|
232
|
+
registered exporters only through the explicit `controller.exportUnsafe(...)`
|
|
233
|
+
path, and registered blocks appear
|
|
220
234
|
in `getDefaultSlashMenuItems`, which powers `@openeditor/ui`.
|
|
221
235
|
|
|
222
236
|
Insert-menu entries accept an optional React `icon` component and numeric `order`.
|
|
@@ -225,8 +239,7 @@ blocks can be placed between them (for example, `order: 250` appears between
|
|
|
225
239
|
Heading 1 and Heading 2). Entries with the same order retain their declaration
|
|
226
240
|
order, and entries without an order appear after ordered entries. This metadata
|
|
227
241
|
is shared by slash menus and `getDefaultBlockPickerItems`; use `insertMenu: false`
|
|
228
|
-
to keep a block out of both discovery surfaces. The
|
|
229
|
-
field remains supported. The same fields are available on direct
|
|
242
|
+
to keep a block out of both discovery surfaces. The same fields are available on direct
|
|
230
243
|
`slashMenuItems`; a slash menu's explicit `items` prop is also sorted by this rule.
|
|
231
244
|
|
|
232
245
|
Block `name` is the stable public identifier. Namespace consumer blocks to avoid
|
|
@@ -238,19 +251,23 @@ Duplicate names and node types fail immediately during editor construction.
|
|
|
238
251
|
|
|
239
252
|
The first-party `page` block models a Notion-style child page as a reference to
|
|
240
253
|
a separately persisted document. Supply `pageRuntime` to connect creation,
|
|
241
|
-
resolution,
|
|
254
|
+
resolution, metadata updates, and navigation to your application:
|
|
242
255
|
|
|
243
256
|
```tsx
|
|
244
257
|
const controller = useOpenEditorController({
|
|
245
258
|
pageRuntime: {
|
|
246
259
|
createPage: async ({ title, icon }) => api.pages.create({ title, icon }),
|
|
247
260
|
resolvePage: async (pageId) => api.pages.get(pageId),
|
|
248
|
-
|
|
261
|
+
updatePage: async (pageId, update) => api.pages.update(pageId, update),
|
|
249
262
|
openPage: (page) => router.push(`/pages/${page.pageId}`),
|
|
250
263
|
},
|
|
251
264
|
});
|
|
252
265
|
```
|
|
253
266
|
|
|
267
|
+
Render `OpenEditorPageHeader` on the opened page surface to provide the canonical
|
|
268
|
+
editable title and icon. Both fields call `pageRuntime.updatePage`; page references
|
|
269
|
+
resolve the same page entity instead of owning independent metadata.
|
|
270
|
+
|
|
254
271
|
The document stores only the stable reference and cached presentation. Page
|
|
255
272
|
contents, hierarchy, permissions, routing, and deletion remain host-owned.
|
|
256
273
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, RefObject, ComponentType } from 'react';
|
|
3
|
-
import { OpenEditorBlockRef, OpenEditorDocument, BlockSpec, ProseMirrorNode, OpenEditorAttachmentRuntime } from '@openeditor/core';
|
|
4
|
-
export { OpenEditorBlockRef } from '@openeditor/core';
|
|
5
|
-
import {
|
|
3
|
+
import { OpenEditorBlockRef, OpenEditorDocument, BlockSpec, ProseMirrorNode, OpenEditorPageRuntime, OpenEditorAttachmentRuntime, OpenEditorPageSnapshot, OpenEditorAuthoringCapabilities } from '@openeditor/core';
|
|
4
|
+
export { OpenEditorBlockRef, OpenEditorPageRuntime, OpenEditorPageSnapshot } from '@openeditor/core';
|
|
5
|
+
import { ExportFormat, OpenEditorExporters } from '@openeditor/exporters';
|
|
6
6
|
export { seedDocument } from '@openeditor/extensions';
|
|
7
7
|
import { AnyExtension, NodeConfig } from '@tiptap/core';
|
|
8
8
|
import { ReactNodeViewProps } from '@tiptap/react';
|
|
@@ -29,23 +29,7 @@ declare function OpenEditorThemeProvider({ children, className, theme }: {
|
|
|
29
29
|
theme: OpenEditorTheme;
|
|
30
30
|
}): react.JSX.Element;
|
|
31
31
|
|
|
32
|
-
type
|
|
33
|
-
pageId: string;
|
|
34
|
-
title: string;
|
|
35
|
-
icon?: string | null;
|
|
36
|
-
href?: string | null;
|
|
37
|
-
};
|
|
38
|
-
type OpenEditorPageRuntime = {
|
|
39
|
-
createPage?: (input: {
|
|
40
|
-
title: string;
|
|
41
|
-
icon?: string | null;
|
|
42
|
-
}) => Promise<OpenEditorPageSnapshot>;
|
|
43
|
-
resolvePage?: (pageId: string) => Promise<OpenEditorPageSnapshot | null>;
|
|
44
|
-
renamePage?: (pageId: string, title: string) => Promise<void> | void;
|
|
45
|
-
updatePageIcon?: (pageId: string, icon: string) => Promise<void> | void;
|
|
46
|
-
openPage?: (page: OpenEditorPageSnapshot) => Promise<void> | void;
|
|
47
|
-
};
|
|
48
|
-
type OpenEditorReactProps = {
|
|
32
|
+
type OpenEditorReactProps = OpenEditorAuthoringCapabilities & {
|
|
49
33
|
initialDocument?: OpenEditorDocument;
|
|
50
34
|
editable?: boolean;
|
|
51
35
|
placeholder?: string;
|
|
@@ -72,7 +56,8 @@ type OpenEditorBlockAction = {
|
|
|
72
56
|
type OpenEditorReactConfig = OpenEditorReactProps;
|
|
73
57
|
type OpenEditorController = {
|
|
74
58
|
document: OpenEditorDocument;
|
|
75
|
-
|
|
59
|
+
export: (format: ExportFormat) => string;
|
|
60
|
+
exportUnsafe: (format: ExportFormat) => string;
|
|
76
61
|
ready: boolean;
|
|
77
62
|
editable: boolean;
|
|
78
63
|
placeholder: string;
|
|
@@ -95,6 +80,7 @@ type OpenEditorController = {
|
|
|
95
80
|
undo: () => void;
|
|
96
81
|
redo: () => void;
|
|
97
82
|
insertBlock: (blockName?: string, range?: InsertRange) => boolean;
|
|
83
|
+
isBlockEnabled: (blockName: string) => boolean;
|
|
98
84
|
setLink: (url?: string | null) => void;
|
|
99
85
|
getActiveLink: () => string | undefined;
|
|
100
86
|
selectBlock: (block: OpenEditorBlockRef) => boolean;
|
|
@@ -112,6 +98,7 @@ type OpenEditorController = {
|
|
|
112
98
|
extensions: readonly OpenEditorReactExtension[];
|
|
113
99
|
pageRuntime?: OpenEditorPageRuntime;
|
|
114
100
|
attachmentRuntime?: OpenEditorAttachmentRuntime<any>;
|
|
101
|
+
enabledBlocks?: readonly string[];
|
|
115
102
|
slashState: SlashState | null;
|
|
116
103
|
selectionBubbleState: SelectionBubbleState | null;
|
|
117
104
|
};
|
|
@@ -206,8 +193,6 @@ type OpenEditorReactExtension = {
|
|
|
206
193
|
blockHandle?: "self" | "nested";
|
|
207
194
|
/** Discovery metadata shared by slash menus and host-owned block pickers. */
|
|
208
195
|
insertMenu?: false | OpenEditorExtensionMenu;
|
|
209
|
-
/** @deprecated Use `insertMenu`. */
|
|
210
|
-
slashMenu?: false | OpenEditorExtensionMenu;
|
|
211
196
|
viewer: OpenEditorViewerRenderer;
|
|
212
197
|
exporters?: OpenEditorExporters;
|
|
213
198
|
};
|
|
@@ -234,7 +219,15 @@ declare module "@tiptap/core" {
|
|
|
234
219
|
}
|
|
235
220
|
}
|
|
236
221
|
declare const formatAttachmentSize: (size: number | null) => string;
|
|
237
|
-
|
|
222
|
+
type OpenEditorPageHeaderProps = {
|
|
223
|
+
page: OpenEditorPageSnapshot;
|
|
224
|
+
runtime: OpenEditorPageRuntime;
|
|
225
|
+
className?: string;
|
|
226
|
+
onPageChange?: (page: OpenEditorPageSnapshot) => void;
|
|
227
|
+
};
|
|
228
|
+
/** Canonical Notion-style page metadata editor shared by host page surfaces. */
|
|
229
|
+
declare const OpenEditorPageHeader: ({ page, runtime, className, onPageChange, }: OpenEditorPageHeaderProps) => react.JSX.Element;
|
|
230
|
+
declare const useOpenEditorController: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
238
231
|
declare const useOpenEditorBlockInteraction: (controller: OpenEditorController) => OpenEditorBlockInteractionSnapshot;
|
|
239
232
|
type OpenEditorBlockDragHandleProps = {
|
|
240
233
|
controller: OpenEditorController;
|
|
@@ -258,6 +251,6 @@ declare const getDefaultBlockPickerItems: (controller: OpenEditorController) =>
|
|
|
258
251
|
declare const getDefaultSlashMenuItems: (controller: OpenEditorController) => OpenEditorSlashMenuItem[];
|
|
259
252
|
declare const OpenEditorContent: ({ controller, className, }: OpenEditorContentProps) => react.JSX.Element;
|
|
260
253
|
declare const OpenEditorViewer: ({ document, className, renderers, extensions, pageRuntime, attachmentRuntime, }: OpenEditorViewerProps) => react.JSX.Element;
|
|
261
|
-
declare const useOpenEditor: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
254
|
+
declare const useOpenEditor: ({ initialDocument, editable, placeholder, onChange, onFilePasteDrop, pageRuntime, attachmentRuntime, enabledBlocks, slashMenuItems, extensions, blockActions, }?: OpenEditorReactProps) => OpenEditorController;
|
|
262
255
|
|
|
263
|
-
export { type InsertRange, type OpenEditorBlockAction, type OpenEditorBlockActionContext, OpenEditorBlockDragHandle, type OpenEditorBlockDragHandleProps, type OpenEditorBlockPickerItem, OpenEditorContent, type OpenEditorContentProps, type OpenEditorController, type OpenEditorExtensionMenu, type OpenEditorOverlayRect,
|
|
256
|
+
export { type InsertRange, type OpenEditorBlockAction, type OpenEditorBlockActionContext, OpenEditorBlockDragHandle, type OpenEditorBlockDragHandleProps, type OpenEditorBlockPickerItem, OpenEditorContent, type OpenEditorContentProps, type OpenEditorController, type OpenEditorExtensionMenu, type OpenEditorOverlayRect, OpenEditorPageHeader, type OpenEditorPageHeaderProps, type OpenEditorReactConfig, type OpenEditorReactExtension, type OpenEditorReactNodeExtension, type OpenEditorReactProps, type OpenEditorSlashMenuIcon, type OpenEditorSlashMenuIconProps, type OpenEditorSlashMenuItem, type OpenEditorTheme, OpenEditorThemeProvider, type OpenEditorThemeStyle, type OpenEditorThemeToken, OpenEditorViewer, type OpenEditorViewerProps, type OpenEditorViewerRenderer, type SelectionBubbleState, type SlashState, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { DEFAULT_CALLOUT_EMOJI, normalizeEmoji, DEFAULT_PAGE_EMOJI, normalizeDocument, toProseMirrorDocument, createDocument as createDocument$1, createBlockRegistry, OPENEDITOR_BLOCK_ID_ATTR, createBlockId, fromProseMirrorDocument, findBlockSpecForNode } from '@openeditor/core';
|
|
1
|
+
import { DEFAULT_CALLOUT_EMOJI, normalizeEmoji, DEFAULT_PAGE_EMOJI, normalizeDocument, toProseMirrorDocument, createDocument as createDocument$1, isOpenEditorBlockEnabled, createBlockRegistry, OPENEDITOR_BLOCK_ID_ATTR, createBlockId, fromProseMirrorDocument, findBlockSpecForNode } from '@openeditor/core';
|
|
2
2
|
import { Checkbox } from '@base-ui/react/checkbox';
|
|
3
3
|
import { shift as shift$1, autoUpdate, computePosition } from '@floating-ui/dom';
|
|
4
4
|
import { OpenEditorEmojiPicker } from '@openeditor/emoji';
|
|
5
|
-
import React, { createContext, forwardRef, useContext, useMemo, createRef, memo, createElement, version, useState,
|
|
5
|
+
import React, { createContext, forwardRef, useContext, useMemo, createRef, memo, createElement, version, useState, useEffect, useRef, useSyncExternalStore as useSyncExternalStore$1, useCallback, use, useDebugValue, Fragment as Fragment$2, useLayoutEffect } from 'react';
|
|
6
6
|
import { jsxs, Fragment as Fragment$1, jsx } from 'react/jsx-runtime';
|
|
7
7
|
import { renderMermaidSVG } from 'beautiful-mermaid';
|
|
8
|
-
import {
|
|
8
|
+
import { exportDocumentUnsafe, exportDocument } from '@openeditor/exporters';
|
|
9
9
|
import { openEditorInsertPresets, resolveOpenEditorInsertBlock, defaultBlockRegistry } from '@openeditor/extensions';
|
|
10
10
|
export { seedDocument } from '@openeditor/extensions';
|
|
11
11
|
import ReactDOM, { flushSync, createPortal } from 'react-dom';
|
|
@@ -31524,11 +31524,12 @@ var OpenEditorPageNodeView = ({ editor, node, updateAttributes: updateAttributes
|
|
|
31524
31524
|
};
|
|
31525
31525
|
const updateIcon = async (nextIcon) => {
|
|
31526
31526
|
updateAttributes2({ icon: nextIcon });
|
|
31527
|
-
if (!page?.pageId || !runtime
|
|
31527
|
+
if (!page?.pageId || !runtime) return;
|
|
31528
31528
|
setResolvedPage({ ...page, icon: nextIcon });
|
|
31529
31529
|
setError(null);
|
|
31530
31530
|
try {
|
|
31531
|
-
await runtime.
|
|
31531
|
+
const updated = await runtime.updatePage(page.pageId, { icon: nextIcon });
|
|
31532
|
+
if (updated) setResolvedPage(updated);
|
|
31532
31533
|
} catch (cause) {
|
|
31533
31534
|
setError(cause instanceof Error ? cause.message : "Could not update the page icon.");
|
|
31534
31535
|
}
|
|
@@ -31557,7 +31558,7 @@ var OpenEditorPageNodeView = ({ editor, node, updateAttributes: updateAttributes
|
|
|
31557
31558
|
OpenEditorEmojiPicker,
|
|
31558
31559
|
{
|
|
31559
31560
|
className: "oe-page-icon-trigger",
|
|
31560
|
-
disabled: !editor.isEditable,
|
|
31561
|
+
disabled: !editor.isEditable || !runtime,
|
|
31561
31562
|
emoji: icon,
|
|
31562
31563
|
label: "Change page emoji",
|
|
31563
31564
|
popupStyle: themeStyle,
|
|
@@ -31626,6 +31627,61 @@ var OpenEditorPageViewer = ({ node }) => {
|
|
|
31626
31627
|
}
|
|
31627
31628
|
return /* @__PURE__ */ jsx("div", { className: "oe-page oe-page-viewer", children: content });
|
|
31628
31629
|
};
|
|
31630
|
+
var OpenEditorPageHeader = ({
|
|
31631
|
+
page,
|
|
31632
|
+
runtime,
|
|
31633
|
+
className = "oe-page-header",
|
|
31634
|
+
onPageChange
|
|
31635
|
+
}) => {
|
|
31636
|
+
const [title, setTitle] = useState(page.title);
|
|
31637
|
+
const [icon, setIcon] = useState(normalizeEmoji(page.icon, DEFAULT_PAGE_EMOJI));
|
|
31638
|
+
const [error, setError] = useState(null);
|
|
31639
|
+
useEffect(() => setTitle(page.title), [page.pageId, page.title]);
|
|
31640
|
+
useEffect(() => setIcon(normalizeEmoji(page.icon, DEFAULT_PAGE_EMOJI)), [page.icon, page.pageId]);
|
|
31641
|
+
const updatePage = async (update) => {
|
|
31642
|
+
setError(null);
|
|
31643
|
+
try {
|
|
31644
|
+
const resolved = await runtime.updatePage(page.pageId, update);
|
|
31645
|
+
const next = resolved ?? { ...page, ...update };
|
|
31646
|
+
setTitle(next.title);
|
|
31647
|
+
setIcon(normalizeEmoji(next.icon, DEFAULT_PAGE_EMOJI));
|
|
31648
|
+
onPageChange?.(next);
|
|
31649
|
+
} catch (cause) {
|
|
31650
|
+
setError(cause instanceof Error ? cause.message : "Could not update the page.");
|
|
31651
|
+
}
|
|
31652
|
+
};
|
|
31653
|
+
return /* @__PURE__ */ jsxs("header", { className: `oe-page-header${className === "oe-page-header" ? "" : ` ${className}`}`, children: [
|
|
31654
|
+
/* @__PURE__ */ jsx(
|
|
31655
|
+
OpenEditorEmojiPicker,
|
|
31656
|
+
{
|
|
31657
|
+
emoji: icon,
|
|
31658
|
+
label: "Change page icon",
|
|
31659
|
+
onEmojiSelect: (nextIcon) => {
|
|
31660
|
+
setIcon(nextIcon);
|
|
31661
|
+
void updatePage({ icon: nextIcon });
|
|
31662
|
+
}
|
|
31663
|
+
}
|
|
31664
|
+
),
|
|
31665
|
+
/* @__PURE__ */ jsx(
|
|
31666
|
+
"input",
|
|
31667
|
+
{
|
|
31668
|
+
"aria-label": "Page title",
|
|
31669
|
+
className: "oe-page-header-title",
|
|
31670
|
+
onBlur: () => {
|
|
31671
|
+
const nextTitle = title.trim() || "Untitled";
|
|
31672
|
+
setTitle(nextTitle);
|
|
31673
|
+
if (nextTitle !== page.title) void updatePage({ title: nextTitle });
|
|
31674
|
+
},
|
|
31675
|
+
onChange: (event) => setTitle(event.target.value),
|
|
31676
|
+
onKeyDown: (event) => {
|
|
31677
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
31678
|
+
},
|
|
31679
|
+
value: title
|
|
31680
|
+
}
|
|
31681
|
+
),
|
|
31682
|
+
error ? /* @__PURE__ */ jsx("span", { className: "oe-page-error", role: "alert", children: error }) : null
|
|
31683
|
+
] });
|
|
31684
|
+
};
|
|
31629
31685
|
var MermaidPreview = ({ code }) => {
|
|
31630
31686
|
const rendered = useMemo(() => {
|
|
31631
31687
|
try {
|
|
@@ -31698,7 +31754,7 @@ var MermaidDiagram = Node3.create({
|
|
|
31698
31754
|
return ReactNodeViewRenderer(OpenEditorDiagramNodeView);
|
|
31699
31755
|
}
|
|
31700
31756
|
});
|
|
31701
|
-
var createEditorExtensions = (
|
|
31757
|
+
var createEditorExtensions = (getPlaceholder, customExtensions) => [
|
|
31702
31758
|
index_default12.configure({
|
|
31703
31759
|
attributeName: OPENEDITOR_BLOCK_ID_ATTR,
|
|
31704
31760
|
types: "all",
|
|
@@ -31719,7 +31775,7 @@ var createEditorExtensions = (placeholder, customExtensions) => [
|
|
|
31719
31775
|
index_default6,
|
|
31720
31776
|
index_default5,
|
|
31721
31777
|
index_default10.configure({ types: ["heading", "paragraph"] }),
|
|
31722
|
-
index_default4.configure({ placeholder, showOnlyCurrent: true }),
|
|
31778
|
+
index_default4.configure({ placeholder: () => getPlaceholder(), showOnlyCurrent: true }),
|
|
31723
31779
|
Columns,
|
|
31724
31780
|
Column,
|
|
31725
31781
|
ToggleList,
|
|
@@ -31825,12 +31881,12 @@ var useOpenEditorController = ({
|
|
|
31825
31881
|
onFilePasteDrop,
|
|
31826
31882
|
pageRuntime,
|
|
31827
31883
|
attachmentRuntime,
|
|
31884
|
+
enabledBlocks,
|
|
31828
31885
|
slashMenuItems = [],
|
|
31829
31886
|
extensions = EMPTY_EXTENSIONS,
|
|
31830
31887
|
blockActions = []
|
|
31831
31888
|
} = {}) => {
|
|
31832
31889
|
const [document2, setDocument] = useState(() => normalizeDocument(initialDocument));
|
|
31833
|
-
const [initialEditorContent] = useState(() => toProseMirrorDocument(document2));
|
|
31834
31890
|
const [selectedBlock, setSelectedBlock] = useState("paragraph");
|
|
31835
31891
|
const [slashState, setSlashState] = useState(null);
|
|
31836
31892
|
const [selectionBubbleState, setSelectionBubbleState] = useState(null);
|
|
@@ -31839,12 +31895,29 @@ var useOpenEditorController = ({
|
|
|
31839
31895
|
const slashStateRef = useRef(null);
|
|
31840
31896
|
const selectionBubbleStateRef = useRef(null);
|
|
31841
31897
|
const documentMetaRef = useRef(document2.meta);
|
|
31898
|
+
const placeholderRef = useRef(placeholder);
|
|
31899
|
+
placeholderRef.current = placeholder;
|
|
31900
|
+
const onChangeRef = useRef(onChange);
|
|
31901
|
+
onChangeRef.current = onChange;
|
|
31902
|
+
const onFilePasteDropRef = useRef(onFilePasteDrop);
|
|
31903
|
+
onFilePasteDropRef.current = onFilePasteDrop;
|
|
31904
|
+
const isBlockEnabled = (blockName) => {
|
|
31905
|
+
const canonicalName = /^heading[1-6]$/.test(blockName) ? "heading" : blockName;
|
|
31906
|
+
if (!isOpenEditorBlockEnabled(canonicalName, { enabledBlocks })) return false;
|
|
31907
|
+
if (canonicalName === "attachment") {
|
|
31908
|
+
return Boolean(attachmentRuntime?.selectAttachment && attachmentRuntime.uploadAttachment);
|
|
31909
|
+
}
|
|
31910
|
+
if (canonicalName === "page") return Boolean(pageRuntime?.createPage);
|
|
31911
|
+
return true;
|
|
31912
|
+
};
|
|
31913
|
+
const isBlockEnabledRef = useRef(isBlockEnabled);
|
|
31914
|
+
isBlockEnabledRef.current = isBlockEnabled;
|
|
31842
31915
|
const registry = useMemo(() => createReactBlockRegistry(extensions), [extensions]);
|
|
31843
31916
|
const exporters = useMemo(() => createReactExporters(extensions), [extensions]);
|
|
31844
31917
|
const editor = useEditor({
|
|
31845
31918
|
immediatelyRender: false,
|
|
31846
|
-
extensions: createEditorExtensions(
|
|
31847
|
-
content:
|
|
31919
|
+
extensions: createEditorExtensions(() => placeholderRef.current, extensions),
|
|
31920
|
+
content: toProseMirrorDocument(document2),
|
|
31848
31921
|
editable,
|
|
31849
31922
|
editorProps: {
|
|
31850
31923
|
attributes: {
|
|
@@ -31861,6 +31934,12 @@ var useOpenEditorController = ({
|
|
|
31861
31934
|
try {
|
|
31862
31935
|
const payload = JSON.parse(encoded);
|
|
31863
31936
|
if (payload.format === "openeditor-blocks" && payload.version === 1 && Array.isArray(payload.blocks)) {
|
|
31937
|
+
const firstBlock = payload.blocks[0];
|
|
31938
|
+
const blockName = firstBlock ? findBlockSpecForNode(registry, firstBlock)?.name : void 0;
|
|
31939
|
+
if (blockName && !isBlockEnabledRef.current(blockName)) {
|
|
31940
|
+
event.preventDefault();
|
|
31941
|
+
return true;
|
|
31942
|
+
}
|
|
31864
31943
|
const nodes = payload.blocks.map(
|
|
31865
31944
|
(node) => view.state.schema.nodeFromJSON(regenerateClipboardNodeIds(node))
|
|
31866
31945
|
);
|
|
@@ -31877,7 +31956,7 @@ var useOpenEditorController = ({
|
|
|
31877
31956
|
}
|
|
31878
31957
|
}
|
|
31879
31958
|
const file = event.clipboardData?.files?.[0];
|
|
31880
|
-
if (!file || !
|
|
31959
|
+
if (!file || !isBlockEnabledRef.current("attachment") || !onFilePasteDropRef.current?.(file)) {
|
|
31881
31960
|
return false;
|
|
31882
31961
|
}
|
|
31883
31962
|
event.preventDefault();
|
|
@@ -31885,7 +31964,7 @@ var useOpenEditorController = ({
|
|
|
31885
31964
|
},
|
|
31886
31965
|
handleDrop: (_view, event, _slice, moved) => {
|
|
31887
31966
|
const file = event.dataTransfer?.files?.[0];
|
|
31888
|
-
if (moved || !file || !
|
|
31967
|
+
if (moved || !file || !isBlockEnabledRef.current("attachment") || !onFilePasteDropRef.current?.(file)) {
|
|
31889
31968
|
return false;
|
|
31890
31969
|
}
|
|
31891
31970
|
event.preventDefault();
|
|
@@ -31907,7 +31986,7 @@ var useOpenEditorController = ({
|
|
|
31907
31986
|
documentMetaRef.current
|
|
31908
31987
|
);
|
|
31909
31988
|
setDocument(nextDocument);
|
|
31910
|
-
|
|
31989
|
+
onChangeRef.current?.(nextDocument);
|
|
31911
31990
|
setSlashState(getSlashState(currentEditor));
|
|
31912
31991
|
setSelectionBubbleState(
|
|
31913
31992
|
isPointerSelectingRef.current ? null : getSelectionBubbleState(currentEditor)
|
|
@@ -31923,10 +32002,12 @@ var useOpenEditorController = ({
|
|
|
31923
32002
|
}
|
|
31924
32003
|
}
|
|
31925
32004
|
}, [extensions]);
|
|
31926
|
-
|
|
31927
|
-
(
|
|
31928
|
-
|
|
31929
|
-
)
|
|
32005
|
+
useEffect(() => {
|
|
32006
|
+
editor?.setEditable(editable, false);
|
|
32007
|
+
}, [editable, editor]);
|
|
32008
|
+
useEffect(() => {
|
|
32009
|
+
if (editor && !editor.isDestroyed) editor.view.dispatch(editor.state.tr);
|
|
32010
|
+
}, [editor, placeholder]);
|
|
31930
32011
|
useEffect(() => {
|
|
31931
32012
|
slashStateRef.current = slashState;
|
|
31932
32013
|
}, [slashState]);
|
|
@@ -31975,7 +32056,7 @@ var useOpenEditorController = ({
|
|
|
31975
32056
|
};
|
|
31976
32057
|
}, [editor]);
|
|
31977
32058
|
const insertBlock = (blockName = selectedBlock, range) => {
|
|
31978
|
-
if (!editor) {
|
|
32059
|
+
if (!editor || !isBlockEnabled(blockName)) {
|
|
31979
32060
|
return false;
|
|
31980
32061
|
}
|
|
31981
32062
|
const block = resolveOpenEditorInsertBlock(blockName, registry);
|
|
@@ -31985,14 +32066,14 @@ var useOpenEditorController = ({
|
|
|
31985
32066
|
return range ? commandChain.deleteRange(range) : commandChain;
|
|
31986
32067
|
};
|
|
31987
32068
|
if (blockName === "page" && pageRuntime?.createPage) {
|
|
31988
|
-
const prepared = chain().run();
|
|
31989
|
-
if (!prepared) return false;
|
|
31990
32069
|
setSlashState(null);
|
|
31991
32070
|
void pageRuntime.createPage({
|
|
31992
32071
|
title: "Untitled",
|
|
31993
32072
|
icon: DEFAULT_PAGE_EMOJI
|
|
31994
32073
|
}).then((created) => {
|
|
31995
|
-
editor.chain().focus()
|
|
32074
|
+
const insertion = editor.chain().focus();
|
|
32075
|
+
const prepared = range ? insertion.deleteRange(range) : insertion;
|
|
32076
|
+
prepared.insertContent({
|
|
31996
32077
|
...block,
|
|
31997
32078
|
attrs: {
|
|
31998
32079
|
...block.attrs,
|
|
@@ -32002,9 +32083,7 @@ var useOpenEditorController = ({
|
|
|
32002
32083
|
},
|
|
32003
32084
|
content: [{ type: "text", text: created.title || "Untitled" }]
|
|
32004
32085
|
}).run();
|
|
32005
|
-
}).catch(() =>
|
|
32006
|
-
editor.chain().focus().insertContent(block).run();
|
|
32007
|
-
});
|
|
32086
|
+
}).catch(() => void 0);
|
|
32008
32087
|
return true;
|
|
32009
32088
|
}
|
|
32010
32089
|
const inserted = chain().insertContent(block).run();
|
|
@@ -32017,7 +32096,9 @@ var useOpenEditorController = ({
|
|
|
32017
32096
|
}
|
|
32018
32097
|
const normalized = normalizeDocument(nextDocument);
|
|
32019
32098
|
documentMetaRef.current = normalized.meta;
|
|
32020
|
-
editor.commands.setContent(toProseMirrorDocument(normalized));
|
|
32099
|
+
editor.commands.setContent(toProseMirrorDocument(normalized), { emitUpdate: false });
|
|
32100
|
+
setDocument(normalized);
|
|
32101
|
+
onChangeRef.current?.(normalized);
|
|
32021
32102
|
};
|
|
32022
32103
|
const resolveBlock = (block) => block ?? (editor ? selectedBlockRef(editor, registry) : null);
|
|
32023
32104
|
const resolveBlockAt = (position) => editor ? blockRefAtPosition(editor, position, registry) : null;
|
|
@@ -32091,10 +32172,10 @@ var useOpenEditorController = ({
|
|
|
32091
32172
|
editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
|
|
32092
32173
|
};
|
|
32093
32174
|
const setParagraph = () => {
|
|
32094
|
-
editor?.chain().focus().setParagraph().run();
|
|
32175
|
+
if (isBlockEnabled("paragraph")) editor?.chain().focus().setParagraph().run();
|
|
32095
32176
|
};
|
|
32096
32177
|
const toggleHeading = (level) => {
|
|
32097
|
-
editor?.chain().focus().toggleHeading({ level }).run();
|
|
32178
|
+
if (isBlockEnabled("heading")) editor?.chain().focus().toggleHeading({ level }).run();
|
|
32098
32179
|
};
|
|
32099
32180
|
const toggleBold = () => {
|
|
32100
32181
|
editor?.chain().focus().toggleBold().run();
|
|
@@ -32112,19 +32193,19 @@ var useOpenEditorController = ({
|
|
|
32112
32193
|
editor?.chain().focus().toggleCode().run();
|
|
32113
32194
|
};
|
|
32114
32195
|
const toggleBulletList = () => {
|
|
32115
|
-
editor?.chain().focus().toggleBulletList().run();
|
|
32196
|
+
if (isBlockEnabled("bulletList")) editor?.chain().focus().toggleBulletList().run();
|
|
32116
32197
|
};
|
|
32117
32198
|
const toggleOrderedList = () => {
|
|
32118
|
-
editor?.chain().focus().toggleOrderedList().run();
|
|
32199
|
+
if (isBlockEnabled("orderedList")) editor?.chain().focus().toggleOrderedList().run();
|
|
32119
32200
|
};
|
|
32120
32201
|
const toggleTaskList = () => {
|
|
32121
|
-
editor?.chain().focus().toggleTaskList().run();
|
|
32202
|
+
if (isBlockEnabled("taskList")) editor?.chain().focus().toggleTaskList().run();
|
|
32122
32203
|
};
|
|
32123
32204
|
const toggleBlockquote = () => {
|
|
32124
|
-
editor?.chain().focus().toggleBlockquote().run();
|
|
32205
|
+
if (isBlockEnabled("blockquote")) editor?.chain().focus().toggleBlockquote().run();
|
|
32125
32206
|
};
|
|
32126
32207
|
const toggleCodeBlock = () => {
|
|
32127
|
-
editor?.chain().focus().toggleCodeBlock().run();
|
|
32208
|
+
if (isBlockEnabled("codeBlock")) editor?.chain().focus().toggleCodeBlock().run();
|
|
32128
32209
|
};
|
|
32129
32210
|
const undo2 = () => {
|
|
32130
32211
|
editor?.chain().focus().undo().run();
|
|
@@ -32135,7 +32216,8 @@ var useOpenEditorController = ({
|
|
|
32135
32216
|
const controller = {
|
|
32136
32217
|
document: document2,
|
|
32137
32218
|
editor,
|
|
32138
|
-
|
|
32219
|
+
export: (format) => exportDocument(document2, format),
|
|
32220
|
+
exportUnsafe: (format) => exportDocumentUnsafe(document2, format, exporters),
|
|
32139
32221
|
ready: editor !== null,
|
|
32140
32222
|
editable,
|
|
32141
32223
|
placeholder,
|
|
@@ -32158,6 +32240,7 @@ var useOpenEditorController = ({
|
|
|
32158
32240
|
undo: undo2,
|
|
32159
32241
|
redo: redo2,
|
|
32160
32242
|
insertBlock,
|
|
32243
|
+
isBlockEnabled,
|
|
32161
32244
|
setLink,
|
|
32162
32245
|
getActiveLink: () => editor?.getAttributes("link").href,
|
|
32163
32246
|
selectBlock,
|
|
@@ -32178,6 +32261,7 @@ var useOpenEditorController = ({
|
|
|
32178
32261
|
extensions,
|
|
32179
32262
|
pageRuntime,
|
|
32180
32263
|
attachmentRuntime,
|
|
32264
|
+
enabledBlocks,
|
|
32181
32265
|
slashState,
|
|
32182
32266
|
selectionBubbleState
|
|
32183
32267
|
};
|
|
@@ -32402,9 +32486,9 @@ var sortMenuItems = (items) => [...items].sort((left, right) => {
|
|
|
32402
32486
|
});
|
|
32403
32487
|
var sortSlashMenuItems = (items) => sortMenuItems(items);
|
|
32404
32488
|
var sortBlockPickerItems = (items) => sortMenuItems(items);
|
|
32405
|
-
var getExtensionInsertMenu = (extension) => extension.insertMenu
|
|
32489
|
+
var getExtensionInsertMenu = (extension) => extension.insertMenu;
|
|
32406
32490
|
var getDefaultBlockPickerItems = (controller) => sortBlockPickerItems([
|
|
32407
|
-
...openEditorInsertPresets.map((preset, index) => ({
|
|
32491
|
+
...openEditorInsertPresets.filter((preset) => controller.isBlockEnabled(preset.key)).map((preset, index) => ({
|
|
32408
32492
|
key: preset.key,
|
|
32409
32493
|
label: preset.label,
|
|
32410
32494
|
group: preset.group,
|
|
@@ -32412,7 +32496,7 @@ var getDefaultBlockPickerItems = (controller) => sortBlockPickerItems([
|
|
|
32412
32496
|
order: (index + 1) * 100,
|
|
32413
32497
|
insert: () => controller.insertBlock(preset.key)
|
|
32414
32498
|
})),
|
|
32415
|
-
...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false).map((extension) => {
|
|
32499
|
+
...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false && controller.isBlockEnabled(extension.block.name)).map((extension) => {
|
|
32416
32500
|
const menu = getExtensionInsertMenu(extension) || void 0;
|
|
32417
32501
|
return {
|
|
32418
32502
|
key: extension.block.name,
|
|
@@ -32428,7 +32512,7 @@ var getDefaultBlockPickerItems = (controller) => sortBlockPickerItems([
|
|
|
32428
32512
|
var getDefaultSlashMenuItems = (controller) => {
|
|
32429
32513
|
const internalController = controller;
|
|
32430
32514
|
return sortSlashMenuItems([
|
|
32431
|
-
...openEditorInsertPresets.map((preset, index) => ({
|
|
32515
|
+
...openEditorInsertPresets.filter((preset) => controller.isBlockEnabled(preset.key)).map((preset, index) => ({
|
|
32432
32516
|
key: preset.key,
|
|
32433
32517
|
label: preset.label,
|
|
32434
32518
|
group: preset.group,
|
|
@@ -32460,7 +32544,7 @@ var getDefaultSlashMenuItems = (controller) => {
|
|
|
32460
32544
|
return true;
|
|
32461
32545
|
}
|
|
32462
32546
|
},
|
|
32463
|
-
...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false).map((extension) => {
|
|
32547
|
+
...controller.extensions.filter((extension) => getExtensionInsertMenu(extension) !== false && controller.isBlockEnabled(extension.block.name)).map((extension) => {
|
|
32464
32548
|
const menu = getExtensionInsertMenu(extension) || void 0;
|
|
32465
32549
|
return {
|
|
32466
32550
|
key: extension.block.name,
|
|
@@ -32615,6 +32699,6 @@ var OpenEditorViewer = ({
|
|
|
32615
32699
|
};
|
|
32616
32700
|
var useOpenEditor = useOpenEditorController;
|
|
32617
32701
|
|
|
32618
|
-
export { NodeViewContent, NodeViewWrapper, OpenEditorBlockDragHandle, OpenEditorContent, OpenEditorThemeProvider, OpenEditorViewer, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
|
|
32702
|
+
export { NodeViewContent, NodeViewWrapper, OpenEditorBlockDragHandle, OpenEditorContent, OpenEditorPageHeader, OpenEditorThemeProvider, OpenEditorViewer, createOpenEditorThemeStyle, defineOpenEditorReactExtension, defineOpenEditorReactNode, formatAttachmentSize, getDefaultBlockPickerItems, getDefaultSlashMenuItems, openEditorThemeTokenNames, sortBlockPickerItems, sortSlashMenuItems, useOpenEditor, useOpenEditorBlockInteraction, useOpenEditorController, useOpenEditorThemeStyle };
|
|
32619
32703
|
//# sourceMappingURL=index.js.map
|
|
32620
32704
|
//# sourceMappingURL=index.js.map
|