@portabletext/editor 0.0.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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/lib/index.d.mts +911 -0
  4. package/lib/index.d.ts +911 -0
  5. package/lib/index.esm.js +4896 -0
  6. package/lib/index.esm.js.map +1 -0
  7. package/lib/index.js +4874 -0
  8. package/lib/index.js.map +1 -0
  9. package/lib/index.mjs +4896 -0
  10. package/lib/index.mjs.map +1 -0
  11. package/package.json +119 -0
  12. package/src/editor/Editable.tsx +683 -0
  13. package/src/editor/PortableTextEditor.tsx +308 -0
  14. package/src/editor/__tests__/PortableTextEditor.test.tsx +386 -0
  15. package/src/editor/__tests__/PortableTextEditorTester.tsx +116 -0
  16. package/src/editor/__tests__/RangeDecorations.test.tsx +115 -0
  17. package/src/editor/__tests__/handleClick.test.tsx +218 -0
  18. package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +389 -0
  19. package/src/editor/__tests__/utils.ts +39 -0
  20. package/src/editor/components/DraggableBlock.tsx +287 -0
  21. package/src/editor/components/Element.tsx +279 -0
  22. package/src/editor/components/Leaf.tsx +288 -0
  23. package/src/editor/components/SlateContainer.tsx +81 -0
  24. package/src/editor/components/Synchronizer.tsx +190 -0
  25. package/src/editor/hooks/usePortableTextEditor.ts +23 -0
  26. package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +24 -0
  27. package/src/editor/hooks/usePortableTextEditorSelection.ts +22 -0
  28. package/src/editor/hooks/usePortableTextEditorValue.ts +16 -0
  29. package/src/editor/hooks/usePortableTextReadOnly.ts +20 -0
  30. package/src/editor/hooks/useSyncValue.test.tsx +125 -0
  31. package/src/editor/hooks/useSyncValue.ts +372 -0
  32. package/src/editor/nodes/DefaultAnnotation.tsx +16 -0
  33. package/src/editor/nodes/DefaultObject.tsx +15 -0
  34. package/src/editor/nodes/index.ts +189 -0
  35. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +244 -0
  36. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +142 -0
  37. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +346 -0
  38. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +162 -0
  39. package/src/editor/plugins/__tests__/withHotkeys.test.tsx +212 -0
  40. package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +204 -0
  41. package/src/editor/plugins/__tests__/withPlaceholderBlock.test.tsx +133 -0
  42. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +65 -0
  43. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +1377 -0
  44. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +91 -0
  45. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +115 -0
  46. package/src/editor/plugins/createWithEditableAPI.ts +573 -0
  47. package/src/editor/plugins/createWithHotKeys.ts +304 -0
  48. package/src/editor/plugins/createWithInsertBreak.ts +45 -0
  49. package/src/editor/plugins/createWithInsertData.ts +359 -0
  50. package/src/editor/plugins/createWithMaxBlocks.ts +24 -0
  51. package/src/editor/plugins/createWithObjectKeys.ts +63 -0
  52. package/src/editor/plugins/createWithPatches.ts +274 -0
  53. package/src/editor/plugins/createWithPlaceholderBlock.ts +36 -0
  54. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +91 -0
  55. package/src/editor/plugins/createWithPortableTextLists.ts +160 -0
  56. package/src/editor/plugins/createWithPortableTextMarkModel.ts +441 -0
  57. package/src/editor/plugins/createWithPortableTextSelections.ts +65 -0
  58. package/src/editor/plugins/createWithSchemaTypes.ts +76 -0
  59. package/src/editor/plugins/createWithUndoRedo.ts +494 -0
  60. package/src/editor/plugins/createWithUtils.ts +81 -0
  61. package/src/editor/plugins/index.ts +155 -0
  62. package/src/index.ts +11 -0
  63. package/src/patch/PatchEvent.ts +33 -0
  64. package/src/patch/applyPatch.ts +29 -0
  65. package/src/patch/array.ts +89 -0
  66. package/src/patch/arrayInsert.ts +27 -0
  67. package/src/patch/object.ts +39 -0
  68. package/src/patch/patches.ts +53 -0
  69. package/src/patch/primitive.ts +43 -0
  70. package/src/patch/string.ts +51 -0
  71. package/src/types/editor.ts +576 -0
  72. package/src/types/options.ts +17 -0
  73. package/src/types/patch.ts +65 -0
  74. package/src/types/slate.ts +25 -0
  75. package/src/utils/__tests__/dmpToOperations.test.ts +181 -0
  76. package/src/utils/__tests__/operationToPatches.test.ts +421 -0
  77. package/src/utils/__tests__/patchToOperations.test.ts +293 -0
  78. package/src/utils/__tests__/ranges.test.ts +18 -0
  79. package/src/utils/__tests__/valueNormalization.test.tsx +62 -0
  80. package/src/utils/__tests__/values.test.ts +253 -0
  81. package/src/utils/applyPatch.ts +407 -0
  82. package/src/utils/bufferUntil.ts +15 -0
  83. package/src/utils/debug.ts +12 -0
  84. package/src/utils/getPortableTextMemberSchemaTypes.ts +100 -0
  85. package/src/utils/operationToPatches.ts +357 -0
  86. package/src/utils/patches.ts +36 -0
  87. package/src/utils/paths.ts +60 -0
  88. package/src/utils/ranges.ts +77 -0
  89. package/src/utils/schema.ts +8 -0
  90. package/src/utils/selection.ts +65 -0
  91. package/src/utils/ucs2Indices.ts +67 -0
  92. package/src/utils/validateValue.ts +394 -0
  93. package/src/utils/values.ts +208 -0
  94. package/src/utils/weakMaps.ts +24 -0
  95. package/src/utils/withChanges.ts +25 -0
  96. package/src/utils/withPreserveKeys.ts +14 -0
  97. package/src/utils/withoutPatching.ts +14 -0
