@portabletext/editor 1.26.0 → 1.26.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.26.0",
3
+ "version": "1.26.2",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -67,21 +67,21 @@
67
67
  "get-random-values-esm": "^1.0.2",
68
68
  "lodash": "^4.17.21",
69
69
  "lodash.startcase": "^4.4.0",
70
- "react-compiler-runtime": "19.0.0-beta-decd7b8-20250118",
70
+ "react-compiler-runtime": "19.0.0-beta-27714ef-20250124",
71
71
  "slate": "0.112.0",
72
72
  "slate-dom": "^0.111.0",
73
73
  "slate-react": "0.112.1",
74
74
  "use-effect-event": "^1.0.2",
75
75
  "xstate": "^5.19.2",
76
- "@portabletext/block-tools": "1.1.2",
76
+ "@portabletext/block-tools": "1.1.3",
77
77
  "@portabletext/patches": "1.1.2"
78
78
  },
79
79
  "devDependencies": {
80
80
  "@portabletext/toolkit": "^2.0.16",
81
81
  "@sanity/diff-match-patch": "^3.2.0",
82
82
  "@sanity/pkg-utils": "^7.0.2",
83
- "@sanity/schema": "^3.71.1",
84
- "@sanity/types": "^3.71.1",
83
+ "@sanity/schema": "^3.71.2",
84
+ "@sanity/types": "^3.71.2",
85
85
  "@testing-library/jest-dom": "^6.6.3",
86
86
  "@testing-library/react": "^16.2.0",
87
87
  "@types/debug": "^4.1.12",
@@ -92,25 +92,25 @@
92
92
  "@typescript-eslint/eslint-plugin": "^8.18.1",
93
93
  "@typescript-eslint/parser": "^8.18.1",
94
94
  "@vitejs/plugin-react": "^4.3.4",
95
- "@vitest/browser": "^3.0.2",
96
- "@vitest/coverage-istanbul": "^3.0.2",
97
- "babel-plugin-react-compiler": "19.0.0-beta-decd7b8-20250118",
95
+ "@vitest/browser": "^3.0.4",
96
+ "@vitest/coverage-istanbul": "^3.0.4",
97
+ "babel-plugin-react-compiler": "19.0.0-beta-27714ef-20250124",
98
98
  "eslint": "8.57.1",
99
- "eslint-plugin-react-compiler": "19.0.0-beta-decd7b8-20250118",
99
+ "eslint-plugin-react-compiler": "19.0.0-beta-27714ef-20250124",
100
100
  "eslint-plugin-react-hooks": "^5.1.0",
101
101
  "jsdom": "^26.0.0",
102
102
  "react": "^19.0.0",
103
103
  "react-dom": "^19.0.0",
104
104
  "rxjs": "^7.8.1",
105
105
  "typescript": "5.7.3",
106
- "vite": "^6.0.4",
107
- "vitest": "^3.0.2",
106
+ "vite": "^6.0.11",
107
+ "vitest": "^3.0.4",
108
108
  "vitest-browser-react": "^0.0.4",
109
- "racejar": "1.1.1"
109
+ "racejar": "1.1.2"
110
110
  },
111
111
  "peerDependencies": {
112
- "@sanity/schema": "^3.71.1",
113
- "@sanity/types": "^3.71.1",
112
+ "@sanity/schema": "^3.71.2",
113
+ "@sanity/types": "^3.71.2",
114
114
  "react": "^16.9 || ^17 || ^18 || ^19",
115
115
  "rxjs": "^7.8.1"
116
116
  },
@@ -6,7 +6,11 @@ import type {BehaviorActionImplementation} from './behavior.actions'
6
6
  export const insertBlockActionImplementation: BehaviorActionImplementation<
7
7
  'insert.block'
