@portabletext/editor 2.12.3 → 2.13.1

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.
Files changed (36) hide show
  1. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +64 -5
  2. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.slice-blocks.cjs +1 -0
  4. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  5. package/lib/_chunks-dts/behavior.types.action.d.cts +9 -17
  6. package/lib/_chunks-dts/behavior.types.action.d.ts +9 -17
  7. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +65 -6
  8. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  9. package/lib/_chunks-es/util.slice-blocks.js +1 -0
  10. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  11. package/lib/index.cjs +262 -29
  12. package/lib/index.cjs.map +1 -1
  13. package/lib/index.js +262 -29
  14. package/lib/index.js.map +1 -1
  15. package/lib/plugins/index.cjs +1 -1
  16. package/lib/plugins/index.d.ts +3 -3
  17. package/lib/plugins/index.js +1 -1
  18. package/lib/selectors/index.cjs +1 -0
  19. package/lib/selectors/index.cjs.map +1 -1
  20. package/lib/selectors/index.d.cts +15 -1
  21. package/lib/selectors/index.d.ts +15 -1
  22. package/lib/selectors/index.js +2 -1
  23. package/lib/utils/index.d.cts +2 -2
  24. package/package.json +13 -13
  25. package/src/behaviors/behavior.abstract.delete.ts +1 -0
  26. package/src/behaviors/behavior.abstract.keyboard.ts +27 -0
  27. package/src/editor/components/render-block-object.tsx +29 -2
  28. package/src/editor/components/render-inline-object.tsx +31 -2
  29. package/src/editor/components/render-span.tsx +91 -6
  30. package/src/editor/components/render-text-block.tsx +95 -6
  31. package/src/internal-utils/asserters.ts +1 -1
  32. package/src/keyboard-shortcuts/default-keyboard-shortcuts.ts +22 -0
  33. package/src/operations/behavior.operation.delete.ts +6 -24
  34. package/src/selectors/index.ts +1 -0
  35. package/src/selectors/selector.get-mark-state.ts +75 -6
  36. package/src/types/paths.ts +18 -0
@@ -8,6 +8,9 @@ import {
8
8
  } from 'slate-react'
9
9
  import type {EventPositionBlock} from '../../internal-utils/event-position'