@@ -0,0 +1,244 @@
1
+ import {describe, expect, it, jest} from '@jest/globals'
2
+ import {render, waitFor} from '@testing-library/react'
3
+ import {createRef, type RefObject} from 'react'
4
+
5
+ import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
6
+ import {PortableTextEditor} from '../../PortableTextEditor'
7
+
8
+ const initialValue = [
9
+ {
10
+ _key: 'a',
11
+ _type: 'myTestBlockType',
12
+ children: [
13
+ {
14
+ _key: 'a1',
15
+ _type: 'span',
16
+ marks: [],
17
+ text: 'Block A',
18
+ },
19
+ ],
20
+ markDefs: [],
21
+ style: 'normal',
22
+ },
23
+ {
24
+ _key: 'b',
25
+ _type: 'myTestBlockType',
26
+ children: [
27
+ {
28
+ _key: 'b1',
29
+ _type: 'span',
30
+ marks: [],
31
+ text: 'Block B',
32
+ },
33
+ ],
34
+ markDefs: [],
35
+ style: 'normal',
36
+ },
37
+ ]
38
+
39
+ const initialSelection = {
40
+ focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 7},
41
+ anchor: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 7},
42
+ }
43
+
44
+ describe('plugin:withEditableAPI: .delete()', () => {
45
+ it('deletes block', async () => {
46
+ const editorRef: RefObject<PortableTextEditor> = createRef()
47
+ const onChange = jest.fn()
48
+ render(
49
+ <PortableTextEditorTester
50
+ onChange={onChange}
51
+ ref={editorRef}
52
+ schemaType={schemaType}
53
+ value={initialValue}
54
+ />,
55
+ )
56
+ await waitFor(() => {
57
+ if (editorRef.current) {
58
+ PortableTextEditor.focus(editorRef.current)
59
+ PortableTextEditor.select(editorRef.current, initialSelection)
60
+ PortableTextEditor.delete(
61
+ editorRef.current,
62
+ PortableTextEditor.getSelection(editorRef.current),
63
+ {mode: 'blocks'},
64
+ )
65
+ expect(PortableTextEditor.getValue(editorRef.current)).toMatchInlineSnapshot(`
66
+ Array [
67
+ Object {
68
+ "_key": "a",
69
+ "_type": "myTestBlockType",
70
+ "children": Array [
71
+ Object {
72
+ "_key": "a1",
73
+ "_type": "span",
74
+ "marks": Array [],
75
+ "text": "Block A",
76
+ },
77
+ ],
78
+ "markDefs": Array [],
79
+ "style": "normal",
80
+ },
81
+ ]
82
+ `)
83
+ }
84
+ })
85
+ })
86
+
87
+ it('deletes all the blocks, but leaves a placeholder block', async () => {
88
+ const editorRef: RefObject<PortableTextEditor> = createRef()
89
+ const onChange = jest.fn()
90
+ render(
91
+ <PortableTextEditorTester
92
+ onChange={onChange}
93
+ ref={editorRef}
94
+ schemaType={schemaType}
95
+ value={initialValue}
96
+ />,
97
+ )
98
+ await waitFor(() => {
99
+ expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
100
+ expect(onChange).toHaveBeenCalledWith({type: 'ready'})
101
+ })
102
+
103
+ await waitFor(() => {
104
+ if (editorRef.current) {
105
+ PortableTextEditor.delete(
106
+ editorRef.current,
107
+ {
108
+ focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 7},
109
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
110
+ },
111
+ {mode: 'blocks'},
112
+ )
113
+ }
114
+ })
115
+ await waitFor(() => {
116
+ if (editorRef.current) {
117
+ // New keys here confirms that a placeholder block has been created
118
+ expect(PortableTextEditor.getValue(editorRef.current)).toMatchInlineSnapshot(`
119
+ Array [
120
+ Object {
121
+ "_key": "1",
122
+ "_type": "myTestBlockType",
123
+ "children": Array [
124
+ Object {
125
+ "_key": "2",
126
+ "_type": "span",
127
+ "marks": Array [],
128
+ "text": "",
129
+ },
130
+ ],
131
+ "markDefs": Array [],
132
+ "style": "normal",
133
+ },
134
+ ]
135
+ `)
136
+ }
137
+ })
138
+ })
139
+
140
+ it('deletes children', async () => {
141
+ const editorRef: RefObject<PortableTextEditor> = createRef()
142
+ const onChange = jest.fn()
143
+ render(
144
+ <PortableTextEditorTester
145
+ onChange={onChange}
146
+ ref={editorRef}
147
+ schemaType={schemaType}
148
+ value={initialValue}
149
+ />,
150
+ )
151
+
152
+ await waitFor(() => {
153
+ if (editorRef.current) {
154
+ PortableTextEditor.select(editorRef.current, {
155
+ focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 5},
156
+ anchor: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 7},
157
+ })
158
+ PortableTextEditor.focus(editorRef.current)
159
+ PortableTextEditor.delete(
160
+ editorRef.current,
161
+ PortableTextEditor.getSelection(editorRef.current),
162
+ {mode: 'children'},
163
+ )
164
+ expect(PortableTextEditor.getValue(editorRef.current)).toMatchInlineSnapshot(`
165
+ Array [
166
+ Object {
167
+ "_key": "a",
168
+ "_type": "myTestBlockType",
169
+ "children": Array [
170
+ Object {
171
+ "_key": "a1",
172
+ "_type": "span",
173
+ "marks": Array [],
174
+ "text": "Block A",
175
+ },
176
+ ],
177
+ "markDefs": Array [],
178
+ "style": "normal",
179
+ },
180
+ Object {
181
+ "_key": "b",
182
+ "_type": "myTestBlockType",
183
+ "children": Array [
184
+ Object {
185
+ "_key": "1",
186
+ "_type": "span",
187
+ "marks": Array [],
188
+ "text": "",
189
+ },
190
+ ],
191
+ "markDefs": Array [],
192
+ "style": "normal",
193
+ },
194
+ ]
195
+ `)
196
+ }
197
+ })
198
+ })
199
+ it('deletes selected', async () => {
200
+ const editorRef: RefObject<PortableTextEditor> = createRef()
201
+ const onChange = jest.fn()
202
+ render(
203
+ <PortableTextEditorTester
204
+ onChange={onChange}
205
+ ref={editorRef}
206
+ schemaType={schemaType}
207
+ value={initialValue}
208
+ />,
209
+ )
210
+
211
+ await waitFor(() => {
212
+ if (editorRef.current) {
213
+ PortableTextEditor.select(editorRef.current, {
214
+ focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 5},
215
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
216
+ })
217
+ PortableTextEditor.focus(editorRef.current)
218
+ PortableTextEditor.delete(
219
+ editorRef.current,
220
+ PortableTextEditor.getSelection(editorRef.current),
221
+ {mode: 'selected'},
222
+ )
223
+ expect(PortableTextEditor.getValue(editorRef.current)).toMatchInlineSnapshot(`
224
+ Array [
225
+ Object {
226
+ "_key": "b",
227
+ "_type": "myTestBlockType",
228
+ "children": Array [
229
+ Object {
230
+ "_key": "b1",
231
+ "_type": "span",
232
+ "marks": Array [],
233
+ "text": " B",
234
+ },
235
+ ],
236
+ "markDefs": Array [],
237
+ "style": "normal",
238
+ },
239
+ ]
240
+ `)
241
+ }
242
+ })
243
+ })
244
+ })
@@ -0,0 +1,142 @@
1
+ import {describe, expect, it, jest} from '@jest/globals'
2
+ import {isPortableTextTextBlock} from '@sanity/types'
3
+ import {render, waitFor} from '@testing-library/react'
4
+ import {createRef, type RefObject} from 'react'
5
+
6
+ import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
7
+ import {PortableTextEditor} from '../../PortableTextEditor'
8
+
9
+ const initialValue = [
10
+ {
11
+ _key: 'a',
12
+ _type: 'myTestBlockType',
13
+ children: [
14
+ {
15
+ _key: 'a1',
16
+ _type: 'span',
17
+ marks: [],
18
+ text: 'Block A',
19
+ },
20
+ ],
21
+ markDefs: [],
22
+ style: 'normal',
23
+ },
24
+ {
25
+ _key: 'b',
26
+ _type: 'myTestBlockType',
27
+ children: [
28
+ {
29
+ _key: 'b1',
30
+ _type: 'span',
31
+ marks: [],
32
+ text: 'Block B ',
33
+ },
34
+ {
35
+ _key: 'b2',
36
+ _type: 'someObject',
37
+ },
38
+ {
39
+ _key: 'b3',
40
+ _type: 'span',
41
+ marks: [],
42
+ text: ' contains a inline object',
43
+ },
44
+ ],
45
+ markDefs: [],
46
+ style: 'normal',
47
+ },
48
+ ]
49
+
50
+ describe('plugin:withEditableAPI: .getFragment()', () => {
51
+ it('can get a Portable Text fragment of the current selection in a single block', async () => {
52
+ const editorRef: RefObject<PortableTextEditor> = createRef()
53
+ const onChange = jest.fn()
54
+ render(
55
+ <PortableTextEditorTester
56
+ onChange={onChange}
57
+ ref={editorRef}
58
+ schemaType={schemaType}
59
+ value={initialValue}
60
+ />,
61
+ )
62
+ const initialSelection = {
63
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6},
64
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 7},
65
+ }
66
+ await waitFor(() => {
67
+ if (editorRef.current) {
68
+ PortableTextEditor.focus(editorRef.current)
69
+ PortableTextEditor.select(editorRef.current, initialSelection)
70
+ const fragment = PortableTextEditor.getFragment(editorRef.current)
71
+ expect(
72
+ fragment && isPortableTextTextBlock(fragment[0]) && fragment[0]?.children[0]?.text,
73
+ ).toBe('A')
74
+ }
75
+ })
76
+ })
77
+ it('can get a Portable Text fragment of the current selection in multiple blocks', async () => {
78
+ const editorRef: RefObject<PortableTextEditor> = createRef()
79
+ const onChange = jest.fn()
80
+ render(
81
+ <PortableTextEditorTester
82
+ onChange={onChange}
83
+ ref={editorRef}
84
+ schemaType={schemaType}
85
+ value={initialValue}
86
+ />,
87
+ )
88
+ const initialSelection = {
89
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6},
90
+ focus: {path: [{_key: 'b'}, 'children', {_key: 'b3'}], offset: 9},
91
+ }
92
+ await waitFor(() => {
93
+ if (editorRef.current) {
94
+ PortableTextEditor.focus(editorRef.current)
95
+ PortableTextEditor.select(editorRef.current, initialSelection)
96
+ const fragment = PortableTextEditor.getFragment(editorRef.current)
97
+ expect(fragment).toMatchInlineSnapshot(`
98
+ Array [
99
+ Object {
100
+ "_key": "a",
101
+ "_type": "myTestBlockType",
102
+ "children": Array [
103
+ Object {
104
+ "_key": "a1",
105
+ "_type": "span",
106
+ "marks": Array [],
107
+ "text": "A",
108
+ },
109
+ ],
110
+ "markDefs": Array [],
111
+ "style": "normal",
112
+ },
113
+ Object {
114
+ "_key": "b",
115
+ "_type": "myTestBlockType",
116
+ "children": Array [
117
+ Object {
118
+ "_key": "b1",
119
+ "_type": "span",
120
+ "marks": Array [],
121
+ "text": "Block B ",
122
+ },
123
+ Object {
124
+ "_key": "b2",
125
+ "_type": "someObject",
126
+ },
127
+ Object {
128
+ "_key": "b3",
129
+ "_type": "span",
130
+ "marks": Array [],
131
+ "text": " contains",
132
+ },
133
+ ],
134
+ "markDefs": Array [],
135
+ "style": "normal",
136
+ },
137
+ ]
138
+ `)
139
+ }
140
+ })
141
+ })
142
+ })