@handlewithcare/react-prosemirror 2.5.0 → 2.5.2

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.
Files changed (53) hide show
  1. package/dist/cjs/ReactEditorView.js +6 -6
  2. package/dist/cjs/components/ChildNodeViews.js +6 -6
  3. package/dist/cjs/components/CustomNodeView.js +77 -121
  4. package/dist/cjs/components/DefaultNodeView.js +67 -0
  5. package/dist/cjs/components/DocNodeView.js +33 -32
  6. package/dist/cjs/components/NodeView.js +31 -21
  7. package/dist/cjs/components/ProseMirror.js +20 -9
  8. package/dist/cjs/components/ProseMirrorDoc.js +7 -27
  9. package/dist/cjs/components/ReactNodeView.js +96 -58
  10. package/dist/cjs/hooks/useEditor.js +1 -1
  11. package/dist/cjs/hooks/useIgnoreMutation.js +1 -1
  12. package/dist/cjs/hooks/useNodeViewDescriptor.js +121 -78
  13. package/dist/cjs/hooks/useSelectNode.js +9 -7
  14. package/dist/cjs/hooks/useStopEvent.js +1 -1
  15. package/dist/cjs/plugins/beforeInputPlugin.js +12 -0
  16. package/dist/cjs/viewdesc.js +95 -17
  17. package/dist/esm/ReactEditorView.js +6 -6
  18. package/dist/esm/components/ChildNodeViews.js +6 -6
  19. package/dist/esm/components/CustomNodeView.js +78 -122
  20. package/dist/esm/components/DefaultNodeView.js +16 -0
  21. package/dist/esm/components/DocNodeView.js +33 -32
  22. package/dist/esm/components/NodeView.js +32 -22
  23. package/dist/esm/components/ProseMirror.js +20 -9
  24. package/dist/esm/components/ProseMirrorDoc.js +7 -28
  25. package/dist/esm/components/ReactNodeView.js +97 -59
  26. package/dist/esm/hooks/useEditor.js +1 -1
  27. package/dist/esm/hooks/useIgnoreMutation.js +1 -1
  28. package/dist/esm/hooks/useNodeViewDescriptor.js +123 -80
  29. package/dist/esm/hooks/useSelectNode.js +9 -7
  30. package/dist/esm/hooks/useStopEvent.js +1 -1
  31. package/dist/esm/plugins/beforeInputPlugin.js +12 -0
  32. package/dist/esm/viewdesc.js +92 -17
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/dist/types/ReactEditorView.d.ts +3 -2
  35. package/dist/types/components/CustomNodeView.d.ts +1 -1
  36. package/dist/types/components/DefaultNodeView.d.ts +3 -0
  37. package/dist/types/components/DocNodeView.d.ts +8 -13
  38. package/dist/types/components/NodeView.d.ts +4 -4
  39. package/dist/types/components/NodeViewComponentProps.d.ts +3 -4
  40. package/dist/types/components/ProseMirrorDoc.d.ts +14 -8
  41. package/dist/types/components/ReactNodeView.d.ts +3 -1
  42. package/dist/types/contexts/IgnoreMutationContext.d.ts +2 -1
  43. package/dist/types/contexts/NodeViewContext.d.ts +3 -1
  44. package/dist/types/contexts/SelectNodeContext.d.ts +3 -1
  45. package/dist/types/contexts/StopEventContext.d.ts +2 -1
  46. package/dist/types/hooks/useEditor.d.ts +1 -1
  47. package/dist/types/hooks/useNodeViewDescriptor.d.ts +18 -10
  48. package/dist/types/hooks/useSelectNode.d.ts +2 -1
  49. package/dist/types/viewdesc.d.ts +25 -8
  50. package/package.json +3 -3
  51. package/dist/cjs/hooks/useClientOnly.js +0 -19
  52. package/dist/esm/hooks/useClientOnly.js +0 -9
  53. package/dist/types/hooks/useClientOnly.d.ts +0 -1
@@ -1,30 +1,45 @@
1
- import React, { cloneElement, memo, useContext, useMemo, useRef } from "react";
1
+ import React, { cloneElement, memo, useCallback, useMemo, useRef, useState } from "react";
2
2
  import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
3
3
  import { IgnoreMutationContext } from "../contexts/IgnoreMutationContext.js";
