@handlewithcare/react-prosemirror 2.4.11 → 2.5.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.
Files changed (107) hide show
  1. package/dist/cjs/AbstractEditorView.js +4 -0
  2. package/dist/cjs/ReactEditorView.js +156 -0
  3. package/dist/cjs/StaticEditorView.js +86 -0
  4. package/dist/cjs/components/ChildNodeViews.js +59 -30
  5. package/dist/cjs/components/CustomNodeView.js +9 -25
  6. package/dist/cjs/components/DocNodeView.js +6 -15
  7. package/dist/cjs/components/MarkView.js +1 -2
  8. package/dist/cjs/components/NativeWidgetView.js +2 -3
  9. package/dist/cjs/components/NodeView.js +1 -1
  10. package/dist/cjs/components/ProseMirror.js +11 -14
  11. package/dist/cjs/components/ReactNodeView.js +3 -4
  12. package/dist/cjs/components/SeparatorHackView.js +1 -2
  13. package/dist/cjs/components/TextNodeView.js +4 -5
  14. package/dist/cjs/components/TrailingHackView.js +1 -2
  15. package/dist/cjs/components/WidgetView.js +2 -4
  16. package/dist/cjs/constants.js +33 -0
  17. package/dist/cjs/hooks/useEditor.js +32 -230
  18. package/dist/cjs/hooks/useEditorEffect.js +2 -2
  19. package/dist/cjs/hooks/useEditorEventCallback.js +8 -5
  20. package/dist/cjs/hooks/useNodeViewDescriptor.js +10 -10
  21. package/dist/cjs/hooks/useReactKeys.js +1 -1
  22. package/dist/cjs/testing/editorViewTestHelpers.js +0 -2
  23. package/dist/cjs/viewdesc.js +10 -9
  24. package/dist/esm/AbstractEditorView.js +1 -0
  25. package/dist/esm/ReactEditorView.js +156 -0
  26. package/dist/esm/StaticEditorView.js +76 -0
  27. package/dist/esm/components/ChildNodeViews.js +60 -32
  28. package/dist/esm/components/CustomNodeView.js +9 -25
  29. package/dist/esm/components/DocNodeView.js +6 -15
  30. package/dist/esm/components/MarkView.js +1 -2
  31. package/dist/esm/components/NativeWidgetView.js +2 -3
  32. package/dist/esm/components/NodeView.js +1 -1
  33. package/dist/esm/components/ProseMirror.js +11 -14
  34. package/dist/esm/components/ReactNodeView.js +3 -4
  35. package/dist/esm/components/SeparatorHackView.js +1 -2
  36. package/dist/esm/components/TextNodeView.js +4 -5
  37. package/dist/esm/components/TrailingHackView.js +1 -2
  38. package/dist/esm/components/WidgetView.js +2 -4
  39. package/dist/esm/constants.js +15 -0
  40. package/dist/esm/hooks/useEditor.js +28 -219
  41. package/dist/esm/hooks/useEditorEffect.js +2 -2
  42. package/dist/esm/hooks/useEditorEventCallback.js +8 -5
  43. package/dist/esm/hooks/useNodeViewDescriptor.js +10 -10
  44. package/dist/esm/hooks/useReactKeys.js +1 -1
  45. package/dist/esm/testing/editorViewTestHelpers.js +0 -2
  46. package/dist/esm/viewdesc.js +3 -2
  47. package/dist/tsconfig.tsbuildinfo +1 -1
  48. package/dist/types/AbstractEditorView.d.ts +27 -0
  49. package/dist/types/ReactEditorView.d.ts +79 -0
  50. package/dist/types/StaticEditorView.d.ts +24 -0
  51. package/dist/types/components/ChildNodeViews.d.ts +2 -2
  52. package/dist/types/components/CustomNodeView.d.ts +2 -2
  53. package/dist/types/components/DocNodeView.d.ts +2 -5
  54. package/dist/types/components/MarkView.d.ts +2 -2
  55. package/dist/types/components/NativeWidgetView.d.ts +2 -2
  56. package/dist/types/components/NodeView.d.ts +2 -2
  57. package/dist/types/components/ReactNodeView.d.ts +2 -2
  58. package/dist/types/components/SeparatorHackView.d.ts +2 -2
  59. package/dist/types/components/TextNodeView.d.ts +4 -3
  60. package/dist/types/components/TrailingHackView.d.ts +2 -2
  61. package/dist/types/components/WidgetView.d.ts +2 -2
  62. package/dist/types/constants.d.ts +4 -0
  63. package/dist/types/contexts/EditorContext.d.ts +6 -4
  64. package/dist/types/decorations/computeDocDeco.d.ts +3 -2
  65. package/dist/types/decorations/viewDecorations.d.ts +3 -2
  66. package/dist/types/hooks/useEditor.d.ts +5 -46
  67. package/dist/types/hooks/useNodeViewDescriptor.d.ts +1 -1
  68. package/dist/types/hooks/useReactKeys.d.ts +1 -1
  69. package/dist/types/props.d.ts +26 -26
  70. package/dist/types/viewdesc.d.ts +6 -5
  71. package/package.json +6 -2
  72. package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +0 -398
  73. package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +0 -270
  74. package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +0 -1010
  75. package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +0 -337
  76. package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +0 -315
  77. package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +0 -444
  78. package/dist/cjs/components/__tests__/ProseMirror.test.js +0 -382
  79. package/dist/cjs/contexts/__tests__/DeferredLayoutEffects.test.js +0 -141
  80. package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -108
  81. package/dist/cjs/plugins/__tests__/reactKeys.test.js +0 -81
  82. package/dist/cjs/selection/SelectionDOMObserver.js +0 -171
  83. package/dist/cjs/selection/hasFocusAndSelection.js +0 -35
  84. package/dist/cjs/selection/selectionFromDOM.js +0 -77
  85. package/dist/cjs/selection/selectionToDOM.js +0 -226
  86. package/dist/cjs/ssr.js +0 -85
  87. package/dist/esm/components/__tests__/ProseMirror.composition.test.js +0 -395
  88. package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +0 -266
  89. package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +0 -967
  90. package/dist/esm/components/__tests__/ProseMirror.draw.test.js +0 -294
  91. package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +0 -272
  92. package/dist/esm/components/__tests__/ProseMirror.selection.test.js +0 -440
  93. package/dist/esm/components/__tests__/ProseMirror.test.js +0 -339
  94. package/dist/esm/contexts/__tests__/DeferredLayoutEffects.test.js +0 -98
  95. package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -99
  96. package/dist/esm/plugins/__tests__/reactKeys.test.js +0 -77
  97. package/dist/esm/selection/SelectionDOMObserver.js +0 -161
  98. package/dist/esm/selection/hasFocusAndSelection.js +0 -17
  99. package/dist/esm/selection/selectionFromDOM.js +0 -59
  100. package/dist/esm/selection/selectionToDOM.js +0 -196
  101. package/dist/esm/ssr.js +0 -82
  102. package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +0 -1
  103. package/dist/types/selection/SelectionDOMObserver.d.ts +0 -33
  104. package/dist/types/selection/hasFocusAndSelection.d.ts +0 -3
  105. package/dist/types/selection/selectionFromDOM.d.ts +0 -4
  106. package/dist/types/selection/selectionToDOM.d.ts +0 -9
  107. package/dist/types/ssr.d.ts +0 -19