10
10
  import type {
11
+ BlockListItemRenderProps,
12
+ BlockRenderProps,
13
+ BlockStyleRenderProps,
11
14
  PortableTextMemberSchemaTypes,
12
15
  RenderBlockFunction,
13
16
  RenderListItemFunction,
@@ -61,7 +64,8 @@ export function RenderTextBlock(props: {
61
64
 
62
65
  if (legacyStyleSchemaType) {
63
66
  children = (
64
- <props.renderStyle
67
+ <RenderStyle
68
+ renderStyle={props.renderStyle}
65
69
  block={props.textBlock}
66
70
  editorElementRef={blockRef}
67
71
  focused={focused}
@@ -71,7 +75,7 @@ export function RenderTextBlock(props: {
71
75
  value={props.textBlock.style}
72
76
  >
73
77
  {children}
74
- </props.renderStyle>
78
+ </RenderStyle>
75
79
  )
76
80
  } else {
77
81
  console.error(
@@ -87,7 +91,8 @@ export function RenderTextBlock(props: {
87
91
 
88
92
  if (legacyListItemSchemaType) {
89
93
  children = (
90
- <props.renderListItem
94
+ <RenderListItem
95
+ renderListItem={props.renderListItem}
91
96
  block={props.textBlock}
92
97
  editorElementRef={blockRef}
93
98
  focused={focused}
@@ -98,7 +103,7 @@ export function RenderTextBlock(props: {
98
103
  schemaType={legacyListItemSchemaType}
99
104
  >
100
105
  {children}
101
- </props.renderListItem>
106
+ </RenderListItem>
102
107
  )
103
108
  } else {
104
109
  console.error(
@@ -152,7 +157,8 @@ export function RenderTextBlock(props: {
152
157
  {dragPositionBlock === 'start' ? <DropIndicator /> : null}
153
158
  <div ref={blockRef}>
154
159
  {props.renderBlock ? (
155
- <props.renderBlock
160
+ <RenderBlock
161
+ renderBlock={props.renderBlock}
156
162
  editorElementRef={blockRef}
157
163
  focused={focused}
158
164
  level={props.textBlock.level}
@@ -165,7 +171,7 @@ export function RenderTextBlock(props: {
165
171
  value={props.textBlock}
166
172
  >
167
173
  {children}
168
- </props.renderBlock>
174
+ </RenderBlock>
169
175
  ) : (
170
176
  children
171
177
  )}
@@ -174,3 +180,86 @@ export function RenderTextBlock(props: {
174
180
  </div>
175
181
  )
176
182
  }
183
+
184
+ function RenderBlock({
185
+ renderBlock,
186
+ children,
187
+ editorElementRef,
188
+ focused,
189
+ level,
190
+ listItem,
191
+ path,
192
+ selected,
193
+ style,
194
+ schemaType,
195
+ type,
196
+ value,
197
+ }: {
198
+ renderBlock: RenderBlockFunction
199
+ } & BlockRenderProps) {
200
+ return renderBlock({
201
+ children,
202
+ editorElementRef,
203
+ focused,
204
+ level,
205
+ listItem,
206
+ path,
207
+ selected,
208
+ style,
209
+ schemaType,
210
+ type,
211
+ value,
212
+ })
213
+ }
214
+
215
+ function RenderListItem({
216
+ renderListItem,
217
+ block,
218
+ children,
219
+ editorElementRef,
220
+ focused,
221
+ level,
222
+ path,
223
+ schemaType,
224
+ selected,
225
+ value,
226
+ }: {
227
+ renderListItem: RenderListItemFunction
228
+ } & BlockListItemRenderProps) {
229
+ return renderListItem({
230
+ block,
231
+ children,
232
+ editorElementRef,
233
+ focused,
234
+ level,
235
+ path,
236
+ schemaType,
237
+ selected,
238
+ value,
239
+ })
240
+ }
241
+
242
+ function RenderStyle({
243
+ renderStyle,
244
+ block,
245
+ children,
246
+ editorElementRef,
247
+ focused,
248
+ path,
249
+ schemaType,
250
+ selected,
251
+ value,
252
+ }: {
253
+ renderStyle: RenderStyleFunction
254
+ } & BlockStyleRenderProps) {
255
+ return renderStyle({
256
+ block,
257
+ children,
258
+ editorElementRef,
259
+ focused,
260
+ path,
261
+ schemaType,
262
+ selected,
263
+ value,
264
+ })
265
+ }
@@ -4,6 +4,6 @@ export function isTypedObject(object: unknown): object is TypedObject {
4
4
  return isRecord(object) && typeof object._type === 'string'
5
5
  }
6
6
 
7
- function isRecord(value: unknown): value is Record<string, unknown> {
7
+ export function isRecord(value: unknown): value is Record<string, unknown> {
8
8
  return !!value && (typeof value === 'object' || typeof value === 'function')
9
9
  }
@@ -31,6 +31,17 @@ export const defaultKeyboardShortcuts = {
31
31
  },
32
32
  ],
33
33
  }),
34
+ backspace: createKeyboardShortcut({
35
+ default: [
36
+ {
37
+ key: 'Backspace',
38
+ alt: false,
39
+ ctrl: false,
40
+ meta: false,
41
+ shift: false,
42
+ },
43
+ ],
44
+ }),
34
45
  break: createKeyboardShortcut({
35
46
  default: [
36
47
  {
@@ -53,6 +64,17 @@ export const defaultKeyboardShortcuts = {
53
64
  underline: underline,
54
65
  code: code,
55
66
  },
67
+ delete: createKeyboardShortcut({
68
+ default: [
69
+ {
70
+ key: 'Delete',
71
+ alt: false,
72
+ ctrl: false,
73
+ meta: false,
74
+ shift: false,
75
+ },
76
+ ],
77
+ }),
56
78
  history: {
57
79
  undo,
58
80
  redo,
@@ -9,7 +9,6 @@ import {
9
9
  } from 'slate'
10
10
  import {DOMEditor} from 'slate-dom'
11
11
  import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
12
- import {getBlockPath} from '../internal-utils/slate-utils'
13
12
  import {toSlateRange} from '../internal-utils/to-slate-range'
14
13
  import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
15
14
  import type {PortableTextSlateEditor} from '../types/editor'
@@ -59,30 +58,13 @@ export const deleteOperationImplementation: BehaviorOperationImplementation<
59
58
  throw new Error('Failed to get end block')
60
59
  }
61
60
 
62
- const anchorBlockPath =
63
- anchorBlockKey !== undefined
64
- ? getBlockPath({
65
- editor: operation.editor,
66
- _key: anchorBlockKey,
67
- })
68
- : undefined
69
- const focusBlockPath =
70
- focusBlockKey !== undefined
71
- ? getBlockPath({
72
- editor: operation.editor,
73
- _key: focusBlockKey,
74
- })
75
- : undefined
76
-
77
- if (
78
- operation.at.anchor.path.length === 1 &&
79
- operation.at.focus.path.length === 1 &&
80
- anchorBlockPath &&
81
- focusBlockPath &&
82
- anchorBlockPath[0] === focusBlockPath[0]
83
- ) {
61
+ if (operation.unit === 'block') {
84
62
  Transforms.removeNodes(operation.editor, {
85
- at: [anchorBlockPath[0]],
63
+ at: {
64
+ anchor: {path: [startBlockIndex], offset: 0},
65
+ focus: {path: [endBlockIndex], offset: 0},
66
+ },
67
+ mode: 'highest',
86
68
  })
87
69
 
88
70
  if (operation.editor.children.length === 0) {
@@ -17,6 +17,7 @@ export {getFocusSpan} from './selector.get-focus-span'
17
17
  export {getFocusTextBlock} from './selector.get-focus-text-block'
18
18
  export {getLastBlock} from './selector.get-last-block'
19
19
  export {getListIndex} from './selector.get-list-state'
20
+ export {getMarkState} from './selector.get-mark-state'
20
21
  export {getNextBlock} from './selector.get-next-block'
21
22
  export {getNextInlineObject} from './selector.get-next-inline-object'
22
23
  export {getPreviousBlock} from './selector.get-previous-block'
@@ -1,4 +1,6 @@
1
1
  import type {EditorSelector} from '../editor/editor-selector'
2
+ import {isBlockPath} from '../types/paths'
3
+ import {blockOffsetToSpanSelectionPoint} from '../utils'
2
4
  import {isSelectionExpanded} from '../utils/util.is-selection-expanded'
3
5
  import {getFocusSpan} from './selector.get-focus-span'
4
6
  import {getFocusTextBlock} from './selector.get-focus-text-block'
@@ -20,6 +22,7 @@ export type MarkState =
20
22
  /**
21
23
  * Given that text is inserted at the current position, what marks should
22
24
  * be applied?
25
+ * @beta
23
26
  */
24
27
  export const getMarkState: EditorSelector<MarkState | undefined> = (
25
28
  snapshot,
@@ -28,15 +31,69 @@ export const getMarkState: EditorSelector<MarkState | undefined> = (
28
31
  return undefined
29
32
  }
30
33
 
34
+ let selection = snapshot.context.selection
31
35
  const focusTextBlock = getFocusTextBlock(snapshot)
32
- const focusSpan = getFocusSpan(snapshot)
33
36
 
34
- if (!focusTextBlock || !focusSpan) {
37
+ if (!focusTextBlock) {
35
38
  return undefined
36
39
  }
37
40
 
38
- if (isSelectionExpanded(snapshot.context.selection)) {
39
- const selectedSpans = getSelectedSpans(snapshot)
41
+ if (isBlockPath(selection.anchor.path)) {
42
+ const spanSelectionPoint = blockOffsetToSpanSelectionPoint({
43
+ context: snapshot.context,
44
+ blockOffset: {
45
+ path: selection.anchor.path,
46
+ offset: selection.anchor.offset,
47
+ },
48
+ direction: selection.backward ? 'backward' : 'forward',
49
+ })
50
+
51
+ selection = spanSelectionPoint
52
+ ? {
53
+ ...selection,
54
+ anchor: spanSelectionPoint,
55
+ }
56
+ : selection
57
+ }
58
+
59
+ if (isBlockPath(selection.focus.path)) {
60
+ const spanSelectionPoint = blockOffsetToSpanSelectionPoint({
61
+ context: snapshot.context,
62
+ blockOffset: {
63
+ path: selection.focus.path,
64
+ offset: selection.focus.offset,
65
+ },
66
+ direction: selection.backward ? 'backward' : 'forward',
67
+ })
68
+
69
+ selection = spanSelectionPoint
70
+ ? {
71
+ ...selection,
72
+ focus: spanSelectionPoint,
73
+ }
74
+ : selection
75
+ }
76
+
77
+ const focusSpan = getFocusSpan({
78
+ ...snapshot,
79
+ context: {
80
+ ...snapshot.context,
81
+ selection,
82
+ },
83
+ })
84
+
85
+ if (!focusSpan) {
86
+ return undefined
87
+ }
88
+
89
+ if (isSelectionExpanded(selection)) {
90
+ const selectedSpans = getSelectedSpans({
91
+ ...snapshot,
92
+ context: {
93
+ ...snapshot.context,
94
+ selection,
95
+ },
96
+ })
40
97
 
41
98
  let index = 0
42
99
  let marks: Array<string> = []
@@ -80,8 +137,20 @@ export const getMarkState: EditorSelector<MarkState | undefined> = (
80
137
  const atTheEndOfSpan =
81
138
  snapshot.context.selection.anchor.offset === focusSpan.node.text.length
82
139
 
83
- const previousSpan = getPreviousSpan(snapshot)
84
- const nextSpan = getNextSpan(snapshot)
140
+ const previousSpan = getPreviousSpan({
141
+ ...snapshot,
142
+ context: {
143
+ ...snapshot.context,
144
+ selection,
145
+ },
146
+ })
147
+ const nextSpan = getNextSpan({
148
+ ...snapshot,
149
+ context: {
150
+ ...snapshot.context,
151
+ selection,
152
+ },
153
+ })
85
154
  const nextSpanAnnotations =
86
155
  nextSpan?.node?.marks?.filter((mark) => !decorators.includes(mark)) ?? []
87
156
  const spanAnnotations = marks.filter((mark) => !decorators.includes(mark))
@@ -1,8 +1,26 @@
1
+ import type {Path} from '@sanity/types'
2
+ import {isRecord} from '../internal-utils/asserters'
3
+
1
4
  /**
2
5
  * @public
3
6
  */
4
7
  export type BlockPath = [{_key: string}]
5
8
 
9
+ /**
10
+ * @public
11
+ */
12
+ export function isBlockPath(path: Path): path is BlockPath {
13
+ const firstSegment = path.at(0)
14
+
15
+ return (
16
+ path.length === 1 &&
17
+ firstSegment !== undefined &&
18
+ isRecord(firstSegment) &&
19
+ '_key' in firstSegment &&
20
+ typeof firstSegment._key === 'string'
21
+ )
22
+ }
23
+
6
24
  /**
7
25
  * @public
8
26
  */