@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.
- package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +64 -5
- package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
- package/lib/_chunks-cjs/util.slice-blocks.cjs +1 -0
- package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
- package/lib/_chunks-dts/behavior.types.action.d.cts +9 -17
- package/lib/_chunks-dts/behavior.types.action.d.ts +9 -17
- package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +65 -6
- package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
- package/lib/_chunks-es/util.slice-blocks.js +1 -0
- package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
- package/lib/index.cjs +262 -29
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +262 -29
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.cjs +1 -1
- package/lib/plugins/index.d.ts +3 -3
- package/lib/plugins/index.js +1 -1
- package/lib/selectors/index.cjs +1 -0
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +15 -1
- package/lib/selectors/index.d.ts +15 -1
- package/lib/selectors/index.js +2 -1
- package/lib/utils/index.d.cts +2 -2
- package/package.json +13 -13
- package/src/behaviors/behavior.abstract.delete.ts +1 -0
- package/src/behaviors/behavior.abstract.keyboard.ts +27 -0
- package/src/editor/components/render-block-object.tsx +29 -2
- package/src/editor/components/render-inline-object.tsx +31 -2
- package/src/editor/components/render-span.tsx +91 -6
- package/src/editor/components/render-text-block.tsx +95 -6
- package/src/internal-utils/asserters.ts +1 -1
- package/src/keyboard-shortcuts/default-keyboard-shortcuts.ts +22 -0
- package/src/operations/behavior.operation.delete.ts +6 -24
- package/src/selectors/index.ts +1 -0
- package/src/selectors/selector.get-mark-state.ts +75 -6
- 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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
|
|
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:
|
|
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) {
|
package/src/selectors/index.ts
CHANGED
|
@@ -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
|
|
37
|
+
if (!focusTextBlock) {
|
|
35
38
|
return undefined
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
if (
|
|
39
|
-
const
|
|
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(
|
|
84
|
-
|
|
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))
|
package/src/types/paths.ts
CHANGED
|
@@ -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
|
*/
|