@portabletext/editor 1.52.7 → 1.53.0

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.52.7",
3
+ "version": "1.53.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -1,8 +1,18 @@
1
1
  import {keyIs} from '../internal-utils/key-is'
2
+ import * as selectors from '../selectors'
2
3
  import {raise} from './behavior.types.action'
3
4
  import {defineBehavior} from './behavior.types.behavior'
4
5
 
5
6
  export const abstractKeyboardBehaviors = [
7
+ defineBehavior({
8
+ on: 'keyboard.keydown',
9
+ guard: ({snapshot, event}) =>
10
+ keyIs.break(event.originEvent) &&
11
+ selectors.isSelectionCollapsed(snapshot) &&
12
+ selectors.getFocusInlineObject(snapshot),
13
+ actions: [() => [raise({type: 'insert.break'})]],
14
+ }),
15
+
6
16
  /**
7
17
  * On WebKit, Shift+Enter results in an `insertParagraph` input event rather
8
18
  * than an `insertLineBreak` input event. This Behavior makes sure we catch
@@ -1,111 +1,132 @@
1
1
  import {isTextBlock, parseBlock} from '../internal-utils/parse-blocks'
2
2
  import * as selectors from '../selectors'
3
- import {getSelectionStartPoint, isSelectionCollapsed} from '../utils'
4
- import {getBlockEndPoint} from '../utils/util.get-block-end-point'
5
- import {getSelectionEndPoint} from '../utils/util.get-selection-end-point'
6
- import {sliceBlocks} from '../utils/util.slice-blocks'
3
+ import * as utils from '../utils'
7
4
  import {raise} from './behavior.types.action'
8
5
  import {defineBehavior} from './behavior.types.behavior'
9
6
 
10
7
  export const abstractSplitBehaviors = [
8
+ /**
9
+ * You can't split an inline object.
10
+ */
11
+ defineBehavior({
12
+ on: 'split',
13
+ guard: ({snapshot}) =>
14
+ selectors.isSelectionCollapsed(snapshot) &&
15
+ selectors.getFocusInlineObject(snapshot),
16
+ actions: [],
17
+ }),
18
+
19
+ /**
20
+ * You can't split a block object.
21
+ */
22
+ defineBehavior({
23
+ on: 'split',
24
+ guard: ({snapshot}) =>
25
+ selectors.isSelectionCollapsed(snapshot) &&
26
+ selectors.getFocusBlockObject(snapshot),
27
+ actions: [],
28
+ }),
29
+
30
+ defineBehavior({
31
+ on: 'split',
32
+ guard: ({snapshot}) => {
33
+ const selection = snapshot.context.selection
34
+
35
+ if (!selection || utils.isSelectionCollapsed(selection)) {
36
+ return false
37
+ }
38
+
39
+ const selectionStartBlock = selectors.getSelectionStartBlock(snapshot)
40
+ const selectionEndBlock = selectors.getSelectionEndBlock(snapshot)
41
+
42
+ if (!selectionStartBlock || !selectionEndBlock) {
43
+ return false
44
+ }
45
+
46
+ if (
47
+ !isTextBlock(snapshot.context, selectionStartBlock.node) &&
48
+ isTextBlock(snapshot.context, selectionEndBlock.node)
49
+ ) {
50
+ return {selection}
51
+ }
52
+
53
+ return false
54
+ },
55
+ actions: [(_, {selection}) => [raise({type: 'delete', at: selection})]],
56
+ }),
57
+
58
+ defineBehavior({
59
+ on: 'split',
60
+ guard: ({snapshot}) => {
61
+ const selection = snapshot.context.selection
62
+
63
+ if (!selection || utils.isSelectionCollapsed(selection)) {
64
+ return false
65
+ }
66
+
67
+ return {selection}
68
+ },
69
+ actions: [
70
+ (_, {selection}) => [
71
+ raise({type: 'delete', at: selection}),
72
+ raise({type: 'split'}),
73
+ ],
74
+ ],
75
+ }),
76
+
11
77
  defineBehavior({
12
78
  on: 'split',
13
79
  guard: ({snapshot}) => {
14
- if (!snapshot.context.selection) {
80
+ const selection = snapshot.context.selection
81
+
82
+ if (!selection || !utils.isSelectionCollapsed(selection)) {
83
+ return false
84
+ }
85
+
86
+ const selectionStartPoint = utils.getSelectionStartPoint(selection)
87
+
88
+ const focusTextBlock = selectors.getFocusTextBlock(snapshot)
89
+
90
+ if (!focusTextBlock) {
15
91
  return false
16
92
  }
17
93
 
18
- const selectionStartPoint = getSelectionStartPoint(
19
- snapshot.context.selection,
20
- )
21
- const selectionEndPoint = getSelectionEndPoint(snapshot.context.selection)
22
-
23
- const focusTextBlock = selectors.getFocusTextBlock({
24
- ...snapshot,
25
- context: {
26
- ...snapshot.context,
27
- selection: {
28
- anchor: selectionStartPoint,
29
- focus: selectionEndPoint,
30
- },
31
- },
94
+ const blockEndPoint = utils.getBlockEndPoint({
95
+ context: snapshot.context,
96
+ block: focusTextBlock,
32
97
  })
33
98
 
34
- if (focusTextBlock) {
35
- const blockEndPoint = getBlockEndPoint({
36
- context: snapshot.context,
37
- block: focusTextBlock,
38
- })
39
- const newTextBlockSelection = {
40
- anchor: selectionEndPoint,
41
- focus: blockEndPoint,
42
- }
43
- const newTextBlock = parseBlock({
44
- block: sliceBlocks({
99
+ const newTextBlockSelection = {
100
+ anchor: selectionStartPoint,
101
+ focus: blockEndPoint,
102
+ }
103
+
104
+ const newTextBlock = parseBlock({
105
+ block: utils
106
+ .sliceBlocks({
45
107
  context: {
46
108
  ...snapshot.context,
47
109
  selection: newTextBlockSelection,
48
110
  },
49
111
  blocks: [focusTextBlock.node],
50
- }).at(0),
51
- context: snapshot.context,
52
- options: {refreshKeys: true, validateFields: true},
53
- })
54
-
55
- if (!newTextBlock || !isTextBlock(snapshot.context, newTextBlock)) {
56
- return false
57
- }
58
-
59
- return {
60
- newTextBlock,
61
- newTextBlockSelection,
62
- selection: {
63
- anchor: selectionStartPoint,
64
- focus: blockEndPoint,
65
- },
66
- }
67
- }
68
-
69
- const focusBlockObject = selectors.getFocusBlockObject({
70
- ...snapshot,
71
- context: {
72
- ...snapshot.context,
73
- selection: {
74
- anchor: selectionStartPoint,
75
- focus: selectionEndPoint,
76
- },
77
- },
112
+ })
113
+ .at(0),
114
+ context: snapshot.context,
115
+ options: {refreshKeys: true, validateFields: true},
78
116
  })
79
117
 
80
- if (focusBlockObject) {
81
- const newTextBlock = parseBlock({
82
- block: {
83
- _type: snapshot.context.schema.block.name,
84
- children: [],
85
- },
86
- context: snapshot.context,
87
- options: {refreshKeys: true, validateFields: true},
88
- })
89
-
90
- if (!newTextBlock) {
91
- return false
92
- }
93
-
94
- return {
95
- newTextBlock,
96
- newTextBlockSelection: {
97
- anchor: selectionEndPoint,
98
- focus: selectionEndPoint,
99
- },
100
- selection: snapshot.context.selection,
101
- }
118
+ if (!newTextBlock) {
119
+ return false
102
120
  }
103
121
 
104
- return false
122
+ return {
123
+ newTextBlock,
124
+ newTextBlockSelection,
125
+ }
105
126
  },
106
127
  actions: [
107
- (_, {newTextBlock, selection}) =>
108
- isSelectionCollapsed(selection)
128
+ (_, {newTextBlock, newTextBlockSelection}) =>
129
+ utils.isSelectionCollapsed(newTextBlockSelection)
109
130
  ? [
110
131
  raise({
111
132
  type: 'insert.block',
@@ -115,10 +136,7 @@ export const abstractSplitBehaviors = [
115
136
  }),
116
137
  ]
117
138
  : [
118
- raise({
119
- type: 'delete',
120
- at: selection,
121
- }),
139
+ raise({type: 'delete', at: newTextBlockSelection}),
122
140
  raise({
123
141
  type: 'insert.block',
124
142
  block: newTextBlock,
@@ -235,9 +235,23 @@ const breakingEntireBlocks = defineBehavior({
235
235
  ],
236
236
  })
237
237
 
238
+ const breakingInlineObject = defineBehavior({
239
+ on: 'insert.break',
240
+ guard: ({snapshot}) => {
241
+ const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
242
+ const focusInlineObject = selectors.getFocusInlineObject(snapshot)
243
+
244
+ return selectionCollapsed && focusInlineObject
245
+ },
246
+ actions: [
247
+ () => [raise({type: 'move.forward', distance: 1}), raise({type: 'split'})],
248
+ ],
249
+ })
250
+
238
251
  export const coreInsertBreakBehaviors = {
239
252
  breakingAtTheEndOfTextBlock,
240
253
  breakingAtTheStartOfTextBlock,
241
254
  breakingEntireDocument,
242
255
  breakingEntireBlocks,
256
+ breakingInlineObject,
243
257
  }
@@ -29,6 +29,7 @@ export const coreBehaviorsConfig = [
29
29
  coreInsertBreakBehaviors.breakingAtTheStartOfTextBlock,
30
30
  coreInsertBreakBehaviors.breakingEntireDocument,
31
31
  coreInsertBreakBehaviors.breakingEntireBlocks,
32
+ coreInsertBreakBehaviors.breakingInlineObject,
32
33
  ].map((behavior) => ({
33
34
  behavior,
34
35
  priority: corePriority,
@@ -154,7 +154,7 @@ export function RenderTextBlock(props: {
154
154
  type: legacyBlockSchemaType,
155
155
  value: props.textBlock,
156
156
  })
157
- : props.children}
157
+ : children}
158
158
  </div>
159
159
  {dragPositionBlock === 'end' ? <DropIndicator /> : null}
160
160
  </div>
@@ -1,4 +1,5 @@
1
1
  export const keyIs = {
2
+ break: (event) => event.key === 'Enter' && !event.shiftKey,
2
3
  lineBreak: (event) => event.key === 'Enter' && event.shiftKey,
3
4
  } satisfies Record<string, KeyboardEventPredicate>
4
5