@@ -1,967 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ // TODO: figure out whether it's possible to support native
2
- // widgets. Right now, I'm not sure how we'd do it without
3
- // wrapping them in another element, which would re-introduce
4
- // all of the issues we had before with node views.
5
- //
6
- // For now, we've updated the factory in this file to use
7
- // our React widgets.
8
- function _extends() {
9
- _extends = Object.assign || function(target) {
10
- for(var i = 1; i < arguments.length; i++){
11
- var source = arguments[i];
12
- for(var key in source){
13
- if (Object.prototype.hasOwnProperty.call(source, key)) {
14
- target[key] = source[key];
15
- }
16
- }
17
- }
18
- return target;
19
- };
20
- return _extends.apply(this, arguments);
21
- }
22
- import { Schema } from "prosemirror-model";
23
- import { Plugin, TextSelection } from "prosemirror-state";
24
- import { blockquote, doc, em, h1, hr, img, p, schema, strong } from "prosemirror-test-builder";
25
- import { Decoration, DecorationSet } from "prosemirror-view";
26
- import React, { forwardRef, useEffect } from "react";
27
- import { widget } from "../../decorations/ReactWidgetType.js";
28
- import { useEditorEffect } from "../../hooks/useEditorEffect.js";
29
- import { tempEditor } from "../../testing/editorViewTestHelpers.js";
30
- const Widget = /*#__PURE__*/ forwardRef(function Widget(param, ref) {
31
- let { widget , getPos , ...props } = param;
32
- return /*#__PURE__*/ React.createElement("button", _extends({
33
- ref: ref
34
- }, props), "ω");
35
- });
36
- function make(str) {
37
- if (typeof str != "string") return str;
38
- const match = /^(\d+)(?:-(\d+))?-(.+)$/.exec(str);
39
- if (match[3] == "widget") {
40
- return widget(+match[1], Widget, {
41
- key: str
42
- });
43
- }
44
- return Decoration.inline(+match[1], +match[2], {
45
- class: match[3]
46
- });
47
- }
48
- function decoPlugin(decos) {
49
- return new Plugin({
50
- state: {
51
- init (config) {
52
- return config.doc ? DecorationSet.create(config.doc, decos.map(make)) : DecorationSet.empty;
53
- },
54
- apply (tr, set, state) {
55
- if (tr.docChanged) set = set.map(tr.mapping, tr.doc);
56
- const change = tr.getMeta("updateDecorations");
57
- if (change) {
58
- if (change.remove) set = set.remove(change.remove);
59
- if (change.add) set = set.add(state.doc, change.add);
60
- }
61
- return set;
62
- }
63
- },
64
- props: {
65
- decorations (state) {
66
- return this.getState(state);
67
- }
68
- }
69
- });
70
- }
71
- function updateDeco(view, add, remove) {
72
- view.dispatch(view.state.tr.setMeta("updateDecorations", {
73
- add,
74
- remove
75
- }));
76
- }
77
- describe("Decoration drawing", ()=>{
78
- it("draws inline decorations", async ()=>{
79
- const { view } = tempEditor({
80
- doc: doc(p("foobar")),
81
- plugins: [
82
- decoPlugin([
83
- "2-5-foo"
84
- ])
85
- ]
86
- });
87
- const found = view.dom.querySelector(".foo");
88
- await expect(found).not.toBeNull();
89
- await expect(found.textContent).toBe("oob");
90
- });
91
- it("draws wrapping decorations", async ()=>{
92
- const { view } = tempEditor({
93
- doc: doc(p("foo")),
94
- plugins: [
95
- decoPlugin([
96
- Decoration.inline(1, 5, {
97
- nodeName: "i"
98
- })
99
- ])
100
- ]
101
- });
102
- const found = view.dom.querySelector("i");
103
- expect(found && found.innerHTML).toBe("foo");
104
- });
105
- it("draws node decorations", async ()=>{
106
- const { view } = tempEditor({
107
- doc: doc(p("foo"), p("bar")),
108
- plugins: [
109
- decoPlugin([
110
- Decoration.node(5, 10, {
111
- class: "cls"
112
- })
113
- ])
114
- ]
115
- });
116
- const found = view.dom.querySelectorAll(".cls");
117
- expect(found).toHaveLength(1);
118
- expect(found[0].nodeName).toBe("P");
119
- expect(found[0].previousSibling.nodeName).toBe("P");
120
- });
121
- it("can update multi-level wrapping decorations", async ()=>{
122
- const d2 = Decoration.inline(1, 5, {
123
- nodeName: "i",
124
- class: "b"
125
- });
126
- const { view } = tempEditor({
127
- doc: doc(p("hello")),
128
- plugins: [
129
- decoPlugin([
130
- Decoration.inline(1, 5, {
131
- nodeName: "i",
132
- class: "a"
133
- }),
134
- d2
135
- ])
136
- ]
137
- });
138
- expect(view.dom.querySelectorAll("i")).toHaveLength(2);
139
- updateDeco(view, [
140
- Decoration.inline(1, 5, {
141
- nodeName: "i",
142
- class: "c"
143
- })
144
- ], [
145
- d2
146
- ]);
147
- const iNodes = view.dom.querySelectorAll("i");
148
- expect(iNodes).toHaveLength(2);
149
- expect(Array.prototype.map.call(iNodes, (n)=>n.className).sort().join()).toBe("a,c");
150
- });
151
- it("draws overlapping inline decorations", async ()=>{
152
- const { view } = tempEditor({
153
- doc: doc(p("abcdef")),
154
- plugins: [
155
- decoPlugin([
156
- "3-5-foo",
157
- "4-6-bar",
158
- "1-7-baz"
159
- ])
160
- ]
161
- });
162
- const baz = view.dom.querySelectorAll(".baz");
163
- expect(baz).toHaveLength(5);
164
- expect(Array.prototype.map.call(baz, (x)=>x.textContent).join("-")).toBe("ab-c-d-e-f");
165
- function classes(n) {
166
- return n.className.split(" ").sort().join(" ");
167
- }
168
- expect(classes(baz[1])).toBe("baz foo");
169
- expect(classes(baz[2])).toBe("bar baz foo");
170
- expect(classes(baz[3])).toBe("bar baz");
171
- });
172
- it("draws multiple widgets", async ()=>{
173
- const { view } = tempEditor({
174
- doc: doc(p("foobar")),
175
- plugins: [
176
- decoPlugin([
177
- "1-widget",
178
- "4-widget",
179
- "7-widget"
180
- ])
181
- ]
182
- });
183
- const found = view.dom.querySelectorAll("button");
184
- expect(found).toHaveLength(3);
185
- expect(found[0].nextSibling.textContent).toBe("foo");
186
- expect(found[1].nextSibling.textContent).toBe("bar");
187
- expect(found[2].previousSibling.textContent).toBe("bar");
188
- });
189
- it("orders widgets by their side option", async ()=>{
190
- const { view } = tempEditor({
191
- doc: doc(p("foobar")),
192
- plugins: [
193
- decoPlugin([
194
- widget(4, /*#__PURE__*/ forwardRef(function B(param, ref) {
195
- let { widget , getPos , ...props } = param;
196
- return /*#__PURE__*/ React.createElement("span", _extends({
197
- ref: ref
198
- }, props), "B");
199
- }), {
200
- key: "widget-b"
201
- }),
202
- widget(4, /*#__PURE__*/ forwardRef(function A(param, ref) {
203
- let { widget , getPos , ...props } = param;
204
- return /*#__PURE__*/ React.createElement("span", _extends({
205
- ref: ref
206
- }, props), "A");
207
- }), {
208
- side: -100,
209
- key: "widget-a"
210
- }),
211
- widget(4, /*#__PURE__*/ forwardRef(function C(param, ref) {
212
- let { widget , getPos , ...props } = param;
213
- return /*#__PURE__*/ React.createElement("span", _extends({
214
- ref: ref
215
- }, props), "C");
216
- }), {
217
- side: 2,
218
- key: "widget-c"
219
- })
220
- ])
221
- ]
222
- });
223
- expect(view.dom.textContent).toBe("fooABCbar");
224
- });
225
- it("draws a widget in an empty node", async ()=>{
226
- const { view } = tempEditor({
227
- doc: doc(p()),
228
- plugins: [
229
- decoPlugin([
230
- "1-widget"
231
- ])
232
- ]
233
- });
234
- expect(view.dom.querySelectorAll("button")).toHaveLength(1);
235
- });
236
- it("draws widgets on node boundaries", async ()=>{
237
- const { view } = tempEditor({
238
- doc: doc(p("foo", em("bar"))),
239
- plugins: [
240
- decoPlugin([
241
- "4-widget"
242
- ])
243
- ]
244
- });
245
- expect(view.dom.querySelectorAll("button")).toHaveLength(1);
246
- });
247
- it("draws decorations from multiple plugins", async ()=>{
248
- const { view } = tempEditor({
249
- doc: doc(p("foo", em("bar"))),
250
- plugins: [
251
- decoPlugin([
252
- "2-widget"
253
- ]),
254
- decoPlugin([
255
- "6-widget"
256
- ])
257
- ]
258
- });
259
- expect(view.dom.querySelectorAll("button")).toHaveLength(2);
260
- });
261
- it("calls widget destroy methods", async ()=>{
262
- let destroyed = false;
263
- const DestroyableWidget = /*#__PURE__*/ forwardRef(function DestroyableWidget(param, ref) {
264
- let { widget , getPos , ...props } = param;
265
- useEffect(()=>{
266
- destroyed = true;
267
- });
268
- return /*#__PURE__*/ React.createElement("button", _extends({
269
- ref: ref
270
- }, props));
271
- });
272
- const { view } = tempEditor({
273
- doc: doc(p("abc")),
274
- plugins: [
275
- decoPlugin([
276
- widget(2, DestroyableWidget, {
277
- key: "destroyable-widget"
278
- })
279
- ])
280
- ]
281
- });
282
- view.dispatch(view.state.tr.delete(1, 4));
283
- expect(destroyed).toBeTruthy();
284
- });
285
- it("draws inline decorations spanning multiple parents", async ()=>{
286
- const { view } = tempEditor({
287
- doc: doc(p("long first ", em("p"), "aragraph"), p("two")),
288
- plugins: [
289
- decoPlugin([
290
- "7-25-foo"
291
- ])
292
- ]
293
- });
294
- const foos = view.dom.querySelectorAll(".foo");
295
- expect(foos).toHaveLength(4);
296
- expect(foos[0].textContent).toBe("irst ");
297
- expect(foos[1].textContent).toBe("p");
298
- expect(foos[2].textContent).toBe("aragraph");
299
- expect(foos[3].textContent).toBe("tw");
300
- });
301
- it("draws inline decorations across empty paragraphs", async ()=>{
302
- const { view } = tempEditor({
303
- doc: doc(p("first"), p(), p("second")),
304
- plugins: [
305
- decoPlugin([
306
- "3-12-foo"
307
- ])
308
- ]
309
- });
310
- const foos = view.dom.querySelectorAll(".foo");
311
- expect(foos).toHaveLength(2);
312
- expect(foos[0].textContent).toBe("rst");
313
- expect(foos[1].textContent).toBe("se");
314
- });
315
- it("can handle inline decorations ending at the start or end of a node", async ()=>{
316
- const { view } = tempEditor({
317
- doc: doc(p(), p()),
318
- plugins: [
319
- decoPlugin([
320
- "1-3-foo"
321
- ])
322
- ]
323
- });
324
- expect(view.dom.querySelector(".foo")).toBeNull();
325
- });
326
- it("can draw decorations with multiple classes", async ()=>{
327
- const { view } = tempEditor({
328
- doc: doc(p("foo")),
329
- plugins: [
330
- decoPlugin([
331
- "1-4-foo bar"
332
- ])
333
- ]
334
- });
335
- expect(view.dom.querySelectorAll(".foo")).toHaveLength(1);
336
- expect(view.dom.querySelectorAll(".bar")).toHaveLength(1);
337
- });
338
- it("supports overlapping inline decorations", async ()=>{
339
- const { view } = tempEditor({
340
- doc: doc(p("foobar")),
341
- plugins: [
342
- decoPlugin([
343
- "1-3-foo",
344
- "2-5-bar"
345
- ])
346
- ]
347
- });
348
- const foos = view.dom.querySelectorAll(".foo");
349
- const bars = view.dom.querySelectorAll(".bar");
350
- expect(foos).toHaveLength(2);
351
- expect(bars).toHaveLength(2);
352
- expect(foos[0].textContent).toBe("f");
353
- expect(foos[1].textContent).toBe("o");
354
- expect(bars[0].textContent).toBe("o");
355
- expect(bars[1].textContent).toBe("ob");
356
- });
357
- it("doesn't redraw when irrelevant decorations change", async ()=>{
358
- const { view } = tempEditor({
359
- doc: doc(p("foo"), p("baz")),
360
- plugins: [
361
- decoPlugin([
362
- "7-8-foo"
363
- ])
364
- ]
365
- });
366
- const para2 = view.dom.lastChild;
367
- updateDeco(view, [
368
- make("2-3-bar")
369
- ]);
370
- expect(view.dom.lastChild).toBe(para2);
371
- expect(view.dom.querySelector(".bar")).not.toBeNull();
372
- });
373
- it("doesn't redraw when irrelevant content changes", async ()=>{
374
- const { view } = tempEditor({
375
- doc: doc(p("foo"), p("baz")),
376
- plugins: [
377
- decoPlugin([
378
- "7-8-foo"
379
- ])
380
- ]
381
- });
382
- const para2 = view.dom.lastChild;
383
- view.dispatch(view.state.tr.delete(2, 3));
384
- view.dispatch(view.state.tr.delete(2, 3));
385
- expect(view.dom.lastChild).toBe(para2);
386
- });
387
- it("can add a widget on a node boundary", async ()=>{
388
- const { view } = tempEditor({
389
- doc: doc(p("foo", em("bar"))),
390
- plugins: [
391
- decoPlugin([])
392
- ]
393
- });
394
- updateDeco(view, [
395
- make("4-widget")
396
- ]);
397
- expect(view.dom.querySelectorAll("button")).toHaveLength(1);
398
- });
399
- it("can remove a widget on a node boundary", async ()=>{
400
- const dec = make("4-widget");
401
- const { view } = tempEditor({
402
- doc: doc(p("foo", em("bar"))),
403
- plugins: [
404
- decoPlugin([
405
- dec
406
- ])
407
- ]
408
- });
409
- updateDeco(view, null, [
410
- dec
411
- ]);
412
- expect(view.dom.querySelector("button")).toBeNull();
413
- });
414
- it("can remove the class from a text node", async ()=>{
415
- const dec = make("1-4-foo");
416
- const { view } = tempEditor({
417
- doc: doc(p("abc")),
418
- plugins: [
419
- decoPlugin([
420
- dec
421
- ])
422
- ]
423
- });
424
- expect(view.dom.querySelector(".foo")).not.toBeNull();
425
- updateDeco(view, null, [
426
- dec
427
- ]);
428
- expect(view.dom.querySelector(".foo")).toBeNull();
429
- });
430
- it("can remove the class from part of a text node", async ()=>{
431
- const dec = make("2-4-foo");
432
- const { view } = tempEditor({
433
- doc: doc(p("abcd")),
434
- plugins: [
435
- decoPlugin([
436
- dec
437
- ])
438
- ]
439
- });
440
- expect(view.dom.querySelector(".foo")).not.toBeNull();
441
- updateDeco(view, null, [
442
- dec
443
- ]);
444
- expect(view.dom.querySelector(".foo")).toBeNull();
445
- expect(view.dom.firstChild.innerHTML).toBe("abcd");
446
- });
447
- it("can change the class for part of a text node", async ()=>{
448
- const dec = make("2-4-foo");
449
- const { view } = tempEditor({
450
- doc: doc(p("abcd")),
451
- plugins: [
452
- decoPlugin([
453
- dec
454
- ])
455
- ]
456
- });
457
- expect(view.dom.querySelector(".foo")).not.toBeNull();
458
- updateDeco(view, [
459
- make("2-4-bar")
460
- ], [
461
- dec
462
- ]);
463
- expect(view.dom.querySelector(".foo")).toBeNull();
464
- expect(view.dom.querySelector(".bar")).not.toBeNull();
465
- });
466
- it("draws a widget added in the middle of a text node", async ()=>{
467
- const { view } = tempEditor({
468
- doc: doc(p("foo")),
469
- plugins: [
470
- decoPlugin([])
471
- ]
472
- });
473
- updateDeco(view, [
474
- make("3-widget")
475
- ]);
476
- expect(view.dom.firstChild.textContent).toBe("foωo");
477
- });
478
- it("can update a text node around a widget", async ()=>{
479
- const { view } = tempEditor({
480
- doc: doc(p("bar")),
481
- plugins: [
482
- decoPlugin([
483
- "3-widget"
484
- ])
485
- ]
486
- });
487
- view.dispatch(view.state.tr.delete(1, 2));
488
- expect(view.dom.querySelectorAll("button")).toHaveLength(1);
489
- expect(view.dom.firstChild.textContent).toBe("aωr");
490
- });
491
- it("can update a text node with an inline decoration", async ()=>{
492
- const { view } = tempEditor({
493
- doc: doc(p("bar")),
494
- plugins: [
495
- decoPlugin([
496
- "1-3-foo"
497
- ])
498
- ]
499
- });
500
- view.dispatch(view.state.tr.delete(1, 2));
501
- const foo = view.dom.querySelector(".foo");
502
- expect(foo).not.toBeNull();
503
- expect(foo.textContent).toBe("a");
504
- expect(foo.nextSibling.textContent).toBe("r");
505
- });
506
- it("correctly redraws a partially decorated node when a widget is added", async ()=>{
507
- const { view } = tempEditor({
508
- doc: doc(p("one", em("two"))),
509
- plugins: [
510
- decoPlugin([
511
- "1-6-foo"
512
- ])
513
- ]
514
- });
515
- updateDeco(view, [
516
- make("6-widget")
517
- ]);
518
- const foos = view.dom.querySelectorAll(".foo");
519
- expect(foos).toHaveLength(2);
520
- expect(foos[0].textContent).toBe("one");
521
- expect(foos[1].textContent).toBe("tw");
522
- });
523
- it("correctly redraws when skipping split text node", async ()=>{
524
- const { view } = tempEditor({
525
- doc: doc(p("foo")),
526
- plugins: [
527
- decoPlugin([
528
- "3-widget",
529
- "3-4-foo"
530
- ])
531
- ]
532
- });
533
- updateDeco(view, [
534
- make("4-widget")
535
- ]);
536
- expect(view.dom.querySelectorAll("button")).toHaveLength(2);
537
- });
538
- it("drops removed node decorations from the view", async ()=>{
539
- const deco = Decoration.node(1, 6, {
540
- class: "cls"
541
- });
542
- const { view } = tempEditor({
543
- doc: doc(blockquote(p("foo"), p("bar"))),
544
- plugins: [
545
- decoPlugin([
546
- deco
547
- ])
548
- ]
549
- });
550
- updateDeco(view, null, [
551
- deco
552
- ]);
553
- expect(view.dom.querySelector(".cls")).toBeNull();
554
- });
555
- it("can update a node's attributes without replacing the node", async ()=>{
556
- const deco = Decoration.node(0, 5, {
557
- title: "title",
558
- class: "foo"
559
- });
560
- const { view } = tempEditor({
561
- doc: doc(p("foo")),
562
- plugins: [
563
- decoPlugin([
564
- deco
565
- ])
566
- ]
567
- });
568
- const para = view.dom.querySelector("p");
569
- updateDeco(view, [
570
- Decoration.node(0, 5, {
571
- class: "foo bar"
572
- })
573
- ], [
574
- deco
575
- ]);
576
- expect(view.dom.querySelector("p")).toBe(para);
577
- expect(para.className).toBe("foo bar");
578
- expect(para.title).toBeFalsy();
579
- });
580
- it("can add and remove CSS custom properties from a node", async ()=>{
581
- const deco = Decoration.node(0, 5, {
582
- style: "--my-custom-property:36px"
583
- });
584
- const { view } = tempEditor({
585
- doc: doc(p("foo")),
586
- plugins: [
587
- decoPlugin([
588
- deco
589
- ])
590
- ]
591
- });
592
- expect(view.dom.querySelector("p").style.getPropertyValue("--my-custom-property")).toBe("36px");
593
- updateDeco(view, null, [
594
- deco
595
- ]);
596
- expect(view.dom.querySelector("p").style.getPropertyValue("--my-custom-property")).toBe("");
597
- });
598
- it("updates decorated nodes even if a widget is added before them", async ()=>{
599
- const { view } = tempEditor({
600
- doc: doc(p("a"), p("b")),
601
- plugins: [
602
- decoPlugin([])
603
- ]
604
- });
605
- const lastP = view.dom.querySelectorAll("p")[1];
606
- updateDeco(view, [
607
- make("3-widget"),
608
- Decoration.node(3, 6, {
609
- style: "color: red"
610
- })
611
- ]);
612
- expect(lastP.style.color).toBe("red");
613
- });
614
- it("doesn't redraw nodes when a widget before them is replaced", async ()=>{
615
- const w0 = make("3-widget");
616
- const { view } = tempEditor({
617
- doc: doc(h1("a"), p("b")),
618
- plugins: [
619
- decoPlugin([
620
- w0
621
- ])
622
- ]
623
- });
624
- const initialP = view.dom.querySelector("p");
625
- view.dispatch(view.state.tr.setMeta("updateDecorations", {
626
- add: [
627
- make("3-widget")
628
- ],
629
- remove: [
630
- w0
631
- ]
632
- }).insertText("c", 5));
633
- expect(view.dom.querySelector("p")).toBe(initialP);
634
- });
635
- it("can add and remove inline style", async ()=>{
636
- const deco = Decoration.inline(1, 6, {
637
- style: "color: rgba(0,10,200,.4); text-decoration: underline"
638
- });
639
- const { view } = tempEditor({
640
- doc: doc(p("al", img(), "lo")),
641
- plugins: [
642
- decoPlugin([
643
- deco
644
- ])
645
- ]
646
- });
647
- expect(view.dom.querySelector("img").style.color).toMatch(/rgba/);
648
- expect(view.dom.querySelector("img").previousSibling.style.textDecoration).toBe("underline");
649
- updateDeco(view, null, [
650
- deco
651
- ]);
652
- expect(view.dom.querySelector("img").style.color).toBe("");
653
- expect(view.dom.querySelector("img").style.textDecoration).toBe("");
654
- });
655
- it("passes decorations to a node view", async ()=>{
656
- let current = "";
657
- const { view } = tempEditor({
658
- doc: doc(p("foo"), hr()),
659
- plugins: [
660
- decoPlugin([])
661
- ],
662
- nodeViews: {
663
- horizontal_rule: /*#__PURE__*/ forwardRef(function HR(param, ref) {
664
- let { nodeProps , children , ...props } = param;
665
- current = nodeProps.decorations.map((d)=>d.spec.name).join();
666
- return /*#__PURE__*/ React.createElement("hr", _extends({
667
- ref: ref
668
- }, props));
669
- })
670
- }
671
- });
672
- const a = Decoration.node(5, 6, {}, {
673
- name: "a"
674
- });
675
- updateDeco(view, [
676
- a
677
- ], []);
678
- expect(current).toBe("a");
679
- updateDeco(view, [
680
- Decoration.node(5, 6, {}, {
681
- name: "b"
682
- }),
683
- Decoration.node(5, 6, {}, {
684
- name: "c"
685
- })
686
- ], [
687
- a
688
- ]);
689
- expect(current).toBe("b,c");
690
- });
691
- it("draws the specified marks around a widget", async ()=>{
692
- const { view } = tempEditor({
693
- doc: doc(p("foobar")),
694
- plugins: [
695
- decoPlugin([
696
- widget(4, /*#__PURE__*/ forwardRef(function Img(param, ref) {
697
- let { widget , getPos , ...props } = param;
698
- return /*#__PURE__*/ React.createElement("img", _extends({}, props, {
699
- ref: ref
700
- }));
701
- }), {
702
- marks: [
703
- schema.mark("em")
704
- ],
705
- key: "img-widget"
706
- })
707
- ])
708
- ]
709
- });
710
- expect(view.dom.querySelector("em img")).not.toBeNull();
711
- });
712
- it("draws widgets inside the marks for their side", async ()=>{
713
- const { view } = tempEditor({
714
- doc: doc(p(em("foo"), strong("bar"))),
715
- plugins: [
716
- decoPlugin([
717
- widget(4, /*#__PURE__*/ forwardRef(function Img(param, ref) {
718
- let { widget , getPos , ...props } = param;
719
- return /*#__PURE__*/ React.createElement("img", _extends({}, props, {
720
- ref: ref
721
- }));
722
- }), {
723
- side: -1,
724
- key: "img-widget"
725
- })
726
- ]),
727
- decoPlugin([
728
- widget(4, /*#__PURE__*/ forwardRef(function BR(param, ref) {
729
- let { widget , getPos , ...props } = param;
730
- return /*#__PURE__*/ React.createElement("br", _extends({}, props, {
731
- ref: ref
732
- }));
733
- }), {
734
- key: "br-widget"
735
- })
736
- ]),
737
- decoPlugin([
738
- widget(7, /*#__PURE__*/ forwardRef(function Span(param, ref) {
739
- let { widget , getPos , ...props } = param;
740
- return /*#__PURE__*/ React.createElement("span", _extends({}, props, {
741
- ref: ref
742
- }));
743
- }), {
744
- side: 1,
745
- key: "span-widget"
746
- })
747
- ])
748
- ]
749
- });
750
- expect(view.dom.querySelector("em img")).not.toBeNull();
751
- expect(view.dom.querySelector("strong img")).toBeNull();
752
- expect(view.dom.querySelector("strong br")).not.toBeNull();
753
- expect(view.dom.querySelector("em br")).toBeNull();
754
- expect(view.dom.querySelector("strong span")).toBeNull();
755
- });
756
- it("draws decorations inside node views", async ()=>{
757
- const { view } = tempEditor({
758
- doc: doc(p("foo")),
759
- nodeViews: {
760
- paragraph: /*#__PURE__*/ forwardRef(function Paragraph(param, ref) {
761
- let { nodeProps , children , ...props } = param;
762
- return /*#__PURE__*/ React.createElement("p", _extends({
763
- ref: ref
764
- }, props), children);
765
- })
766
- },
767
- plugins: [
768
- decoPlugin([
769
- widget(2, /*#__PURE__*/ forwardRef(function Img(param, ref) {
770
- let { widget , getPos , ...props } = param;
771
- return /*#__PURE__*/ React.createElement("img", _extends({}, props, {
772
- ref: ref
773
- }));
774
- }), {
775
- key: "img-widget"
776
- })
777
- ])
778
- ]
779
- });
780
- expect(view.dom.querySelector("img")).not.toBeNull();
781
- });
782
- it("can delay widget drawing to render time", async ()=>{
783
- const { view } = tempEditor({
784
- doc: doc(p("hi")),
785
- decorations (state) {
786
- return DecorationSet.create(state.doc, [
787
- widget(3, /*#__PURE__*/ forwardRef(function Span(param, ref) {
788
- let { widget , getPos , ...props } = param;
789
- useEditorEffect((view)=>{
790
- expect(view.state).toBe(state);
791
- });
792
- return /*#__PURE__*/ React.createElement("span", _extends({}, props, {
793
- ref: ref
794
- }), "!");
795
- }), {
796
- key: "span-widget"
797
- })
798
- ]);
799
- }
800
- });
801
- expect(view.dom.textContent).toBe("hi!");
802
- });
803
- it("supports widgets querying their own position", async ()=>{
804
- tempEditor({
805
- doc: doc(p("hi")),
806
- decorations (state) {
807
- return DecorationSet.create(state.doc, [
808
- widget(3, /*#__PURE__*/ forwardRef(function Widget(param, ref) {
809
- let { widget , getPos , ...props } = param;
810
- expect(getPos()).toBe(3);
811
- return /*#__PURE__*/ React.createElement("button", _extends({
812
- ref: ref
813
- }, props), "ω");
814
- }), {
815
- key: "button-widget"
816
- })
817
- ]);
818
- }
819
- });
820
- });
821
- it("doesn't redraw widgets with matching keys", async ()=>{
822
- const { view } = tempEditor({
823
- doc: doc(p("hi")),
824
- decorations (state) {
825
- return DecorationSet.create(state.doc, [
826
- widget(2, Widget, {
827
- key: "myButton"
828
- })
829
- ]);
830
- }
831
- });
832
- const widgetDOM = view.dom.querySelector("button");
833
- view.dispatch(view.state.tr.insertText("!", 2, 2));
834
- expect(view.dom.querySelector("button")).toBe(widgetDOM);
835
- });
836
- it("doesn't redraw widgets with identical specs", async ()=>{
837
- const { view } = tempEditor({
838
- doc: doc(p("hi")),
839
- decorations (state) {
840
- return DecorationSet.create(state.doc, [
841
- widget(2, Widget, {
842
- side: 1,
843
- key: "widget"
844
- })
845
- ]);
846
- }
847
- });
848
- const widgetDOM = view.dom.querySelector("button");
849
- view.dispatch(view.state.tr.insertText("!", 2, 2));
850
- expect(view.dom.querySelector("button")).toBe(widgetDOM);
851
- });
852
- it("doesn't get confused by split text nodes", async ()=>{
853
- const { view } = tempEditor({
854
- doc: doc(p("abab")),
855
- decorations (state) {
856
- return state.selection.from <= 1 ? null : DecorationSet.create(view.state.doc, [
857
- Decoration.inline(1, 2, {
858
- class: "foo"
859
- }),
860
- Decoration.inline(3, 4, {
861
- class: "foo"
862
- })
863
- ]);
864
- }
865
- });
866
- view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, 5)));
867
- expect(view.dom.textContent).toBe("abab");
868
- });
869
- it("only draws inline decorations on the innermost level", async ()=>{
870
- const s = new Schema({
871
- nodes: {
872
- doc: {
873
- content: "(text | thing)*"
874
- },
875
- text: {},
876
- thing: {
877
- inline: true,
878
- content: "text*",
879
- toDOM: ()=>[
880
- "strong",
881
- 0
882
- ],
883
- parseDOM: [
884
- {
885
- tag: "strong"
886
- }
887
- ]
888
- }
889
- }
890
- });
891
- const { view } = tempEditor({
892
- doc: s.node("doc", null, [
893
- s.text("abc"),
894
- s.node("thing", null, [
895
- s.text("def")
896
- ]),
897
- s.text("ghi")
898
- ]),
899
- decorations: (s)=>DecorationSet.create(s.doc, [
900
- Decoration.inline(1, 10, {
901
- class: "dec"
902
- })
903
- ])
904
- });
905
- const styled = view.dom.querySelectorAll(".dec");
906
- expect(styled).toHaveLength(3);
907
- expect(Array.prototype.map.call(styled, (n)=>n.textContent).join()).toBe("bc,def,gh");
908
- expect(styled[1].parentNode.nodeName).toBe("STRONG");
909
- });
910
- it("can handle nodeName decoration overlapping with classes", async ()=>{
911
- const { view } = tempEditor({
912
- doc: doc(p("one two three")),
913
- plugins: [
914
- decoPlugin([
915
- Decoration.inline(2, 13, {
916
- class: "foo"
917
- }),
918
- Decoration.inline(5, 8, {
919
- nodeName: "em"
920
- })
921
- ])
922
- ]
923
- });
924
- expect(view.dom.firstChild.innerHTML).toBe('o<span class="foo">ne </span><em class="foo">two</em><span class="foo"> thre</span>e');
925
- });
926
- it("can handle combining decorations from parent editors in child editors", async ()=>{
927
- let decosFromFirstEditor;
928
- let { view } = tempEditor({
929
- doc: doc(p("one two three")),
930
- plugins: [
931
- decoPlugin([
932
- Decoration.inline(2, 13, {
933
- class: "foo"
934
- })
935
- ]),
936
- decoPlugin([
937
- Decoration.inline(2, 13, {
938
- class: "bar"
939
- })
940
- ])
941
- ],
942
- nodeViews: {
943
- paragraph: /*#__PURE__*/ forwardRef(function Paragraph(param, ref) {
944
- let { nodeProps , children , ...props } = param;
945
- decosFromFirstEditor = nodeProps.innerDecorations;
946
- return /*#__PURE__*/ React.createElement("p", _extends({
947
- ref: ref
948
- }, props), children);
949
- })
950
- }
951
- });
952
- ({ view } = tempEditor({
953
- doc: doc(p("one two three")),
954
- plugins: [
955
- decoPlugin([
956
- Decoration.inline(1, 12, {
957
- class: "baz"
958
- })
959
- ])
960
- ],
961
- decorations: ()=>decosFromFirstEditor
962
- }));
963
- expect(view.dom.querySelectorAll(".foo")).toHaveLength(1);
964
- expect(view.dom.querySelectorAll(".bar")).toHaveLength(1);
965
- expect(view.dom.querySelectorAll(".baz")).toHaveLength(1);
966
- });
967
- });