8
8
  > = ({context, action}) => {
9
- const parsedBlock = parseBlock({block: action.block, context})
9
+ const parsedBlock = parseBlock({
10
+ block: action.block,
11
+ context,
12
+ options: {refreshKeys: false},
13
+ })
10
14
 
11
15
  if (!parsedBlock) {
12
16
  throw new Error(`Failed to parse block ${JSON.stringify(action.block)}`)
@@ -38,7 +38,11 @@ export const converterPortableText = defineConverter({
38
38
  }
39
39
 
40
40
  const parsedBlocks = blocks.flatMap((block) => {
41
- const parsedBlock = parseBlock({context, block})
41
+ const parsedBlock = parseBlock({
42
+ context,
43
+ block,
44
+ options: {refreshKeys: true},
45
+ })
42
46
  return parsedBlock ? [parsedBlock] : []
43
47
  })
44
48
 
@@ -28,7 +28,7 @@ async function getEditableElement(
28
28
  describe('adds empty text block if its needed', () => {
29
29
  const newBlock = {
30
30
  _type: 'myTestBlockType',
31
- _key: '3',
31
+ _key: '1',
32
32
  style: 'normal',
33
33
  markDefs: [],
34
34
  children: [
@@ -96,7 +96,7 @@ describe('adds empty text block if its needed', () => {
96
96
  },
97
97
  {
98
98
  _type: 'myTestBlockType',
99
- _key: '3',
99
+ _key: '1',
100
100
  style: 'normal',
101
101
  markDefs: [],
102
102
  children: [
@@ -337,7 +337,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
337
337
  PortableTextEditor.focus(editorRef.current)
338
338
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
339
339
  {
340
- _key: '5',
340
+ _key: '3',
341
341
  _type: 'myTestBlockType',
342
342
  children: [{_key: '4', _type: 'span', marks: [], text: ''}],
343
343
  markDefs: [],
@@ -111,12 +111,12 @@ describe('plugin:withEditableAPI: .insertChild()', () => {
111
111
  text: 'Block A',
112
112
  },
113
113
  {
114
- _key: '3',
114
+ _key: '2',
115
115
  _type: 'someObject',
116
116
  color: 'red',
117
117
  },
118
118
  {
119
- _key: '4',
119
+ _key: '3',
120
120
  _type: 'span',
121
121
  marks: [],
122
122
  text: '',
@@ -155,12 +155,12 @@ describe('plugin:withEditableAPI: .insertChild()', () => {
155
155
  text: 'Block A',
156
156
  },
157
157
  {
158
- _key: '3',
158
+ _key: '2',
159
159
  _type: 'someObject',
160
160
  color: 'red',
161
161
  },
162
162
  {
163
- _key: '7',
163
+ _key: '5',
164
164
  _type: 'span',
165
165
  marks: [],
166
166
  text: ' ',
@@ -172,8 +172,8 @@ describe('plugin:withEditableAPI: .insertChild()', () => {
172
172
  ])
173
173
 
174
174
  expect(PortableTextEditor.getSelection(editorRef.current)).toEqual({
175
- anchor: {path: [{_key: 'a'}, 'children', {_key: '7'}], offset: 1},
176
- focus: {path: [{_key: 'a'}, 'children', {_key: '7'}], offset: 1},
175
+ anchor: {path: [{_key: 'a'}, 'children', {_key: '5'}], offset: 1},
176
+ focus: {path: [{_key: 'a'}, 'children', {_key: '5'}], offset: 1},
177
177
  backward: false,
178
178
  })
179
179
  }
@@ -240,7 +240,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
240
240
  await waitFor(() => {
241
241
  if (editorRef.current) {
242
242
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
243
- {_key: '2', _type: 'someObject', color: 'red'},
243
+ {_key: '1', _type: 'someObject', color: 'red'},
244
244
  ])
245
245
  }
246
246
  })
@@ -292,7 +292,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
292
292
  if (editorRef.current) {
293
293
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
294
294
  ...initialValue,
295
- {_key: '2', _type: 'someObject', color: 'red'},
295
+ {_key: '1', _type: 'someObject', color: 'red'},
296
296
  ])
297
297
  }
298
298
  })
@@ -345,7 +345,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
345
345
  await waitFor(() => {
346
346
  if (editorRef.current) {
347
347
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
348
- {_key: '2', _type: 'someObject', color: 'red'},
348
+ {_key: '1', _type: 'someObject', color: 'red'},
349
349
  ...initialValue,
350
350
  ])
351
351
  }
@@ -402,7 +402,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
402
402
  if (editorRef.current) {
403
403
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
404
404
  ...value,
405
- {_key: '2', _type: 'someObject', color: 'yellow'},
405
+ {_key: '1', _type: 'someObject', color: 'yellow'},
406
406
  ])
407
407
  }
408
408
  })
@@ -455,7 +455,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
455
455
  if (editorRef.current) {
456
456
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
457
457
  value[0],
458
- {_key: '2', _type: 'someObject', color: 'yellow'},
458
+ {_key: '1', _type: 'someObject', color: 'yellow'},
459
459
  value[1],
460
460
  ])
461
461
  }
@@ -504,7 +504,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
504
504
  if (editorRef.current) {
505
505
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
506
506
  value[0],
507
- {_key: '2', _type: 'someObject', color: 'yellow'},
507
+ {_key: '1', _type: 'someObject', color: 'yellow'},
508
508
  ])
509
509
  }
510
510
  })
