@portabletext/editor 1.44.11 → 1.44.13

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.44.11",
3
+ "version": "1.44.13",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -89,14 +89,14 @@
89
89
  "@sanity/schema": "^3.82.0",
90
90
  "@sanity/types": "^3.82.0",
91
91
  "@testing-library/jest-dom": "^6.6.3",
92
- "@testing-library/react": "^16.2.0",
92
+ "@testing-library/react": "^16.3.0",
93
93
  "@types/debug": "^4.1.12",
94
94
  "@types/lodash": "^4.17.13",
95
95
  "@types/lodash.startcase": "^4.4.9",
96
- "@types/react": "^19.0.12",
97
- "@types/react-dom": "^19.0.4",
98
- "@typescript-eslint/eslint-plugin": "^8.26.1",
99
- "@typescript-eslint/parser": "^8.26.1",
96
+ "@types/react": "^19.1.0",
97
+ "@types/react-dom": "^19.1.1",
98
+ "@typescript-eslint/eslint-plugin": "^8.29.0",
99
+ "@typescript-eslint/parser": "^8.29.0",
100
100
  "@vitejs/plugin-react": "^4.3.4",
101
101
  "@vitest/browser": "^3.1.1",
102
102
  "@vitest/coverage-istanbul": "^3.1.1",
@@ -109,7 +109,7 @@
109
109
  "react-dom": "^19.1.0",
110
110
  "rxjs": "^7.8.2",
111
111
  "typescript": "5.8.2",
112
- "vite": "^6.2.0",
112
+ "vite": "^6.2.5",
113
113
  "vitest": "^3.1.1",
114
114
  "vitest-browser-react": "^0.1.1",
115
115
  "racejar": "1.2.3"
