@portabletext/editor 1.22.0 → 1.23.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.
- package/lib/_chunks-cjs/util.slice-blocks.cjs +7 -7
- package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
- package/lib/_chunks-es/util.slice-blocks.js +7 -7
- package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
- package/lib/index.cjs +7 -0
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +288 -0
- package/lib/index.d.ts +288 -0
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -1
- package/package.json +15 -11
- package/src/editor/create-editor.ts +1 -0
- package/src/editor/editor-machine.ts +7 -0
- package/src/utils/util.slice-blocks.test.ts +129 -35
- package/src/utils/util.slice-blocks.ts +14 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -66,14 +66,14 @@
|
|
|
66
66
|
"get-random-values-esm": "^1.0.2",
|
|
67
67
|
"lodash": "^4.17.21",
|
|
68
68
|
"lodash.startcase": "^4.4.0",
|
|
69
|
-
"react-compiler-runtime": "19.0.0-beta-
|
|
69
|
+
"react-compiler-runtime": "19.0.0-beta-decd7b8-20250118",
|
|
70
70
|
"slate": "0.112.0",
|
|
71
71
|
"slate-dom": "^0.111.0",
|
|
72
72
|
"slate-react": "0.112.0",
|
|
73
73
|
"use-effect-event": "^1.0.2",
|
|
74
74
|
"xstate": "^5.19.2",
|
|
75
|
-
"@portabletext/
|
|
76
|
-
"@portabletext/
|
|
75
|
+
"@portabletext/patches": "1.1.1",
|
|
76
|
+
"@portabletext/block-tools": "1.1.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@portabletext/toolkit": "^2.0.16",
|
|
@@ -91,11 +91,11 @@
|
|
|
91
91
|
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
|
92
92
|
"@typescript-eslint/parser": "^8.18.1",
|
|
93
93
|
"@vitejs/plugin-react": "^4.3.4",
|
|
94
|
-
"@vitest/browser": "^
|
|
95
|
-
"@vitest/coverage-istanbul": "^
|
|
96
|
-
"babel-plugin-react-compiler": "19.0.0-beta-
|
|
94
|
+
"@vitest/browser": "^3.0.2",
|
|
95
|
+
"@vitest/coverage-istanbul": "^3.0.2",
|
|
96
|
+
"babel-plugin-react-compiler": "19.0.0-beta-decd7b8-20250118",
|
|
97
97
|
"eslint": "8.57.1",
|
|
98
|
-
"eslint-plugin-react-compiler": "19.0.0-beta-
|
|
98
|
+
"eslint-plugin-react-compiler": "19.0.0-beta-decd7b8-20250118",
|
|
99
99
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
100
100
|
"jsdom": "^26.0.0",
|
|
101
101
|
"react": "^19.0.0",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"rxjs": "^7.8.1",
|
|
104
104
|
"typescript": "5.7.3",
|
|
105
105
|
"vite": "^6.0.4",
|
|
106
|
-
"vitest": "^
|
|
106
|
+
"vitest": "^3.0.2",
|
|
107
107
|
"vitest-browser-react": "^0.0.4",
|
|
108
108
|
"racejar": "1.1.1"
|
|
109
109
|
},
|
|
@@ -129,8 +129,12 @@
|
|
|
129
129
|
"lint:fix": "biome lint --write .",
|
|
130
130
|
"test": "vitest --run",
|
|
131
131
|
"test:watch": "vitest",
|
|
132
|
-
"test:chromium": "vitest --run --project chromium",
|
|
133
|
-
"test:chromium:watch": "vitest --project chromium",
|
|
132
|
+
"test:chromium": "vitest --run --project \"browser (chromium)\"",
|
|
133
|
+
"test:chromium:watch": "vitest --project \"browser (chromium)\"",
|
|
134
|
+
"test:firefox": "vitest --run --project \"browser (firefox)\"",
|
|
135
|
+
"test:firefox:watch": "vitest --project \"browser (firefox)\"",
|
|
136
|
+
"test:webkit": "vitest --run --project \"browser (webkit)\"",
|
|
137
|
+
"test:webkit:watch": "vitest --project \"browser (webkit)\"",
|
|
134
138
|
"test:unit": "vitest --run --project unit",
|
|
135
139
|
"test:unit:watch": "vitest --project unit"
|
|
136
140
|
}
|
|
@@ -105,6 +105,10 @@ export type InternalEditorEvent =
|
|
|
105
105
|
type: 'update behaviors'
|
|
106
106
|
behaviors: Array<Behavior>
|
|
107
107
|
}
|
|
108
|
+
| {
|
|
109
|
+
type: 'update key generator'
|
|
110
|
+
keyGenerator: () => string
|
|
111
|
+
}
|
|
108
112
|
| {
|
|
109
113
|
type: 'update value'
|
|
110
114
|
value: Array<PortableTextBlock> | undefined
|
|
@@ -493,6 +497,9 @@ export const editorMachine = setup({
|
|
|
493
497
|
'patches': {actions: emit(({event}) => event)},
|
|
494
498
|
'done loading': {actions: emit({type: 'done loading'})},
|
|
495
499
|
'update behaviors': {actions: 'assign behaviors'},
|
|
500
|
+
'update key generator': {
|
|
501
|
+
actions: assign({keyGenerator: ({event}) => event.keyGenerator}),
|
|
502
|
+
},
|
|
496
503
|
'update schema': {actions: 'assign schema'},
|
|
497
504
|
'update value': {actions: assign({value: ({event}) => event.value})},
|
|
498
505
|
'update maxBlocks': {
|
|
@@ -2,43 +2,62 @@ import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'
|
|
|
2
2
|
import {describe, expect, test} from 'vitest'
|
|
3
3
|
import {sliceBlocks} from './util.slice-blocks'
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const b1: PortableTextTextBlock = {
|
|
6
6
|
_type: 'block',
|
|
7
7
|
_key: 'b1',
|
|
8
8
|
children: [
|
|
9
9
|
{
|
|
10
10
|
_type: 'span',
|
|
11
|
-
_key: '
|
|
11
|
+
_key: 'b1c1',
|
|
12
12
|
text: 'foo',
|
|
13
|
-
marks: ['strong'],
|
|
14
13
|
},
|
|
15
14
|
{
|
|
16
15
|
_type: 'span',
|
|
17
|
-
_key: '
|
|
16
|
+
_key: 'b1c2',
|
|
18
17
|
text: 'bar',
|
|
19
18
|
},
|
|
20
19
|
],
|
|
21
20
|
}
|
|
22
|
-
const
|
|
21
|
+
const b2: PortableTextBlock = {
|
|
22
|
+
_type: 'image',
|
|
23
|
+
_key: 'b2',
|
|
24
|
+
src: 'https://example.com/image.jpg',
|
|
25
|
+
alt: 'Example',
|
|
26
|
+
}
|
|
27
|
+
const b3: PortableTextTextBlock = {
|
|
23
28
|
_type: 'block',
|
|
24
29
|
_key: 'b3',
|
|
25
30
|
children: [
|
|
26
31
|
{
|
|
27
32
|
_type: 'span',
|
|
28
|
-
_key: '
|
|
33
|
+
_key: 'b3c1',
|
|
29
34
|
text: 'baz',
|
|
30
35
|
},
|
|
31
36
|
],
|
|
32
37
|
}
|
|
38
|
+
const b4: PortableTextTextBlock = {
|
|
39
|
+
_type: 'block',
|
|
40
|
+
_key: 'b4',
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
_type: 'span',
|
|
44
|
+
_key: 'b4c1',
|
|
45
|
+
text: 'fizz',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
_type: 'stock-ticker',
|
|
49
|
+
_key: 'b4c2',
|
|
50
|
+
symbol: 'AAPL',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
_type: 'span',
|
|
54
|
+
_key: 'b4c3',
|
|
55
|
+
text: 'buzz',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
}
|
|
33
59
|
|
|
34
|
-
const blocks: Array<PortableTextBlock> = [
|
|
35
|
-
textBlock1,
|
|
36
|
-
{
|
|
37
|
-
_type: 'image',
|
|
38
|
-
_key: 'b2',
|
|
39
|
-
},
|
|
40
|
-
textBlock2,
|
|
41
|
-
]
|
|
60
|
+
const blocks: Array<PortableTextBlock> = [b1, b2, b3, b4]
|
|
42
61
|
|
|
43
62
|
describe(sliceBlocks.name, () => {
|
|
44
63
|
test('sensible defaults', () => {
|
|
@@ -52,19 +71,19 @@ describe(sliceBlocks.name, () => {
|
|
|
52
71
|
blocks,
|
|
53
72
|
selection: {
|
|
54
73
|
anchor: {
|
|
55
|
-
path: [{_key:
|
|
74
|
+
path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
|
|
56
75
|
offset: 0,
|
|
57
76
|
},
|
|
58
77
|
focus: {
|
|
59
|
-
path: [{_key:
|
|
78
|
+
path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
|
|
60
79
|
offset: 3,
|
|
61
80
|
},
|
|
62
81
|
},
|
|
63
82
|
}),
|
|
64
83
|
).toEqual([
|
|
65
84
|
{
|
|
66
|
-
...
|
|
67
|
-
children: [
|
|
85
|
+
...b1,
|
|
86
|
+
children: [b1.children[0]],
|
|
68
87
|
},
|
|
69
88
|
])
|
|
70
89
|
})
|
|
@@ -75,21 +94,21 @@ describe(sliceBlocks.name, () => {
|
|
|
75
94
|
blocks,
|
|
76
95
|
selection: {
|
|
77
96
|
anchor: {
|
|
78
|
-
path: [{_key:
|
|
97
|
+
path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
|
|
79
98
|
offset: 1,
|
|
80
99
|
},
|
|
81
100
|
focus: {
|
|
82
|
-
path: [{_key:
|
|
101
|
+
path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
|
|
83
102
|
offset: 2,
|
|
84
103
|
},
|
|
85
104
|
},
|
|
86
105
|
}),
|
|
87
106
|
).toEqual([
|
|
88
107
|
{
|
|
89
|
-
...
|
|
108
|
+
...b1,
|
|
90
109
|
children: [
|
|
91
110
|
{
|
|
92
|
-
...
|
|
111
|
+
...b1.children[0],
|
|
93
112
|
text: 'o',
|
|
94
113
|
},
|
|
95
114
|
],
|
|
@@ -97,64 +116,139 @@ describe(sliceBlocks.name, () => {
|
|
|
97
116
|
])
|
|
98
117
|
})
|
|
99
118
|
|
|
119
|
+
test('starting and ending selection on a block object', () => {
|
|
120
|
+
expect(
|
|
121
|
+
sliceBlocks({
|
|
122
|
+
blocks,
|
|
123
|
+
selection: {
|
|
124
|
+
anchor: {
|
|
125
|
+
path: [{_key: b2._key}],
|
|
126
|
+
offset: 0,
|
|
127
|
+
},
|
|
128
|
+
focus: {
|
|
129
|
+
path: [{_key: b2._key}],
|
|
130
|
+
offset: 0,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
134
|
+
).toEqual([b2])
|
|
135
|
+
})
|
|
136
|
+
|
|
100
137
|
test('ending selection on a block object', () => {
|
|
101
138
|
expect(
|
|
102
139
|
sliceBlocks({
|
|
103
140
|
blocks,
|
|
104
141
|
selection: {
|
|
105
142
|
anchor: {
|
|
106
|
-
path: [{_key:
|
|
143
|
+
path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
|
|
107
144
|
offset: 3,
|
|
108
145
|
},
|
|
109
146
|
focus: {
|
|
110
|
-
path: [{_key:
|
|
147
|
+
path: [{_key: b2._key}],
|
|
111
148
|
offset: 0,
|
|
112
149
|
},
|
|
113
150
|
},
|
|
114
151
|
}),
|
|
115
152
|
).toEqual([
|
|
116
153
|
{
|
|
117
|
-
...
|
|
154
|
+
...b1,
|
|
118
155
|
children: [
|
|
119
156
|
{
|
|
120
|
-
...
|
|
157
|
+
...b1.children[0],
|
|
121
158
|
text: '',
|
|
122
159
|
},
|
|
160
|
+
...b1.children.slice(1),
|
|
123
161
|
],
|
|
124
162
|
},
|
|
125
163
|
blocks[1],
|
|
126
164
|
])
|
|
127
165
|
})
|
|
128
166
|
|
|
167
|
+
test('slicing across block object', () => {
|
|
168
|
+
expect(
|
|
169
|
+
sliceBlocks({
|
|
170
|
+
blocks,
|
|
171
|
+
selection: {
|
|
172
|
+
anchor: {
|
|
173
|
+
path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
|
|
174
|
+
offset: 0,
|
|
175
|
+
},
|
|
176
|
+
focus: {
|
|
177
|
+
path: [{_key: b3._key}, 'children', {_key: b3.children[0]._key}],
|
|
178
|
+
offset: 3,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
182
|
+
).toEqual([b1, b2, b3])
|
|
183
|
+
})
|
|
184
|
+
|
|
129
185
|
test('starting and ending mid-span', () => {
|
|
130
186
|
expect(
|
|
131
187
|
sliceBlocks({
|
|
132
188
|
blocks,
|
|
133
189
|
selection: {
|
|
134
190
|
anchor: {
|
|
135
|
-
path: [{_key:
|
|
191
|
+
path: [{_key: b3._key}, 'children', {_key: b3.children[0]._key}],
|
|
136
192
|
offset: 2,
|
|
137
193
|
},
|
|
138
|
-
focus: {
|
|
194
|
+
focus: {
|
|
195
|
+
path: [{_key: b4._key}, 'children', {_key: b4.children[0]._key}],
|
|
196
|
+
offset: 1,
|
|
197
|
+
},
|
|
139
198
|
},
|
|
140
199
|
}),
|
|
141
200
|
).toEqual([
|
|
142
201
|
{
|
|
143
|
-
...
|
|
202
|
+
...b3,
|
|
144
203
|
children: [
|
|
145
204
|
{
|
|
146
|
-
...
|
|
147
|
-
text: '
|
|
205
|
+
...b3.children[0],
|
|
206
|
+
text: 'z',
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
...b4,
|
|
212
|
+
children: [
|
|
213
|
+
{
|
|
214
|
+
...b4.children[0],
|
|
215
|
+
text: 'f',
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
])
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test('starting mid-span and ending end-span', () => {
|
|
223
|
+
expect(
|
|
224
|
+
sliceBlocks({
|
|
225
|
+
blocks,
|
|
226
|
+
selection: {
|
|
227
|
+
anchor: {
|
|
228
|
+
path: [{_key: b3._key}, 'children', {_key: b3.children[0]._key}],
|
|
229
|
+
offset: 2,
|
|
230
|
+
},
|
|
231
|
+
focus: {
|
|
232
|
+
path: [{_key: b4._key}, 'children', {_key: b4.children[0]._key}],
|
|
233
|
+
offset: 4,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
}),
|
|
237
|
+
).toEqual([
|
|
238
|
+
{
|
|
239
|
+
...b3,
|
|
240
|
+
children: [
|
|
241
|
+
{
|
|
242
|
+
...b3.children[0],
|
|
243
|
+
text: 'z',
|
|
148
244
|
},
|
|
149
245
|
],
|
|
150
246
|
},
|
|
151
|
-
blocks[1],
|
|
152
247
|
{
|
|
153
|
-
...
|
|
248
|
+
...b4,
|
|
154
249
|
children: [
|
|
155
250
|
{
|
|
156
|
-
...
|
|
157
|
-
text: 'az',
|
|
251
|
+
...b4.children[0],
|
|
158
252
|
},
|
|
159
253
|
],
|
|
160
254
|
},
|
|
@@ -66,13 +66,16 @@ export function sliceBlocks({
|
|
|
66
66
|
},
|
|
67
67
|
],
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
continue
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
startBlock = {
|
|
73
73
|
...block,
|
|
74
74
|
children: [child],
|
|
75
75
|
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (startChildKey === endChildKey) {
|
|
76
79
|
break
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -97,6 +100,10 @@ export function sliceBlocks({
|
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
startBlock = block
|
|
103
|
+
|
|
104
|
+
if (startBlockKey === endBlockKey) {
|
|
105
|
+
break
|
|
106
|
+
}
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
if (block._key === endBlockKey) {
|
|
@@ -111,8 +118,9 @@ export function sliceBlocks({
|
|
|
111
118
|
if (child._key === endChildKey && isPortableTextSpan(child)) {
|
|
112
119
|
endBlock.children.push({
|
|
113
120
|
...child,
|
|
114
|
-
text: child.text.slice(endPoint.offset),
|
|
121
|
+
text: child.text.slice(0, endPoint.offset),
|
|
115
122
|
})
|
|
123
|
+
|
|
116
124
|
break
|
|
117
125
|
}
|
|
118
126
|
|
|
@@ -124,7 +132,7 @@ export function sliceBlocks({
|
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
|
|
135
|
+
break
|
|
128
136
|
}
|
|
129
137
|
|
|
130
138
|
endBlock = block
|
|
@@ -132,7 +140,9 @@ export function sliceBlocks({
|
|
|
132
140
|
break
|
|
133
141
|
}
|
|
134
142
|
|
|
135
|
-
|
|
143
|
+
if (startBlock) {
|
|
144
|
+
middleBlocks.push(block)
|
|
145
|
+
}
|
|
136
146
|
}
|
|
137
147
|
|
|
138
148
|
return [
|