@portabletext/editor 1.23.0 → 1.25.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 (71) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +249 -62
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/{selector.is-selection-collapsed.cjs → selector.is-active-style.cjs} +158 -3
  4. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -0
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs +23 -9
  6. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  7. package/lib/_chunks-es/behavior.core.js +225 -38
  8. package/lib/_chunks-es/behavior.core.js.map +1 -1
  9. package/lib/_chunks-es/{selector.is-selection-collapsed.js → selector.is-active-style.js} +159 -4
  10. package/lib/_chunks-es/selector.is-active-style.js.map +1 -0
  11. package/lib/_chunks-es/util.slice-blocks.js +23 -9
  12. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  13. package/lib/behaviors/index.cjs +27 -27
  14. package/lib/behaviors/index.cjs.map +1 -1
  15. package/lib/behaviors/index.d.cts +2830 -139
  16. package/lib/behaviors/index.d.ts +2830 -139
  17. package/lib/behaviors/index.js +1 -1
  18. package/lib/index.cjs +695 -526
  19. package/lib/index.cjs.map +1 -1
  20. package/lib/index.d.cts +8950 -246
  21. package/lib/index.d.ts +8950 -246
  22. package/lib/index.js +696 -525
  23. package/lib/index.js.map +1 -1
  24. package/lib/selectors/index.cjs +24 -171
  25. package/lib/selectors/index.cjs.map +1 -1
  26. package/lib/selectors/index.d.cts +73 -0
  27. package/lib/selectors/index.d.ts +73 -0
  28. package/lib/selectors/index.js +3 -151
  29. package/lib/selectors/index.js.map +1 -1
  30. package/package.json +11 -10
  31. package/src/behavior-actions/behavior.action.data-transfer-set.ts +7 -0
  32. package/src/behavior-actions/behavior.action.insert-blocks.ts +61 -0
  33. package/src/behavior-actions/behavior.actions.ts +159 -83
  34. package/src/behaviors/behavior.core.annotations.ts +29 -0
  35. package/src/behaviors/behavior.core.block-objects.ts +13 -13
  36. package/src/behaviors/behavior.core.decorators.ts +19 -0
  37. package/src/behaviors/behavior.core.deserialize.ts +46 -0
  38. package/src/behaviors/behavior.core.lists.ts +57 -23
  39. package/src/behaviors/behavior.core.serialize.ts +44 -0
  40. package/src/behaviors/behavior.core.style.ts +19 -0
  41. package/src/behaviors/behavior.core.ts +19 -0
  42. package/src/behaviors/behavior.types.ts +126 -89
  43. package/src/converters/converter.json.ts +53 -0
  44. package/src/converters/converter.portable-text.deserialize.test.ts +686 -0
  45. package/src/converters/converter.portable-text.ts +59 -0
  46. package/src/converters/converter.text-html.deserialize.test.ts +349 -0
  47. package/src/converters/converter.text-html.serialize.test.ts +233 -0
  48. package/src/converters/converter.text-html.ts +61 -0
  49. package/src/converters/converter.text-plain.test.ts +241 -0
  50. package/src/converters/converter.text-plain.ts +91 -0
  51. package/src/converters/converter.ts +65 -0
  52. package/src/converters/converters.ts +11 -0
  53. package/src/editor/Editable.tsx +3 -13
  54. package/src/editor/create-editor.ts +48 -6
  55. package/src/editor/editor-machine.ts +56 -2
  56. package/src/editor/editor-selector.ts +1 -0
  57. package/src/editor/editor-snapshot.ts +5 -0
  58. package/src/editor/plugins/create-with-event-listeners.ts +82 -106
  59. package/src/internal-utils/asserters.ts +9 -0
  60. package/src/internal-utils/mime-type.ts +1 -0
  61. package/src/internal-utils/parse-blocks.ts +136 -0
  62. package/src/internal-utils/test-key-generator.ts +9 -0
  63. package/src/selectors/selector.get-selected-spans.test.ts +1 -0
  64. package/src/selectors/selector.get-selection-text.test.ts +1 -0
  65. package/src/selectors/selector.is-active-decorator.test.ts +1 -0
  66. package/src/utils/util.slice-blocks.test.ts +87 -0
  67. package/src/utils/util.slice-blocks.ts +27 -10
  68. package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +0 -1
  69. package/lib/_chunks-es/selector.is-selection-collapsed.js.map +0 -1
  70. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +0 -181
  71. package/src/editor/plugins/createWithInsertData.ts +0 -425