4
- import { NodeViewContext } from "../contexts/NodeViewContext.js";
5
4
  import { SelectNodeContext } from "../contexts/SelectNodeContext.js";
6
5
  import { StopEventContext } from "../contexts/StopEventContext.js";
7
6
  import { useNodeViewDescriptor } from "../hooks/useNodeViewDescriptor.js";
8
7
  import { ChildNodeViews, wrapInDeco } from "./ChildNodeViews.js";
9
- import { OutputSpec } from "./OutputSpec.js";
10
8
  export const ReactNodeView = /*#__PURE__*/ memo(function ReactNodeView(param) {
11
- let { outerDeco, getPos, node, innerDeco, ...props } = param;
12
- const domRef = useRef(null);
13
- const nodeDomRef = useRef(null);
14
- const contentDomRef = useRef(null);
15
- const { nodeViews } = useContext(NodeViewContext);
16
- let element = null;
17
- const Component = nodeViews[node.type.name];
18
- const outputSpec = useMemo(()=>node.type.spec.toDOM?.(node), [
19
- node
20
- ]);
21
- const { hasContentDOM, childDescriptors, setStopEvent, setSelectNode, setIgnoreMutation, nodeViewDescRef } = useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDeco, outerDeco, contentDomRef);
22
- const finalProps = {
23
- ...props,
24
- ...!hasContentDOM && nodeDomRef.current?.tagName !== "BR" && {
25
- contentEditable: false
26
- }
27
- };
9
+ let { component: Component, outerDeco, getPos, node, innerDeco } = param;
10
+ const [controlSelected, setControlSelected] = useState(false);
11
+ const [selected, setSelected] = useState(false);
12
+ const ref = useRef(null);
13
+ const innerRef = useRef(null);
14
+ const selectNodeRef = useRef(null);
15
+ const deselectNodeRef = useRef(null);
16
+ const stopEventRef = useRef(null);
17
+ const ignoreMutationRef = useRef(null);
18
+ const setSelectNode = useCallback((selectHandler, deselectHandler)=>{
19
+ selectNodeRef.current = selectHandler;
20
+ deselectNodeRef.current = deselectHandler;
21
+ setControlSelected(true);
22
+ return ()=>{
23
+ selectNodeRef.current = null;
24
+ deselectNodeRef.current = null;
25
+ setControlSelected(false);
26
+ };
27
+ }, []);
28
+ const setStopEvent = useCallback((handler)=>{
29
+ stopEventRef.current = handler;
30
+ return ()=>{
31
+ stopEventRef.current = null;
32
+ };
33
+ }, []);
34
+ const setIgnoreMutation = useCallback((handler)=>{
35
+ ignoreMutationRef.current = handler;
36
+ return ()=>{
37
+ ignoreMutationRef.current = null;
38
+ return ()=>{
39
+ ignoreMutationRef.current = null;
40
+ };
41
+ };
42
+ }, []);
28
43
  const nodeProps = useMemo(()=>({
29
44
  node: node,
30
45
  getPos: getPos,
@@ -36,45 +51,68 @@ export const ReactNodeView = /*#__PURE__*/ memo(function ReactNodeView(param) {
36
51
  node,
37
52
  outerDeco
38
53
  ]);
39
- if (Component) {
40
- element = /*#__PURE__*/ React.createElement(Component, {
41
- ...finalProps,
42
- ref: nodeDomRef,
43
- nodeProps: nodeProps
44
- }, /*#__PURE__*/ React.createElement(ChildNodeViews, {
45
- getPos: getPos,
46
- node: node,
47
- innerDecorations: innerDeco
48
- }));
49
- } else {
50
- if (outputSpec) {
51
- element = /*#__PURE__*/ React.createElement(OutputSpec, {
52
- ...finalProps,
53
- ref: nodeDomRef,
54
- outputSpec: outputSpec
55
- }, /*#__PURE__*/ React.createElement(ChildNodeViews, {
56
- getPos: getPos,
57
- node: node,
58
- innerDecorations: innerDeco
59
- }));
60
- }
61
- }
62
- if (!element) {
63
- throw new Error(`Node spec for ${node.type.name} is missing toDOM`);
64
- }
65
- const decoratedElement = /*#__PURE__*/ cloneElement(outerDeco.reduce(wrapInDeco, element), // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
- outerDeco.some((d)=>d.type.attrs.nodeName) ? {
67
- ref: domRef
68
- } : // we've already passed the domRef to the NodeView component
69
- // as a prop
70
- undefined);
71
- const childContextValue = useMemo(()=>({
72
- parentRef: nodeViewDescRef,
73
- siblingsRef: childDescriptors
74
- }), [
75
- childDescriptors,
76
- nodeViewDescRef
77
- ]);
54
+ const { childContextValue, contentDOM, nodeDOM } = useNodeViewDescriptor(ref, ()=>{
55
+ setSelected(false);
56
+ return {
57
+ dom: innerRef.current ?? ref.current,
58
+ update () {
59
+ return true;
60
+ },
61
+ multiType: true,
62
+ selectNode () {
63
+ const selectNode = selectNodeRef.current;
64
+ if (selectNode) {
65
+ selectNode();
66
+ }
67
+ setSelected(true);
68
+ },
69
+ deselectNode () {
70
+ const deselectNode = deselectNodeRef.current;
71
+ if (deselectNode) {
72
+ deselectNode();
73
+ }
74
+ setSelected(false);
75
+ },
76
+ stopEvent (event) {
77
+ const stopEvent = stopEventRef.current;
78
+ if (stopEvent) {
79
+ return stopEvent(event);
80
+ }
81
+ return false;
82
+ },
83
+ ignoreMutation (mutation) {
84
+ const ignoreMutation = ignoreMutationRef.current;
85
+ if (ignoreMutation) {
86
+ return ignoreMutation(mutation);
87
+ }
88
+ return false;
89
+ }
90
+ };
91
+ }, nodeProps);
92
+ const children = !node.isLeaf ? /*#__PURE__*/ React.createElement(ChildNodeViews, {
93
+ getPos: getPos,
94
+ node: node,
95
+ innerDecorations: innerDeco
96
+ }) : null;
97
+ const innerProps = {
98
+ nodeProps,
99
+ ...!contentDOM && !nodeProps.node.isText && nodeDOM?.nodeName !== "BR" ? {
100
+ contentEditable: false,
101
+ suppressContentEditableWarning: true
102
+ } : null,
103
+ ...controlSelected && selected ? {
104
+ className: "ProseMirror-selectednode"
105
+ } : null,
106
+ ref: innerRef
107
+ };
108
+ const innerElement = /*#__PURE__*/ React.createElement(Component, innerProps, children);
109
+ const props = {
110
+ ...controlSelected && selected || node.type.spec.draggable ? {
111
+ draggable: true
112
+ } : null,
113
+ ref
114
+ };
115
+ const decoratedElement = /*#__PURE__*/ cloneElement(outerDeco.reduce(wrapInDeco, innerElement), props);
78
116
  return /*#__PURE__*/ React.createElement(SelectNodeContext.Provider, {
79
117
  value: setSelectNode
80
118
  }, /*#__PURE__*/ React.createElement(StopEventContext.Provider, {
@@ -19,7 +19,7 @@ let didWarnValueDefaultValue = false;
19
19
  */ export function useEditor(mount, options) {
20
20
  if (process.env.NODE_ENV !== "production") {
21
21
  if (options.defaultState !== undefined && options.state !== undefined && !didWarnValueDefaultValue) {
22
- console.error("A component contains a ProseMirror editor with both value and defaultValue 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");
22
+ 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");
23
23
  didWarnValueDefaultValue = true;
24
24
  }
25
25
  }
@@ -6,7 +6,7 @@ export function useIgnoreMutation(ignoreMutation) {
6
6
  const register = useContext(IgnoreMutationContext);
7
7
  const ignoreMutationMemo = useEditorEventCallback(ignoreMutation);
8
8
  useEditorEffect(()=>{
9
- register(ignoreMutationMemo);
9
+ return register(ignoreMutationMemo);
10
10
  }, [
11
11
  register,
12
12
  ignoreMutationMemo
@@ -1,109 +1,152 @@
1
- import { useCallback, useContext, useRef, useState } from "react";
1
+ import { useCallback, useContext, useMemo, useRef, useState } from "react";
2
+ import { ReactEditorView } from "../ReactEditorView.js";
2
3
  import { ChildDescriptorsContext } from "../contexts/ChildDescriptorsContext.js";
3
4
  import { EditorContext } from "../contexts/EditorContext.js";
4
- import { CompositionViewDesc, NodeViewDesc, sortViewDescs } from "../viewdesc.js";
5
+ import { CompositionViewDesc, ReactNodeViewDesc, sortViewDescs } from "../viewdesc.js";
5
6
  import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
6
- export function useNodeViewDescriptor(node, getPos, domRef, nodeDomRef, innerDecorations, outerDecorations, contentDOMRef) {
7
+ function findContentDOM(source, children) {
8
+ return source?.contentDOM ?? children[0]?.dom?.parentElement ?? null;
9
+ }
10
+ export function useNodeViewDescriptor(ref, constructor, props) {
7
11
  const { view } = useContext(EditorContext);
8
- const [hasContentDOM, setHasContentDOM] = useState(true);
9
- const nodeViewDescRef = useRef();
10
- const stopEvent = useRef(()=>false);
11
- const setStopEvent = useCallback((newStopEvent)=>{
12
- stopEvent.current = newStopEvent;
13
- }, []);
14
- const ignoreMutation = useRef(()=>false);
15
- const setIgnoreMutation = useCallback((newIgnoreMutation)=>{
16
- ignoreMutation.current = newIgnoreMutation;
17
- }, []);
18
- const selectNode = useRef(()=>{
19
- if (!nodeDomRef.current) return;
20
- if (nodeDomRef.current.nodeType == 1) nodeDomRef.current.classList.add("ProseMirror-selectednode");
21
- if (contentDOMRef?.current || !node.type.spec.draggable) (domRef?.current ?? nodeDomRef.current).draggable = true;
22
- });
23
- const deselectNode = useRef(()=>{
24
- if (!nodeDomRef.current) return;
25
- if (nodeDomRef.current.nodeType == 1) {
26
- nodeDomRef.current.classList.remove("ProseMirror-selectednode");
27
- if (contentDOMRef?.current || !node.type.spec.draggable) (domRef?.current ?? nodeDomRef.current).removeAttribute("draggable");
12
+ const { parentRef, siblingsRef } = useContext(ChildDescriptorsContext);
13
+ const [dom, setDOM] = useState(null);
14
+ const [nodeDOM, setNodeDOM] = useState(null);
15
+ const [contentDOM, setContentDOM] = useState(null);
16
+ const viewDescRef = useRef();
17
+ const childrenRef = useRef([]);
18
+ const create = useCallback((props)=>{
19
+ if (!(view instanceof ReactEditorView)) {
20
+ return;
28
21
  }
29
- });
30
- const setSelectNode = useCallback((newSelectNode, newDeselectNode)=>{
31
- selectNode.current = newSelectNode;
32
- deselectNode.current = newDeselectNode;
33
- }, []);
34
- const { siblingsRef, parentRef } = useContext(ChildDescriptorsContext);
35
- const childDescriptors = useRef([]);
36
- useClientLayoutEffect(()=>{
22
+ const dom = ref.current;
23
+ if (!dom) {
24
+ return;
25
+ }
26
+ const { node, getPos, decorations, innerDecorations } = props;
27
+ const nodeView = constructor(node, view, getPos, decorations, innerDecorations);
28
+ if (!nodeView) {
29
+ return;
30
+ }
31
+ const parent = parentRef.current;
32
+ const children = childrenRef.current;
33
+ const contentDOM = findContentDOM(nodeView, children);
34
+ const nodeDOM = nodeView.dom;
35
+ const viewDesc = new ReactNodeViewDesc(parent, children, getPos, node, decorations, innerDecorations, dom, contentDOM, nodeDOM, nodeView);
36
+ setDOM(dom);
37
+ setContentDOM(contentDOM);
38
+ setNodeDOM(nodeDOM);
39
+ return viewDesc;
40
+ }, [
41
+ ref,
42
+ parentRef,
43
+ constructor,
44
+ view
45
+ ]);
46
+ const update = useCallback((props)=>{
47
+ if (!(view instanceof ReactEditorView)) {
48
+ return false;
49
+ }
50
+ const viewDesc = viewDescRef.current;
51
+ if (!viewDesc) {
52
+ return false;
53
+ }
54
+ const dom = ref.current;
55
+ if (!dom || dom !== viewDesc.dom) {
56
+ return false;
57
+ }
58
+ const contentDOM = findContentDOM(viewDesc, viewDesc.children);
59
+ if (contentDOM !== viewDesc.contentDOM) {
60
+ return false;
61
+ }
62
+ if (!dom.contains(viewDesc.nodeDOM)) {
63
+ return false;
64
+ }
65
+ const { node, decorations, innerDecorations } = props;
66
+ return viewDesc.matchesNode(node, decorations, innerDecorations) || viewDesc.update(node, decorations, innerDecorations, view);
67
+ }, [
68
+ ref,
69
+ view
70
+ ]);
71
+ const destroy = useCallback(()=>{
72
+ const viewDesc = viewDescRef.current;
73
+ if (!viewDesc) {
74
+ return;
75
+ }
76
+ viewDesc.destroy();
37
77
  const siblings = siblingsRef.current;
38
- return ()=>{
39
- if (!nodeViewDescRef.current) return;
40
- if (siblings.includes(nodeViewDescRef.current)) {
41
- const index = siblings.indexOf(nodeViewDescRef.current);
42
- siblings.splice(index, 1);
43
- }
44
- };
78
+ if (siblings.includes(viewDesc)) {
79
+ const index = siblings.indexOf(viewDesc);
80
+ siblings.splice(index, 1);
81
+ }
82
+ setDOM(null);
83
+ setContentDOM(null);
84
+ setNodeDOM(null);
45
85
  }, [
46
86
  siblingsRef
47
87
  ]);
48
- // eslint-disable-next-line react-hooks/exhaustive-deps
49
88
  useClientLayoutEffect(()=>{
50
- if (!nodeDomRef.current) return;
51
- const firstChildDesc = childDescriptors.current[0];
52
- if (!nodeViewDescRef.current) {
53
- nodeViewDescRef.current = new NodeViewDesc(parentRef.current, childDescriptors.current, getPos, node, outerDecorations, innerDecorations, domRef?.current ?? nodeDomRef.current, contentDOMRef?.current ?? firstChildDesc?.dom.parentElement ?? null, nodeDomRef.current, (event)=>!!stopEvent.current(event), ()=>selectNode.current(), ()=>deselectNode.current(), (mutation)=>ignoreMutation.current(mutation));
54
- } else {
55
- nodeViewDescRef.current.parent = parentRef.current;
56
- nodeViewDescRef.current.children = childDescriptors.current;
57
- nodeViewDescRef.current.node = node;
58
- nodeViewDescRef.current.outerDeco = outerDecorations;
59
- nodeViewDescRef.current.innerDeco = innerDecorations;
60
- nodeViewDescRef.current.dom = domRef?.current ?? nodeDomRef.current;
61
- nodeViewDescRef.current.dom.pmViewDesc = nodeViewDescRef.current;
62
- nodeViewDescRef.current.contentDOM = // If there's already a contentDOM, we can just
63
- // keep it; it won't have changed. This is especially
64
- // important during compositions, where the
65
- // firstChildDesc might not have a correct dom node set yet.
66
- contentDOMRef?.current ?? nodeViewDescRef.current.contentDOM ?? firstChildDesc?.dom.parentElement ?? null;
67
- nodeViewDescRef.current.nodeDOM = nodeDomRef.current;
89
+ if (!update(props)) {
90
+ destroy();
91
+ viewDescRef.current = create(props);
92
+ }
93
+ const viewDesc = viewDescRef.current;
94
+ if (!viewDesc) {
95
+ return;
68
96
  }
69
- setHasContentDOM(nodeViewDescRef.current.contentDOM !== null);
70
- if (!siblingsRef.current.includes(nodeViewDescRef.current)) {
71
- siblingsRef.current.push(nodeViewDescRef.current);
97
+ if (view.dom === viewDesc.dom && view instanceof ReactEditorView) {
98
+ view.docView = viewDesc;
99
+ }
100
+ const parent = parentRef.current;
101
+ const siblings = siblingsRef.current;
102
+ const children = childrenRef.current;
103
+ viewDesc.parent = parent;
104
+ if (!siblings.includes(viewDesc)) {
105
+ siblings.push(viewDesc);
72
106
  }
73
- siblingsRef.current.sort(sortViewDescs);
74
- for (const childDesc of childDescriptors.current){
75
- childDesc.parent = nodeViewDescRef.current;
107
+ siblings.sort(sortViewDescs);
108
+ for (const child of children){
109
+ child.parent = viewDesc;
76
110
  // Because TextNodeViews can't locate the DOM nodes
77
111
  // for compositions, we need to override them here
78
- if (childDesc instanceof CompositionViewDesc) {
79
- const compositionTopDOM = nodeViewDescRef.current.contentDOM?.firstChild;
112
+ if (child instanceof CompositionViewDesc) {
113
+ const compositionTopDOM = viewDesc?.contentDOM?.firstChild;
80
114
  if (!compositionTopDOM) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
81
115
  let textDOM = compositionTopDOM;
82
116
  while(textDOM.firstChild){
83
117
  textDOM = textDOM.firstChild;
84
118
  }
85
119
  if (!textDOM || !(textDOM instanceof Text)) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
86
- childDesc.dom = compositionTopDOM;
87
- childDesc.textDOM = textDOM;
88
- childDesc.text = textDOM.data;
89
- childDesc.textDOM.pmViewDesc = childDesc;
120
+ child.dom = compositionTopDOM;
121
+ child.textDOM = textDOM;
122
+ child.text = textDOM.data;
123
+ child.textDOM.pmViewDesc = child;
90
124
  // It should not be possible to be in a composition because one could
91
125
  // not start between the renders that switch the view type.
92
- view.input.compositionNodes.push(childDesc);
126
+ view.input.compositionNodes.push(child);
93
127
  }
94
128
  }
129
+ });
130
+ useClientLayoutEffect(()=>{
95
131
  return ()=>{
96
- if (nodeViewDescRef.current?.children[0] instanceof CompositionViewDesc && !view.composing) {
97
- nodeViewDescRef.current?.children[0].dom.parentNode?.removeChild(nodeViewDescRef.current?.children[0].dom);
98
- }
132
+ destroy();
133
+ viewDescRef.current = undefined;
99
134
  };
100
- });
135
+ }, [
136
+ destroy
137
+ ]);
138
+ const childContextValue = useMemo(()=>({
139
+ parentRef: viewDescRef,
140
+ siblingsRef: childrenRef
141
+ }), [
142
+ childrenRef,
143
+ viewDescRef
144
+ ]);
101
145
  return {
102
- hasContentDOM,
103
- childDescriptors,
104
- nodeViewDescRef,
105
- setStopEvent,
106
- setSelectNode,
107
- setIgnoreMutation
146
+ childContextValue,
147
+ dom,
148
+ contentDOM,
149
+ nodeDOM,
150
+ ref
108
151
  };
109
152
  }
@@ -2,17 +2,19 @@ import { useContext } from "react";
2
2
  import { SelectNodeContext } from "../contexts/SelectNodeContext.js";
3
3
  import { useEditorEffect } from "./useEditorEffect.js";
4
4
  import { useEditorEventCallback } from "./useEditorEventCallback.js";
5
- export function useSelectNode(selectNode, deselectNode) {
5
+ function noop() {
6
+ // empty
7
+ }
8
+ export function useSelectNode(selectNode) {
9
+ let deselectNode = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
6
10
  const register = useContext(SelectNodeContext);
7
11
  const selectNodeMemo = useEditorEventCallback(selectNode);
8
- const deselectNodeMemo = useEditorEventCallback(deselectNode ?? (()=>{
9
- // empty
10
- }));
12
+ const deselectNodeMemo = useEditorEventCallback(deselectNode);
11
13
  return useEditorEffect(()=>{
12
- register(selectNodeMemo, deselectNodeMemo);
14
+ return register(selectNodeMemo, deselectNodeMemo);
13
15
  }, [
14
- deselectNodeMemo,
15
16
  register,
16
- selectNodeMemo
17
+ selectNodeMemo,
18
+ deselectNodeMemo
17
19
  ]);
18
20
  }
@@ -6,7 +6,7 @@ export function useStopEvent(stopEvent) {
6
6
  const register = useContext(StopEventContext);
7
7
  const stopEventMemo = useEditorEventCallback(stopEvent);
8
8
  useEditorEffect(()=>{
9
- register(stopEventMemo);
9
+ return register(stopEventMemo);
10
10
  }, [
11
11
  register,
12
12
  stopEventMemo
@@ -86,6 +86,14 @@ export function beforeInputPlugin(setCursorWrapper) {
86
86
  case "insertParagraph":
87
87
  case "insertLineBreak":
88
88
  {
89
+ // ProseMirror-view has a hack that runs the Enter event handlers
90
+ // on iOS, to avoid a bug in Safari with calling event.preventDefault() on
91
+ // Enter events.
92
+ //
93
+ // We want to prevent that hack, because we run the Enter event handlers
94
+ // here, where there is no such bug. So we set this flag, which prosemirror-view
95
+ // uses to check whether it should run the deferred event handlers.
96
+ view.input.lastIOSEnter = 0;
89
97
  // Fire a synthetic keydown event to trigger ProseMirror's keymap
90
98
  const keyEvent = new KeyboardEvent("keydown", {
91
99
  bubbles: true,
@@ -119,8 +127,12 @@ export function beforeInputPlugin(setCursorWrapper) {
119
127
  break;
120
128
  }
121
129
  case "deleteWordBackward":
130
+ case "deleteHardLineBackward":
131
+ case "deleteSoftLineBackward":
122
132
  case "deleteContentBackward":
123
133
  case "deleteWordForward":
134
+ case "deleteHardLineForward":
135
+ case "deleteSoftLineForward":
124
136
  case "deleteContentForward":
125
137
  case "deleteContent":
126
138
  {
@@ -82,7 +82,9 @@ export class ViewDesc {
82
82
  return 0;
83
83
  }
84
84
  destroy() {
85
- // pass
85
+ this.parent = undefined;
86
+ if (this.dom.pmViewDesc == this) this.dom.pmViewDesc = undefined;
87
+ for(let i = 0; i < this.children.length; i++)this.children[i].destroy();
86
88
  }
87
89
  posBeforeChild(child) {
88
90
  for(let i = 0, pos = this.posAtStart;; i++){
@@ -541,15 +543,8 @@ export class NodeViewDesc extends ViewDesc {
541
543
  outerDeco;
542
544
  innerDeco;
543
545
  nodeDOM;
544
- stopEvent;
545
- selectNode;
546
- deselectNode;
547
- ignoreMutation;
548
- constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, stopEvent, selectNode, deselectNode, ignoreMutation){
549
- super(parent, children, getPos, dom, contentDOM), this.node = node, this.outerDeco = outerDeco, this.innerDeco = innerDeco, this.nodeDOM = nodeDOM, this.stopEvent = stopEvent, this.selectNode = selectNode, this.deselectNode = deselectNode, this.ignoreMutation = ignoreMutation;
550
- }
551
- updateOuterDeco() {
552
- // pass
546
+ constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM){
547
+ super(parent, children, getPos, dom, contentDOM), this.node = node, this.outerDeco = outerDeco, this.innerDeco = innerDeco, this.nodeDOM = nodeDOM;
553
548
  }
554
549
  parseRule() {
555
550
  // Experimental kludge to allow opt-in re-parsing of nodes
@@ -593,23 +588,46 @@ export class NodeViewDesc extends ViewDesc {
593
588
  get border() {
594
589
  return this.node.isLeaf ? 0 : 1;
595
590
  }
591
+ updateChildren(_view, _pos) {
592
+ // Omitted to avoid reproducing lots of unused code.
593
+ // Overidden elsewhere in case this is ever vendored.
594
+ }
596
595
  // If this desc must be updated to match the given node decoration,
597
596
  // do so and return true.
598
- update(_node, _outerDeco, _innerDeco, _view) {
599
- this.dirty = NOT_DIRTY;
597
+ update(node, outerDeco, innerDeco, view) {
598
+ if (this.dirty == NODE_DIRTY || !node.sameMarkup(this.node)) return false;
599
+ this.updateInner(node, outerDeco, innerDeco, view);
600
600
  return true;
601
601
  }
602
+ updateInner(node, outerDeco, innerDeco, view) {
603
+ this.updateOuterDeco(outerDeco);
604
+ this.node = node;
605
+ this.innerDeco = innerDeco;
606
+ if (this.contentDOM) this.updateChildren(view, this.posAtStart);
607
+ this.dirty = NOT_DIRTY;
608
+ }
609
+ updateOuterDeco(outerDeco) {
610
+ this.outerDeco = outerDeco;
611
+ }
612
+ // Mark this node as being the selected node.
613
+ selectNode() {
614
+ if (this.nodeDOM.nodeType == 1) this.nodeDOM.classList.add("ProseMirror-selectednode");
615
+ if (this.contentDOM || !this.node.type.spec.draggable) this.dom.draggable = true;
616
+ }
617
+ // Remove selected node marking from this node.
618
+ deselectNode() {
619
+ if (this.nodeDOM.nodeType == 1) {
620
+ this.nodeDOM.classList.remove("ProseMirror-selectednode");
621
+ if (this.contentDOM || !this.node.type.spec.draggable) this.dom.removeAttribute("draggable");
622
+ }
623
+ }
602
624
  get domAtom() {
603
625
  return this.node.isAtom;
604
626
  }
605
627
  }
606
628
  export class TextViewDesc extends NodeViewDesc {
607
629
  constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, nodeDOM){
608
- super(parent, children, getPos, node, outerDeco, innerDeco, dom, null, nodeDOM, ()=>false, ()=>{
609
- /* Text nodes can't have node selections */ }, ()=>{
610
- /* Text nodes can't have node selections */ }, (mutation)=>{
611
- return mutation.type != "characterData" && mutation.type != "selection";
612
- });
630
+ super(parent, children, getPos, node, outerDeco, innerDeco, dom, null, nodeDOM);
613
631
  }
614
632
  parseRule() {
615
633
  let skip = this.nodeDOM.parentNode;
@@ -663,6 +681,63 @@ export class TrailingHackViewDesc extends ViewDesc {
663
681
  return this.dom.nodeName == "IMG";
664
682
  }
665
683
  }
684
+ // A separate subclass is used for customized node views, so that the
685
+ // extra checks only have to be made for nodes that are actually
686
+ // customized.
687
+ let CustomNodeViewDesc = class CustomNodeViewDesc extends NodeViewDesc {
688
+ spec;
689
+ constructor(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM, spec){
690
+ super(parent, children, getPos, node, outerDeco, innerDeco, dom, contentDOM, nodeDOM), this.spec = spec;
691
+ }
692
+ // A custom `update` method gets to decide whether the update goes
693
+ // through. If it does, and there's a `contentDOM` node, our logic
694
+ // updates the children.
695
+ update(node, outerDeco, innerDeco, view) {
696
+ if (this.dirty == NODE_DIRTY) return false;
697
+ if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
698
+ const result = this.spec.update(node, outerDeco, innerDeco);
699
+ if (result) this.updateInner(node, outerDeco, innerDeco, view);
700
+ return result;
701
+ } else if (!this.contentDOM && !node.isLeaf) {
702
+ return false;
703
+ } else {
704
+ return super.update(node, outerDeco, innerDeco, view);
705
+ }
706
+ }
707
+ selectNode() {
708
+ this.spec.selectNode ? this.spec.selectNode() : super.selectNode();
709
+ }
710
+ deselectNode() {
711
+ this.spec.deselectNode ? this.spec.deselectNode() : super.deselectNode();
712
+ }
713
+ setSelection(anchor, head, view, force) {
714
+ this.spec.setSelection ? this.spec.setSelection(anchor, head, view.root) : super.setSelection(anchor, head, view, force);
715
+ }
716
+ destroy() {
717
+ if (this.spec.destroy) this.spec.destroy();
718
+ super.destroy();
719
+ }
720
+ stopEvent(event) {
721
+ return this.spec.stopEvent ? this.spec.stopEvent(event) : false;
722
+ }
723
+ ignoreMutation(mutation) {
724
+ return this.spec.ignoreMutation ? this.spec.ignoreMutation(mutation) : super.ignoreMutation(mutation);
725
+ }
726
+ };
727
+ export class ReactNodeViewDesc extends CustomNodeViewDesc {
728
+ updateChildren(_view, _pos) {
729
+ // React has already updated the children.
730
+ }
731
+ updateOuterDeco(outerDeco) {
732
+ // React has already updated the decorations.
733
+ this.outerDeco = outerDeco;
734
+ }
735
+ destroy() {
736
+ // React has already destroyed the children (if needed).
737
+ this.children = [];
738
+ super.destroy();
739
+ }
740
+ }
666
741
  function sameOuterDeco(a, b) {
667
742
  if (a.length != b.length) return false;
668
743
  // @ts-expect-error ...