@stesura/react-prosemirror 3.2.11-stesura.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/LICENSE.txt +21 -0
- package/README.md +939 -0
- package/dist/cjs/AbstractEditorView.js +4 -0
- package/dist/cjs/ReactEditorView.js +194 -0
- package/dist/cjs/StaticEditorView.js +89 -0
- package/dist/cjs/browser.js +53 -0
- package/dist/cjs/commands/reorderSiblings.js +72 -0
- package/dist/cjs/components/ChildNodeViews.js +418 -0
- package/dist/cjs/components/CursorWrapper.js +84 -0
- package/dist/cjs/components/EditorStateSelectorsProvider.js +90 -0
- package/dist/cjs/components/LayoutGroup.js +112 -0
- package/dist/cjs/components/NativeWidgetView.js +108 -0
- package/dist/cjs/components/OutputSpec.js +121 -0
- package/dist/cjs/components/ProseMirror.js +146 -0
- package/dist/cjs/components/ProseMirrorDoc.js +74 -0
- package/dist/cjs/components/SeparatorHackView.js +99 -0
- package/dist/cjs/components/TextNodeView.js +113 -0
- package/dist/cjs/components/TrailingHackView.js +87 -0
- package/dist/cjs/components/WidgetView.js +94 -0
- package/dist/cjs/components/WidgetViewComponentProps.js +4 -0
- package/dist/cjs/components/marks/DefaultMarkView.js +70 -0
- package/dist/cjs/components/marks/MarkView.js +85 -0
- package/dist/cjs/components/marks/MarkViewComponentProps.js +4 -0
- package/dist/cjs/components/marks/MarkViewConstructorView.js +116 -0
- package/dist/cjs/components/marks/ReactMarkView.js +109 -0
- package/dist/cjs/components/nodes/DefaultNodeView.js +68 -0
- package/dist/cjs/components/nodes/DocNodeView.js +97 -0
- package/dist/cjs/components/nodes/NodeView.js +129 -0
- package/dist/cjs/components/nodes/NodeViewComponentProps.js +4 -0
- package/dist/cjs/components/nodes/NodeViewConstructorView.js +145 -0
- package/dist/cjs/components/nodes/ReactNodeView.js +189 -0
- package/dist/cjs/constants.js +33 -0
- package/dist/cjs/contexts/ChildDescriptionsContext.js +19 -0
- package/dist/cjs/contexts/EditorContext.js +12 -0
- package/dist/cjs/contexts/EditorStateContext.js +12 -0
- package/dist/cjs/contexts/EditorStateStoreContext.js +45 -0
- package/dist/cjs/contexts/IgnoreMutationContext.js +12 -0
- package/dist/cjs/contexts/LayoutGroupContext.js +12 -0
- package/dist/cjs/contexts/NodeViewContext.js +12 -0
- package/dist/cjs/contexts/RenderStoreContext.js +54 -0
- package/dist/cjs/contexts/SelectNodeContext.js +12 -0
- package/dist/cjs/contexts/StopEventContext.js +12 -0
- package/dist/cjs/decorations/ReactWidgetType.js +58 -0
- package/dist/cjs/decorations/computeDocDeco.js +44 -0
- package/dist/cjs/decorations/internalTypes.js +4 -0
- package/dist/cjs/decorations/iterDeco.js +91 -0
- package/dist/cjs/decorations/viewDecorations.js +155 -0
- package/dist/cjs/dom.js +147 -0
- package/dist/cjs/findDOMNode.js +46 -0
- package/dist/cjs/hooks/useClientLayoutEffect.js +19 -0
- package/dist/cjs/hooks/useComponentEventListeners.js +72 -0
- package/dist/cjs/hooks/useEditor.js +130 -0
- package/dist/cjs/hooks/useEditorEffect.js +39 -0
- package/dist/cjs/hooks/useEditorEventCallback.js +38 -0
- package/dist/cjs/hooks/useEditorEventListener.js +34 -0
- package/dist/cjs/hooks/useEditorState.js +18 -0
- package/dist/cjs/hooks/useEditorStateSelector.js +17 -0
- package/dist/cjs/hooks/useEffectEvent.js +34 -0
- package/dist/cjs/hooks/useForceUpdate.js +15 -0
- package/dist/cjs/hooks/useIgnoreMutation.js +24 -0
- package/dist/cjs/hooks/useIsComposingIn.js +25 -0
- package/dist/cjs/hooks/useIsEditorStatic.js +15 -0
- package/dist/cjs/hooks/useIsNodeSelected.js +21 -0
- package/dist/cjs/hooks/useLayoutGroupEffect.js +20 -0
- package/dist/cjs/hooks/useMarkViewDescription.js +134 -0
- package/dist/cjs/hooks/useNodePos.js +18 -0
- package/dist/cjs/hooks/useNodeViewDescription.js +154 -0
- package/dist/cjs/hooks/useReactKeys.js +17 -0
- package/dist/cjs/hooks/useSelectNode.js +30 -0
- package/dist/cjs/hooks/useSetDom.js +57 -0
- package/dist/cjs/hooks/useStopEvent.js +24 -0
- package/dist/cjs/index.js +85 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plugins/beforeInputPlugin.js +356 -0
- package/dist/cjs/plugins/reactKeys.js +121 -0
- package/dist/cjs/props.js +283 -0
- package/dist/cjs/refs.js +28 -0
- package/dist/cjs/testing/editorViewTestHelpers.js +109 -0
- package/dist/cjs/testing/setupProseMirrorView.js +94 -0
- package/dist/cjs/tiptap/ReactProseMirrorNodeView.js +26 -0
- package/dist/cjs/tiptap/TiptapEditor.js +34 -0
- package/dist/cjs/tiptap/TiptapEditorContent.js +143 -0
- package/dist/cjs/tiptap/TiptapEditorView.js +130 -0
- package/dist/cjs/tiptap/contexts/TiptapEditorContext.js +12 -0
- package/dist/cjs/tiptap/extensions/ReactProseMirror.js +40 -0
- package/dist/cjs/tiptap/extensions/ReactProseMirrorCommands.js +21 -0
- package/dist/cjs/tiptap/extensions/commands/updateAttributes.js +104 -0
- package/dist/cjs/tiptap/hooks/useEditor.js +318 -0
- package/dist/cjs/tiptap/hooks/useIsInReactProseMirror.js +15 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditor.js +35 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +35 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +35 -0
- package/dist/cjs/tiptap/index.js +48 -0
- package/dist/cjs/tiptap/tiptapNodeView.js +245 -0
- package/dist/cjs/viewdesc.js +862 -0
- package/dist/esm/AbstractEditorView.js +1 -0
- package/dist/esm/ReactEditorView.js +194 -0
- package/dist/esm/StaticEditorView.js +79 -0
- package/dist/esm/browser.js +43 -0
- package/dist/esm/commands/reorderSiblings.js +63 -0
- package/dist/esm/components/ChildNodeViews.js +359 -0
- package/dist/esm/components/CursorWrapper.js +33 -0
- package/dist/esm/components/EditorStateSelectorsProvider.js +31 -0
- package/dist/esm/components/LayoutGroup.js +67 -0
- package/dist/esm/components/NativeWidgetView.js +57 -0
- package/dist/esm/components/OutputSpec.js +71 -0
- package/dist/esm/components/ProseMirror.js +95 -0
- package/dist/esm/components/ProseMirrorDoc.js +15 -0
- package/dist/esm/components/SeparatorHackView.js +48 -0
- package/dist/esm/components/TextNodeView.js +103 -0
- package/dist/esm/components/TrailingHackView.js +36 -0
- package/dist/esm/components/WidgetView.js +43 -0
- package/dist/esm/components/WidgetViewComponentProps.js +1 -0
- package/dist/esm/components/marks/DefaultMarkView.js +19 -0
- package/dist/esm/components/marks/MarkView.js +34 -0
- package/dist/esm/components/marks/MarkViewComponentProps.js +1 -0
- package/dist/esm/components/marks/MarkViewConstructorView.js +65 -0
- package/dist/esm/components/marks/ReactMarkView.js +58 -0
- package/dist/esm/components/nodes/DefaultNodeView.js +17 -0
- package/dist/esm/components/nodes/DocNodeView.js +46 -0
- package/dist/esm/components/nodes/NodeView.js +67 -0
- package/dist/esm/components/nodes/NodeViewComponentProps.js +1 -0
- package/dist/esm/components/nodes/NodeViewConstructorView.js +94 -0
- package/dist/esm/components/nodes/ReactNodeView.js +138 -0
- package/dist/esm/constants.js +15 -0
- package/dist/esm/contexts/ChildDescriptionsContext.js +9 -0
- package/dist/esm/contexts/EditorContext.js +7 -0
- package/dist/esm/contexts/EditorStateContext.js +2 -0
- package/dist/esm/contexts/EditorStateStoreContext.js +27 -0
- package/dist/esm/contexts/IgnoreMutationContext.js +2 -0
- package/dist/esm/contexts/LayoutGroupContext.js +2 -0
- package/dist/esm/contexts/NodeViewContext.js +2 -0
- package/dist/esm/contexts/RenderStoreContext.js +33 -0
- package/dist/esm/contexts/SelectNodeContext.js +2 -0
- package/dist/esm/contexts/StopEventContext.js +2 -0
- package/dist/esm/decorations/ReactWidgetType.js +40 -0
- package/dist/esm/decorations/computeDocDeco.js +44 -0
- package/dist/esm/decorations/internalTypes.js +1 -0
- package/dist/esm/decorations/iterDeco.js +85 -0
- package/dist/esm/decorations/viewDecorations.js +155 -0
- package/dist/esm/dom.js +110 -0
- package/dist/esm/findDOMNode.js +31 -0
- package/dist/esm/hooks/useClientLayoutEffect.js +9 -0
- package/dist/esm/hooks/useComponentEventListeners.js +87 -0
- package/dist/esm/hooks/useEditor.js +128 -0
- package/dist/esm/hooks/useEditorEffect.js +46 -0
- package/dist/esm/hooks/useEditorEventCallback.js +38 -0
- package/dist/esm/hooks/useEditorEventListener.js +28 -0
- package/dist/esm/hooks/useEditorState.js +10 -0
- package/dist/esm/hooks/useEditorStateSelector.js +14 -0
- package/dist/esm/hooks/useEffectEvent.js +24 -0
- package/dist/esm/hooks/useForceUpdate.js +8 -0
- package/dist/esm/hooks/useIgnoreMutation.js +14 -0
- package/dist/esm/hooks/useIsComposingIn.js +17 -0
- package/dist/esm/hooks/useIsEditorStatic.js +8 -0
- package/dist/esm/hooks/useIsNodeSelected.js +11 -0
- package/dist/esm/hooks/useLayoutGroupEffect.js +10 -0
- package/dist/esm/hooks/useMarkViewDescription.js +124 -0
- package/dist/esm/hooks/useNodePos.js +15 -0
- package/dist/esm/hooks/useNodeViewDescription.js +144 -0
- package/dist/esm/hooks/useReactKeys.js +7 -0
- package/dist/esm/hooks/useSelectNode.js +20 -0
- package/dist/esm/hooks/useSetDom.js +47 -0
- package/dist/esm/hooks/useStopEvent.js +14 -0
- package/dist/esm/index.js +19 -0
- package/dist/esm/plugins/beforeInputPlugin.js +346 -0
- package/dist/esm/plugins/reactKeys.js +106 -0
- package/dist/esm/props.js +267 -0
- package/dist/esm/refs.js +18 -0
- package/dist/esm/testing/editorViewTestHelpers.js +86 -0
- package/dist/esm/testing/setupProseMirrorView.js +76 -0
- package/dist/esm/tiptap/ReactProseMirrorNodeView.js +22 -0
- package/dist/esm/tiptap/TiptapEditor.js +24 -0
- package/dist/esm/tiptap/TiptapEditorContent.js +99 -0
- package/dist/esm/tiptap/TiptapEditorView.js +76 -0
- package/dist/esm/tiptap/contexts/TiptapEditorContext.js +2 -0
- package/dist/esm/tiptap/extensions/ReactProseMirror.js +30 -0
- package/dist/esm/tiptap/extensions/ReactProseMirrorCommands.js +11 -0
- package/dist/esm/tiptap/extensions/commands/updateAttributes.js +96 -0
- package/dist/esm/tiptap/hooks/useEditor.js +308 -0
- package/dist/esm/tiptap/hooks/useIsInReactProseMirror.js +9 -0
- package/dist/esm/tiptap/hooks/useTiptapEditor.js +31 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +42 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +35 -0
- package/dist/esm/tiptap/index.js +9 -0
- package/dist/esm/tiptap/tiptapNodeView.js +211 -0
- package/dist/esm/viewdesc.js +844 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/AbstractEditorView.d.ts +28 -0
- package/dist/types/ReactEditorView.d.ts +102 -0
- package/dist/types/StaticEditorView.d.ts +26 -0
- package/dist/types/__tests__/viewdesc.test.d.ts +1 -0
- package/dist/types/browser.d.ts +15 -0
- package/dist/types/commands/__tests__/reorderSiblings.test.d.ts +1 -0
- package/dist/types/commands/reorderSiblings.d.ts +14 -0
- package/dist/types/components/ChildNodeViews.d.ts +9 -0
- package/dist/types/components/CursorWrapper.d.ts +3 -0
- package/dist/types/components/EditorStateSelectorsProvider.d.ts +10 -0
- package/dist/types/components/LayoutGroup.d.ts +12 -0
- package/dist/types/components/NativeWidgetView.d.ts +8 -0
- package/dist/types/components/OutputSpec.d.ts +10 -0
- package/dist/types/components/ProseMirror.d.ts +14 -0
- package/dist/types/components/ProseMirrorDoc.d.ts +16 -0
- package/dist/types/components/SeparatorHackView.d.ts +6 -0
- package/dist/types/components/TextNodeView.d.ts +27 -0
- package/dist/types/components/TrailingHackView.d.ts +6 -0
- package/dist/types/components/WidgetView.d.ts +8 -0
- package/dist/types/components/WidgetViewComponentProps.d.ts +7 -0
- package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +17 -0
- package/dist/types/components/__tests__/ProseMirror.domchange.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.draw-decoration.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.draw.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.memory-leak.test.d.ts +6 -0
- package/dist/types/components/__tests__/ProseMirror.mobile.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.node-view.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.selection.test.d.ts +1 -0
- package/dist/types/components/__tests__/ProseMirror.test.d.ts +1 -0
- package/dist/types/components/__tests__/stableContexts.test.d.ts +10 -0
- package/dist/types/components/marks/DefaultMarkView.d.ts +3 -0
- package/dist/types/components/marks/MarkView.d.ts +10 -0
- package/dist/types/components/marks/MarkViewComponentProps.d.ts +11 -0
- package/dist/types/components/marks/MarkViewConstructorView.d.ts +12 -0
- package/dist/types/components/marks/ReactMarkView.d.ts +12 -0
- package/dist/types/components/nodes/DefaultNodeView.d.ts +3 -0
- package/dist/types/components/nodes/DocNodeView.d.ts +12 -0
- package/dist/types/components/nodes/NodeView.d.ts +14 -0
- package/dist/types/components/nodes/NodeViewComponentProps.d.ts +13 -0
- package/dist/types/components/nodes/NodeViewConstructorView.d.ts +12 -0
- package/dist/types/components/nodes/ReactNodeView.d.ts +13 -0
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/contexts/ChildDescriptionsContext.d.ts +7 -0
- package/dist/types/contexts/EditorContext.d.ts +18 -0
- package/dist/types/contexts/EditorStateContext.d.ts +2 -0
- package/dist/types/contexts/EditorStateStoreContext.d.ts +9 -0
- package/dist/types/contexts/IgnoreMutationContext.d.ts +5 -0
- package/dist/types/contexts/LayoutGroupContext.d.ts +5 -0
- package/dist/types/contexts/NodeViewContext.d.ts +9 -0
- package/dist/types/contexts/RenderStoreContext.d.ts +24 -0
- package/dist/types/contexts/SelectNodeContext.d.ts +5 -0
- package/dist/types/contexts/StopEventContext.d.ts +5 -0
- package/dist/types/contexts/__tests__/DeferredLayoutEffects.test.d.ts +1 -0
- package/dist/types/decorations/ReactWidgetType.d.ts +40 -0
- package/dist/types/decorations/computeDocDeco.d.ts +14 -0
- package/dist/types/decorations/internalTypes.d.ts +20 -0
- package/dist/types/decorations/iterDeco.d.ts +3 -0
- package/dist/types/decorations/viewDecorations.d.ts +14 -0
- package/dist/types/dom.d.ts +22 -0
- package/dist/types/findDOMNode.d.ts +3 -0
- package/dist/types/hooks/__tests__/useEditorEffect.test.d.ts +1 -0
- package/dist/types/hooks/useClientLayoutEffect.d.ts +2 -0
- package/dist/types/hooks/useComponentEventListeners.d.ts +34 -0
- package/dist/types/hooks/useEditor.d.ts +37 -0
- package/dist/types/hooks/useEditorEffect.d.ts +21 -0
- package/dist/types/hooks/useEditorEventCallback.d.ts +13 -0
- package/dist/types/hooks/useEditorEventListener.d.ts +8 -0
- package/dist/types/hooks/useEditorState.d.ts +5 -0
- package/dist/types/hooks/useEditorStateSelector.d.ts +10 -0
- package/dist/types/hooks/useEffectEvent.d.ts +1 -0
- package/dist/types/hooks/useForceUpdate.d.ts +5 -0
- package/dist/types/hooks/useIgnoreMutation.d.ts +2 -0
- package/dist/types/hooks/useIsComposingIn.d.ts +4 -0
- package/dist/types/hooks/useIsEditorStatic.d.ts +5 -0
- package/dist/types/hooks/useIsNodeSelected.d.ts +1 -0
- package/dist/types/hooks/useLayoutGroupEffect.d.ts +3 -0
- package/dist/types/hooks/useMarkViewDescription.d.ts +15 -0
- package/dist/types/hooks/useNodePos.d.ts +9 -0
- package/dist/types/hooks/useNodeViewDescription.d.ts +16 -0
- package/dist/types/hooks/useReactKeys.d.ts +2 -0
- package/dist/types/hooks/useSelectNode.d.ts +2 -0
- package/dist/types/hooks/useSetDom.d.ts +2 -0
- package/dist/types/hooks/useStopEvent.d.ts +2 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/plugins/__tests__/reactKeys.test.d.ts +1 -0
- package/dist/types/plugins/beforeInputPlugin.d.ts +2 -0
- package/dist/types/plugins/reactKeys.d.ts +23 -0
- package/dist/types/props.d.ts +1173 -0
- package/dist/types/refs.d.ts +2 -0
- package/dist/types/testing/editorViewTestHelpers.d.ts +24 -0
- package/dist/types/testing/setupProseMirrorView.d.ts +2 -0
- package/dist/types/tiptap/ReactProseMirrorNodeView.d.ts +15 -0
- package/dist/types/tiptap/TiptapEditor.d.ts +6 -0
- package/dist/types/tiptap/TiptapEditorContent.d.ts +28 -0
- package/dist/types/tiptap/TiptapEditorView.d.ts +16 -0
- package/dist/types/tiptap/contexts/TiptapEditorContext.d.ts +6 -0
- package/dist/types/tiptap/extensions/ReactProseMirror.d.ts +9 -0
- package/dist/types/tiptap/extensions/ReactProseMirrorCommands.d.ts +2 -0
- package/dist/types/tiptap/extensions/commands/updateAttributes.d.ts +24 -0
- package/dist/types/tiptap/hooks/useEditor.d.ts +38 -0
- package/dist/types/tiptap/hooks/useIsInReactProseMirror.d.ts +6 -0
- package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +11 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEffect.d.ts +21 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEventCallback.d.ts +13 -0
- package/dist/types/tiptap/index.d.ts +9 -0
- package/dist/types/tiptap/tiptapNodeView.d.ts +50 -0
- package/dist/types/viewdesc.d.ts +179 -0
- package/package.json +167 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { DecorationSet } from "prosemirror-view";
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
+
const none = [], noSpec = {};
|
|
4
|
+
const empty = DecorationSet.empty;
|
|
5
|
+
// An abstraction that allows the code dealing with decorations to
|
|
6
|
+
// treat multiple DecorationSet objects as if it were a single object
|
|
7
|
+
// with (a subset of) the same interface.
|
|
8
|
+
let DecorationGroup = class DecorationGroup {
|
|
9
|
+
members;
|
|
10
|
+
constructor(members){
|
|
11
|
+
this.members = members;
|
|
12
|
+
}
|
|
13
|
+
map(mapping, doc) {
|
|
14
|
+
const mappedDecos = this.members.map((member)=>member.map(mapping, doc, noSpec));
|
|
15
|
+
return DecorationGroup.from(mappedDecos);
|
|
16
|
+
}
|
|
17
|
+
forChild(offset, child) {
|
|
18
|
+
if (child.isLeaf) return DecorationSet.empty;
|
|
19
|
+
let found = [];
|
|
20
|
+
for(let i = 0; i < this.members.length; i++){
|
|
21
|
+
const result = this.members[i].forChild(offset, child);
|
|
22
|
+
if (result == empty) continue;
|
|
23
|
+
if (result instanceof DecorationGroup) found = found.concat(result.members);
|
|
24
|
+
else found.push(result);
|
|
25
|
+
}
|
|
26
|
+
return DecorationGroup.from(found);
|
|
27
|
+
}
|
|
28
|
+
eq(other) {
|
|
29
|
+
if (!(other instanceof DecorationGroup) || other.members.length != this.members.length) return false;
|
|
30
|
+
for(let i = 0; i < this.members.length; i++)if (!this.members[i].eq(other.members[i])) return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
locals(node) {
|
|
34
|
+
let result, sorted = true;
|
|
35
|
+
for(let i = 0; i < this.members.length; i++){
|
|
36
|
+
const locals = this.members[i].localsInner(node);
|
|
37
|
+
if (!locals.length) continue;
|
|
38
|
+
if (!result) {
|
|
39
|
+
result = locals;
|
|
40
|
+
} else {
|
|
41
|
+
if (sorted) {
|
|
42
|
+
result = result.slice();
|
|
43
|
+
sorted = false;
|
|
44
|
+
}
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
46
|
+
for(let j = 0; j < locals.length; j++)result.push(locals[j]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return result ? removeOverlap(sorted ? result : result.sort(byPos)) : none;
|
|
50
|
+
}
|
|
51
|
+
// Create a group for the given array of decoration sets, or return
|
|
52
|
+
// a single set when possible.
|
|
53
|
+
static from(members) {
|
|
54
|
+
switch(members.length){
|
|
55
|
+
case 0:
|
|
56
|
+
return empty;
|
|
57
|
+
case 1:
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
59
|
+
return members[0];
|
|
60
|
+
default:
|
|
61
|
+
return new DecorationGroup(members.every((m)=>m instanceof DecorationSet) ? members : members.reduce((r, m)=>r.concat(m instanceof DecorationSet ? m : m.members), []));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
forEachSet(f) {
|
|
65
|
+
for(let i = 0; i < this.members.length; i++)// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
66
|
+
this.members[i].forEachSet(f);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
// Used to sort decorations so that ones with a low start position
|
|
70
|
+
// come first, and within a set with the same start position, those
|
|
71
|
+
// with an smaller end position come first.
|
|
72
|
+
function byPos(a, b) {
|
|
73
|
+
return a.from - b.from || a.to - b.to;
|
|
74
|
+
}
|
|
75
|
+
// Scan a sorted array of decorations for partially overlapping spans,
|
|
76
|
+
// and split those so that only fully overlapping spans are left (to
|
|
77
|
+
// make subsequent rendering easier). Will return the input array if
|
|
78
|
+
// no partially overlapping spans are found (the common case).
|
|
79
|
+
function removeOverlap(spans) {
|
|
80
|
+
let working = spans;
|
|
81
|
+
for(let i = 0; i < working.length - 1; i++){
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
83
|
+
const span = working[i];
|
|
84
|
+
if (span.from != span.to) for(let j = i + 1; j < working.length; j++){
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
86
|
+
const next = working[j];
|
|
87
|
+
if (next.from == span.from) {
|
|
88
|
+
if (next.to != span.to) {
|
|
89
|
+
if (working == spans) working = spans.slice();
|
|
90
|
+
// Followed by a partially overlapping larger span. Split that
|
|
91
|
+
// span.
|
|
92
|
+
working[j] = next.copy(next.from, span.to);
|
|
93
|
+
insertAhead(working, j + 1, next.copy(span.to, next.to));
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
} else {
|
|
97
|
+
if (next.from < span.to) {
|
|
98
|
+
if (working == spans) working = spans.slice();
|
|
99
|
+
// The end of this one overlaps with a subsequent span. Split
|
|
100
|
+
// this one.
|
|
101
|
+
working[i] = span.copy(span.from, next.from);
|
|
102
|
+
insertAhead(working, j, span.copy(next.from, span.to));
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return working;
|
|
109
|
+
}
|
|
110
|
+
function insertAhead(array, i, deco) {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
112
|
+
while(i < array.length && byPos(deco, array[i]) > 0)i++;
|
|
113
|
+
array.splice(i, 0, deco);
|
|
114
|
+
}
|
|
115
|
+
const ViewDecorationsCache = new WeakMap();
|
|
116
|
+
/**
|
|
117
|
+
* Produces the DecorationSource for the current state, based
|
|
118
|
+
* on the decorations editor prop.
|
|
119
|
+
*
|
|
120
|
+
* The return value of this function is memoized; if it is to
|
|
121
|
+
* return an equivalent value to the last time it was called for
|
|
122
|
+
* a given EditorView, it will return exactly that previous value.
|
|
123
|
+
*
|
|
124
|
+
* This makes it safe to call in a React render function, even
|
|
125
|
+
* if its result is used in a dependencies array for a hook.
|
|
126
|
+
*/ export function viewDecorations(view) {
|
|
127
|
+
const found = [];
|
|
128
|
+
view.someProp("decorations", (f)=>{
|
|
129
|
+
const result = f(view.state);
|
|
130
|
+
if (result && result != empty) found.push(result);
|
|
131
|
+
});
|
|
132
|
+
const previous = ViewDecorationsCache.get(view);
|
|
133
|
+
if (!previous) {
|
|
134
|
+
const result = DecorationGroup.from(found);
|
|
135
|
+
ViewDecorationsCache.set(view, result);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
let numPrevious = 0;
|
|
139
|
+
let areSetsEqual = true;
|
|
140
|
+
previous.forEachSet((set)=>{
|
|
141
|
+
const next = found[numPrevious++];
|
|
142
|
+
if (next !== set) {
|
|
143
|
+
areSetsEqual = false;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
if (numPrevious !== found.length) {
|
|
147
|
+
areSetsEqual = false;
|
|
148
|
+
}
|
|
149
|
+
if (!areSetsEqual) {
|
|
150
|
+
const result = DecorationGroup.from(found);
|
|
151
|
+
ViewDecorationsCache.set(view, result);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
return previous;
|
|
155
|
+
}
|
package/dist/esm/dom.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ export const domIndex = function(node) {
|
|
2
|
+
for(let index = 0;; index++){
|
|
3
|
+
node = node.previousSibling;
|
|
4
|
+
if (!node) return index;
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
export const parentNode = function(node) {
|
|
8
|
+
const parent = node.assignedSlot || node.parentNode;
|
|
9
|
+
return parent && parent.nodeType == 11 ? parent.host : parent;
|
|
10
|
+
};
|
|
11
|
+
let reusedRange = null;
|
|
12
|
+
// Note that this will always return the same range, because DOM range
|
|
13
|
+
// objects are every expensive, and keep slowing down subsequent DOM
|
|
14
|
+
// updates, for some reason.
|
|
15
|
+
export const textRange = function(node, from, to) {
|
|
16
|
+
const range = reusedRange || (reusedRange = document.createRange());
|
|
17
|
+
range.setEnd(node, to == null ? node.nodeValue.length : to);
|
|
18
|
+
range.setStart(node, from || 0);
|
|
19
|
+
return range;
|
|
20
|
+
};
|
|
21
|
+
// Scans forward and backward through DOM positions equivalent to the
|
|
22
|
+
// given one to see if the two are in the same place (i.e. after a
|
|
23
|
+
// text node vs at the end of that text node)
|
|
24
|
+
export const isEquivalentPosition = function(node, off, targetNode, targetOff) {
|
|
25
|
+
return targetNode && (scanFor(node, off, targetNode, targetOff, -1) || scanFor(node, off, targetNode, targetOff, 1));
|
|
26
|
+
};
|
|
27
|
+
const atomElements = /^(img|br|input|textarea|hr)$/i;
|
|
28
|
+
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
29
|
+
for(;;){
|
|
30
|
+
if (node == targetNode && off == targetOff) return true;
|
|
31
|
+
if (off == (dir < 0 ? 0 : nodeSize(node))) {
|
|
32
|
+
const parent = node.parentNode;
|
|
33
|
+
if (!parent || parent.nodeType != 1 || hasBlockDesc(node) || atomElements.test(node.nodeName) || node.contentEditable == "false") return false;
|
|
34
|
+
off = domIndex(node) + (dir < 0 ? 0 : 1);
|
|
35
|
+
node = parent;
|
|
36
|
+
} else if (node.nodeType == 1) {
|
|
37
|
+
const child = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
|
38
|
+
if (child.nodeType == 1 && child.contentEditable == "false") {
|
|
39
|
+
if (child.pmViewDesc?.ignoreForSelection) off += dir;
|
|
40
|
+
else return false;
|
|
41
|
+
} else {
|
|
42
|
+
node = child;
|
|
43
|
+
off = dir < 0 ? nodeSize(node) : 0;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function nodeSize(node) {
|
|
51
|
+
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
|
52
|
+
}
|
|
53
|
+
export function isOnEdge(node, offset, parent) {
|
|
54
|
+
for(let atStart = offset == 0, atEnd = offset == nodeSize(node); atStart || atEnd;){
|
|
55
|
+
if (node == parent) return true;
|
|
56
|
+
const index = domIndex(node);
|
|
57
|
+
node = node.parentNode;
|
|
58
|
+
if (!node) return false;
|
|
59
|
+
atStart = atStart && index == 0;
|
|
60
|
+
atEnd = atEnd && index == nodeSize(node);
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
export function hasBlockDesc(dom) {
|
|
65
|
+
let desc;
|
|
66
|
+
for(let cur = dom; cur; cur = cur.parentNode)if (desc = cur.pmViewDesc) break;
|
|
67
|
+
return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom);
|
|
68
|
+
}
|
|
69
|
+
// Work around Chrome issue https://bugs.chromium.org/p/chromium/issues/detail?id=447523
|
|
70
|
+
// (isCollapsed inappropriately returns true in shadow dom)
|
|
71
|
+
export const selectionCollapsed = function(domSel) {
|
|
72
|
+
return domSel.focusNode && isEquivalentPosition(domSel.focusNode, domSel.focusOffset, domSel.anchorNode, domSel.anchorOffset);
|
|
73
|
+
};
|
|
74
|
+
export function keyEvent(keyCode, key) {
|
|
75
|
+
const event = document.createEvent("Event");
|
|
76
|
+
event.initEvent("keydown", true, true);
|
|
77
|
+
event.keyCode = keyCode;
|
|
78
|
+
event.key = event.code = key;
|
|
79
|
+
return event;
|
|
80
|
+
}
|
|
81
|
+
export function deepActiveElement(doc) {
|
|
82
|
+
let elt = doc.activeElement;
|
|
83
|
+
while(elt && elt.shadowRoot)elt = elt.shadowRoot.activeElement;
|
|
84
|
+
return elt;
|
|
85
|
+
}
|
|
86
|
+
export function caretFromPoint(doc, x, y) {
|
|
87
|
+
if (doc.caretPositionFromPoint) {
|
|
88
|
+
try {
|
|
89
|
+
// Firefox throws for this call in hard-to-predict circumstances (#994)
|
|
90
|
+
const pos = doc.caretPositionFromPoint(x, y);
|
|
91
|
+
// Clip the offset, because Chrome will return a text offset
|
|
92
|
+
// into <input> nodes, which can't be treated as a regular DOM
|
|
93
|
+
// offset
|
|
94
|
+
if (pos) return {
|
|
95
|
+
node: pos.offsetNode,
|
|
96
|
+
offset: Math.min(nodeSize(pos.offsetNode), pos.offset)
|
|
97
|
+
};
|
|
98
|
+
} catch (_) {
|
|
99
|
+
// pass
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (doc.caretRangeFromPoint) {
|
|
103
|
+
const range = doc.caretRangeFromPoint(x, y);
|
|
104
|
+
if (range) return {
|
|
105
|
+
node: range.startContainer,
|
|
106
|
+
offset: Math.min(nodeSize(range.startContainer), range.startOffset)
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//@ts-expect-error This module isn't typed
|
|
2
|
+
import { findCurrentHostFiber } from "react-reconciler/reflection.js";
|
|
3
|
+
// https://github.com/facebook/react/blob/main/packages/shared/ReactInstanceMap.js#L18
|
|
4
|
+
export function getInstance(key) {
|
|
5
|
+
return key._reactInternals;
|
|
6
|
+
}
|
|
7
|
+
// https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js#L322
|
|
8
|
+
function getPublicInstance(instance) {
|
|
9
|
+
return instance;
|
|
10
|
+
}
|
|
11
|
+
// https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberReconciler.js#L153
|
|
12
|
+
function findHostInstance(component) {
|
|
13
|
+
const fiber = getInstance(component);
|
|
14
|
+
if (fiber === undefined) {
|
|
15
|
+
if (typeof component.render === "function") {
|
|
16
|
+
throw new Error("Unable to find node on an unmounted component.");
|
|
17
|
+
} else {
|
|
18
|
+
const keys = Object.keys(component).join(",");
|
|
19
|
+
throw new Error(`Argument appears to not be a ReactComponent. Keys: ${keys}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const hostFiber = findCurrentHostFiber(fiber);
|
|
23
|
+
if (hostFiber === null) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return getPublicInstance(hostFiber.stateNode);
|
|
27
|
+
}
|
|
28
|
+
// https://github.com/facebook/react/blob/main/packages/react-dom/src/client/ReactDOMClient.js#L43
|
|
29
|
+
export function findDOMNode(componentOrElement) {
|
|
30
|
+
return findHostInstance(componentOrElement);
|
|
31
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useLayoutEffect } from "react";
|
|
2
|
+
export function useClientLayoutEffect() {
|
|
3
|
+
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
4
|
+
args[_key] = arguments[_key];
|
|
5
|
+
}
|
|
6
|
+
if (typeof document === "undefined") return;
|
|
7
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks, react-hooks/exhaustive-deps
|
|
8
|
+
useLayoutEffect(...args);
|
|
9
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* Copyright (c) The New York Times Company */ import { useCallback, useLayoutEffect, useMemo, useState } from "react";
|
|
2
|
+
import { unstable_batchedUpdates as batch } from "react-dom";
|
|
3
|
+
/**
|
|
4
|
+
* Produces a plugin that can be used with ProseMirror to handle DOM
|
|
5
|
+
* events at the EditorView.dom element.
|
|
6
|
+
*
|
|
7
|
+
* - `reactEventsPlugin` is a ProseMirror plugin for handling DOM events
|
|
8
|
+
* at the EditorView.dom element. It should be passed to `useEditorView`,
|
|
9
|
+
* along with any other plugins.
|
|
10
|
+
*
|
|
11
|
+
* - `registerEventListener` and `unregisterEventListener` should be
|
|
12
|
+
* passed to `EditorContext.Provider`.
|
|
13
|
+
*
|
|
14
|
+
* @privateRemarks
|
|
15
|
+
*
|
|
16
|
+
* This hook uses a combination of mutable and immutable updates to give
|
|
17
|
+
* us precise control over when we re-create the event listeners.
|
|
18
|
+
*
|
|
19
|
+
* The hook has a mutable reference to the set of handlers for each
|
|
20
|
+
* event type, but the set of event types is static. This means that we
|
|
21
|
+
* need to produce a new handleDOMEVents record whenever a new event type is
|
|
22
|
+
* registered. We avoid producing a new record in any other
|
|
23
|
+
* scenario to avoid the performance overhead of re-registering the event
|
|
24
|
+
* listeners in the EditorView.
|
|
25
|
+
*
|
|
26
|
+
* To accomplish this, we shallowly clone the registry whenever a new event
|
|
27
|
+
* type is registered.
|
|
28
|
+
*/ export function useComponentEventListeners(handleDOMEventsProp) {
|
|
29
|
+
const [registry, setRegistry] = useState(new Map());
|
|
30
|
+
const registerEventListener = useCallback((eventType, handler)=>{
|
|
31
|
+
const handlers = registry.get(eventType) ?? [];
|
|
32
|
+
handlers.unshift(handler);
|
|
33
|
+
if (!registry.has(eventType)) {
|
|
34
|
+
registry.set(eventType, handlers);
|
|
35
|
+
setRegistry(new Map(registry));
|
|
36
|
+
}
|
|
37
|
+
}, [
|
|
38
|
+
registry
|
|
39
|
+
]);
|
|
40
|
+
const unregisterEventListener = useCallback((eventType, handler)=>{
|
|
41
|
+
const handlers = registry.get(eventType);
|
|
42
|
+
handlers?.splice(handlers.indexOf(handler), 1);
|
|
43
|
+
}, [
|
|
44
|
+
registry
|
|
45
|
+
]);
|
|
46
|
+
useLayoutEffect(()=>{
|
|
47
|
+
if (!handleDOMEventsProp) return;
|
|
48
|
+
for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
|
|
49
|
+
if (!handler) return;
|
|
50
|
+
registerEventListener(eventType, handler);
|
|
51
|
+
}
|
|
52
|
+
return ()=>{
|
|
53
|
+
for (const [eventType, handler] of Object.entries(handleDOMEventsProp)){
|
|
54
|
+
if (!handler) return;
|
|
55
|
+
unregisterEventListener(eventType, handler);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}, [
|
|
59
|
+
handleDOMEventsProp,
|
|
60
|
+
registerEventListener,
|
|
61
|
+
unregisterEventListener
|
|
62
|
+
]);
|
|
63
|
+
const handleDOMEvents = useMemo(()=>{
|
|
64
|
+
const domEventHandlers = {};
|
|
65
|
+
for (const [eventType, handlers] of registry.entries()){
|
|
66
|
+
function handleEvent(view, event) {
|
|
67
|
+
for (const handler of handlers){
|
|
68
|
+
let handled = false;
|
|
69
|
+
batch(()=>{
|
|
70
|
+
handled = !!handler(view, event);
|
|
71
|
+
});
|
|
72
|
+
if (handled || event.defaultPrevented) return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
domEventHandlers[eventType] = handleEvent;
|
|
77
|
+
}
|
|
78
|
+
return domEventHandlers;
|
|
79
|
+
}, [
|
|
80
|
+
registry
|
|
81
|
+
]);
|
|
82
|
+
return {
|
|
83
|
+
registerEventListener,
|
|
84
|
+
unregisterEventListener,
|
|
85
|
+
handleDOMEvents
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { flushSync } from "react-dom";
|
|
3
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
4
|
+
import { StaticEditorView } from "../StaticEditorView.js";
|
|
5
|
+
import { EMPTY_STATE } from "../constants.js";
|
|
6
|
+
import { createRenderStore } from "../contexts/RenderStoreContext.js";
|
|
7
|
+
import { beforeInputPlugin } from "../plugins/beforeInputPlugin.js";
|
|
8
|
+
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
9
|
+
import { useComponentEventListeners } from "./useComponentEventListeners.js";
|
|
10
|
+
import { useEffectEvent } from "./useEffectEvent.js";
|
|
11
|
+
import { useForceUpdate } from "./useForceUpdate.js";
|
|
12
|
+
let didWarnValueDefaultValue = false;
|
|
13
|
+
/**
|
|
14
|
+
* Creates, mounts, and manages a ProseMirror `EditorView`.
|
|
15
|
+
*
|
|
16
|
+
* All state and props updates are executed in a layout effect.
|
|
17
|
+
* To ensure that the EditorState and EditorView are never out of
|
|
18
|
+
* sync, it's important that the EditorView produced by this hook
|
|
19
|
+
* is only accessed through the `useEditorViewEvent` and
|
|
20
|
+
* `useEditorViewLayoutEffect` hooks.
|
|
21
|
+
*/ export function useEditor(mount, options) {
|
|
22
|
+
if (process.env.NODE_ENV !== "production") {
|
|
23
|
+
if (options.defaultState !== undefined && options.state !== undefined && !didWarnValueDefaultValue) {
|
|
24
|
+
console.error("A component contains a ProseMirror editor with both state and defaultState props. " + "ProseMirror editors must be either controlled or uncontrolled " + "(specify either the state prop, or the defaultState prop, but not both). " + "Decide between using a controlled or uncontrolled ProseMirror editor " + "and remove one of these props. More info: " + "https://reactjs.org/link/controlled-components");
|
|
25
|
+
didWarnValueDefaultValue = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const flushSyncRef = useRef(true);
|
|
29
|
+
const forceUpdate = useForceUpdate();
|
|
30
|
+
const defaultState = options.defaultState ?? EMPTY_STATE;
|
|
31
|
+
const [_state, setState] = useState(defaultState);
|
|
32
|
+
const state = options.state ?? _state;
|
|
33
|
+
const [renderStore] = useState(createRenderStore);
|
|
34
|
+
const { handleDOMEvents, registerEventListener, unregisterEventListener } = useComponentEventListeners(options.handleDOMEvents);
|
|
35
|
+
const plugins = useMemo(()=>[
|
|
36
|
+
...options.plugins ?? [],
|
|
37
|
+
beforeInputPlugin()
|
|
38
|
+
], [
|
|
39
|
+
options.plugins
|
|
40
|
+
]);
|
|
41
|
+
const dispatchTransaction = useCallback(function dispatchTransaction(tr) {
|
|
42
|
+
const async = tr.getMeta("async");
|
|
43
|
+
if (flushSyncRef.current && !async) {
|
|
44
|
+
flushSync(()=>{
|
|
45
|
+
if (!options.state) {
|
|
46
|
+
setState((s)=>s.apply(tr));
|
|
47
|
+
}
|
|
48
|
+
if (options.dispatchTransaction) {
|
|
49
|
+
options.dispatchTransaction.call(this, tr);
|
|
50
|
+
}
|
|
51
|
+
if (options.stableContexts) {
|
|
52
|
+
renderStore.announce();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
renderStore.announced = false;
|
|
56
|
+
} else {
|
|
57
|
+
if (!options.state) {
|
|
58
|
+
setState((s)=>s.apply(tr));
|
|
59
|
+
}
|
|
60
|
+
if (options.dispatchTransaction) {
|
|
61
|
+
options.dispatchTransaction.call(this, tr);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}, [
|
|
65
|
+
options.dispatchTransaction,
|
|
66
|
+
options.state,
|
|
67
|
+
options.stableContexts,
|
|
68
|
+
renderStore
|
|
69
|
+
]);
|
|
70
|
+
const directEditorProps = {
|
|
71
|
+
...options,
|
|
72
|
+
state,
|
|
73
|
+
plugins,
|
|
74
|
+
dispatchTransaction,
|
|
75
|
+
handleDOMEvents
|
|
76
|
+
};
|
|
77
|
+
const [view, setView] = useState(()=>{
|
|
78
|
+
return new StaticEditorView(directEditorProps);
|
|
79
|
+
});
|
|
80
|
+
const createEditorView = useEffectEvent((mount)=>{
|
|
81
|
+
if (mount && !options.static) {
|
|
82
|
+
const view = new ReactEditorView({
|
|
83
|
+
mount
|
|
84
|
+
}, directEditorProps);
|
|
85
|
+
view.dom.addEventListener("compositionend", forceUpdate);
|
|
86
|
+
return view;
|
|
87
|
+
}
|
|
88
|
+
return new StaticEditorView(directEditorProps);
|
|
89
|
+
});
|
|
90
|
+
useClientLayoutEffect(()=>{
|
|
91
|
+
const view = createEditorView(mount);
|
|
92
|
+
setView(view);
|
|
93
|
+
return ()=>{
|
|
94
|
+
view.destroy();
|
|
95
|
+
};
|
|
96
|
+
}, [
|
|
97
|
+
createEditorView,
|
|
98
|
+
mount
|
|
99
|
+
]);
|
|
100
|
+
useClientLayoutEffect(()=>{
|
|
101
|
+
// Ensure that the EditorView hasn't been destroyed before
|
|
102
|
+
// running effects. Running effects will reattach selection
|
|
103
|
+
// change listeners if the EditorView has been destroyed.
|
|
104
|
+
if (view instanceof ReactEditorView && !view.isDestroyed && view.domSelection()) {
|
|
105
|
+
flushSyncRef.current = false;
|
|
106
|
+
view.commitPendingEffects();
|
|
107
|
+
flushSyncRef.current = true;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
view.update(directEditorProps);
|
|
111
|
+
const editor = useMemo(()=>({
|
|
112
|
+
view,
|
|
113
|
+
flushSyncRef,
|
|
114
|
+
registerEventListener,
|
|
115
|
+
unregisterEventListener,
|
|
116
|
+
isStatic: options.static ?? false
|
|
117
|
+
}), [
|
|
118
|
+
options.static,
|
|
119
|
+
registerEventListener,
|
|
120
|
+
unregisterEventListener,
|
|
121
|
+
view
|
|
122
|
+
]);
|
|
123
|
+
return {
|
|
124
|
+
editor,
|
|
125
|
+
state,
|
|
126
|
+
renderStore: options.stableContexts ? renderStore : null
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* Copyright (c) The New York Times Company */ import { useContext } from "react";
|
|
2
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
3
|
+
import { EditorContext } from "../contexts/EditorContext.js";
|
|
4
|
+
import { useLayoutGroupEffect } from "./useLayoutGroupEffect.js";
|
|
5
|
+
/**
|
|
6
|
+
* Registers a layout effect to run after the EditorView has
|
|
7
|
+
* been updated with the latest EditorState and Decorations.
|
|
8
|
+
*
|
|
9
|
+
* Effects can take an EditorView instance as an argument.
|
|
10
|
+
* This hook should be used to execute layout effects that
|
|
11
|
+
* depend on the EditorView, such as for positioning DOM
|
|
12
|
+
* nodes based on ProseMirror positions.
|
|
13
|
+
*
|
|
14
|
+
* Layout effects registered with this hook still fire
|
|
15
|
+
* synchronously after all DOM mutations, but they do so
|
|
16
|
+
* _after_ the EditorView has been updated, even when the
|
|
17
|
+
* EditorView lives in an ancestor component.
|
|
18
|
+
*
|
|
19
|
+
* This hook can only be used in a component that is mounted
|
|
20
|
+
* as a child of the TiptapEditorView component, including
|
|
21
|
+
* React node view components.
|
|
22
|
+
*/ export function useEditorEffect(effect, dependencies) {
|
|
23
|
+
const { view, flushSyncRef } = useContext(EditorContext);
|
|
24
|
+
// The rules of hooks want `effect` to be included in the
|
|
25
|
+
// dependency list, but dependency issues for `effect` will
|
|
26
|
+
// be caught by the linter at the call-site for
|
|
27
|
+
// `useEditorViewLayoutEffect`.
|
|
28
|
+
// Note: we specifically don't want to re-run the effect
|
|
29
|
+
// every time it changes, because it will most likely
|
|
30
|
+
// be defined inline and run on every re-render.
|
|
31
|
+
useLayoutGroupEffect(()=>{
|
|
32
|
+
if (view instanceof ReactEditorView) {
|
|
33
|
+
flushSyncRef.current = false;
|
|
34
|
+
const result = effect(view);
|
|
35
|
+
flushSyncRef.current = true;
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
}, // The rules of hooks want to be able to statically
|
|
39
|
+
// verify the dependencies for the effect, but this will
|
|
40
|
+
// have already happened at the call-site.
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
dependencies && [
|
|
43
|
+
view,
|
|
44
|
+
...dependencies
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Copyright (c) The New York Times Company */ import { useCallback, useContext, useRef } from "react";
|
|
2
|
+
import { ReactEditorView } from "../ReactEditorView.js";
|
|
3
|
+
import { EditorContext } from "../contexts/EditorContext.js";
|
|
4
|
+
import { useEditorEffect } from "./useEditorEffect.js";
|
|
5
|
+
function assertIsReactEditorView(view) {
|
|
6
|
+
if (view instanceof ReactEditorView) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
throw new DOMException("ProseMirror document is not mounted", "InvalidStateError");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns a stable function reference to be used as an
|
|
13
|
+
* event handler callback.
|
|
14
|
+
*
|
|
15
|
+
* The callback will be called with the EditorView instance
|
|
16
|
+
* as its first argument.
|
|
17
|
+
*
|
|
18
|
+
* This hook can only be used in a component that is mounted
|
|
19
|
+
* as a child of the TiptapEditorView component, including
|
|
20
|
+
* React node view components.
|
|
21
|
+
*/ export function useEditorEventCallback(callback) {
|
|
22
|
+
const ref = useRef(callback);
|
|
23
|
+
const { view } = useContext(EditorContext);
|
|
24
|
+
useEditorEffect(()=>{
|
|
25
|
+
ref.current = callback;
|
|
26
|
+
}, [
|
|
27
|
+
callback
|
|
28
|
+
]);
|
|
29
|
+
return useCallback(function() {
|
|
30
|
+
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
31
|
+
args[_key] = arguments[_key];
|
|
32
|
+
}
|
|
33
|
+
assertIsReactEditorView(view);
|
|
34
|
+
return ref.current.call(this, view, ...args);
|
|
35
|
+
}, [
|
|
36
|
+
view
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* Copyright (c) The New York Times Company */ import { useCallback, useContext, useRef } from "react";
|
|
2
|
+
import { EditorContext } from "../contexts/EditorContext.js";
|
|
3
|
+
import { useEditorEffect } from "./useEditorEffect.js";
|
|
4
|
+
/**
|
|
5
|
+
* Attaches an event listener at the `EditorView`'s DOM node. See
|
|
6
|
+
* [the ProseMirror docs](https://prosemirror.net/docs/ref/#view.EditorProps.handleDOMEvents)
|
|
7
|
+
* for more details.
|
|
8
|
+
*/ export function useEditorEventListener(eventType, handler) {
|
|
9
|
+
const { registerEventListener, unregisterEventListener } = useContext(EditorContext);
|
|
10
|
+
const ref = useRef(handler);
|
|
11
|
+
useEditorEffect(()=>{
|
|
12
|
+
ref.current = handler;
|
|
13
|
+
}, [
|
|
14
|
+
handler
|
|
15
|
+
]);
|
|
16
|
+
const eventHandler = useCallback(function(view, event) {
|
|
17
|
+
return ref.current.call(this, view, event);
|
|
18
|
+
}, []);
|
|
19
|
+
useEditorEffect(()=>{
|
|
20
|
+
registerEventListener(eventType, eventHandler);
|
|
21
|
+
return ()=>unregisterEventListener(eventType, eventHandler);
|
|
22
|
+
}, [
|
|
23
|
+
eventHandler,
|
|
24
|
+
eventType,
|
|
25
|
+
registerEventListener,
|
|
26
|
+
unregisterEventListener
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Copyright (c) The New York Times Company */ import { useContext } from "react";
|
|
2
|
+
import { EditorStateContext } from "../contexts/EditorStateContext.js";
|
|
3
|
+
import { useRenderStore } from "../contexts/RenderStoreContext.js";
|
|
4
|
+
/**
|
|
5
|
+
* Provides access to the current EditorState value.
|
|
6
|
+
*/ export function useEditorState() {
|
|
7
|
+
const editorState = useContext(EditorStateContext);
|
|
8
|
+
const store = useRenderStore();
|
|
9
|
+
return store?.state ?? editorState;
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useContext, useSyncExternalStore } from "react";
|
|
2
|
+
import { EditorStateStoreContext } from "../contexts/EditorStateStoreContext.js";
|
|
3
|
+
/**
|
|
4
|
+
* Select a piece of the EditorState, a la Redux’s
|
|
5
|
+
* `useSelector`.
|
|
6
|
+
*
|
|
7
|
+
* This hook will only trigger a re-render of the
|
|
8
|
+
* consuming component if the return value of the selector
|
|
9
|
+
* changes.
|
|
10
|
+
*/ export function useEditorStateSelector(selector) {
|
|
11
|
+
const store = useContext(EditorStateStoreContext);
|
|
12
|
+
const getSnapshot = ()=>selector(store.getState());
|
|
13
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
14
|
+
}
|