@@ -73,41 +73,34 @@ describe('plugin:withPortableTextMarksModel', () => {
73
73
  anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 3},
74
74
  })
75
75
  PortableTextEditor.toggleMark(editorRef.current, 'strong')
76
- expect(PortableTextEditor.getValue(editorRef.current))
77
- .toMatchInlineSnapshot(`
78
- [
79
- {
80
- "_key": "a",
81
- "_type": "myTestBlockType",
82
- "children": [
83
- {
84
- "_key": "a1",
85
- "_type": "span",
86
- "marks": [
87
- "strong",
88
- ],
89
- "text": "1",
90
- },
91
- {
92
- "_key": "2",
93
- "_type": "span",
94
- "marks": [],
95
- "text": "23",
96
- },
97
- {
98
- "_key": "1",
99
- "_type": "span",
100
- "marks": [
101
- "strong",
102
- ],
103
- "text": "4",
104
- },
105
- ],
106
- "markDefs": [],
107
- "style": "normal",
108
- },
109
- ]
110
- `)
76
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
77
+ {
78
+ _key: 'a',
79
+ _type: 'myTestBlockType',
80
+ children: [
81
+ {
82
+ _key: 'a1',
83
+ _type: 'span',
84
+ marks: ['strong'],
85
+ text: '1',
86
+ },
87
+ {
88
+ _key: '2',
89
+ _type: 'span',
90
+ marks: [],
91
+ text: '23',
92
+ },
93
+ {
94
+ _key: '1',
95
+ _type: 'span',
96
+ marks: ['strong'],
97
+ text: '4',
98
+ },
99
+ ],
100
+ markDefs: [],
101
+ style: 'normal',
102
+ },
103
+ ])
111
104
  }
112
105
  })
