@portabletext/editor 1.55.7 → 1.55.9

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.55.7",
3
+ "version": "1.55.9",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -1,6 +1,4 @@
1
- import type {PortableTextObject} from '@sanity/types'
2
- import {useSelector} from '@xstate/react'
3
- import {useContext, useRef, useState, type ReactElement} from 'react'
1
+ import {useRef, useState, type ReactElement} from 'react'
4
2
  import {Range, type Element as SlateElement} from 'slate'
5
3
  import {
6
4
  useSelected,
@@ -8,24 +6,27 @@ import {
8
6
  type RenderElementProps,
9
7
  } from 'slate-react'
10
8
  import type {EventPositionBlock} from '../../internal-utils/event-position'
11
- import type {RenderBlockFunction} from '../../types/editor'
12
- import {EditorActorContext} from '../editor-actor-context'
9
+ import type {
10
+ PortableTextMemberSchemaTypes,
11
+ RenderBlockFunction,
12
+ } from '../../types/editor'
13
+ import type {EditorSchema} from '../editor-schema'
13
14
  import {DropIndicator} from './drop-indicator'
14
15
  import {RenderDefaultBlockObject} from './render-default-object'
15
16
  import {useCoreBlockElementBehaviors} from './use-core-block-element-behaviors'
16
17
 
17
18
  export function RenderBlockObject(props: {
18
19
  attributes: RenderElementProps['attributes']
19
- blockObject: PortableTextObject
20
20
  children: ReactElement
21
21
  element: SlateElement
22
+ legacySchema: PortableTextMemberSchemaTypes
22
23
  readOnly: boolean
23
24
  renderBlock?: RenderBlockFunction
25
+ schema: EditorSchema
24
26
  }) {
25
27
  const [dragPositionBlock, setDragPositionBlock] =
26
28
  useState<EventPositionBlock>()
27
29
  const blockObjectRef = useRef<HTMLDivElement>(null)
28
-
29
30
  const selected = useSelected()
30
31
  const focused = useSlateSelector(
31
32
  (editor) =>
@@ -34,27 +35,29 @@ export function RenderBlockObject(props: {
34
35
  Range.isCollapsed(editor.selection),
35
36
  )
36
37
 
37
- const editorActor = useContext(EditorActorContext)
38
-
39
38
  useCoreBlockElementBehaviors({
40
39
  key: props.element._key,
41
40
  onSetDragPositionBlock: setDragPositionBlock,
42
41
  })
43
42
 
44
- const legacySchemaType = useSelector(editorActor, (s) =>
45
- s.context
46
- .getLegacySchema()
47
- .blockObjects.find(
48
- (blockObject) => blockObject.name === props.element._type,
49
- ),
43
+ const legacySchemaType = props.legacySchema.blockObjects.find(
44
+ (blockObject) => blockObject.name === props.element._type,
50
45
  )
51
46
 
52
47
  if (!legacySchemaType) {
53
48
  console.error(
54
- `Block object type ${props.element._type} not found in Schema`,
49
+ `Unable to find Block Object "${props.element._type}" in Schema`,
55
50
  )
56
51
  }
57
52
 
53
+ const blockObject = {
54
+ _key: props.element._key,
55
+ _type: props.element._type,
56
+ ...('value' in props.element && typeof props.element.value === 'object'
57
+ ? props.element.value
58
+ : {}),
59
+ }
60
+
58
61
  return (
59
62
  <div
60
63
  {...props.attributes}
@@ -72,19 +75,17 @@ export function RenderBlockObject(props: {
72
75
  >
73
76
  {props.renderBlock && legacySchemaType ? (
74
77
  props.renderBlock({
75
- children: (
76
- <RenderDefaultBlockObject blockObject={props.blockObject} />
77
- ),
78
+ children: <RenderDefaultBlockObject blockObject={blockObject} />,
78
79
  editorElementRef: blockObjectRef,
79
80
  focused,
80
81
  path: [{_key: props.element._key}],
81
82
  schemaType: legacySchemaType,
82
83
  selected,
83
84
  type: legacySchemaType,
84
- value: props.blockObject,
85
+ value: blockObject,
85
86
  })
86
87
  ) : (
87
- <RenderDefaultBlockObject blockObject={props.blockObject} />
88
+ <RenderDefaultBlockObject blockObject={blockObject} />
88
89
  )}
89
90
  </div>
90
91
  {dragPositionBlock === 'end' ? <DropIndicator /> : null}
@@ -10,8 +10,7 @@ import type {
10
10
  RenderStyleFunction,
11
11
  } from '../../types/editor'
12
12
  import {EditorActorContext} from '../editor-actor-context'
13
- import {RenderBlockObject} from './render-block-object'
14
- import {RenderInlineObject} from './render-inline-object'
13
+ import {RenderObject} from './render-object'
15
14
  import {RenderTextBlock} from './render-text-block'
16
15
 
17
16
  export function RenderElement(props: {
@@ -27,51 +26,16 @@ export function RenderElement(props: {
27
26
  }) {
28
27
  const editorActor = useContext(EditorActorContext)
29
28
  const schema = useSelector(editorActor, (s) => s.context.schema)
30
- const isInline =
31
- '__inline' in props.element && props.element.__inline === true
32
-
33
- if (isInline) {
34
- const inlineObject = {
35
- _key: props.element._key,
36
- _type: props.element._type,
37
- ...('value' in props.element && typeof props.element.value === 'object'
38
- ? props.element.value
39
- : {}),
40
- }
41
-
42
- if (
43
- !schema.inlineObjects.find(
44
- (inlineObject) => inlineObject.name === props.element._type,
45
- )
46
- ) {
47
- console.error(
48
- `Unable to find Inline Object "${props.element._type}" in Schema`,
49
- )
50
- }
51
-
52
- return (
53
- <RenderInlineObject
54
- attributes={props.attributes}
55
- element={props.element}
56
- inlineObject={
57
- inlineObject ?? {
58
- _key: props.element._key,
59
- _type: props.element._type,
60
- }
61
- }
62
- readOnly={props.readOnly}
63
- renderChild={props.renderChild}
64
- >
65
- {props.children}
66
- </RenderInlineObject>
67
- )
68
- }
29
+ const legacySchema = useSelector(editorActor, (s) =>
30
+ s.context.getLegacySchema(),
31
+ )
69
32
 
70
33
  if (isTextBlock({schema}, props.element)) {
71
34
  return (
72
35
  <RenderTextBlock
73
36
  attributes={props.attributes}
74
37
  element={props.element}
38
+ legacySchema={legacySchema}
75
39
  readOnly={props.readOnly}
76
40
  renderBlock={props.renderBlock}
77
41
  renderListItem={props.renderListItem}
@@ -84,38 +48,17 @@ export function RenderElement(props: {
84
48
  )
85
49
  }
86
50
 
87
- const blockObject = {
88
- _key: props.element._key,
89
- _type: props.element._type,
90
- ...('value' in props.element && typeof props.element.value === 'object'
91
- ? props.element.value
92
- : {}),
93
- }
94
-
95
- if (
96
- !schema.blockObjects.find(
97
- (blockObject) => blockObject.name === props.element._type,
98
- )
99
- ) {
100
- console.error(
101
- `Unable to find Block Object "${props.element._type}" in Schema`,
102
- )
103
- }
104
-
105
51
  return (
106
- <RenderBlockObject
52
+ <RenderObject
107
53
  attributes={props.attributes}
108
- blockObject={
109
- blockObject ?? {
110
- _key: props.element._key,
111
- _type: props.element._type,
112
- }
113
- }
114
54
  element={props.element}
55
+ legacySchema={legacySchema}
115
56
  readOnly={props.readOnly}
116
57
  renderBlock={props.renderBlock}
58
+ renderChild={props.renderChild}
59
+ schema={schema}
117
60
  >
118
61
  {props.children}
119
- </RenderBlockObject>
62
+ </RenderObject>
120
63
  )
121
64
  }
@@ -1,46 +1,49 @@
1
- import type {PortableTextObject} from '@sanity/types'
2
- import {useSelector} from '@xstate/react'
3
- import {useContext, useRef, type ReactElement} from 'react'
1
+ import {useRef, type ReactElement} from 'react'
4
2
  import {Range, type Element as SlateElement} from 'slate'
5
3
  import {DOMEditor} from 'slate-dom'
6
- import {useSelected, useSlateStatic, type RenderElementProps} from 'slate-react'
4
+ import {
5
+ useSelected,
6
+ useSlateSelector,
7
+ useSlateStatic,
8
+ type RenderElementProps,
9
+ } from 'slate-react'
7
10
  import {getPointBlock} from '../../internal-utils/slate-utils'
8
- import type {RenderChildFunction} from '../../types/editor'
9
- import {EditorActorContext} from '../editor-actor-context'
11
+ import type {
12
+ PortableTextMemberSchemaTypes,
13
+ RenderChildFunction,
14
+ } from '../../types/editor'
15
+ import type {EditorSchema} from '../editor-schema'
10
16
  import {RenderDefaultInlineObject} from './render-default-object'
11
17
 
12
18
  export function RenderInlineObject(props: {
13
19
  attributes: RenderElementProps['attributes']
14
20
  children: ReactElement
15
21
  element: SlateElement
16
- inlineObject: PortableTextObject
22
+ legacySchema: PortableTextMemberSchemaTypes
17
23
  readOnly: boolean
18
24
  renderChild?: RenderChildFunction
25
+ schema: EditorSchema
19
26
  }) {
20
27
  const inlineObjectRef = useRef<HTMLElement>(null)
21
-
22
28
  const slateEditor = useSlateStatic()
23
29
  const selected = useSelected()
30
+ const focused = useSlateSelector(
31
+ (editor) =>
32
+ selected &&
33
+ editor.selection !== null &&
34
+ Range.isCollapsed(editor.selection),
35
+ )
24
36
 
25
- const editorActor = useContext(EditorActorContext)
26
- const legacySchemaType = useSelector(editorActor, (s) =>
27
- s.context
28
- .getLegacySchema()
29
- .inlineObjects.find(
30
- (inlineObject) => inlineObject.name === props.element._type,
31
- ),
37
+ const legacySchemaType = props.legacySchema.inlineObjects.find(
38
+ (inlineObject) => inlineObject.name === props.element._type,
32
39
  )
33
40
 
34
41
  if (!legacySchemaType) {
35
42
  console.error(
36
- `Inline object type ${props.element._type} not found in Schema`,
43
+ `Unable to find Inline Object "${props.element._type}" in Schema`,
37
44
  )
38
45
  }
39
46
 
40
- const focused =
41
- selected &&
42
- slateEditor.selection !== null &&
43
- Range.isCollapsed(slateEditor.selection)
44
47
  const path = DOMEditor.findPath(slateEditor, props.element)
45
48
  const [block] = getPointBlock({
46
49
  editor: slateEditor,
@@ -56,13 +59,21 @@ export function RenderInlineObject(props: {
56
59
  )
57
60
  }
58
61
 
62
+ const inlineObject = {
63
+ _key: props.element._key,
64
+ _type: props.element._type,
65
+ ...('value' in props.element && typeof props.element.value === 'object'
66
+ ? props.element.value
67
+ : {}),
68
+ }
69
+
59
70
  return (
60
71
  <span
61
72
  {...props.attributes}
62
73
  draggable={!props.readOnly}
63
74
  className="pt-inline-object"
64
- data-child-key={props.inlineObject._key}
65
- data-child-name={props.inlineObject._type}
75
+ data-child-key={inlineObject._key}
76
+ data-child-name={inlineObject._type}
66
77
  data-child-type="object"
67
78
  >
68
79
  {props.children}
@@ -70,19 +81,17 @@ export function RenderInlineObject(props: {
70
81
  {props.renderChild && block && legacySchemaType ? (
71
82
  props.renderChild({
72
83
  annotations: [],
73
- children: (
74
- <RenderDefaultInlineObject inlineObject={props.inlineObject} />
75
- ),
84
+ children: <RenderDefaultInlineObject inlineObject={inlineObject} />,
76
85
  editorElementRef: inlineObjectRef,
77
86
  selected,
78
87
  focused,
79
88
  path: [{_key: block._key}, 'children', {_key: props.element._key}],
80
89
  schemaType: legacySchemaType,
81
- value: props.inlineObject,
90
+ value: inlineObject,
82
91
  type: legacySchemaType,
83
92
  })
84
93
  ) : (
85
- <RenderDefaultInlineObject inlineObject={props.inlineObject} />
94
+ <RenderDefaultInlineObject inlineObject={inlineObject} />
86
95
  )}
87
96
  </span>
88
97
  </span>
@@ -0,0 +1,53 @@
1
+ import type {ReactElement} from 'react'
2
+ import type {Element as SlateElement} from 'slate'
3
+ import type {RenderElementProps} from 'slate-react'
4
+ import type {
5
+ PortableTextMemberSchemaTypes,
6
+ RenderBlockFunction,
7
+ RenderChildFunction,
8
+ } from '../../types/editor'
9
+ import type {EditorSchema} from '../editor-schema'
10
+ import {RenderBlockObject} from './render-block-object'
11
+ import {RenderInlineObject} from './render-inline-object'
12
+
13
+ export function RenderObject(props: {
14
+ attributes: RenderElementProps['attributes']
15
+ children: ReactElement
16
+ element: SlateElement
17
+ legacySchema: PortableTextMemberSchemaTypes
18
+ readOnly: boolean
19
+ renderBlock?: RenderBlockFunction
20
+ renderChild?: RenderChildFunction
21
+ schema: EditorSchema
22
+ }) {
23
+ const isInline =
24
+ '__inline' in props.element && props.element.__inline === true
25
+
26
+ if (isInline) {
27
+ return (
28
+ <RenderInlineObject
29
+ attributes={props.attributes}
30
+ element={props.element}
31
+ legacySchema={props.legacySchema}
32
+ readOnly={props.readOnly}
33
+ renderChild={props.renderChild}
34
+ schema={props.schema}
35
+ >
36
+ {props.children}
37
+ </RenderInlineObject>
38
+ )
39
+ }
40
+
41
+ return (
42
+ <RenderBlockObject
43
+ attributes={props.attributes}
44
+ element={props.element}
45
+ legacySchema={props.legacySchema}
46
+ readOnly={props.readOnly}
47
+ renderBlock={props.renderBlock}
48
+ schema={props.schema}
49
+ >
50
+ {props.children}
51
+ </RenderBlockObject>
52
+ )
53
+ }
@@ -1,6 +1,5 @@
1
1
  import type {PortableTextTextBlock} from '@sanity/types'
2
- import {useSelector} from '@xstate/react'
3
- import {useContext, useRef, useState, type ReactElement} from 'react'
2
+ import {useRef, useState, type ReactElement} from 'react'
4
3
  import {Range, type Element as SlateElement} from 'slate'
5
4
  import {
6
5
  useSelected,
@@ -9,11 +8,11 @@ import {
9
8
  } from 'slate-react'
10
9
  import type {EventPositionBlock} from '../../internal-utils/event-position'
11
10
  import type {
11
+ PortableTextMemberSchemaTypes,
12
12
  RenderBlockFunction,
13
13
  RenderListItemFunction,
14
14
  RenderStyleFunction,
15
15
  } from '../../types/editor'
16
- import {EditorActorContext} from '../editor-actor-context'
17
16
  import {DropIndicator} from './drop-indicator'
18
17
  import {useCoreBlockElementBehaviors} from './use-core-block-element-behaviors'
19
18
 
@@ -21,6 +20,7 @@ export function RenderTextBlock(props: {
21
20
  attributes: RenderElementProps['attributes']
22
21
  children: ReactElement
23
22
  element: SlateElement
23
+ legacySchema: PortableTextMemberSchemaTypes
24
24
  readOnly: boolean
25
25
  renderBlock?: RenderBlockFunction
26
26
  renderListItem?: RenderListItemFunction
@@ -40,29 +40,21 @@ export function RenderTextBlock(props: {
40
40
  Range.isCollapsed(editor.selection),
41
41
  )
42
42
 
43
- const editorActor = useContext(EditorActorContext)
44
-
45
43
  useCoreBlockElementBehaviors({
46
44
  key: props.element._key,
47
45
  onSetDragPositionBlock: setDragPositionBlock,
48
46
  })
49
47
 
50
- const legacySchema = useSelector(editorActor, (s) =>
51
- s.context.getLegacySchema(),
52
- )
53
-
54
48
  const listIndex = useSlateSelector((editor) =>
55
49
  editor.listIndexMap.get(props.textBlock._key),
56
50
  )
57
51
 
58
52
  let children = props.children
59
53
 
60
- const legacyBlockSchemaType = legacySchema.block
61
-
62
54
  if (props.renderStyle && props.textBlock.style) {
63
55
  const legacyStyleSchemaType =
64
56
  props.textBlock.style !== undefined
65
- ? legacySchema.styles.find(
57
+ ? props.legacySchema.styles.find(
66
58
  (style) => style.value === props.textBlock.style,
67
59
  )
68
60
  : undefined
@@ -86,7 +78,7 @@ export function RenderTextBlock(props: {
86
78
  }
87
79
 
88
80
  if (props.renderListItem && props.textBlock.listItem) {
89
- const legacyListItemSchemaType = legacySchema.lists.find(
81
+ const legacyListItemSchemaType = props.legacySchema.lists.find(
90
82
  (list) => list.value === props.textBlock.listItem,
91
83
  )
92
84
 
@@ -162,9 +154,9 @@ export function RenderTextBlock(props: {
162
154
  listItem: props.textBlock.listItem,
163
155
  path: [{_key: props.textBlock._key}],
164
156
  selected,
165
- schemaType: legacyBlockSchemaType,
157
+ schemaType: props.legacySchema.block,
166
158
  style: props.textBlock.style,
167
- type: legacyBlockSchemaType,
159
+ type: props.legacySchema.block,
168
160
  value: props.textBlock,
169
161
  })
170
162
  : children}
@@ -141,6 +141,7 @@ export const editorMachine = setup({
141
141
  types: {
142
142
  context: {} as {
143
143
  behaviors: Set<BehaviorConfig>
144
+ behaviorsSorted: boolean
144
145
  converters: Set<Converter>
145
146
  getLegacySchema: () => PortableTextMemberSchemaTypes
146
147
  keyGenerator: () => string
@@ -177,6 +178,7 @@ export const editorMachine = setup({
177
178
 
178
179
  return new Set([...context.behaviors, event.behaviorConfig])
179
180
  },
181
+ behaviorsSorted: false,
180
182
  }),
181
183
  'remove behavior from context': assign({
182
184
  behaviors: ({context, event}) => {
@@ -257,10 +259,9 @@ export const editorMachine = setup({
257
259
  assertEvent(event, ['behavior event'])
258
260
 
259
261
  try {
260
- const behaviors = sortByPriority([
261
- ...context.behaviors.values(),
262
- ...coreBehaviorsConfig,
263
- ]).map((config) => config.behavior)
262
+ const behaviors = [...context.behaviors.values()].map(
263
+ (config) => config.behavior,
264
+ )
264
265
 
265
266
  performEvent({
266
267
  mode: 'raise',
@@ -289,6 +290,13 @@ export const editorMachine = setup({
289
290
  )
290
291
  }
291
292
  },
293
+ 'sort behaviors': assign({
294
+ behaviors: ({context}) =>
295
+ !context.behaviorsSorted
296
+ ? new Set(sortByPriority([...context.behaviors.values()]))
297
+ : context.behaviors,
298
+ behaviorsSorted: true,
299
+ }),
292
300
  },
293
301
  guards: {
294
302
  'slate is busy': ({context}) => {
@@ -302,7 +310,8 @@ export const editorMachine = setup({
302
310
  }).createMachine({
303
311
  id: 'editor',
304
312
  context: ({input}) => ({
305
- behaviors: new Set([]),
313
+ behaviors: new Set(coreBehaviorsConfig),
314
+ behaviorsSorted: false,
306
315
  converters: new Set(input.converters ?? []),
307
316
  getLegacySchema: input.getLegacySchema,
308
317
  keyGenerator: input.keyGenerator,
@@ -339,7 +348,7 @@ export const editorMachine = setup({
339
348
  initial: 'determine initial edit mode',
340
349
  on: {
341
350
  'behavior event': {
342
- actions: 'handle behavior event',
351
+ actions: ['sort behaviors', 'handle behavior event'],
343
352
  guard: ({event}) =>
344
353
  event.behaviorEvent.type === 'clipboard.copy' ||
345
354
  event.behaviorEvent.type === 'mouse.click' ||
@@ -406,7 +415,7 @@ export const editorMachine = setup({
406
415
  actions: ['emit read only'],
407
416
  },
408
417
  'behavior event': {
409
- actions: 'handle behavior event',
418
+ actions: ['sort behaviors', 'handle behavior event'],
410
419
  },
411
420
  'blur': {
412
421
  actions: 'handle blur',
@@ -21,6 +21,13 @@ export function pluginUpdateValue(
21
21
  operation,
22
22
  )
23
23
 
24
+ if (operation.type === 'insert_text' || operation.type === 'remove_text') {
25
+ // Inserting and removing text has no effect on index maps so there is
26
+ // no need to rebuild those.
27
+ apply(operation)
28
+ return
29
+ }
30
+
24
31
  buildIndexMaps(
25
32
  {
26
33
  schema: context.schema,