@blockslides/react 0.1.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/LICENSE.md +36 -0
- package/dist/index.cjs +1100 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +349 -0
- package/dist/index.d.ts +349 -0
- package/dist/index.js +1042 -0
- package/dist/index.js.map +1 -0
- package/dist/menus/index.cjs +166 -0
- package/dist/menus/index.cjs.map +1 -0
- package/dist/menus/index.d.cts +19 -0
- package/dist/menus/index.d.ts +19 -0
- package/dist/menus/index.js +128 -0
- package/dist/menus/index.js.map +1 -0
- package/package.json +75 -0
- package/src/Context.tsx +60 -0
- package/src/Editor.ts +15 -0
- package/src/EditorContent.tsx +229 -0
- package/src/NodeViewContent.tsx +30 -0
- package/src/NodeViewWrapper.tsx +27 -0
- package/src/ReactMarkViewRenderer.tsx +106 -0
- package/src/ReactNodeViewRenderer.tsx +344 -0
- package/src/ReactRenderer.tsx +265 -0
- package/src/index.ts +12 -0
- package/src/menus/BubbleMenu.tsx +89 -0
- package/src/menus/FloatingMenu.tsx +69 -0
- package/src/menus/index.ts +2 -0
- package/src/types.ts +6 -0
- package/src/useEditor.ts +405 -0
- package/src/useEditorState.ts +188 -0
- package/src/useReactNodeView.ts +28 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
// src/Context.tsx
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
|
|
4
|
+
// src/EditorContent.tsx
|
|
5
|
+
import React, { forwardRef } from "react";
|
|
6
|
+
import ReactDOM from "react-dom";
|
|
7
|
+
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
var mergeRefs = (...refs) => {
|
|
10
|
+
return (node) => {
|
|
11
|
+
refs.forEach((ref) => {
|
|
12
|
+
if (typeof ref === "function") {
|
|
13
|
+
ref(node);
|
|
14
|
+
} else if (ref) {
|
|
15
|
+
;
|
|
16
|
+
ref.current = node;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
var Portals = ({ contentComponent }) => {
|
|
22
|
+
const renderers = useSyncExternalStore(
|
|
23
|
+
contentComponent.subscribe,
|
|
24
|
+
contentComponent.getSnapshot,
|
|
25
|
+
contentComponent.getServerSnapshot
|
|
26
|
+
);
|
|
27
|
+
return /* @__PURE__ */ jsx(Fragment, { children: Object.values(renderers) });
|
|
28
|
+
};
|
|
29
|
+
function getInstance() {
|
|
30
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
31
|
+
let renderers = {};
|
|
32
|
+
return {
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to the editor instance's changes.
|
|
35
|
+
*/
|
|
36
|
+
subscribe(callback) {
|
|
37
|
+
subscribers.add(callback);
|
|
38
|
+
return () => {
|
|
39
|
+
subscribers.delete(callback);
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
getSnapshot() {
|
|
43
|
+
return renderers;
|
|
44
|
+
},
|
|
45
|
+
getServerSnapshot() {
|
|
46
|
+
return renderers;
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Adds a new NodeView Renderer to the editor.
|
|
50
|
+
*/
|
|
51
|
+
setRenderer(id, renderer) {
|
|
52
|
+
renderers = {
|
|
53
|
+
...renderers,
|
|
54
|
+
[id]: ReactDOM.createPortal(renderer.reactElement, renderer.element, id)
|
|
55
|
+
};
|
|
56
|
+
subscribers.forEach((subscriber) => subscriber());
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Removes a NodeView Renderer from the editor.
|
|
60
|
+
*/
|
|
61
|
+
removeRenderer(id) {
|
|
62
|
+
const nextRenderers = { ...renderers };
|
|
63
|
+
delete nextRenderers[id];
|
|
64
|
+
renderers = nextRenderers;
|
|
65
|
+
subscribers.forEach((subscriber) => subscriber());
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
var PureEditorContent = class extends React.Component {
|
|
70
|
+
constructor(props) {
|
|
71
|
+
var _a;
|
|
72
|
+
super(props);
|
|
73
|
+
this.editorContentRef = React.createRef();
|
|
74
|
+
this.initialized = false;
|
|
75
|
+
this.state = {
|
|
76
|
+
hasContentComponentInitialized: Boolean((_a = props.editor) == null ? void 0 : _a.contentComponent)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
componentDidMount() {
|
|
80
|
+
this.init();
|
|
81
|
+
}
|
|
82
|
+
componentDidUpdate() {
|
|
83
|
+
this.init();
|
|
84
|
+
}
|
|
85
|
+
init() {
|
|
86
|
+
const editor = this.props.editor;
|
|
87
|
+
if (editor && !editor.isDestroyed && editor.options.element) {
|
|
88
|
+
if (editor.contentComponent) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const element = this.editorContentRef.current;
|
|
92
|
+
element.append(editor.view.dom);
|
|
93
|
+
editor.setOptions({
|
|
94
|
+
element
|
|
95
|
+
});
|
|
96
|
+
editor.contentComponent = getInstance();
|
|
97
|
+
if (!this.state.hasContentComponentInitialized) {
|
|
98
|
+
this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => {
|
|
99
|
+
this.setState((prevState) => {
|
|
100
|
+
if (!prevState.hasContentComponentInitialized) {
|
|
101
|
+
return {
|
|
102
|
+
hasContentComponentInitialized: true
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return prevState;
|
|
106
|
+
});
|
|
107
|
+
if (this.unsubscribeToContentComponent) {
|
|
108
|
+
this.unsubscribeToContentComponent();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
editor.createNodeViews();
|
|
113
|
+
this.initialized = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
componentWillUnmount() {
|
|
117
|
+
var _a;
|
|
118
|
+
const editor = this.props.editor;
|
|
119
|
+
if (!editor) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.initialized = false;
|
|
123
|
+
if (!editor.isDestroyed) {
|
|
124
|
+
editor.view.setProps({
|
|
125
|
+
nodeViews: {}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (this.unsubscribeToContentComponent) {
|
|
129
|
+
this.unsubscribeToContentComponent();
|
|
130
|
+
}
|
|
131
|
+
editor.contentComponent = null;
|
|
132
|
+
try {
|
|
133
|
+
if (!((_a = editor.view.dom) == null ? void 0 : _a.firstChild)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const newElement = document.createElement("div");
|
|
137
|
+
newElement.append(editor.view.dom);
|
|
138
|
+
editor.setOptions({
|
|
139
|
+
element: newElement
|
|
140
|
+
});
|
|
141
|
+
} catch {
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
render() {
|
|
145
|
+
const { editor, innerRef, ...rest } = this.props;
|
|
146
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
147
|
+
/* @__PURE__ */ jsx("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
|
|
148
|
+
(editor == null ? void 0 : editor.contentComponent) && /* @__PURE__ */ jsx(Portals, { contentComponent: editor.contentComponent })
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var EditorContentWithKey = forwardRef(
|
|
153
|
+
(props, ref) => {
|
|
154
|
+
const key = React.useMemo(() => {
|
|
155
|
+
return Math.floor(Math.random() * 4294967295).toString();
|
|
156
|
+
}, [props.editor]);
|
|
157
|
+
return React.createElement(PureEditorContent, {
|
|
158
|
+
key,
|
|
159
|
+
innerRef: ref,
|
|
160
|
+
...props
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
var EditorContent = React.memo(EditorContentWithKey);
|
|
165
|
+
|
|
166
|
+
// src/useEditor.ts
|
|
167
|
+
import { Editor } from "@blockslides/core";
|
|
168
|
+
import { useDebugValue as useDebugValue2, useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
169
|
+
import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
|
|
170
|
+
|
|
171
|
+
// src/useEditorState.ts
|
|
172
|
+
import deepEqual from "fast-deep-equal/es6/react.js";
|
|
173
|
+
import { useDebugValue, useEffect, useLayoutEffect, useState } from "react";
|
|
174
|
+
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
|
|
175
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
176
|
+
var EditorStateManager = class {
|
|
177
|
+
constructor(initialEditor) {
|
|
178
|
+
this.transactionNumber = 0;
|
|
179
|
+
this.lastTransactionNumber = 0;
|
|
180
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
181
|
+
this.editor = initialEditor;
|
|
182
|
+
this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 };
|
|
183
|
+
this.getSnapshot = this.getSnapshot.bind(this);
|
|
184
|
+
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
185
|
+
this.watch = this.watch.bind(this);
|
|
186
|
+
this.subscribe = this.subscribe.bind(this);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get the current editor instance.
|
|
190
|
+
*/
|
|
191
|
+
getSnapshot() {
|
|
192
|
+
if (this.transactionNumber === this.lastTransactionNumber) {
|
|
193
|
+
return this.lastSnapshot;
|
|
194
|
+
}
|
|
195
|
+
this.lastTransactionNumber = this.transactionNumber;
|
|
196
|
+
this.lastSnapshot = {
|
|
197
|
+
editor: this.editor,
|
|
198
|
+
transactionNumber: this.transactionNumber
|
|
199
|
+
};
|
|
200
|
+
return this.lastSnapshot;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Always disable the editor on the server-side.
|
|
204
|
+
*/
|
|
205
|
+
getServerSnapshot() {
|
|
206
|
+
return { editor: null, transactionNumber: 0 };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Subscribe to the editor instance's changes.
|
|
210
|
+
*/
|
|
211
|
+
subscribe(callback) {
|
|
212
|
+
this.subscribers.add(callback);
|
|
213
|
+
return () => {
|
|
214
|
+
this.subscribers.delete(callback);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Watch the editor instance for changes.
|
|
219
|
+
*/
|
|
220
|
+
watch(nextEditor) {
|
|
221
|
+
this.editor = nextEditor;
|
|
222
|
+
if (this.editor) {
|
|
223
|
+
const fn = () => {
|
|
224
|
+
this.transactionNumber += 1;
|
|
225
|
+
this.subscribers.forEach((callback) => callback());
|
|
226
|
+
};
|
|
227
|
+
const currentEditor = this.editor;
|
|
228
|
+
currentEditor.on("transaction", fn);
|
|
229
|
+
return () => {
|
|
230
|
+
currentEditor.off("transaction", fn);
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return void 0;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
function useEditorState(options) {
|
|
237
|
+
var _a;
|
|
238
|
+
const [editorStateManager] = useState(
|
|
239
|
+
() => new EditorStateManager(options.editor)
|
|
240
|
+
);
|
|
241
|
+
const selectedState = useSyncExternalStoreWithSelector(
|
|
242
|
+
editorStateManager.subscribe,
|
|
243
|
+
editorStateManager.getSnapshot,
|
|
244
|
+
editorStateManager.getServerSnapshot,
|
|
245
|
+
options.selector,
|
|
246
|
+
(_a = options.equalityFn) != null ? _a : deepEqual
|
|
247
|
+
);
|
|
248
|
+
useIsomorphicLayoutEffect(() => {
|
|
249
|
+
return editorStateManager.watch(options.editor);
|
|
250
|
+
}, [options.editor, editorStateManager]);
|
|
251
|
+
useDebugValue(selectedState);
|
|
252
|
+
return selectedState;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/useEditor.ts
|
|
256
|
+
var isDev = process.env.NODE_ENV !== "production";
|
|
257
|
+
var isSSR = typeof window === "undefined";
|
|
258
|
+
var isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
|
|
259
|
+
var EditorInstanceManager = class _EditorInstanceManager {
|
|
260
|
+
constructor(options) {
|
|
261
|
+
/**
|
|
262
|
+
* The current editor instance.
|
|
263
|
+
*/
|
|
264
|
+
this.editor = null;
|
|
265
|
+
/**
|
|
266
|
+
* The subscriptions to notify when the editor instance
|
|
267
|
+
* has been created or destroyed.
|
|
268
|
+
*/
|
|
269
|
+
this.subscriptions = /* @__PURE__ */ new Set();
|
|
270
|
+
/**
|
|
271
|
+
* Whether the editor has been mounted.
|
|
272
|
+
*/
|
|
273
|
+
this.isComponentMounted = false;
|
|
274
|
+
/**
|
|
275
|
+
* The most recent dependencies array.
|
|
276
|
+
*/
|
|
277
|
+
this.previousDeps = null;
|
|
278
|
+
/**
|
|
279
|
+
* The unique instance ID. This is used to identify the editor instance. And will be re-generated for each new instance.
|
|
280
|
+
*/
|
|
281
|
+
this.instanceId = "";
|
|
282
|
+
this.options = options;
|
|
283
|
+
this.subscriptions = /* @__PURE__ */ new Set();
|
|
284
|
+
this.setEditor(this.getInitialEditor());
|
|
285
|
+
this.scheduleDestroy();
|
|
286
|
+
this.getEditor = this.getEditor.bind(this);
|
|
287
|
+
this.getServerSnapshot = this.getServerSnapshot.bind(this);
|
|
288
|
+
this.subscribe = this.subscribe.bind(this);
|
|
289
|
+
this.refreshEditorInstance = this.refreshEditorInstance.bind(this);
|
|
290
|
+
this.scheduleDestroy = this.scheduleDestroy.bind(this);
|
|
291
|
+
this.onRender = this.onRender.bind(this);
|
|
292
|
+
this.createEditor = this.createEditor.bind(this);
|
|
293
|
+
}
|
|
294
|
+
setEditor(editor) {
|
|
295
|
+
this.editor = editor;
|
|
296
|
+
this.instanceId = Math.random().toString(36).slice(2, 9);
|
|
297
|
+
this.subscriptions.forEach((cb) => cb());
|
|
298
|
+
}
|
|
299
|
+
getInitialEditor() {
|
|
300
|
+
if (this.options.current.immediatelyRender === void 0) {
|
|
301
|
+
if (isSSR || isNext) {
|
|
302
|
+
if (isDev) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
"Editor Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches."
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
return this.createEditor();
|
|
310
|
+
}
|
|
311
|
+
if (this.options.current.immediatelyRender && isSSR && isDev) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
"Editor Error: SSR has been detected, and `immediatelyRender` has been set to `true` this is an unsupported configuration that may result in errors, explicitly set `immediatelyRender` to `false` to avoid hydration mismatches."
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
if (this.options.current.immediatelyRender) {
|
|
317
|
+
return this.createEditor();
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Create a new editor instance. And attach event listeners.
|
|
323
|
+
*/
|
|
324
|
+
createEditor() {
|
|
325
|
+
const optionsToApply = {
|
|
326
|
+
...this.options.current,
|
|
327
|
+
// Always call the most recent version of the callback function by default
|
|
328
|
+
onBeforeCreate: (...args) => {
|
|
329
|
+
var _a, _b;
|
|
330
|
+
return (_b = (_a = this.options.current).onBeforeCreate) == null ? void 0 : _b.call(_a, ...args);
|
|
331
|
+
},
|
|
332
|
+
onBlur: (...args) => {
|
|
333
|
+
var _a, _b;
|
|
334
|
+
return (_b = (_a = this.options.current).onBlur) == null ? void 0 : _b.call(_a, ...args);
|
|
335
|
+
},
|
|
336
|
+
onCreate: (...args) => {
|
|
337
|
+
var _a, _b;
|
|
338
|
+
return (_b = (_a = this.options.current).onCreate) == null ? void 0 : _b.call(_a, ...args);
|
|
339
|
+
},
|
|
340
|
+
onDestroy: (...args) => {
|
|
341
|
+
var _a, _b;
|
|
342
|
+
return (_b = (_a = this.options.current).onDestroy) == null ? void 0 : _b.call(_a, ...args);
|
|
343
|
+
},
|
|
344
|
+
onFocus: (...args) => {
|
|
345
|
+
var _a, _b;
|
|
346
|
+
return (_b = (_a = this.options.current).onFocus) == null ? void 0 : _b.call(_a, ...args);
|
|
347
|
+
},
|
|
348
|
+
onSelectionUpdate: (...args) => {
|
|
349
|
+
var _a, _b;
|
|
350
|
+
return (_b = (_a = this.options.current).onSelectionUpdate) == null ? void 0 : _b.call(_a, ...args);
|
|
351
|
+
},
|
|
352
|
+
onTransaction: (...args) => {
|
|
353
|
+
var _a, _b;
|
|
354
|
+
return (_b = (_a = this.options.current).onTransaction) == null ? void 0 : _b.call(_a, ...args);
|
|
355
|
+
},
|
|
356
|
+
onUpdate: (...args) => {
|
|
357
|
+
var _a, _b;
|
|
358
|
+
return (_b = (_a = this.options.current).onUpdate) == null ? void 0 : _b.call(_a, ...args);
|
|
359
|
+
},
|
|
360
|
+
onContentError: (...args) => {
|
|
361
|
+
var _a, _b;
|
|
362
|
+
return (_b = (_a = this.options.current).onContentError) == null ? void 0 : _b.call(_a, ...args);
|
|
363
|
+
},
|
|
364
|
+
onDrop: (...args) => {
|
|
365
|
+
var _a, _b;
|
|
366
|
+
return (_b = (_a = this.options.current).onDrop) == null ? void 0 : _b.call(_a, ...args);
|
|
367
|
+
},
|
|
368
|
+
onPaste: (...args) => {
|
|
369
|
+
var _a, _b;
|
|
370
|
+
return (_b = (_a = this.options.current).onPaste) == null ? void 0 : _b.call(_a, ...args);
|
|
371
|
+
},
|
|
372
|
+
onDelete: (...args) => {
|
|
373
|
+
var _a, _b;
|
|
374
|
+
return (_b = (_a = this.options.current).onDelete) == null ? void 0 : _b.call(_a, ...args);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
const editor = new Editor(optionsToApply);
|
|
378
|
+
return editor;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get the current editor instance.
|
|
382
|
+
*/
|
|
383
|
+
getEditor() {
|
|
384
|
+
return this.editor;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Always disable the editor on the server-side.
|
|
388
|
+
*/
|
|
389
|
+
getServerSnapshot() {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Subscribe to the editor instance's changes.
|
|
394
|
+
*/
|
|
395
|
+
subscribe(onStoreChange) {
|
|
396
|
+
this.subscriptions.add(onStoreChange);
|
|
397
|
+
return () => {
|
|
398
|
+
this.subscriptions.delete(onStoreChange);
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
static compareOptions(a, b) {
|
|
402
|
+
return Object.keys(a).every((key) => {
|
|
403
|
+
if ([
|
|
404
|
+
"onCreate",
|
|
405
|
+
"onBeforeCreate",
|
|
406
|
+
"onDestroy",
|
|
407
|
+
"onUpdate",
|
|
408
|
+
"onTransaction",
|
|
409
|
+
"onFocus",
|
|
410
|
+
"onBlur",
|
|
411
|
+
"onSelectionUpdate",
|
|
412
|
+
"onContentError",
|
|
413
|
+
"onDrop",
|
|
414
|
+
"onPaste"
|
|
415
|
+
].includes(key)) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
if (key === "extensions" && a.extensions && b.extensions) {
|
|
419
|
+
if (a.extensions.length !== b.extensions.length) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return a.extensions.every((extension, index) => {
|
|
423
|
+
var _a;
|
|
424
|
+
if (extension !== ((_a = b.extensions) == null ? void 0 : _a[index])) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
return true;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (a[key] !== b[key]) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* On each render, we will create, update, or destroy the editor instance.
|
|
438
|
+
* @param deps The dependencies to watch for changes
|
|
439
|
+
* @returns A cleanup function
|
|
440
|
+
*/
|
|
441
|
+
onRender(deps) {
|
|
442
|
+
return () => {
|
|
443
|
+
this.isComponentMounted = true;
|
|
444
|
+
clearTimeout(this.scheduledDestructionTimeout);
|
|
445
|
+
if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
|
|
446
|
+
if (!_EditorInstanceManager.compareOptions(
|
|
447
|
+
this.options.current,
|
|
448
|
+
this.editor.options
|
|
449
|
+
)) {
|
|
450
|
+
this.editor.setOptions({
|
|
451
|
+
...this.options.current,
|
|
452
|
+
editable: this.editor.isEditable
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
this.refreshEditorInstance(deps);
|
|
457
|
+
}
|
|
458
|
+
return () => {
|
|
459
|
+
this.isComponentMounted = false;
|
|
460
|
+
this.scheduleDestroy();
|
|
461
|
+
};
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Recreate the editor instance if the dependencies have changed.
|
|
466
|
+
*/
|
|
467
|
+
refreshEditorInstance(deps) {
|
|
468
|
+
if (this.editor && !this.editor.isDestroyed) {
|
|
469
|
+
if (this.previousDeps === null) {
|
|
470
|
+
this.previousDeps = deps;
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const depsAreEqual = this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index]);
|
|
474
|
+
if (depsAreEqual) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (this.editor && !this.editor.isDestroyed) {
|
|
479
|
+
this.editor.destroy();
|
|
480
|
+
}
|
|
481
|
+
this.setEditor(this.createEditor());
|
|
482
|
+
this.previousDeps = deps;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Schedule the destruction of the editor instance.
|
|
486
|
+
* This will only destroy the editor if it was not mounted on the next tick.
|
|
487
|
+
* This is to avoid destroying the editor instance when it's actually still mounted.
|
|
488
|
+
*/
|
|
489
|
+
scheduleDestroy() {
|
|
490
|
+
const currentInstanceId = this.instanceId;
|
|
491
|
+
const currentEditor = this.editor;
|
|
492
|
+
this.scheduledDestructionTimeout = setTimeout(() => {
|
|
493
|
+
if (this.isComponentMounted && this.instanceId === currentInstanceId) {
|
|
494
|
+
if (currentEditor) {
|
|
495
|
+
currentEditor.setOptions(this.options.current);
|
|
496
|
+
}
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (currentEditor && !currentEditor.isDestroyed) {
|
|
500
|
+
currentEditor.destroy();
|
|
501
|
+
if (this.instanceId === currentInstanceId) {
|
|
502
|
+
this.setEditor(null);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}, 1);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
function useEditor(options = {}, deps = []) {
|
|
509
|
+
const mostRecentOptions = useRef(options);
|
|
510
|
+
mostRecentOptions.current = options;
|
|
511
|
+
const [instanceManager] = useState2(
|
|
512
|
+
() => new EditorInstanceManager(mostRecentOptions)
|
|
513
|
+
);
|
|
514
|
+
const editor = useSyncExternalStore2(
|
|
515
|
+
instanceManager.subscribe,
|
|
516
|
+
instanceManager.getEditor,
|
|
517
|
+
instanceManager.getServerSnapshot
|
|
518
|
+
);
|
|
519
|
+
useDebugValue2(editor);
|
|
520
|
+
useEffect2(instanceManager.onRender(deps));
|
|
521
|
+
useEditorState({
|
|
522
|
+
editor,
|
|
523
|
+
selector: ({ transactionNumber }) => {
|
|
524
|
+
if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === void 0) {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
if (options.immediatelyRender && transactionNumber === 0) {
|
|
528
|
+
return 0;
|
|
529
|
+
}
|
|
530
|
+
return transactionNumber + 1;
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
return editor;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// src/Context.tsx
|
|
537
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
538
|
+
var EditorContext = createContext({
|
|
539
|
+
editor: null
|
|
540
|
+
});
|
|
541
|
+
var EditorConsumer = EditorContext.Consumer;
|
|
542
|
+
var useCurrentEditor = () => useContext(EditorContext);
|
|
543
|
+
function EditorProvider({
|
|
544
|
+
children,
|
|
545
|
+
slotAfter,
|
|
546
|
+
slotBefore,
|
|
547
|
+
editorContainerProps = {},
|
|
548
|
+
...editorOptions
|
|
549
|
+
}) {
|
|
550
|
+
const editor = useEditor(editorOptions);
|
|
551
|
+
const contextValue = useMemo(() => ({ editor }), [editor]);
|
|
552
|
+
if (!editor) {
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
return /* @__PURE__ */ jsxs2(EditorContext.Provider, { value: contextValue, children: [
|
|
556
|
+
slotBefore,
|
|
557
|
+
/* @__PURE__ */ jsx2(EditorConsumer, { children: ({ editor: currentEditor }) => /* @__PURE__ */ jsx2(EditorContent, { editor: currentEditor, ...editorContainerProps }) }),
|
|
558
|
+
children,
|
|
559
|
+
slotAfter
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// src/useReactNodeView.ts
|
|
564
|
+
import { createContext as createContext2, createElement, useContext as useContext2 } from "react";
|
|
565
|
+
var ReactNodeViewContext = createContext2({
|
|
566
|
+
onDragStart: () => {
|
|
567
|
+
},
|
|
568
|
+
nodeViewContentChildren: void 0,
|
|
569
|
+
nodeViewContentRef: () => {
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
var ReactNodeViewContentProvider = ({ children, content }) => {
|
|
573
|
+
return createElement(ReactNodeViewContext.Provider, { value: { nodeViewContentChildren: content } }, children);
|
|
574
|
+
};
|
|
575
|
+
var useReactNodeView = () => useContext2(ReactNodeViewContext);
|
|
576
|
+
|
|
577
|
+
// src/NodeViewContent.tsx
|
|
578
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
579
|
+
function NodeViewContent({
|
|
580
|
+
as: Tag = "div",
|
|
581
|
+
...props
|
|
582
|
+
}) {
|
|
583
|
+
const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView();
|
|
584
|
+
return (
|
|
585
|
+
// @ts-ignore
|
|
586
|
+
/* @__PURE__ */ jsx3(
|
|
587
|
+
Tag,
|
|
588
|
+
{
|
|
589
|
+
...props,
|
|
590
|
+
ref: nodeViewContentRef,
|
|
591
|
+
"data-node-view-content": "",
|
|
592
|
+
style: {
|
|
593
|
+
whiteSpace: "pre-wrap",
|
|
594
|
+
...props.style
|
|
595
|
+
},
|
|
596
|
+
children: nodeViewContentChildren
|
|
597
|
+
}
|
|
598
|
+
)
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/NodeViewWrapper.tsx
|
|
603
|
+
import React3 from "react";
|
|
604
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
605
|
+
var NodeViewWrapper = React3.forwardRef((props, ref) => {
|
|
606
|
+
const { onDragStart } = useReactNodeView();
|
|
607
|
+
const Tag = props.as || "div";
|
|
608
|
+
return (
|
|
609
|
+
// @ts-ignore
|
|
610
|
+
/* @__PURE__ */ jsx4(
|
|
611
|
+
Tag,
|
|
612
|
+
{
|
|
613
|
+
...props,
|
|
614
|
+
ref,
|
|
615
|
+
"data-node-view-wrapper": "",
|
|
616
|
+
onDragStart,
|
|
617
|
+
style: {
|
|
618
|
+
whiteSpace: "normal",
|
|
619
|
+
...props.style
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
)
|
|
623
|
+
);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// src/ReactMarkViewRenderer.tsx
|
|
627
|
+
import { MarkView } from "@blockslides/core";
|
|
628
|
+
import React4 from "react";
|
|
629
|
+
|
|
630
|
+
// src/ReactRenderer.tsx
|
|
631
|
+
import { version as reactVersion } from "react";
|
|
632
|
+
import { flushSync } from "react-dom";
|
|
633
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
634
|
+
function isClassComponent(Component) {
|
|
635
|
+
return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent);
|
|
636
|
+
}
|
|
637
|
+
function isForwardRefComponent(Component) {
|
|
638
|
+
return !!(typeof Component === "object" && Component.$$typeof && (Component.$$typeof.toString() === "Symbol(react.forward_ref)" || Component.$$typeof.description === "react.forward_ref"));
|
|
639
|
+
}
|
|
640
|
+
function isMemoComponent(Component) {
|
|
641
|
+
return !!(typeof Component === "object" && Component.$$typeof && (Component.$$typeof.toString() === "Symbol(react.memo)" || Component.$$typeof.description === "react.memo"));
|
|
642
|
+
}
|
|
643
|
+
function canReceiveRef(Component) {
|
|
644
|
+
if (isClassComponent(Component)) {
|
|
645
|
+
return true;
|
|
646
|
+
}
|
|
647
|
+
if (isForwardRefComponent(Component)) {
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
if (isMemoComponent(Component)) {
|
|
651
|
+
const wrappedComponent = Component.type;
|
|
652
|
+
if (wrappedComponent) {
|
|
653
|
+
return isClassComponent(wrappedComponent) || isForwardRefComponent(wrappedComponent);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
function isReact19Plus() {
|
|
659
|
+
try {
|
|
660
|
+
if (reactVersion) {
|
|
661
|
+
const majorVersion = parseInt(reactVersion.split(".")[0], 10);
|
|
662
|
+
return majorVersion >= 19;
|
|
663
|
+
}
|
|
664
|
+
} catch {
|
|
665
|
+
}
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
var ReactRenderer = class {
|
|
669
|
+
/**
|
|
670
|
+
* Immediately creates element and renders the provided React component.
|
|
671
|
+
*/
|
|
672
|
+
constructor(component, { editor, props = {}, as = "div", className = "" }) {
|
|
673
|
+
this.ref = null;
|
|
674
|
+
this.id = Math.floor(Math.random() * 4294967295).toString();
|
|
675
|
+
this.component = component;
|
|
676
|
+
this.editor = editor;
|
|
677
|
+
this.props = props;
|
|
678
|
+
this.element = document.createElement(as);
|
|
679
|
+
this.element.classList.add("react-renderer");
|
|
680
|
+
if (className) {
|
|
681
|
+
this.element.classList.add(...className.split(" "));
|
|
682
|
+
}
|
|
683
|
+
if (this.editor.isInitialized) {
|
|
684
|
+
flushSync(() => {
|
|
685
|
+
this.render();
|
|
686
|
+
});
|
|
687
|
+
} else {
|
|
688
|
+
queueMicrotask(() => {
|
|
689
|
+
this.render();
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Render the React component.
|
|
695
|
+
*/
|
|
696
|
+
render() {
|
|
697
|
+
var _a;
|
|
698
|
+
const Component = this.component;
|
|
699
|
+
const props = this.props;
|
|
700
|
+
const editor = this.editor;
|
|
701
|
+
const isReact19 = isReact19Plus();
|
|
702
|
+
const componentCanReceiveRef = canReceiveRef(Component);
|
|
703
|
+
const elementProps = { ...props };
|
|
704
|
+
if (elementProps.ref && !(isReact19 || componentCanReceiveRef)) {
|
|
705
|
+
delete elementProps.ref;
|
|
706
|
+
}
|
|
707
|
+
if (!elementProps.ref && (isReact19 || componentCanReceiveRef)) {
|
|
708
|
+
elementProps.ref = (ref) => {
|
|
709
|
+
this.ref = ref;
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
this.reactElement = /* @__PURE__ */ jsx5(Component, { ...elementProps });
|
|
713
|
+
(_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.setRenderer(this.id, this);
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Re-renders the React component with new props.
|
|
717
|
+
*/
|
|
718
|
+
updateProps(props = {}) {
|
|
719
|
+
this.props = {
|
|
720
|
+
...this.props,
|
|
721
|
+
...props
|
|
722
|
+
};
|
|
723
|
+
this.render();
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Destroy the React component.
|
|
727
|
+
*/
|
|
728
|
+
destroy() {
|
|
729
|
+
var _a;
|
|
730
|
+
const editor = this.editor;
|
|
731
|
+
(_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.removeRenderer(this.id);
|
|
732
|
+
try {
|
|
733
|
+
if (this.element && this.element.parentNode) {
|
|
734
|
+
this.element.parentNode.removeChild(this.element);
|
|
735
|
+
}
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Update the attributes of the element that holds the React component.
|
|
741
|
+
*/
|
|
742
|
+
updateAttributes(attributes) {
|
|
743
|
+
Object.keys(attributes).forEach((key) => {
|
|
744
|
+
this.element.setAttribute(key, attributes[key]);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
// src/ReactMarkViewRenderer.tsx
|
|
750
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
751
|
+
var ReactMarkViewContext = React4.createContext({
|
|
752
|
+
markViewContentRef: () => {
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
var MarkViewContent = (props) => {
|
|
756
|
+
const { as: Tag = "span", ...rest } = props;
|
|
757
|
+
const { markViewContentRef } = React4.useContext(ReactMarkViewContext);
|
|
758
|
+
return (
|
|
759
|
+
// @ts-ignore
|
|
760
|
+
/* @__PURE__ */ jsx6(Tag, { ...rest, ref: markViewContentRef, "data-mark-view-content": "" })
|
|
761
|
+
);
|
|
762
|
+
};
|
|
763
|
+
var ReactMarkView = class extends MarkView {
|
|
764
|
+
constructor(component, props, options) {
|
|
765
|
+
super(component, props, options);
|
|
766
|
+
const { as = "span", attrs, className = "" } = options || {};
|
|
767
|
+
const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) };
|
|
768
|
+
this.contentDOMElement = document.createElement("span");
|
|
769
|
+
const markViewContentRef = (el) => {
|
|
770
|
+
if (el && !el.contains(this.contentDOMElement)) {
|
|
771
|
+
el.appendChild(this.contentDOMElement);
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
const context = {
|
|
775
|
+
markViewContentRef
|
|
776
|
+
};
|
|
777
|
+
const ReactMarkViewProvider = React4.memo((componentProps2) => {
|
|
778
|
+
return /* @__PURE__ */ jsx6(ReactMarkViewContext.Provider, { value: context, children: React4.createElement(component, componentProps2) });
|
|
779
|
+
});
|
|
780
|
+
ReactMarkViewProvider.displayName = "ReactMarkView";
|
|
781
|
+
this.renderer = new ReactRenderer(ReactMarkViewProvider, {
|
|
782
|
+
editor: props.editor,
|
|
783
|
+
props: componentProps,
|
|
784
|
+
as,
|
|
785
|
+
className: `mark-${props.mark.type.name} ${className}`.trim()
|
|
786
|
+
});
|
|
787
|
+
if (attrs) {
|
|
788
|
+
this.renderer.updateAttributes(attrs);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
get dom() {
|
|
792
|
+
return this.renderer.element;
|
|
793
|
+
}
|
|
794
|
+
get contentDOM() {
|
|
795
|
+
return this.contentDOMElement;
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
function ReactMarkViewRenderer(component, options = {}) {
|
|
799
|
+
return (props) => new ReactMarkView(component, props, options);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// src/ReactNodeViewRenderer.tsx
|
|
803
|
+
import { getRenderedAttributes, NodeView } from "@blockslides/core";
|
|
804
|
+
import { createElement as createElement2, createRef, memo } from "react";
|
|
805
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
806
|
+
var ReactNodeView = class extends NodeView {
|
|
807
|
+
constructor(component, props, options) {
|
|
808
|
+
super(component, props, options);
|
|
809
|
+
if (!this.node.isLeaf) {
|
|
810
|
+
if (this.options.contentDOMElementTag) {
|
|
811
|
+
this.contentDOMElement = document.createElement(this.options.contentDOMElementTag);
|
|
812
|
+
} else {
|
|
813
|
+
this.contentDOMElement = document.createElement(this.node.isInline ? "span" : "div");
|
|
814
|
+
}
|
|
815
|
+
this.contentDOMElement.dataset.nodeViewContentReact = "";
|
|
816
|
+
this.contentDOMElement.dataset.nodeViewWrapper = "";
|
|
817
|
+
this.contentDOMElement.style.whiteSpace = "inherit";
|
|
818
|
+
const contentTarget = this.dom.querySelector("[data-node-view-content]");
|
|
819
|
+
if (!contentTarget) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
contentTarget.appendChild(this.contentDOMElement);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Setup the React component.
|
|
827
|
+
* Called on initialization.
|
|
828
|
+
*/
|
|
829
|
+
mount() {
|
|
830
|
+
const props = {
|
|
831
|
+
editor: this.editor,
|
|
832
|
+
node: this.node,
|
|
833
|
+
decorations: this.decorations,
|
|
834
|
+
innerDecorations: this.innerDecorations,
|
|
835
|
+
view: this.view,
|
|
836
|
+
selected: false,
|
|
837
|
+
extension: this.extension,
|
|
838
|
+
HTMLAttributes: this.HTMLAttributes,
|
|
839
|
+
getPos: () => this.getPos(),
|
|
840
|
+
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
|
|
841
|
+
deleteNode: () => this.deleteNode(),
|
|
842
|
+
ref: createRef()
|
|
843
|
+
};
|
|
844
|
+
if (!this.component.displayName) {
|
|
845
|
+
const capitalizeFirstChar = (string) => {
|
|
846
|
+
return string.charAt(0).toUpperCase() + string.substring(1);
|
|
847
|
+
};
|
|
848
|
+
this.component.displayName = capitalizeFirstChar(this.extension.name);
|
|
849
|
+
}
|
|
850
|
+
const onDragStart = this.onDragStart.bind(this);
|
|
851
|
+
const nodeViewContentRef = (element) => {
|
|
852
|
+
if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
|
|
853
|
+
if (element.hasAttribute("data-node-view-wrapper")) {
|
|
854
|
+
element.removeAttribute("data-node-view-wrapper");
|
|
855
|
+
}
|
|
856
|
+
element.appendChild(this.contentDOMElement);
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
const context = { onDragStart, nodeViewContentRef };
|
|
860
|
+
const Component = this.component;
|
|
861
|
+
const ReactNodeViewProvider = memo((componentProps) => {
|
|
862
|
+
return /* @__PURE__ */ jsx7(ReactNodeViewContext.Provider, { value: context, children: createElement2(Component, componentProps) });
|
|
863
|
+
});
|
|
864
|
+
ReactNodeViewProvider.displayName = "ReactNodeView";
|
|
865
|
+
let as = this.node.isInline ? "span" : "div";
|
|
866
|
+
if (this.options.as) {
|
|
867
|
+
as = this.options.as;
|
|
868
|
+
}
|
|
869
|
+
const { className = "" } = this.options;
|
|
870
|
+
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
|
|
871
|
+
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
|
|
872
|
+
editor: this.editor,
|
|
873
|
+
props,
|
|
874
|
+
as,
|
|
875
|
+
className: `node-${this.node.type.name} ${className}`.trim()
|
|
876
|
+
});
|
|
877
|
+
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
|
|
878
|
+
this.updateElementAttributes();
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Return the DOM element.
|
|
882
|
+
* This is the element that will be used to display the node view.
|
|
883
|
+
*/
|
|
884
|
+
get dom() {
|
|
885
|
+
var _a;
|
|
886
|
+
if (this.renderer.element.firstElementChild && !((_a = this.renderer.element.firstElementChild) == null ? void 0 : _a.hasAttribute("data-node-view-wrapper"))) {
|
|
887
|
+
throw Error("Please use the NodeViewWrapper component for your node view.");
|
|
888
|
+
}
|
|
889
|
+
return this.renderer.element;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Return the content DOM element.
|
|
893
|
+
* This is the element that will be used to display the rich-text content of the node.
|
|
894
|
+
*/
|
|
895
|
+
get contentDOM() {
|
|
896
|
+
if (this.node.isLeaf) {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
return this.contentDOMElement;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* On editor selection update, check if the node is selected.
|
|
903
|
+
* If it is, call `selectNode`, otherwise call `deselectNode`.
|
|
904
|
+
*/
|
|
905
|
+
handleSelectionUpdate() {
|
|
906
|
+
const { from, to } = this.editor.state.selection;
|
|
907
|
+
const pos = this.getPos();
|
|
908
|
+
if (typeof pos !== "number") {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
if (from <= pos && to >= pos + this.node.nodeSize) {
|
|
912
|
+
if (this.renderer.props.selected) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
this.selectNode();
|
|
916
|
+
} else {
|
|
917
|
+
if (!this.renderer.props.selected) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
this.deselectNode();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* On update, update the React component.
|
|
925
|
+
* To prevent unnecessary updates, the `update` option can be used.
|
|
926
|
+
*/
|
|
927
|
+
update(node, decorations, innerDecorations) {
|
|
928
|
+
const rerenderComponent = (props) => {
|
|
929
|
+
this.renderer.updateProps(props);
|
|
930
|
+
if (typeof this.options.attrs === "function") {
|
|
931
|
+
this.updateElementAttributes();
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
if (node.type !== this.node.type) {
|
|
935
|
+
return false;
|
|
936
|
+
}
|
|
937
|
+
if (typeof this.options.update === "function") {
|
|
938
|
+
const oldNode = this.node;
|
|
939
|
+
const oldDecorations = this.decorations;
|
|
940
|
+
const oldInnerDecorations = this.innerDecorations;
|
|
941
|
+
this.node = node;
|
|
942
|
+
this.decorations = decorations;
|
|
943
|
+
this.innerDecorations = innerDecorations;
|
|
944
|
+
return this.options.update({
|
|
945
|
+
oldNode,
|
|
946
|
+
oldDecorations,
|
|
947
|
+
newNode: node,
|
|
948
|
+
newDecorations: decorations,
|
|
949
|
+
oldInnerDecorations,
|
|
950
|
+
innerDecorations,
|
|
951
|
+
updateProps: () => rerenderComponent({ node, decorations, innerDecorations })
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
this.node = node;
|
|
958
|
+
this.decorations = decorations;
|
|
959
|
+
this.innerDecorations = innerDecorations;
|
|
960
|
+
rerenderComponent({ node, decorations, innerDecorations });
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Select the node.
|
|
965
|
+
* Add the `selected` prop and the `ProseMirror-selectednode` class.
|
|
966
|
+
*/
|
|
967
|
+
selectNode() {
|
|
968
|
+
this.renderer.updateProps({
|
|
969
|
+
selected: true
|
|
970
|
+
});
|
|
971
|
+
this.renderer.element.classList.add("ProseMirror-selectednode");
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Deselect the node.
|
|
975
|
+
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
|
|
976
|
+
*/
|
|
977
|
+
deselectNode() {
|
|
978
|
+
this.renderer.updateProps({
|
|
979
|
+
selected: false
|
|
980
|
+
});
|
|
981
|
+
this.renderer.element.classList.remove("ProseMirror-selectednode");
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Destroy the React component instance.
|
|
985
|
+
*/
|
|
986
|
+
destroy() {
|
|
987
|
+
this.renderer.destroy();
|
|
988
|
+
this.editor.off("selectionUpdate", this.handleSelectionUpdate);
|
|
989
|
+
this.contentDOMElement = null;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Update the attributes of the top-level element that holds the React component.
|
|
993
|
+
* Applying the attributes defined in the `attrs` option.
|
|
994
|
+
*/
|
|
995
|
+
updateElementAttributes() {
|
|
996
|
+
if (this.options.attrs) {
|
|
997
|
+
let attrsObj = {};
|
|
998
|
+
if (typeof this.options.attrs === "function") {
|
|
999
|
+
const extensionAttributes = this.editor.extensionManager.attributes;
|
|
1000
|
+
const HTMLAttributes = getRenderedAttributes(this.node, extensionAttributes);
|
|
1001
|
+
attrsObj = this.options.attrs({ node: this.node, HTMLAttributes });
|
|
1002
|
+
} else {
|
|
1003
|
+
attrsObj = this.options.attrs;
|
|
1004
|
+
}
|
|
1005
|
+
this.renderer.updateAttributes(attrsObj);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
function ReactNodeViewRenderer(component, options) {
|
|
1010
|
+
return (props) => {
|
|
1011
|
+
if (!props.editor.contentComponent) {
|
|
1012
|
+
return {};
|
|
1013
|
+
}
|
|
1014
|
+
return new ReactNodeView(component, props, options);
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// src/index.ts
|
|
1019
|
+
export * from "@blockslides/core";
|
|
1020
|
+
export {
|
|
1021
|
+
EditorConsumer,
|
|
1022
|
+
EditorContent,
|
|
1023
|
+
EditorContext,
|
|
1024
|
+
EditorProvider,
|
|
1025
|
+
MarkViewContent,
|
|
1026
|
+
NodeViewContent,
|
|
1027
|
+
NodeViewWrapper,
|
|
1028
|
+
PureEditorContent,
|
|
1029
|
+
ReactMarkView,
|
|
1030
|
+
ReactMarkViewContext,
|
|
1031
|
+
ReactMarkViewRenderer,
|
|
1032
|
+
ReactNodeView,
|
|
1033
|
+
ReactNodeViewContentProvider,
|
|
1034
|
+
ReactNodeViewContext,
|
|
1035
|
+
ReactNodeViewRenderer,
|
|
1036
|
+
ReactRenderer,
|
|
1037
|
+
useCurrentEditor,
|
|
1038
|
+
useEditor,
|
|
1039
|
+
useEditorState,
|
|
1040
|
+
useReactNodeView
|
|
1041
|
+
};
|
|
1042
|
+
//# sourceMappingURL=index.js.map
|