@@ -0,0 +1,686 @@
1
+ import {assert, describe, expect, test} from 'vitest'
2
+ import {
3
+ compileSchemaDefinition,
4
+ defineSchema,
5
+ type SchemaDefinition,
6
+ } from '../editor/define-schema'
7
+ import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
8
+ import {converterPortableText} from './converter.portable-text'
9
+ import {coreConverters} from './converters'
10
+
11
+ function createContext(schema: SchemaDefinition) {
12
+ return {
13
+ converters: coreConverters,
14
+ activeDecorators: [],
15
+ keyGenerator: createTestKeyGenerator(),
16
+ schema: compileSchemaDefinition(schema),
17
+ value: [],
18
+ selection: null,
19
+ }
20
+ }
21
+
22
+ describe(converterPortableText.deserialize, () => {
23
+ test('non-array', () => {
24
+ expect(
25
+ converterPortableText.deserialize({
26
+ context: createContext(defineSchema({})),
27
+ event: {
28
+ type: 'deserialize',
29
+ data: JSON.stringify(''),
30
+ },
31
+ }),
32
+ ).toMatchObject({
33
+ type: 'deserialization.failure',
34
+ })
35
+ })
36
+
37
+ test('empty array', () => {
38
+ expect(
39
+ converterPortableText.deserialize({
40
+ context: createContext(defineSchema({})),
41
+ event: {
42
+ type: 'deserialize',
43
+ data: JSON.stringify([]),
44
+ },
45
+ }),
46
+ ).toMatchObject({
47
+ data: [],
48
+ })
49
+ })
50
+
51
+ test('no known array entries', () => {
52
+ expect(
53
+ converterPortableText.deserialize({
54
+ context: createContext(defineSchema({})),
55
+ event: {
56
+ type: 'deserialize',
57
+ data: JSON.stringify([{foo: 'bar'}]),
58
+ },
59
+ }),
60
+ ).toMatchObject({
61
+ type: 'deserialization.failure',
62
+ })
63
+ })
64
+
65
+ test('some known array entries', () => {
66
+ expect(
67
+ converterPortableText.deserialize({
68
+ context: createContext(defineSchema({})),
69
+ event: {
70
+ type: 'deserialize',
71
+ data: JSON.stringify([{_type: 'block', children: []}, {foo: 'bar'}]),
72
+ },
73
+ }),
74
+ ).toMatchObject({
75
+ data: [
76
+ {
77
+ _type: 'block',
78
+ _key: 'k0',
79
+ children: [
80
+ {
81
+ _key: 'k1',
82
+ _type: 'span',
83
+ text: '',
84
+ marks: [],
85
+ },
86
+ ],
87
+ markDefs: [],
88
+ },
89
+ ],
90
+ })
91
+ })
92
+
93
+ test('no marks or markDefs', () => {
94
+ expect(
95
+ converterPortableText.deserialize({
96
+ context: createContext(defineSchema({})),
97
+ event: {
98
+ type: 'deserialize',
99
+ data: JSON.stringify([
100
+ {
101
+ _type: 'block',
102
+ children: [
103
+ {
104
+ _type: 'span',
105
+ text: 'foo',
106
+ },
107
+ ],
108
+ },
109
+ ]),
110
+ },
111
+ }),
112
+ ).toMatchObject({
113
+ data: [
114
+ {
115
+ _type: 'block',
116
+ children: [
117
+ {
118
+ _type: 'span',
119
+ text: 'foo',
120
+ marks: [],
121
+ },
122
+ ],
123
+ markDefs: [],
124
+ },
125
+ ],
126
+ })
127
+ })
128
+
129
+ test('unknown block object', () => {
130
+ expect(
131
+ converterPortableText.deserialize({
132
+ context: createContext(defineSchema({})),
133
+ event: {
134
+ type: 'deserialize',
135
+ data: JSON.stringify([{_type: 'foo'}]),
136
+ },
137
+ }),
138
+ ).toMatchObject({
139
+ type: 'deserialization.failure',
140
+ })
141
+ })
142
+
143
+ test('known block object', () => {
144
+ expect(
145
+ converterPortableText.deserialize({
146
+ context: createContext(
147
+ defineSchema({
148
+ blockObjects: [{name: 'image'}],
149
+ }),
150
+ ),
151
+ event: {
152
+ type: 'deserialize',
153
+ data: JSON.stringify([
154
+ {
155
+ _key: 'b2',
156
+ _type: 'image',
157
+ src: 'https://example.com/image.jpg',
158
+ },
159
+ ]),
160
+ },
161
+ }),
162
+ ).toMatchObject({
163
+ data: [
164
+ {
165
+ _key: 'k0',
166
+ _type: 'image',
167
+ src: 'https://example.com/image.jpg',
168
+ },
169
+ ],
170
+ })
171
+ })
172
+
173
+ test('unknown inline object', () => {
174
+ expect(
175
+ converterPortableText.deserialize({
176
+ context: createContext(defineSchema({})),
177
+ event: {
178
+ type: 'deserialize',
179
+ data: JSON.stringify([
180
+ {
181
+ _type: 'block',
182
+ children: [{_type: 'stock-ticker', symbol: 'AAPL'}],
183
+ },
184
+ ]),
185
+ },
186
+ }),
187
+ ).toMatchObject({
188
+ data: [
189
+ {
190
+ _type: 'block',
191
+ children: [
192
+ {
193
+ _key: 'k1',
194
+ _type: 'span',
195
+ text: '',
196
+ marks: [],
197
+ },
198
+ ],
199
+ },
200
+ ],
201
+ })
202
+ })
203
+
204
+ test('known inline object', () => {
205
+ expect(
206
+ converterPortableText.deserialize({
207
+ context: createContext(
208
+ defineSchema({
209
+ inlineObjects: [{name: 'stock-ticker'}],
210
+ }),
211
+ ),
212
+ event: {
213
+ type: 'deserialize',
214
+ data: JSON.stringify([
215
+ {
216
+ _type: 'block',
217
+ children: [{_type: 'stock-ticker', symbol: 'AAPL'}],
218
+ },
219
+ ]),
220
+ },
221
+ }),
222
+ ).toMatchObject({
223
+ data: [
224
+ {
225
+ _type: 'block',
226
+ children: [{_type: 'stock-ticker', symbol: 'AAPL'}],
227
+ },
228
+ ],
229
+ })
230
+ })
231
+
232
+ test('no style', () => {
233
+ const deserializedEvent = converterPortableText.deserialize({
234
+ context: createContext(defineSchema({})),
235
+ event: {
236
+ type: 'deserialize',
237
+ data: JSON.stringify([
238
+ {
239
+ _type: 'block',
240
+ children: [],
241
+ },
242
+ ]),
243
+ },
244
+ })
245
+
246
+ if (deserializedEvent.type !== 'deserialization.success') {
247
+ assert.fail()
248
+ }
249
+
250
+ expect(deserializedEvent.data).toEqual([
251
+ {
252
+ _key: 'k0',
253
+ _type: 'block',
254
+ children: [
255
+ {
256
+ _key: 'k1',
257
+ _type: 'span',
258
+ text: '',
259
+ marks: [],
260
+ },
261
+ ],
262
+ markDefs: [],
263
+ style: 'normal',
264
+ },
265
+ ])
266
+ })
267
+
268
+ test('default style', () => {
269
+ const deserializedEvent = converterPortableText.deserialize({
270
+ context: createContext(defineSchema({styles: [{name: 'h1'}]})),
271
+ event: {
272
+ type: 'deserialize',
273
+ data: JSON.stringify([
274
+ {
275
+ _type: 'block',
276
+ children: [],
277
+ },
278
+ ]),
279
+ },
280
+ })
281
+
282
+ if (deserializedEvent.type !== 'deserialization.success') {
283
+ assert.fail()
284
+ }
285
+
286
+ expect(deserializedEvent.data).toEqual([
287
+ {
288
+ _key: 'k0',
289
+ _type: 'block',
290
+ children: [
291
+ {
292
+ _key: 'k1',
293
+ _type: 'span',
294
+ text: '',
295
+ marks: [],
296
+ },
297
+ ],
298
+ markDefs: [],
299
+ style: 'normal',
300
+ },
301
+ ])
302
+ })
303
+
304
+ test('unknown style', () => {
305
+ const deserializedEvent = converterPortableText.deserialize({
306
+ context: createContext(defineSchema({})),
307
+ event: {
308
+ type: 'deserialize',
309
+ data: JSON.stringify([
310
+ {
311
+ _type: 'block',
312
+ children: [],
313
+ style: 'h1',
314
+ },
315
+ ]),
316
+ },
317
+ })
318
+
319
+ if (deserializedEvent.type !== 'deserialization.success') {
320
+ assert.fail()
321
+ }
322
+
323
+ expect(deserializedEvent.data).toEqual([
324
+ {
325
+ _type: 'block',
326
+ _key: 'k0',
327
+ children: [
328
+ {
329
+ _key: 'k1',
330
+ _type: 'span',
331
+ text: '',
332
+ marks: [],
333
+ },
334
+ ],
335
+ markDefs: [],
336
+ style: 'normal',
337
+ },
338
+ ])
339
+ })
340
+
341
+ test('known style', () => {
342
+ const deserializedEvent = converterPortableText.deserialize({
343
+ context: createContext(
344
+ defineSchema({
345
+ styles: [{name: 'h1'}],
346
+ }),
347
+ ),
348
+ event: {
349
+ type: 'deserialize',
350
+ data: JSON.stringify([
351
+ {
352
+ _type: 'block',
353
+ children: [],
354
+ style: 'h1',
355
+ },
356
+ ]),
357
+ },
358
+ })
359
+
360
+ if (deserializedEvent.type !== 'deserialization.success') {
361
+ assert.fail()
362
+ }
363
+
364
+ expect(deserializedEvent.data).toEqual([
365
+ {
366
+ _type: 'block',
367
+ _key: 'k0',
368
+ children: [
369
+ {
370
+ _key: 'k1',
371
+ _type: 'span',
372
+ text: '',
373
+ marks: [],
374
+ },
375
+ ],
376
+ markDefs: [],
377
+ style: 'h1',
378
+ },
379
+ ])
380
+ })
381
+
382
+ test('unknown listItem', () => {
383
+ const deserializedEvent = converterPortableText.deserialize({
384
+ context: createContext(defineSchema({})),
385
+ event: {
386
+ type: 'deserialize',
387
+ data: JSON.stringify([
388
+ {
389
+ _type: 'block',
390
+ children: [],
391
+ listItem: 'bullet',
392
+ level: 1,
393
+ },
394
+ ]),
395
+ },
396
+ })
397
+
398
+ if (deserializedEvent.type !== 'deserialization.success') {
399
+ assert.fail()
400
+ }
401
+
402
+ expect(deserializedEvent.data).toEqual([
403
+ {
404
+ _type: 'block',
405
+ _key: 'k0',
406
+ children: [
407
+ {
408
+ _key: 'k1',
409
+ _type: 'span',
410
+ text: '',
411
+ marks: [],
412
+ },
413
+ ],
414
+ markDefs: [],
415
+ style: 'normal',
416
+ },
417
+ ])
418
+ })
419
+
420
+ test('known listItem', () => {
421
+ const deserializedEvent = converterPortableText.deserialize({
422
+ context: createContext(
423
+ defineSchema({
424
+ lists: [{name: 'bullet'}],
425
+ }),
426
+ ),
427
+ event: {
428
+ type: 'deserialize',
429
+ data: JSON.stringify([
430
+ {
431
+ _type: 'block',
432
+ children: [],
433
+ listItem: 'bullet',
434
+ level: 1,
435
+ },
436
+ ]),
437
+ },
438
+ })
439
+
440
+ if (deserializedEvent.type !== 'deserialization.success') {
441
+ assert.fail()
442
+ }
443
+
444
+ expect(deserializedEvent.data).toEqual([
445
+ {
446
+ _type: 'block',
447
+ _key: 'k0',
448
+ children: [
449
+ {
450
+ _key: 'k1',
451
+ _type: 'span',
452
+ text: '',
453
+ marks: [],
454
+ },
455
+ ],
456
+ markDefs: [],
457
+ listItem: 'bullet',
458
+ level: 1,
459
+ style: 'normal',
460
+ },
461
+ ])
462
+ })
463
+
464
+ test('unknown annotations', () => {
465
+ expect(
466
+ converterPortableText.deserialize({
467
+ context: createContext(defineSchema({})),
468
+ event: {
469
+ type: 'deserialize',
470
+ data: JSON.stringify([
471
+ {
472
+ _type: 'block',
473
+ children: [
474
+ {
475
+ _type: 'span',
476
+ marks: ['b0m0'],
477
+ text: 'foo',
478
+ },
479
+ {
480
+ _type: 'span',
481
+ marks: ['b0m1'],
482
+ text: 'bar',
483
+ },
484
+ ],
485
+ markDefs: [
486
+ {
487
+ _key: 'b0m0',
488
+ _type: 'link',
489
+ href: 'https://example.com',
490
+ },
491
+ {
492
+ _key: 'b0m1',
493
+ _type: 'color',
494
+ color: 'red',
495
+ },
496
+ ],
497
+ },
498
+ ]),
499
+ },
500
+ }),
501
+ ).toMatchObject({
502
+ data: [
503
+ {
504
+ _type: 'block',
505
+ children: [
506
+ {
507
+ _type: 'span',
508
+ text: 'foo',
509
+ marks: [],
510
+ },
511
+ {
512
+ _type: 'span',
513
+ text: 'bar',
514
+ marks: [],
515
+ },
516
+ ],
517
+ },
518
+ ],
519
+ })
520
+ })
521
+
522
+ test('known annotations', () => {
523
+ expect(
524
+ converterPortableText.deserialize({
525
+ context: createContext(
526
+ defineSchema({
527
+ annotations: [{name: 'link'}],
528
+ }),
529
+ ),
530
+ event: {
531
+ type: 'deserialize',
532
+ data: JSON.stringify([
533
+ {
534
+ _type: 'block',
535
+ children: [
536
+ {
537
+ _type: 'span',
538
+ marks: ['b0m0'],
539
+ text: 'foo',
540
+ },
541
+ {
542
+ _type: 'span',
543
+ marks: ['b0m1'],
544
+ text: 'bar',
545
+ },
546
+ ],
547
+ markDefs: [
548
+ {
549
+ _key: 'b0m0',
550
+ _type: 'link',
551
+ href: 'https://example.com',
552
+ },
553
+ {
554
+ _key: 'b0m1',
555
+ _type: 'color',
556
+ color: 'red',
557
+ },
558
+ ],
559
+ },
560
+ ]),
561
+ },
562
+ }),
563
+ ).toMatchObject({
564
+ data: [
565
+ {
566
+ _type: 'block',
567
+ children: [
568
+ {
569
+ _type: 'span',
570
+ text: 'foo',
571
+ marks: ['k0'],
572
+ },
573
+ {
574
+ _type: 'span',
575
+ text: 'bar',
576
+ marks: [],
577
+ },
578
+ ],
579
+ markDefs: [
580
+ {
581
+ _key: 'k0',
582
+ _type: 'link',
583
+ href: 'https://example.com',
584
+ },
585
+ ],
586
+ },
587
+ ],
588
+ })
589
+ })
590
+
591
+ test('unknown decorators', () => {
592
+ expect(
593
+ converterPortableText.deserialize({
594
+ context: createContext(defineSchema({})),
595
+ event: {
596
+ type: 'deserialize',
597
+ data: JSON.stringify([
598
+ {
599
+ _type: 'block',
600
+ children: [
601
+ {
602
+ _type: 'span',
603
+ text: 'foo',
604
+ marks: ['strong'],
605
+ },
606
+ {
607
+ _type: 'span',
608
+ text: 'bar',
609
+ marks: ['em'],
610
+ },
611
+ ],
612
+ },
613
+ ]),
614
+ },
615
+ }),
616
+ ).toMatchObject({
617
+ data: [
618
+ {
619
+ _type: 'block',
620
+ children: [
621
+ {
622
+ _type: 'span',
623
+ text: 'foo',
624
+ marks: [],
625
+ },
626
+ {
627
+ _type: 'span',
628
+ text: 'bar',
629
+ marks: [],
630
+ },
631
+ ],
632
+ },
633
+ ],
634
+ })
635
+ })
636
+
637
+ test('known decorators', () => {
638
+ expect(
639
+ converterPortableText.deserialize({
640
+ context: createContext(
641
+ defineSchema({
642
+ decorators: [{name: 'strong'}],
643
+ }),
644
+ ),
645
+ event: {
646
+ type: 'deserialize',
647
+ data: JSON.stringify([
648
+ {
649
+ _type: 'block',
650
+ children: [
651
+ {
652
+ _type: 'span',
653
+ text: 'foo',
654
+ marks: ['strong'],
655
+ },
656
+ {
657
+ _type: 'span',
658
+ text: 'bar',
659
+ marks: ['em'],
660
+ },
661
+ ],
662
+ },
663
+ ]),
664
+ },
665
+ }),
666
+ ).toMatchObject({
667
+ data: [
668
+ {
669
+ _type: 'block',
670
+ children: [
671
+ {
672
+ _type: 'span',
673
+ text: 'foo',
674
+ marks: ['strong'],
675
+ },
676
+ {
677
+ _type: 'span',
678
+ text: 'bar',
679
+ marks: [],
680
+ },
681
+ ],
682
+ },
683
+ ],
684
+ })
685
+ })
686
+ })