@handlewithcare/react-prosemirror 2.4.10 → 2.4.12

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 (61) hide show
  1. package/README.md +16 -0
  2. package/dist/cjs/components/Editor.js +28 -0
  3. package/dist/cjs/components/NodeViews.js +73 -0
  4. package/dist/cjs/{contexts/__tests__/DeferredLayoutEffects.test.js → components/__tests__/LayoutGroup.test.js} +2 -2
  5. package/dist/cjs/components/__tests__/ProseMirror.test.js +136 -263
  6. package/dist/cjs/contexts/NodeViewsContext.js +10 -0
  7. package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +27 -28
  8. package/dist/cjs/hooks/__tests__/useNodeViews.test.js +159 -0
  9. package/dist/cjs/hooks/useEditor.js +2 -4
  10. package/dist/cjs/hooks/useEditorView.js +100 -0
  11. package/dist/cjs/hooks/useNodePos.js +69 -0
  12. package/dist/cjs/hooks/useNodeViews.js +100 -0
  13. package/dist/cjs/nodeViews/createReactNodeViewConstructor.js +244 -0
  14. package/dist/cjs/nodeViews/phrasingContentTags.js +57 -0
  15. package/dist/cjs/plugins/__tests__/react.test.js +139 -0
  16. package/dist/cjs/plugins/react.js +71 -0
  17. package/dist/esm/components/Editor.js +15 -0
  18. package/dist/esm/components/NodeViews.js +26 -0
  19. package/dist/esm/{contexts/__tests__/DeferredLayoutEffects.test.js → components/__tests__/LayoutGroup.test.js} +2 -2
  20. package/dist/esm/components/__tests__/ProseMirror.test.js +135 -267
  21. package/dist/esm/contexts/NodeViewsContext.js +9 -0
  22. package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +27 -28
  23. package/dist/esm/hooks/__tests__/useNodeViews.test.js +116 -0
  24. package/dist/esm/hooks/useEditor.js +2 -4
  25. package/dist/esm/hooks/useEditorView.js +99 -0
  26. package/dist/esm/hooks/useNodePos.js +16 -0
  27. package/dist/esm/hooks/useNodeViews.js +53 -0
  28. package/dist/esm/nodeViews/createReactNodeViewConstructor.js +214 -0
  29. package/dist/esm/nodeViews/phrasingContentTags.js +49 -0
  30. package/dist/esm/plugins/__tests__/react.test.js +135 -0
  31. package/dist/esm/plugins/react.js +64 -0
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/dist/types/components/Editor.d.ts +7 -0
  34. package/dist/types/components/NodeViews.d.ts +6 -0
  35. package/dist/types/components/__tests__/LayoutGroup.test.d.ts +1 -0
  36. package/dist/types/contexts/NodeViewsContext.d.ts +19 -0
  37. package/dist/types/decorations/ReactWidgetType.d.ts +1 -0
  38. package/dist/types/hooks/__tests__/useNodeViews.test.d.ts +1 -0
  39. package/dist/types/hooks/useEditorView.d.ts +23 -0
  40. package/dist/types/hooks/useNodePos.d.ts +9 -0
  41. package/dist/types/hooks/useNodeViews.d.ts +5 -0
  42. package/dist/types/nodeViews/createReactNodeViewConstructor.d.ts +48 -0
  43. package/dist/types/nodeViews/phrasingContentTags.d.ts +1 -0
  44. package/dist/types/plugins/__tests__/react.test.d.ts +1 -0
  45. package/dist/types/plugins/react.d.ts +21 -0
  46. package/dist/types/props.d.ts +27 -27
  47. package/package.json +1 -1
  48. package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +0 -398
  49. package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +0 -270
  50. package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +0 -1010
  51. package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +0 -337
  52. package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +0 -315
  53. package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +0 -444
  54. package/dist/cjs/plugins/__tests__/reactKeys.test.js +0 -81
  55. package/dist/esm/components/__tests__/ProseMirror.composition.test.js +0 -395
  56. package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +0 -266
  57. package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +0 -967
  58. package/dist/esm/components/__tests__/ProseMirror.draw.test.js +0 -294
  59. package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +0 -272
  60. package/dist/esm/components/__tests__/ProseMirror.selection.test.js +0 -440
  61. package/dist/esm/plugins/__tests__/reactKeys.test.js +0 -77
