@portabletext/editor 1.50.3 → 1.50.4

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.
@@ -2950,6 +2950,7 @@ declare interface PortableTextSlateEditor extends ReactEditor {
2950
2950
  isTextBlock: (value: unknown) => value is PortableTextTextBlock
2951
2951
  isTextSpan: (value: unknown) => value is PortableTextSpan
2952
2952
  isListBlock: (value: unknown) => value is PortableTextListBlock
2953
+ value: Array<PortableTextBlock>
2953
2954
  /**
2954
2955
  * Use hotkeys
2955
2956
  */
@@ -2950,6 +2950,7 @@ declare interface PortableTextSlateEditor extends ReactEditor {
2950
2950
  isTextBlock: (value: unknown) => value is PortableTextTextBlock
2951
2951
  isTextSpan: (value: unknown) => value is PortableTextSpan
2952
2952
  isListBlock: (value: unknown) => value is PortableTextListBlock
2953
+ value: Array<PortableTextBlock>
2953
2954
  /**
2954
2955
  * Use hotkeys
2955
2956
  */
@@ -3049,6 +3049,7 @@ declare interface PortableTextSlateEditor extends ReactEditor {
3049
3049
  isTextBlock: (value: unknown) => value is PortableTextTextBlock
3050
3050
  isTextSpan: (value: unknown) => value is PortableTextSpan
3051
3051
  isListBlock: (value: unknown) => value is PortableTextListBlock
3052
+ value: Array<PortableTextBlock>
3052
3053
  /**
3053
3054
  * Use hotkeys
3054
3055
  */
@@ -3049,6 +3049,7 @@ declare interface PortableTextSlateEditor extends ReactEditor {
3049
3049
  isTextBlock: (value: unknown) => value is PortableTextTextBlock
3050
3050
  isTextSpan: (value: unknown) => value is PortableTextSpan
3051
3051
  isListBlock: (value: unknown) => value is PortableTextListBlock
3052
+ value: Array<PortableTextBlock>
3052
3053
  /**
3053
3054
  * Use hotkeys
3054
3055
  */
@@ -2810,6 +2810,7 @@ declare interface PortableTextSlateEditor extends ReactEditor {
2810
2810
  isTextBlock: (value: unknown) => value is PortableTextTextBlock
2811
2811
  isTextSpan: (value: unknown) => value is PortableTextSpan
2812
2812
  isListBlock: (value: unknown) => value is PortableTextListBlock
2813
+ value: Array<PortableTextBlock>
2813
2814
  /**
2814
2815
  * Use hotkeys
2815
2816
  */
@@ -2810,6 +2810,7 @@ declare interface PortableTextSlateEditor extends ReactEditor {
2810
2810
  isTextBlock: (value: unknown) => value is PortableTextTextBlock
2811
2811
  isTextSpan: (value: unknown) => value is PortableTextSpan
2812
2812
  isListBlock: (value: unknown) => value is PortableTextListBlock
2813
+ value: Array<PortableTextBlock>
2813
2814
  /**
2814
2815
  * Use hotkeys
2815
2816
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.50.3",
3
+ "version": "1.50.4",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -71,6 +71,7 @@
71
71
  "@xstate/react": "^5.0.4",
72
72
  "debug": "^4.4.1",
73
73
  "get-random-values-esm": "^1.0.2",
74
+ "immer": "^10.1.1",
74
75
  "lodash": "^4.17.21",
75
76
  "lodash.startcase": "^4.4.0",
76
77
  "react-compiler-runtime": "19.1.0-rc.1",
@@ -138,6 +138,7 @@ export class PortableTextEditor extends Component<
138
138
  syncActor: SyncActor
139
139
  }
140
140
 
141
+ private subscriptions: Array<() => () => void> = []
141
142
  private unsubscribers: Array<() => void> = []
142
143
 
143
144
  constructor(props: PortableTextEditorProps) {
@@ -160,28 +161,7 @@ export class PortableTextEditor extends Component<
160
161
  schema: props.schemaType,
161
162
  })
162
163
 
163
- this.unsubscribers.push(
164
- (() => {
165
- const subscription = actors.relayActor.on('*', (event) => {
166
- const change = eventToChange(event)
167
-
168
- if (change) {
169
- props.onChange(change)
170
-
171
- this.change$.next(change)
172
- }
173
- })
174
-
175
- return () => {
176
- subscription.unsubscribe()
177
- }
178
- })(),
179
- )
180
-
181
- for (const subscription of subscriptions) {
182
- this.unsubscribers.push(subscription())
183
- }
184
-
164
+ this.subscriptions = subscriptions
185
165
  this.actors = actors
186
166
 
187
167
  this.editor = editor
@@ -198,6 +178,26 @@ export class PortableTextEditor extends Component<
198
178
  return
199
179
  }
200
180
 
181
+ for (const subscription of this.subscriptions) {
182
+ this.unsubscribers.push(subscription())
183
+ }
184
+
185
+ const relayActorSubscription = this.actors.relayActor.on('*', (event) => {
186
+ const change = eventToChange(event)
187
+
188
+ if (!change) {
189
+ return
190
+ }
191
+
192
+ if (!this.props.editor) {
193
+ this.props.onChange(change)
194
+ }
195
+
196
+ this.change$.next(change)
197
+ })
198
+
199
+ this.unsubscribers.push(relayActorSubscription.unsubscribe)
200
+
201
201
  this.actors.editorActor.start()
202
202
  this.actors.mutationActor.start()
203
203
  this.actors.relayActor.start()
@@ -1,6 +1,8 @@
1
1
  import {createEditor, type Descendant} from 'slate'
2
2
  import {withReact} from 'slate-react'
3
+ import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
3
4
  import {debugWithName} from '../internal-utils/debug'
5
+ import {toSlateValue} from '../internal-utils/values'
4
6
  import {
5
7
  KEY_TO_SLATE_ELEMENT,
6
8
  KEY_TO_VALUE_ELEMENT,
@@ -35,7 +37,13 @@ export function createSlateEditor(config: SlateEditorConfig): SlateEditor {
35
37
  KEY_TO_VALUE_ELEMENT.set(instance, {})
36
38
  KEY_TO_SLATE_ELEMENT.set(instance, {})
37
39
 
38
- const initialValue = [instance.pteCreateTextBlock({decorators: []})]
40
+ instance.value = [
41
+ createPlaceholderBlock(config.editorActor.getSnapshot().context),
42
+ ]
43
+
44
+ const initialValue = toSlateValue(instance.value, {
45
+ schemaTypes: config.editorActor.getSnapshot().context.schema,
46
+ })
39
47
 
40
48
  const slateEditor: SlateEditor = {
41
49
  instance,
@@ -1,6 +1,5 @@
1
1
  import {useSelector} from '@xstate/react'
2
2
  import type {Editor} from '../editor'
3
- import {slateChildrenToBlocks} from '../internal-utils/slate-children-to-blocks'
4
3
  import type {PortableTextSlateEditor} from '../types/editor'
5
4
  import type {InternalEditor} from './create-editor'
6
5
  import type {EditorActor} from './editor-machine'
@@ -78,10 +77,7 @@ export function getEditorSnapshot({
78
77
  readOnly: editorActorSnapshot.matches({'edit mode': 'read only'}),
79
78
  schema: editorActorSnapshot.context.schema,
80
79
  selection: editorActorSnapshot.context.selection,
81
- value: slateChildrenToBlocks(
82
- editorActorSnapshot.context.schema,
83
- slateEditorInstance.children,
84
- ),
80
+ value: slateEditorInstance.value,
85
81
  },
86
82
  beta: {
87
83
  hasTag: (tag) => editorActorSnapshot.hasTag(tag),
@@ -1,7 +1,6 @@
1
1
  import type {PortableTextBlock} from '@sanity/types'
2
2
  import type {Converter} from '../converters/converter.types'
3
3
  import type {EventPosition} from '../internal-utils/event-position'
4
- import {slateChildrenToBlocks} from '../internal-utils/slate-children-to-blocks'
5
4
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
6
5
  import type {EditorSelection, PortableTextSlateEditor} from '../types/editor'
7
6
  import type {HasTag} from './editor-machine'
@@ -61,7 +60,6 @@ export function createEditorSnapshot({
61
60
  }
62
61
  | undefined
63
62
  }) {
64
- const value = slateChildrenToBlocks(schema, editor.children)
65
63
  const selection = editor.selection
66
64
  ? slateRangeToSelection({
67
65
  schema,
@@ -80,7 +78,7 @@ export function createEditorSnapshot({
80
78
  readOnly,
81
79
  schema,
82
80
  selection,
83
- value,
81
+ value: editor.value,
84
82
  } satisfies EditorContext
85
83
 
86
84
  return {
@@ -0,0 +1,30 @@
1
+ import {applyOperationToPortableText} from '../../internal-utils/apply-operation-to-portable-text'
2
+ import type {PortableTextSlateEditor} from '../../types/editor'
3
+ import type {EditorContext} from '../editor-snapshot'
4
+
5
+ export function pluginUpdateValue(
6
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>,
7
+ editor: PortableTextSlateEditor,
8
+ ) {
9
+ const {apply} = editor
10
+
11
+ editor.apply = (operation) => {
12
+ if (operation.type === 'set_selection') {
13
+ apply(operation)
14
+ return
15
+ }
16
+
17
+ editor.value = applyOperationToPortableText(
18
+ {
19
+ keyGenerator: context.keyGenerator,
20
+ schema: context.schema,
21
+ },
22
+ editor.value,
23
+ operation,
24
+ )
25
+
26
+ apply(operation)
27
+ }
28
+
29
+ return editor
30
+ }
@@ -13,6 +13,7 @@ import {createWithPortableTextSelections} from './createWithPortableTextSelectio
13
13
  import {createWithSchemaTypes} from './createWithSchemaTypes'
14
14
  import {createWithUndoRedo} from './createWithUndoRedo'
15
15
  import {createWithUtils} from './createWithUtils'
16
+ import {pluginUpdateValue} from './slate-plugin.update-value'
16
17
 
17
18
  export interface OriginalEditorFunctions {
18
19
  apply: (operation: BaseOperation) => void
@@ -68,7 +69,13 @@ export const withPlugins = <T extends Editor>(
68
69
  withPlaceholderBlock(
69
70
  withUtils(
70
71
  withMaxBlocks(
71
- withUndoRedo(withPatches(withPortableTextSelections(e))),
72
+ withUndoRedo(
73
+ withPatches(
74
+ withPortableTextSelections(
75
+ pluginUpdateValue(editorActor.getSnapshot().context, e),
76
+ ),
77
+ ),
78
+ ),
72
79
  ),
73
80
  ),
74
81
  ),
@@ -0,0 +1,175 @@
1
+ import {describe, expect, test} from 'vitest'
2
+ import {compileSchemaDefinition, defineSchema} from '../editor/editor-schema'
3
+ import {applyOperationToPortableText} from './apply-operation-to-portable-text'
4
+ import {createTestKeyGenerator} from './test-key-generator'
5
+
6
+ function createContext() {
7
+ const keyGenerator = createTestKeyGenerator()
8
+ const schema = compileSchemaDefinition(defineSchema({}))
9
+
10
+ return {
11
+ keyGenerator,
12
+ schema,
13
+ }
14
+ }
15
+
16
+ describe(applyOperationToPortableText.name, () => {
17
+ test('setting block object properties', () => {
18
+ expect(
19
+ applyOperationToPortableText(
20
+ createContext(),
21
+ [
22
+ {
23
+ _type: 'image',
24
+ _key: 'k0',
25
+ },
26
+ ],
27
+ {
28
+ type: 'set_node',
29
+ path: [0],
30
+ properties: {},
31
+ newProperties: {
32
+ value: {src: 'https://example.com/image.jpg'},
33
+ },
34
+ },
35
+ ),
36
+ ).toEqual([
37
+ {
38
+ _type: 'image',
39
+ _key: 'k0',
40
+ src: 'https://example.com/image.jpg',
41
+ },
42
+ ])
43
+ })
44
+
45
+ test('updating block object properties', () => {
46
+ expect(
47
+ applyOperationToPortableText(
48
+ createContext(),
49
+ [
50
+ {
51
+ _type: 'image',
52
+ _key: 'k0',
53
+ src: 'https://example.com/image.jpg',
54
+ },
55
+ ],
56
+ {
57
+ type: 'set_node',
58
+ path: [0],
59
+ properties: {
60
+ value: {src: 'https://example.com/image.jpg'},
61
+ },
62
+ newProperties: {
63
+ value: {
64
+ src: 'https://example.com/image.jpg',
65
+ alt: 'An image',
66
+ },
67
+ },
68
+ },
69
+ ),
70
+ ).toEqual([
71
+ {
72
+ _type: 'image',
73
+ _key: 'k0',
74
+ src: 'https://example.com/image.jpg',
75
+ alt: 'An image',
76
+ },
77
+ ])
78
+ })
79
+
80
+ test('removing block object properties', () => {
81
+ expect(
82
+ applyOperationToPortableText(
83
+ createContext(),
84
+ [{_type: 'image', _key: 'k0', alt: 'An image'}],
85
+ {
86
+ type: 'set_node',
87
+ path: [0],
88
+ properties: {
89
+ value: {
90
+ alt: 'An image',
91
+ },
92
+ },
93
+ newProperties: {value: {}},
94
+ },
95
+ ),
96
+ ).toEqual([{_type: 'image', _key: 'k0'}])
97
+ })
98
+
99
+ test('updating block object _key', () => {
100
+ expect(
101
+ applyOperationToPortableText(
102
+ createContext(),
103
+ [
104
+ {
105
+ _type: 'image',
106
+ _key: 'k0',
107
+ src: 'https://example.com/image.jpg',
108
+ },
109
+ ],
110
+ {
111
+ type: 'set_node',
112
+ path: [0],
113
+ properties: {_key: 'k0'},
114
+ newProperties: {_key: 'k1'},
115
+ },
116
+ ),
117
+ ).toEqual([
118
+ {
119
+ _type: 'image',
120
+ _key: 'k1',
121
+ src: 'https://example.com/image.jpg',
122
+ },
123
+ ])
124
+ })
125
+
126
+ test('updating inline object properties', () => {
127
+ expect(
128
+ applyOperationToPortableText(
129
+ createContext(),
130
+ [
131
+ {
132
+ _key: 'k0',
133
+ _type: 'block',
134
+ children: [
135
+ {
136
+ _key: 'k1',
137
+ _type: 'span',
138
+ text: '',
139
+ },
140
+ {
141
+ _key: 'k2',
142
+ _type: 'stock ticker',
143
+ },
144
+ {
145
+ _key: 'k3',
146
+ _type: 'span',
147
+ text: '',
148
+ },
149
+ ],
150
+ },
151
+ ],
152
+ {
153
+ type: 'set_node',
154
+ path: [0, 1],
155
+ properties: {},
156
+ newProperties: {
157
+ value: {
158
+ symbol: 'AAPL',
159
+ },
160
+ },
161
+ },
162
+ ),
163
+ ).toEqual([
164
+ {
165
+ _type: 'block',
166
+ _key: 'k0',
167
+ children: [
168
+ {_type: 'span', _key: 'k1', text: ''},
169
+ {_type: 'stock ticker', _key: 'k2', symbol: 'AAPL'},
170
+ {_type: 'span', _key: 'k3', text: ''},
171
+ ],
172
+ },
173
+ ])
174
+ })
175
+ })