@portabletext/editor 1.44.6 → 1.44.8
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/index.cjs +45 -12
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +45 -12
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/editor/Editable.tsx +1 -0
- package/src/editor/__tests__/RangeDecorations.test.tsx +2 -2
- package/src/editor/range-decorations-machine.ts +72 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.44.
|
|
3
|
+
"version": "1.44.8",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"check:lint": "biome lint .",
|
|
132
132
|
"check:types": "tsc",
|
|
133
133
|
"check:types:watch": "tsc --watch",
|
|
134
|
-
"check:react-compiler": "eslint --cache --no-inline-config --no-eslintrc --ignore-pattern '**/__tests__/**' --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --plugin react-hooks --rule 'react-compiler/react-compiler: [warn
|
|
134
|
+
"check:react-compiler": "eslint --cache --no-inline-config --no-eslintrc --ignore-pattern '**/__tests__/**' --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --plugin react-hooks --rule 'react-compiler/react-compiler: [warn]' --rule 'react-hooks/rules-of-hooks: [error]' --rule 'react-hooks/exhaustive-deps: [error]' src",
|
|
135
135
|
"clean": "del .turbo && del lib && del node_modules",
|
|
136
136
|
"dev": "pkg-utils watch",
|
|
137
137
|
"lint:fix": "biome lint --write .",
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -181,6 +181,7 @@ export const PortableTextEditable = forwardRef<
|
|
|
181
181
|
rangeDecorations: rangeDecorations ?? [],
|
|
182
182
|
},
|
|
183
183
|
})
|
|
184
|
+
useSelector(rangeDecorationsActor, (s) => s.context.updateCount)
|
|
184
185
|
const decorate = useMemo(
|
|
185
186
|
() => createDecorate(rangeDecorationsActor),
|
|
186
187
|
[rangeDecorationsActor],
|
|
@@ -120,7 +120,7 @@ describe('RangeDecorations', () => {
|
|
|
120
120
|
)
|
|
121
121
|
await waitFor(() => {
|
|
122
122
|
expect([rangeDecorationIteration, 'updated-with-different']).toEqual([
|
|
123
|
-
|
|
123
|
+
3,
|
|
124
124
|
'updated-with-different',
|
|
125
125
|
])
|
|
126
126
|
})
|
|
@@ -146,7 +146,7 @@ describe('RangeDecorations', () => {
|
|
|
146
146
|
)
|
|
147
147
|
await waitFor(() => {
|
|
148
148
|
expect([rangeDecorationIteration, 'updated-with-different']).toEqual([
|
|
149
|
-
|
|
149
|
+
4,
|
|
150
150
|
'updated-with-different',
|
|
151
151
|
])
|
|
152
152
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {isEqual} from 'lodash'
|
|
2
2
|
import {
|
|
3
|
+
Element,
|
|
3
4
|
Path,
|
|
4
5
|
Range,
|
|
5
6
|
type BaseRange,
|
|
@@ -52,6 +53,7 @@ export const rangeDecorationsMachine = setup({
|
|
|
52
53
|
readOnly: boolean
|
|
53
54
|
schema: EditorSchema
|
|
54
55
|
slateEditor: PortableTextSlateEditor
|
|
56
|
+
updateCount: number
|
|
55
57
|
},
|
|
56
58
|
input: {} as {
|
|
57
59
|
rangeDecorations: Array<RangeDecoration>
|
|
@@ -192,7 +194,14 @@ export const rangeDecorationsMachine = setup({
|
|
|
192
194
|
if (newRange !== null) {
|
|
193
195
|
rangeDecorationState.push({
|
|
194
196
|
...(newRange || slateRange),
|
|
195
|
-
rangeDecoration:
|
|
197
|
+
rangeDecoration: {
|
|
198
|
+
...decoratedRange.rangeDecoration,
|
|
199
|
+
selection: slateRangeToSelection({
|
|
200
|
+
schema: context.schema,
|
|
201
|
+
editor: context.slateEditor,
|
|
202
|
+
range: newRange,
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
196
205
|
})
|
|
197
206
|
}
|
|
198
207
|
}
|
|
@@ -206,16 +215,39 @@ export const rangeDecorationsMachine = setup({
|
|
|
206
215
|
return event.readOnly
|
|
207
216
|
},
|
|
208
217
|
}),
|
|
218
|
+
'increment update count': assign({
|
|
219
|
+
updateCount: ({context}) => {
|
|
220
|
+
return context.updateCount + 1
|
|
221
|
+
},
|
|
222
|
+
}),
|
|
209
223
|
},
|
|
210
224
|
actors: {
|
|
211
225
|
'slate operation listener': fromCallback(slateOperationCallback),
|
|
212
226
|
},
|
|
213
227
|
guards: {
|
|
228
|
+
'has pending range decorations': ({context}) =>
|
|
229
|
+
context.pendingRangeDecorations.length > 0,
|
|
214
230
|
'has range decorations': ({context}) => context.decoratedRanges.length > 0,
|
|
215
231
|
'has different decorations': ({context, event}) => {
|
|
216
232
|
assertEvent(event, 'range decorations updated')
|
|
217
233
|
|
|
218
|
-
|
|
234
|
+
const existingRangeDecorations = context.decoratedRanges.map(
|
|
235
|
+
(decoratedRange) => ({
|
|
236
|
+
anchor: decoratedRange.rangeDecoration.selection?.anchor,
|
|
237
|
+
focus: decoratedRange.rangeDecoration.selection?.focus,
|
|
238
|
+
}),
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
const newRangeDecorations = event.rangeDecorations.map(
|
|
242
|
+
(rangeDecoration) => ({
|
|
243
|
+
anchor: rangeDecoration.selection?.anchor,
|
|
244
|
+
focus: rangeDecoration.selection?.focus,
|
|
245
|
+
}),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
const different = !isEqual(existingRangeDecorations, newRangeDecorations)
|
|
249
|
+
|
|
250
|
+
return different
|
|
219
251
|
},
|
|
220
252
|
'not read only': ({context}) => !context.readOnly,
|
|
221
253
|
},
|
|
@@ -227,6 +259,7 @@ export const rangeDecorationsMachine = setup({
|
|
|
227
259
|
decoratedRanges: [],
|
|
228
260
|
schema: input.schema,
|
|
229
261
|
slateEditor: input.slateEditor,
|
|
262
|
+
updateCount: 0,
|
|
230
263
|
}),
|
|
231
264
|
invoke: {
|
|
232
265
|
src: 'slate operation listener',
|
|
@@ -244,10 +277,19 @@ export const rangeDecorationsMachine = setup({
|
|
|
244
277
|
'range decorations updated': {
|
|
245
278
|
actions: ['update pending range decorations'],
|
|
246
279
|
},
|
|
247
|
-
'ready':
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
280
|
+
'ready': [
|
|
281
|
+
{
|
|
282
|
+
target: 'ready',
|
|
283
|
+
guard: 'has pending range decorations',
|
|
284
|
+
actions: [
|
|
285
|
+
'set up initial range decorations',
|
|
286
|
+
'increment update count',
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
target: 'ready',
|
|
291
|
+
},
|
|
292
|
+
],
|
|
251
293
|
},
|
|
252
294
|
},
|
|
253
295
|
ready: {
|
|
@@ -256,10 +298,7 @@ export const rangeDecorationsMachine = setup({
|
|
|
256
298
|
'range decorations updated': {
|
|
257
299
|
target: '.idle',
|
|
258
300
|
guard: 'has different decorations',
|
|
259
|
-
actions: [
|
|
260
|
-
'update range decorations',
|
|
261
|
-
'update pending range decorations',
|
|
262
|
-
],
|
|
301
|
+
actions: ['update range decorations', 'increment update count'],
|
|
263
302
|
},
|
|
264
303
|
},
|
|
265
304
|
states: {
|
|
@@ -285,7 +324,7 @@ export const rangeDecorationsMachine = setup({
|
|
|
285
324
|
export function createDecorate(
|
|
286
325
|
rangeDecorationActor: ActorRefFrom<typeof rangeDecorationsMachine>,
|
|
287
326
|
) {
|
|
288
|
-
return function decorate([, path]: NodeEntry): Array<BaseRange> {
|
|
327
|
+
return function decorate([node, path]: NodeEntry): Array<BaseRange> {
|
|
289
328
|
if (
|
|
290
329
|
isEqualToEmptyEditor(
|
|
291
330
|
rangeDecorationActor.getSnapshot().context.slateEditor.children,
|
|
@@ -312,35 +351,38 @@ export function createDecorate(
|
|
|
312
351
|
return []
|
|
313
352
|
}
|
|
314
353
|
|
|
315
|
-
|
|
354
|
+
if (!Element.isElement(node) || node.children.length === 0) {
|
|
355
|
+
return []
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const blockIndex = path.at(0)
|
|
359
|
+
|
|
360
|
+
if (blockIndex === undefined) {
|
|
361
|
+
return []
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return rangeDecorationActor
|
|
316
365
|
.getSnapshot()
|
|
317
|
-
.context.decoratedRanges.filter((
|
|
366
|
+
.context.decoratedRanges.filter((decoratedRange) => {
|
|
318
367
|
// Special case in order to only return one decoration for collapsed ranges
|
|
319
|
-
if (Range.isCollapsed(
|
|
368
|
+
if (Range.isCollapsed(decoratedRange)) {
|
|
320
369
|
// Collapsed ranges should only be decorated if they are on a block child level (length 2)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
370
|
+
return node.children.some(
|
|
371
|
+
(_, childIndex) =>
|
|
372
|
+
Path.equals(decoratedRange.anchor.path, [
|
|
373
|
+
blockIndex,
|
|
374
|
+
childIndex,
|
|
375
|
+
]) &&
|
|
376
|
+
Path.equals(decoratedRange.focus.path, [blockIndex, childIndex]),
|
|
328
377
|
)
|
|
329
378
|
}
|
|
330
379
|
|
|
331
|
-
// Include decorations that either include or intersects with this path
|
|
332
380
|
return (
|
|
333
|
-
Range.intersection(
|
|
381
|
+
Range.intersection(decoratedRange, {
|
|
334
382
|
anchor: {path, offset: 0},
|
|
335
383
|
focus: {path, offset: 0},
|
|
336
|
-
}) || Range.includes(
|
|
384
|
+
}) || Range.includes(decoratedRange, path)
|
|
337
385
|
)
|
|
338
386
|
})
|
|
339
|
-
|
|
340
|
-
if (result.length > 0) {
|
|
341
|
-
return result
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return []
|
|
345
387
|
}
|
|
346
388
|
}
|