@@ -0,0 +1,135 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Schema } from "prosemirror-model";
2
+ import { EditorState } from "prosemirror-state";
3
+ import { findWrapping } from "prosemirror-transform";
4
+ import { react, reactPluginKey } from "../react.js";
5
+ const schema = new Schema({
6
+ nodes: {
7
+ doc: {
8
+ content: "block+"
9
+ },
10
+ paragraph: {
11
+ group: "block",
12
+ content: "inline*"
13
+ },
14
+ list: {
15
+ group: "block",
16
+ content: "list_item+"
17
+ },
18
+ list_item: {
19
+ content: "paragraph+"
20
+ },
21
+ text: {
22
+ group: "inline"
23
+ }
24
+ }
25
+ });
26
+ describe("reactNodeViewPlugin", ()=>{
27
+ it("should create a unique key for each node", ()=>{
28
+ const editorState = EditorState.create({
29
+ doc: schema.topNodeType.create(null, [
30
+ schema.nodes.paragraph.create(),
31
+ schema.nodes.paragraph.create(),
32
+ schema.nodes.paragraph.create()
33
+ ]),
34
+ plugins: [
35
+ react()
36
+ ]
37
+ });
38
+ const pluginState = reactPluginKey.getState(editorState);
39
+ expect(pluginState.posToKey.size).toBe(3);
40
+ });
41
+ it("should maintain key stability when possible", ()=>{
42
+ const initialEditorState = EditorState.create({
43
+ doc: schema.topNodeType.create(null, [
44
+ schema.nodes.paragraph.create(),
45
+ schema.nodes.paragraph.create(),
46
+ schema.nodes.paragraph.create()
47
+ ]),
48
+ plugins: [
49
+ react()
50
+ ]
51
+ });
52
+ const initialPluginState = reactPluginKey.getState(initialEditorState);
53
+ const nextEditorState = initialEditorState.apply(initialEditorState.tr.insertText("Hello, world!", 1));
54
+ const nextPluginState = reactPluginKey.getState(nextEditorState);
55
+ expect(Array.from(nextPluginState.keyToPos.keys())).toEqual(Array.from(initialPluginState.keyToPos.keys()));
56
+ });
57
+ it("should create unique keys for new nodes", ()=>{
58
+ const initialEditorState = EditorState.create({
59
+ doc: schema.topNodeType.create(null, [
60
+ schema.nodes.paragraph.create(),
61
+ schema.nodes.paragraph.create(),
62
+ schema.nodes.paragraph.create()
63
+ ]),
64
+ plugins: [
65
+ react()
66
+ ]
67
+ });
68
+ const initialPluginState = reactPluginKey.getState(initialEditorState);
69
+ const nextEditorState = initialEditorState.apply(initialEditorState.tr.insert(0, schema.nodes.list.createAndFill()));
70
+ const nextPluginState = reactPluginKey.getState(nextEditorState);
71
+ // Adds new keys for new nodes
72
+ expect(nextPluginState.keyToPos.size).toBe(6);
73
+ // Maintains keys for previous nodes that are still there
74
+ Array.from(initialPluginState.keyToPos.keys()).forEach((key)=>{
75
+ expect(Array.from(nextPluginState.keyToPos.keys())).toContain(key);
76
+ });
77
+ });
78
+ it("should maintain key stability when splitting a node", ()=>{
79
+ const initialEditorState = EditorState.create({
80
+ doc: schema.topNodeType.create(null, [
81
+ schema.nodes.list.create(null, [
82
+ schema.nodes.list_item.create(null, [
83
+ schema.nodes.paragraph.create(null, [
84
+ schema.text("first")
85
+ ])
86
+ ])
87
+ ])
88
+ ]),
89
+ plugins: [
90
+ react()
91
+ ]
92
+ });
93
+ const initialPluginState = reactPluginKey.getState(initialEditorState);
94
+ const nextEditorState = initialEditorState.apply(initialEditorState.tr.insert(1, schema.nodes.list_item.create(null, [
95
+ schema.nodes.paragraph.create(null, [
96
+ schema.text("second")
97
+ ])
98
+ ])));
99
+ const nextPluginState = reactPluginKey.getState(nextEditorState);
100
+ // The new list item was inserted before the original one,
101
+ // pushing it further into the document. The original list
102
+ // item should keep its original key, and the new list item
103
+ // should be assigned a new one
104
+ expect(nextPluginState.posToKey.get(11)).toBe(initialPluginState.posToKey.get(1));
105
+ expect(nextPluginState.posToKey.get(1)).not.toBe(initialPluginState.posToKey.get(1));
106
+ });
107
+ it("should maintain key stability when wrapping a node", ()=>{
108
+ const initialEditorState = EditorState.create({
109
+ doc: schema.topNodeType.create(null, [
110
+ schema.nodes.paragraph.create(null, [
111
+ schema.text("content")
112
+ ])
113
+ ]),
114
+ plugins: [
115
+ react()
116
+ ]
117
+ });
118
+ const initialPluginState = reactPluginKey.getState(initialEditorState);
119
+ const start = 1;
120
+ const end = 9;
121
+ const tr = initialEditorState.tr.delete(start, end);
122
+ const $start = tr.doc.resolve(start);
123
+ const range = $start.blockRange();
124
+ const wrapping = range && findWrapping(range, schema.nodes.list, null);
125
+ tr.wrap(range, wrapping);
126
+ const nextEditorState = initialEditorState.apply(tr);
127
+ const nextPluginState = reactPluginKey.getState(nextEditorState);
128
+ // The new list and list item nodes were wrapped around the
129
+ // paragraph, pushing it further into the document. The paragraph
130
+ // should keep its original key, and the new nodes
131
+ // should be assigned a new one
132
+ expect(nextPluginState.posToKey.get(2)).toBe(initialPluginState.posToKey.get(0));
133
+ expect(nextPluginState.posToKey.get(0)).not.toBe(initialPluginState.posToKey.get(0));
134
+ });
135
+ });
@@ -0,0 +1,64 @@
1
+ import { Plugin, PluginKey } from "prosemirror-state";
2
+ /**
3
+ * This is a stand-in for the doc node itself, which doesn't have a
4
+ * unique position to map to.
5
+ */ export const ROOT_NODE_KEY = Symbol("@nytimes/react-prosemirror/root-node-key");
6
+ export function createNodeKey() {
7
+ return Math.floor(Math.random() * 0xffffff).toString(16);
8
+ }
9
+ export const reactPluginKey = new PluginKey("@nytimes/react-prosemirror/react");
10
+ /**
11
+ * Tracks a unique key for each (non-text) node in the
12
+ * document, identified by its current position. Keys are
13
+ * (mostly) stable across transaction applications. The
14
+ * key for a given node can be accessed by that node's
15
+ * current position in the document, and vice versa.
16
+ */ export function react() {
17
+ return new Plugin({
18
+ key: reactPluginKey,
19
+ state: {
20
+ init (_, state) {
21
+ const next = {
22
+ posToKey: new Map(),
23
+ keyToPos: new Map()
24
+ };
25
+ state.doc.descendants((node, pos)=>{
26
+ if (node.isText) return false;
27
+ const key = createNodeKey();
28
+ next.posToKey.set(pos, key);
29
+ next.keyToPos.set(key, pos);
30
+ return true;
31
+ });
32
+ return next;
33
+ },
34
+ /**
35
+ * Keeps node keys (mostly) stable across transactions.
36
+ *
37
+ * To accomplish this, we map each node position backwards
38
+ * through the transaction to identify its previous position,
39
+ * and thereby retrieve its previous key.
40
+ */ apply (tr, value, _, newState) {
41
+ if (!tr.docChanged) return value;
42
+ const next = {
43
+ posToKey: new Map(),
44
+ keyToPos: new Map()
45
+ };
46
+ for (const [pos, key] of value.posToKey.entries()){
47
+ const { pos: newPos , deleted } = tr.mapping.mapResult(pos);
48
+ if (deleted) continue;
49
+ next.posToKey.set(newPos, key);
50
+ next.keyToPos.set(key, newPos);
51
+ }
52
+ newState.doc.descendants((node, pos)=>{
53
+ if (node.isText) return false;
54
+ if (next.posToKey.has(pos)) return true;
55
+ const key = createNodeKey();
56
+ next.posToKey.set(pos, key);
57
+ next.keyToPos.set(key, pos);
58
+ return true;
59
+ });
60
+ return next;
61
+ }
62
+ }
63
+ });
64
+ }