@@ -95,12 +95,31 @@ const breakingBlockObject = defineBehavior({
95
95
  const clickingAboveLonelyBlockObject = defineBehavior({
96
96
  on: 'mouse.click',
97
97
  guard: ({snapshot, event}) => {
98
- if (!selectors.isSelectionCollapsed(snapshot)) {
98
+ if (snapshot.context.readOnly) {
99
99
  return false
100
100
  }
101
101
 
102
- const focusBlockObject = selectors.getFocusBlockObject(snapshot)
103
- const previousBlock = selectors.getPreviousBlock(snapshot)
102
+ if (
103
+ snapshot.context.selection &&
104
+ !selectors.isSelectionCollapsed(snapshot)
105
+ ) {
106
+ return false
107
+ }
108
+
109
+ const focusBlockObject = selectors.getFocusBlockObject({
110
+ ...snapshot,
111
+ context: {
112
+ ...snapshot.context,
113
+ selection: event.position.selection,
114
+ },
115
+ })
116
+ const previousBlock = selectors.getPreviousBlock({
117
+ ...snapshot,
118
+ context: {
119
+ ...snapshot.context,
120
+ selection: event.position.selection,
121
+ },
122
+ })
104
123
 
105
124
  return (
106
125
  event.position.isEditor &&
@@ -110,13 +129,18 @@ const clickingAboveLonelyBlockObject = defineBehavior({
110
129
  )
111
130
  },
112
131
  actions: [
113
- ({snapshot}) => [
132
+ ({snapshot, event}) => [
133
+ raise({
134
+ type: 'select',
135
+ selection: event.position.selection,
136
+ }),
114
137
  raise({
115
138
  type: 'insert.block',
116
139
  block: {
117
140
  _type: snapshot.context.schema.block.name,
118
141
  },
119
142
  placement: 'before',
143
+ select: 'start',
120
144
  }),
121
145
  ],
122
146
  ],
@@ -125,12 +149,31 @@ const clickingAboveLonelyBlockObject = defineBehavior({
125
149
  const clickingBelowLonelyBlockObject = defineBehavior({
126
150
  on: 'mouse.click',
127
151
  guard: ({snapshot, event}) => {
128
- if (!selectors.isSelectionCollapsed(snapshot)) {
152
+ if (snapshot.context.readOnly) {
129
153
  return false
130
154
  }
131
155
 
132
- const focusBlockObject = selectors.getFocusBlockObject(snapshot)
133
- const nextBlock = selectors.getNextBlock(snapshot)
156
+ if (
157
+ snapshot.context.selection &&
158
+ !selectors.isSelectionCollapsed(snapshot)
159
+ ) {
160
+ return false
161
+ }
162
+
163
+ const focusBlockObject = selectors.getFocusBlockObject({
164
+ ...snapshot,
165
+ context: {
166
+ ...snapshot.context,
167
+ selection: event.position.selection,
168
+ },
169
+ })
170
+ const nextBlock = selectors.getNextBlock({
171
+ ...snapshot,
172
+ context: {
173
+ ...snapshot.context,
174
+ selection: event.position.selection,
175
+ },
176
+ })
134
177
 
135
178
  return (
136
179
  event.position.isEditor &&
@@ -140,13 +183,18 @@ const clickingBelowLonelyBlockObject = defineBehavior({
140
183
  )
141
184
  },
142
185
  actions: [
143
- ({snapshot}) => [
186
+ ({snapshot, event}) => [
187
+ raise({
188
+ type: 'select',
189
+ selection: event.position.selection,
190
+ }),
144
191
  raise({
145
192
  type: 'insert.block',
146
193
  block: {
147
194
  _type: snapshot.context.schema.block.name,
148
195
  },
149
196
  placement: 'after',
197
+ select: 'start',
150
198
  }),
151
199
  ],
152
200
  ],
@@ -372,7 +372,13 @@ export const PortableTextEditable = forwardRef<
372
372
  event.stopPropagation()
373
373
  event.preventDefault()
374
374
 
375
- const selection = editorActor.getSnapshot().context.selection
375
+ const selection = slateEditor.selection
376
+ ? slateRangeToSelection({
377
+ schema: editorActor.getSnapshot().context.schema,
378
+ editor: slateEditor,
379
+ range: slateEditor.selection,
380
+ })
381
+ : undefined
376
382
  const position = selection ? {selection} : undefined
377
383
 
378
384
  if (!position) {
@@ -586,25 +592,22 @@ export const PortableTextEditable = forwardRef<
586
592
  }
587
593
 
588
594
  const position = getEventPosition({
589
- schema: editorActor.getSnapshot().context.schema,
595
+ editorActor,
590
596
  slateEditor,
591
597
  event: event.nativeEvent,
592
598
  })
593
599
 
594
- if (!position) {
595
- console.warn('Could not find EventPosition for MouseEvent')
596
- return
600
+ if (position) {
601
+ editorActor.send({
602
+ type: 'behavior event',
603
+ behaviorEvent: {
604
+ type: 'mouse.click',
605
+ position,
606
+ },
607
+ editor: slateEditor,
608
+ nativeEvent: event,
609
+ })
597
610
  }
598
-
599
- editorActor.send({
600
- type: 'behavior event',
601
- behaviorEvent: {
602
- type: 'mouse.click',
603
- position,
604
- },
605
- editor: slateEditor,
606
- nativeEvent: event,
607
- })
608
611
  },
609
612
  [onClick, editorActor, slateEditor],
610
613
  )
@@ -819,7 +822,7 @@ export const PortableTextEditable = forwardRef<
819
822
  }
820
823
 
821
824
  const position = getEventPosition({
822
- schema: editorActor.getSnapshot().context.schema,
825
+ editorActor,
823
826
  slateEditor,
824
827
  event: event.nativeEvent,
825
828
  })
@@ -985,7 +988,7 @@ export const PortableTextEditable = forwardRef<
985
988
  }
986
989
 
987
990
  const position = getEventPosition({
988
- schema: editorActor.getSnapshot().context.schema,
991
+ editorActor,
989
992
  slateEditor,
990
993
  event: event.nativeEvent,
991
994
  })
@@ -1045,7 +1048,7 @@ export const PortableTextEditable = forwardRef<
1045
1048
  }
1046
1049
 
1047
1050
  const position = getEventPosition({
1048
- schema: editorActor.getSnapshot().context.schema,
1051
+ editorActor,
1049
1052
  slateEditor,
1050
1053
  event: event.nativeEvent,
1051
1054
  })
@@ -1081,7 +1084,7 @@ export const PortableTextEditable = forwardRef<
1081
1084
  }
1082
1085
 
1083
1086
  const position = getEventPosition({
1084
- schema: editorActor.getSnapshot().context.schema,
1087
+ editorActor,
1085
1088
  slateEditor,
1086
1089
  event: event.nativeEvent,
1087
1090
  })
@@ -1118,7 +1121,7 @@ export const PortableTextEditable = forwardRef<
1118
1121
  }
1119
1122
 
1120
1123
  const position = getEventPosition({
1121
- schema: editorActor.getSnapshot().context.schema,
1124
+ editorActor,
1122
1125
  slateEditor,
1123
1126
  event: event.nativeEvent,
1124
1127
  })
@@ -1156,7 +1159,7 @@ export const PortableTextEditable = forwardRef<
1156
1159
  }
1157
1160
 
1158
1161
  const position = getEventPosition({
1159
- schema: editorActor.getSnapshot().context.schema,
1162
+ editorActor,
1160
1163
  slateEditor,
1161
1164
  event: event.nativeEvent,
1162
1165
  })
@@ -3,6 +3,7 @@ import {defineField, defineType, type ObjectSchemaType} from '@sanity/types'
3
3
  import startCase from 'lodash.startcase'
4
4
  import type {PortableTextMemberSchemaTypes} from '../types/editor'
5
5
  import {createEditorSchema} from './create-editor-schema'
6
+ import {defaultKeyGenerator} from './key-generator'
6
7
 
7
8
  /**
8
9
  * @public
@@ -55,6 +56,24 @@ export function defineSchema<const TSchemaDefinition extends SchemaDefinition>(
55
56
  return definition
56
57
  }
57
58
 
59
+ const temporaryImageName = `tmp-${defaultKeyGenerator()}-image`
60
+ const temporaryUrlName = `tmp-${defaultKeyGenerator()}-url`
61
+
62
+ const temporaryObjectNames: Record<string, string> = {
63
+ image: temporaryImageName,
64
+ url: temporaryUrlName,
65
+ }
66
+
67
+ const objectNames: Record<string, string> = {
68
+ [temporaryImageName]: 'image',
69
+ [temporaryUrlName]: 'url',
70
+ }
71
+
72
+ const defaultObjectTitles: Record<string, string> = {
73
+ image: 'Image',
74
+ url: 'URL',
75
+ }
76
+
58
77
  /**
59
78
  * @public
60
79
  */
@@ -68,21 +87,30 @@ export function compileSchemaDefinition<
68
87
  defineType({
69
88
  type: 'object',
70
89
  // Very naive way to work around `SanitySchema.compile` adding default
71
- // fields to objects with the name `image`
72
- name: blockObject.name === 'image' ? 'tmp-image' : blockObject.name,
90
+ // fields to objects with certain names.
91
+ name: temporaryObjectNames[blockObject.name] ?? blockObject.name,
73
92
  title:
74
- blockObject.name === 'image' && blockObject.title === undefined
75
- ? 'Image'
93
+ blockObject.title === undefined
94
+ ? // This avoids the default title which is a title case of the object name
95
+ defaultObjectTitles[blockObject.name]
76
96
  : blockObject.title,
77
97
  fields: [],
78
98
  }),
79
99
  ) ?? []
100
+
80
101
  const inlineObjects =
81
102
  definition?.inlineObjects?.map((inlineObject) =>
82
103
  defineType({
83
104
  type: 'object',
84
- name: inlineObject.name,
85
- title: inlineObject.title,
105
+ // Very naive way to work around `SanitySchema.compile` adding default
106
+ // fields to objects with certain names.
107
+ name: temporaryObjectNames[inlineObject.name] ?? inlineObject.name,
108
+
109
+ title:
110
+ inlineObject.title === undefined
111
+ ? // This avoids the default title which is a title case of the object name
112
+ defaultObjectTitles[inlineObject.name]
113
+ : inlineObject.title,
86
114
  fields: [],
87
115
  }),
88
116
  ) ?? []
@@ -132,16 +160,24 @@ export function compileSchemaDefinition<
132
160
  return {
133
161
  ...pteSchema,
134
162
  blockObjects: pteSchema.blockObjects.map((blockObject) =>
135
- blockObject.name === 'tmp-image'
163
+ objectNames[blockObject.name] !== undefined
136
164
  ? ({
137
165
  ...blockObject,
138
- name: 'image',
166
+ name: objectNames[blockObject.name],
139
167
  type: {
140
168
  ...blockObject.type,
141
- name: 'image',
169
+ name: objectNames[blockObject.name],
142
170
  },
143
171
  } as ObjectSchemaType)
144
172
  : blockObject,
145
173
  ),
174
+ inlineObjects: pteSchema.inlineObjects.map((inlineObject) =>
175
+ objectNames[inlineObject.name] !== undefined
176
+ ? ({
177
+ ...inlineObject,
178
+ name: objectNames[inlineObject.name],
179
+ } as ObjectSchemaType)
180
+ : inlineObject,
181
+ ),
146
182
  } satisfies EditorSchema
147
183
  }
@@ -382,6 +382,7 @@ export const editorMachine = setup({
382
382
  actions: 'handle behavior event',
383
383
  guard: ({event}) =>
384
384
  event.behaviorEvent.type === 'clipboard.copy' ||
385
+ event.behaviorEvent.type === 'mouse.click' ||
385
386
  event.behaviorEvent.type === 'serialize' ||
386
387
  event.behaviorEvent.type === 'serialization.failure' ||
387
388
  event.behaviorEvent.type === 'serialization.success' ||
@@ -1,6 +1,7 @@
1
1
  import {Editor, type BaseRange, type Node} from 'slate'
2
2
  import {DOMEditor, isDOMNode} from 'slate-dom'
3
3
  import type {EditorSchema, EditorSelection} from '..'
4
+ import type {EditorActor} from '../editor/editor-machine'
4
5
  import type {PortableTextSlateEditor} from '../types/editor'
5
6
  import * as utils from '../utils'
6
7
  import {
@@ -21,14 +22,18 @@ export type EventPosition = {
21
22
  export type EventPositionBlock = EventPosition['block']
22
23
 
23
24
  export function getEventPosition({
24
- schema,
25
+ editorActor,
25
26
  slateEditor,
26
27
  event,
27
28
  }: {
28
- schema: EditorSchema
29
+ editorActor: EditorActor
29
30
  slateEditor: PortableTextSlateEditor
30
31
  event: DragEvent | MouseEvent
31
32
  }): EventPosition | undefined {
33
+ if (editorActor.getSnapshot().matches({setup: 'setting up'})) {
34
+ return undefined
35
+ }
36
+
32
37
  const node = getEventNode({slateEditor, event})
33
38
 
34
39
  if (!node) {
@@ -37,13 +42,13 @@ export function getEventPosition({
37
42
 
38
43
  const block = getNodeBlock({
39
44
  editor: slateEditor,
40
- schema,
45
+ schema: editorActor.getSnapshot().context.schema,
41
46
  node,
42
47
  })
43
48
 
44
49
  const positionBlock = getEventPositionBlock({node, slateEditor, event})
45
50
  const selection = getEventSelection({
46
- schema,
51
+ schema: editorActor.getSnapshot().context.schema,
47
52
  slateEditor,
48
53
  event,
49
54
  })