113
106
  await waitFor(() => {
@@ -461,11 +454,11 @@ describe('plugin:withPortableTextMarksModel', () => {
461
454
  style: 'normal',
462
455
  },
463
456
  {
464
- _key: '5',
457
+ _key: '1',
465
458
  _type: 'myTestBlockType',
466
459
  children: [
467
460
  {
468
- _key: '3',
461
+ _key: '2',
469
462
  _type: 'span',
470
463
  marks: [],
471
464
  text: '',
@@ -43,11 +43,19 @@ export function createWithObjectKeys(
43
43
  }
44
44
 
45
45
  if (operation.type === 'split_node') {
46
+ const existingKeys = [...Node.descendants(editor)].map(
47
+ ([node]) => node._key,
48
+ )
49
+
46
50
  apply({
47
51
  ...operation,
48
52
  properties: {
49
53
  ...operation.properties,
50
- _key: editorActor.getSnapshot().context.keyGenerator(),
54
+ _key:
55
+ operation.properties._key === undefined ||
56
+ existingKeys.includes(operation.properties._key)
57
+ ? editorActor.getSnapshot().context.keyGenerator()
58
+ : operation.properties._key,
51
59
  },
52
60
  })
53
61
 
@@ -56,11 +64,19 @@ export function createWithObjectKeys(
56
64
 
57
65
  if (operation.type === 'insert_node') {
58
66
  if (!Editor.isEditor(operation.node)) {
67
+ const existingKeys = [...Node.descendants(editor)].map(
68
+ ([node]) => node._key,
69
+ )
70
+
59
71
  apply({
60
72
  ...operation,
61
73
  node: {
62
74
  ...operation.node,
63
- _key: editorActor.getSnapshot().context.keyGenerator(),
75
+ _key:
76
+ operation.node._key === undefined ||
77
+ existingKeys.includes(operation.node._key)
78
+ ? editorActor.getSnapshot().context.keyGenerator()
79
+ : operation.node._key,
64
80
  },
65
81
  })
66
82
 
@@ -361,6 +361,7 @@ export function createWithPortableTextMarkModel(
361
361
  ) {
362
362
  Transforms.insertNodes(editor, {
363
363
  ...op.node,
364
+ _key: editorActor.getSnapshot().context.keyGenerator(),
364
365
  marks:
365
366
  op.node.marks?.filter(
366
367
  (mark) => !annotationsEnding.includes(mark),
@@ -382,6 +383,7 @@ export function createWithPortableTextMarkModel(
382
383
  ) {
383
384
  Transforms.insertNodes(editor, {
384
385
  ...op.node,
386
+ _key: editorActor.getSnapshot().context.keyGenerator(),
385
387
  marks:
386
388
  op.node.marks?.filter(
387
389
  (mark) => !annotationsStarting.includes(mark),
@@ -403,6 +405,7 @@ export function createWithPortableTextMarkModel(
403
405
  ) {
404
406
  Transforms.insertNodes(editor, {
405
407
  ...op.node,
408
+ _key: editorActor.getSnapshot().context.keyGenerator(),
406
409
  marks: nextSpanDecorators,
407
410
  })
408
411
  return
@@ -9,9 +9,13 @@ import {isTypedObject} from './asserters'
9
9
  export function parseBlock({
10
10
  context,
11
11
  block,
12
+ options,
12
13
  }: {
13
14
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
14
15
  block: unknown
16
+ options: {
17
+ refreshKeys: boolean
18
+ }
15
19
  }): PortableTextBlock | undefined {
16
20
  if (!isTypedObject(block)) {
17
21
  return undefined
@@ -26,10 +30,36 @@ export function parseBlock({
26
30
  return undefined
27
31
  }
28
32
 
29
- if (!isPortableTextTextBlock(block)) {
33
+ if (block._type !== context.schema.block.name) {
34
+ const _key = options.refreshKeys
35
+ ? context.keyGenerator()
36
+ : typeof block._key === 'string'
37
+ ? block._key
38
+ : context.keyGenerator()
30
39
  return {
31
40
  ...block,
32
- _key: context.keyGenerator(),
41
+ _key,
42
+ }
43
+ }
44
+
45
+ if (!isPortableTextTextBlock(block)) {
46
+ return {
47
+ _type: context.schema.block.name,
48
+ _key: options.refreshKeys
49
+ ? context.keyGenerator()
50
+ : typeof block._key === 'string'
51
+ ? block._key
52
+ : context.keyGenerator(),
53
+ children: [
54
+ {
55
+ _key: context.keyGenerator(),
56
+ _type: context.schema.span.name,
57
+ text: '',
58
+ marks: [],
59
+ },
60
+ ],
61
+ markDefs: [],
62
+ style: context.schema.styles[0].value,
33
63
  }
34
64
  }
35
65
 
@@ -40,7 +70,7 @@ export function parseBlock({
40
70
  (annotation) => annotation.name === markDef._type,
41
71
  )
42
72
  ) {
43
- const _key = context.keyGenerator()
73
+ const _key = options.refreshKeys ? context.keyGenerator() : markDef._key
44
74
  markDefKeyMap.set(markDef._key, _key)
45
75
 
46
76
  return [
@@ -72,7 +102,7 @@ export function parseBlock({
72
102
  return [
73
103
  {
74
104
  ...child,
75
- _key: context.keyGenerator(),
105
+ _key: options.refreshKeys ? context.keyGenerator() : child._key,
76
106
  },
77
107
  ]
78
108
  }
@@ -94,7 +124,7 @@ export function parseBlock({
94
124
  return [
95
125
  {
96
126
  ...child,
97
- _key: context.keyGenerator(),
127
+ _key: options.refreshKeys ? context.keyGenerator() : child._key,
98
128
  marks,
99
129
  },
100
130
  ]
@@ -102,7 +132,7 @@ export function parseBlock({
102
132
 
103
133
  const parsedBlock = {
104
134
  ...block,
105
- _key: context.keyGenerator(),
135
+ _key: options.refreshKeys ? context.keyGenerator() : block._key,
106
136
  children:
107
137
  children.length > 0
108
138
  ? children