@portabletext/editor 1.33.5 → 1.34.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/lib/_chunks-cjs/behavior.core.cjs +14 -8
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/behavior.markdown.cjs +20 -12
- package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
- package/lib/_chunks-cjs/plugin.event-listener.cjs +95 -70
- package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
- package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs +103 -46
- package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js +14 -8
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/behavior.markdown.js +20 -12
- package/lib/_chunks-es/behavior.markdown.js.map +1 -1
- package/lib/_chunks-es/plugin.event-listener.js +101 -75
- package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
- package/lib/_chunks-es/util.block-offsets-to-selection.js +102 -46
- package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
- package/lib/behaviors/index.d.cts +5 -34
- package/lib/behaviors/index.d.ts +5 -34
- package/lib/index.d.cts +300 -1460
- package/lib/index.d.ts +300 -1460
- package/lib/plugins/index.cjs +60 -43
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +300 -1460
- package/lib/plugins/index.d.ts +300 -1460
- package/lib/plugins/index.js +63 -45
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.d.cts +12 -0
- package/lib/selectors/index.d.ts +12 -0
- package/lib/utils/index.cjs +23 -1
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +11 -0
- package/lib/utils/index.d.ts +11 -0
- package/lib/utils/index.js +25 -2
- package/lib/utils/index.js.map +1 -1
- package/package.json +6 -6
- package/src/behavior-actions/behavior.action.block.set.ts +48 -5
- package/src/behavior-actions/behavior.action.block.unset.ts +77 -3
- package/src/behavior-actions/behavior.action.decorator.add.ts +30 -8
- package/src/behavior-actions/behavior.actions.ts +1 -18
- package/src/behaviors/behavior.core.lists.ts +18 -14
- package/src/behaviors/behavior.markdown-emphasis.ts +82 -41
- package/src/behaviors/behavior.markdown.ts +14 -12
- package/src/behaviors/behavior.types.ts +2 -14
- package/src/converters/converter.portable-text.deserialize.test.ts +3 -2
- package/src/internal-utils/parse-blocks.test.ts +455 -0
- package/src/internal-utils/parse-blocks.ts +202 -87
- package/src/utils/index.ts +1 -0
- package/src/utils/util.child-selection-point-to-block-offset.ts +55 -0
- package/src/behavior-actions/behavior.action.text-block.set.ts +0 -25
- package/src/behavior-actions/behavior.action.text-block.unset.ts +0 -17
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type {
|
|
2
|
+
PortableTextBlock,
|
|
3
|
+
PortableTextObject,
|
|
4
|
+
PortableTextSpan,
|
|
5
|
+
PortableTextTextBlock,
|
|
5
6
|
} from '@sanity/types'
|
|
7
|
+
import type {EditorSchema} from '../editor/define-schema'
|
|
6
8
|
import type {EditorContext} from '../editor/editor-snapshot'
|
|
7
9
|
import {isTypedObject} from './asserters'
|
|
8
10
|
|
|
@@ -17,54 +19,92 @@ export function parseBlock({
|
|
|
17
19
|
refreshKeys: boolean
|
|
18
20
|
}
|
|
19
21
|
}): PortableTextBlock | undefined {
|
|
20
|
-
|
|
22
|
+
return (
|
|
23
|
+
parseTextBlock({block, context, options}) ??
|
|
24
|
+
parseBlockObject({blockObject: block, context, options})
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseBlockObject({
|
|
29
|
+
blockObject,
|
|
30
|
+
context,
|
|
31
|
+
options,
|
|
32
|
+
}: {
|
|
33
|
+
blockObject: unknown
|
|
34
|
+
context: Pick<EditorContext, 'keyGenerator' | 'schema'>
|
|
35
|
+
options: {refreshKeys: boolean}
|
|
36
|
+
}): PortableTextObject | undefined {
|
|
37
|
+
if (!isTypedObject(blockObject)) {
|
|
21
38
|
return undefined
|
|
22
39
|
}
|
|
23
40
|
|
|
24
41
|
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
42
|
+
blockObject._type === context.schema.block.name ||
|
|
43
|
+
blockObject._type === 'block' ||
|
|
44
|
+
!context.schema.blockObjects.some(({name}) => name === blockObject._type)
|
|
29
45
|
) {
|
|
30
46
|
return undefined
|
|
31
47
|
}
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
return {
|
|
50
|
+
...blockObject,
|
|
51
|
+
_key: options.refreshKeys
|
|
35
52
|
? context.keyGenerator()
|
|
36
|
-
: typeof
|
|
37
|
-
?
|
|
38
|
-
: context.keyGenerator()
|
|
39
|
-
return {
|
|
40
|
-
...block,
|
|
41
|
-
_key,
|
|
42
|
-
}
|
|
53
|
+
: typeof blockObject._key === 'string'
|
|
54
|
+
? blockObject._key
|
|
55
|
+
: context.keyGenerator(),
|
|
43
56
|
}
|
|
57
|
+
}
|
|
44
58
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
export function isTextBlock(
|
|
60
|
+
schema: EditorSchema,
|
|
61
|
+
block: unknown,
|
|
62
|
+
): block is PortableTextTextBlock {
|
|
63
|
+
return (
|
|
64
|
+
parseTextBlock({
|
|
65
|
+
block,
|
|
66
|
+
context: {schema, keyGenerator: () => ''},
|
|
67
|
+
options: {refreshKeys: false},
|
|
68
|
+
}) !== undefined
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseTextBlock({
|
|
73
|
+
block,
|
|
74
|
+
context,
|
|
75
|
+
options,
|
|
76
|
+
}: {
|
|
77
|
+
block: unknown
|
|
78
|
+
context: Pick<EditorContext, 'keyGenerator' | 'schema'>
|
|
79
|
+
options: {refreshKeys: boolean}
|
|
80
|
+
}): PortableTextTextBlock | undefined {
|
|
81
|
+
if (!isTypedObject(block)) {
|
|
82
|
+
return undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (block._type !== context.schema.block.name) {
|
|
86
|
+
return undefined
|
|
64
87
|
}
|
|
65
88
|
|
|
89
|
+
const _key = options.refreshKeys
|
|
90
|
+
? context.keyGenerator()
|
|
91
|
+
: typeof block._key === 'string'
|
|
92
|
+
? block._key
|
|
93
|
+
: context.keyGenerator()
|
|
94
|
+
|
|
95
|
+
const unparsedMarkDefs: Array<unknown> = Array.isArray(block.markDefs)
|
|
96
|
+
? block.markDefs
|
|
97
|
+
: []
|
|
66
98
|
const markDefKeyMap = new Map<string, string>()
|
|
67
|
-
const markDefs =
|
|
99
|
+
const markDefs = unparsedMarkDefs.flatMap((markDef) => {
|
|
100
|
+
if (!isTypedObject(markDef)) {
|
|
101
|
+
return []
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof markDef._key !== 'string') {
|
|
105
|
+
return []
|
|
106
|
+
}
|
|
107
|
+
|
|
68
108
|
if (
|
|
69
109
|
context.schema.annotations.some(
|
|
70
110
|
(annotation) => annotation.name === markDef._type,
|
|
@@ -84,55 +124,22 @@ export function parseBlock({
|
|
|
84
124
|
return []
|
|
85
125
|
})
|
|
86
126
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
child._type !== context.schema.span.name &&
|
|
94
|
-
!context.schema.inlineObjects.some(
|
|
95
|
-
(inlineObject) => inlineObject.name === child._type,
|
|
96
|
-
)
|
|
97
|
-
) {
|
|
98
|
-
return []
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!isPortableTextSpan(child)) {
|
|
102
|
-
return [
|
|
103
|
-
{
|
|
104
|
-
...child,
|
|
105
|
-
_key: options.refreshKeys ? context.keyGenerator() : child._key,
|
|
106
|
-
},
|
|
107
|
-
]
|
|
108
|
-
}
|
|
127
|
+
const unparsedChildren: Array<unknown> = Array.isArray(block.children)
|
|
128
|
+
? block.children
|
|
129
|
+
: []
|
|
109
130
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
) {
|
|
118
|
-
return [mark]
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return []
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
return [
|
|
125
|
-
{
|
|
126
|
-
...child,
|
|
127
|
-
_key: options.refreshKeys ? context.keyGenerator() : child._key,
|
|
128
|
-
marks,
|
|
129
|
-
},
|
|
130
|
-
]
|
|
131
|
-
})
|
|
131
|
+
const children = unparsedChildren
|
|
132
|
+
.map(
|
|
133
|
+
(child) =>
|
|
134
|
+
parseSpan({span: child, context, markDefKeyMap, options}) ??
|
|
135
|
+
parseInlineObject({inlineObject: child, context, options}),
|
|
136
|
+
)
|
|
137
|
+
.filter((child) => child !== undefined)
|
|
132
138
|
|
|
133
|
-
const parsedBlock = {
|
|
139
|
+
const parsedBlock: PortableTextTextBlock = {
|
|
140
|
+
// Spread the entire block to allow custom properties on it
|
|
134
141
|
...block,
|
|
135
|
-
_key
|
|
142
|
+
_key,
|
|
136
143
|
children:
|
|
137
144
|
children.length > 0
|
|
138
145
|
? children
|
|
@@ -147,8 +154,14 @@ export function parseBlock({
|
|
|
147
154
|
markDefs,
|
|
148
155
|
}
|
|
149
156
|
|
|
150
|
-
|
|
151
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Reset text block .style if it's somehow set to an invalid type
|
|
159
|
+
*/
|
|
160
|
+
if (
|
|
161
|
+
typeof parsedBlock.style !== 'string' ||
|
|
162
|
+
!context.schema.styles.find((style) => style.value === block.style)
|
|
163
|
+
) {
|
|
164
|
+
const defaultStyle = context.schema.styles.at(0)?.value
|
|
152
165
|
|
|
153
166
|
if (defaultStyle !== undefined) {
|
|
154
167
|
parsedBlock.style = defaultStyle
|
|
@@ -157,10 +170,112 @@ export function parseBlock({
|
|
|
157
170
|
}
|
|
158
171
|
}
|
|
159
172
|
|
|
160
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Reset text block .listItem if it's somehow set to an invalid type
|
|
175
|
+
*/
|
|
176
|
+
if (
|
|
177
|
+
typeof parsedBlock.listItem !== 'string' ||
|
|
178
|
+
!context.schema.lists.find((list) => list.value === block.listItem)
|
|
179
|
+
) {
|
|
161
180
|
delete parsedBlock.listItem
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Reset text block .level if it's somehow set to an invalid type
|
|
185
|
+
*/
|
|
186
|
+
if (typeof parsedBlock.level !== 'number') {
|
|
162
187
|
delete parsedBlock.level
|
|
163
188
|
}
|
|
164
189
|
|
|
165
190
|
return parsedBlock
|
|
166
191
|
}
|
|
192
|
+
|
|
193
|
+
export function parseSpan({
|
|
194
|
+
span,
|
|
195
|
+
context,
|
|
196
|
+
markDefKeyMap,
|
|
197
|
+
options,
|
|
198
|
+
}: {
|
|
199
|
+
span: unknown
|
|
200
|
+
context: Pick<EditorContext, 'keyGenerator' | 'schema'>
|
|
201
|
+
markDefKeyMap: Map<string, string>
|
|
202
|
+
options: {refreshKeys: boolean}
|
|
203
|
+
}): PortableTextSpan | undefined {
|
|
204
|
+
if (!isTypedObject(span)) {
|
|
205
|
+
return undefined
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// In reality, the span schema name is always 'span', but we only the check here anyway
|
|
209
|
+
if (span._type !== context.schema.span.name || span._type !== 'span') {
|
|
210
|
+
return undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const unparsedMarks: Array<unknown> = Array.isArray(span.marks)
|
|
214
|
+
? span.marks
|
|
215
|
+
: []
|
|
216
|
+
const marks = unparsedMarks.flatMap((mark) => {
|
|
217
|
+
if (typeof mark !== 'string') {
|
|
218
|
+
return []
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const markDefKey = markDefKeyMap.get(mark)
|
|
222
|
+
|
|
223
|
+
if (markDefKey !== undefined) {
|
|
224
|
+
return [markDefKey]
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
context.schema.decorators.some((decorator) => decorator.value === mark)
|
|
229
|
+
) {
|
|
230
|
+
return [mark]
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return []
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
// Spread the entire span to allow custom properties on it
|
|
238
|
+
...span,
|
|
239
|
+
_type: 'span',
|
|
240
|
+
_key: options.refreshKeys
|
|
241
|
+
? context.keyGenerator()
|
|
242
|
+
: typeof span._key === 'string'
|
|
243
|
+
? span._key
|
|
244
|
+
: context.keyGenerator(),
|
|
245
|
+
text: typeof span.text === 'string' ? span.text : '',
|
|
246
|
+
marks,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseInlineObject({
|
|
251
|
+
inlineObject,
|
|
252
|
+
context,
|
|
253
|
+
options,
|
|
254
|
+
}: {
|
|
255
|
+
inlineObject: unknown
|
|
256
|
+
context: Pick<EditorContext, 'keyGenerator' | 'schema'>
|
|
257
|
+
options: {refreshKeys: boolean}
|
|
258
|
+
}): PortableTextObject | undefined {
|
|
259
|
+
if (!isTypedObject(inlineObject)) {
|
|
260
|
+
return undefined
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (
|
|
264
|
+
inlineObject._type === context.schema.span.name ||
|
|
265
|
+
inlineObject._type === 'span' ||
|
|
266
|
+
// Respect the schema definition and don't parse inline objects that are not defined
|
|
267
|
+
!context.schema.inlineObjects.some(({name}) => name === inlineObject._type)
|
|
268
|
+
) {
|
|
269
|
+
return undefined
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
// Spread the entire inline object to allow custom properties on it
|
|
274
|
+
...inlineObject,
|
|
275
|
+
_key: options.refreshKeys
|
|
276
|
+
? context.keyGenerator()
|
|
277
|
+
: typeof inlineObject._key === 'string'
|
|
278
|
+
? inlineObject._key
|
|
279
|
+
: context.keyGenerator(),
|
|
280
|
+
}
|
|
281
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export {
|
|
|
5
5
|
spanSelectionPointToBlockOffset,
|
|
6
6
|
} from './util.block-offset'
|
|
7
7
|
export {blockOffsetsToSelection} from './util.block-offsets-to-selection'
|
|
8
|
+
export {childSelectionPointToBlockOffset} from './util.child-selection-point-to-block-offset'
|
|
8
9
|
export {getBlockEndPoint} from './util.get-block-end-point'
|
|
9
10
|
export {getBlockStartPoint} from './util.get-block-start-point'
|
|
10
11
|
export {getTextBlockText} from './util.get-text-block-text'
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isPortableTextSpan,
|
|
3
|
+
isPortableTextTextBlock,
|
|
4
|
+
type PortableTextBlock,
|
|
5
|
+
} from '@sanity/types'
|
|
6
|
+
import type {BlockOffset} from '../behaviors/behavior.types'
|
|
7
|
+
import type {EditorSelectionPoint} from '../types/editor'
|
|
8
|
+
import {isKeyedSegment} from './util.is-keyed-segment'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export function childSelectionPointToBlockOffset({
|
|
14
|
+
value,
|
|
15
|
+
selectionPoint,
|
|
16
|
+
}: {
|
|
17
|
+
value: Array<PortableTextBlock>
|
|
18
|
+
selectionPoint: EditorSelectionPoint
|
|
19
|
+
}): BlockOffset | undefined {
|
|
20
|
+
let offset = 0
|
|
21
|
+
|
|
22
|
+
const blockKey = isKeyedSegment(selectionPoint.path[0])
|
|
23
|
+
? selectionPoint.path[0]._key
|
|
24
|
+
: undefined
|
|
25
|
+
const childKey = isKeyedSegment(selectionPoint.path[2])
|
|
26
|
+
? selectionPoint.path[2]._key
|
|
27
|
+
: undefined
|
|
28
|
+
|
|
29
|
+
if (!blockKey || !childKey) {
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const block of value) {
|
|
34
|
+
if (block._key !== blockKey) {
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!isPortableTextTextBlock(block)) {
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const child of block.children) {
|
|
43
|
+
if (child._key === childKey) {
|
|
44
|
+
return {
|
|
45
|
+
path: [{_key: block._key}],
|
|
46
|
+
offset: offset + selectionPoint.offset,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isPortableTextSpan(child)) {
|
|
51
|
+
offset += child.text.length
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {Transforms} from 'slate'
|
|
2
|
-
import {toSlateRange} from '../internal-utils/ranges'
|
|
3
|
-
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
|
-
|
|
5
|
-
export const textBlockSetActionImplementation: BehaviorActionImplementation<
|
|
6
|
-
'text block.set'
|
|
7
|
-
> = ({action}) => {
|
|
8
|
-
const at = toSlateRange(
|
|
9
|
-
{
|
|
10
|
-
anchor: {path: action.at, offset: 0},
|
|
11
|
-
focus: {path: action.at, offset: 0},
|
|
12
|
-
},
|
|
13
|
-
action.editor,
|
|
14
|
-
)!
|
|
15
|
-
|
|
16
|
-
Transforms.setNodes(
|
|
17
|
-
action.editor,
|
|
18
|
-
{
|
|
19
|
-
...(action.style ? {style: action.style} : {}),
|
|
20
|
-
...(action.listItem ? {listItem: action.listItem} : {}),
|
|
21
|
-
...(action.level ? {level: action.level} : {}),
|
|
22
|
-
},
|
|
23
|
-
{at},
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {Transforms} from 'slate'
|
|
2
|
-
import {toSlateRange} from '../internal-utils/ranges'
|
|
3
|
-
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
|
-
|
|
5
|
-
export const textBlockUnsetActionImplementation: BehaviorActionImplementation<
|
|
6
|
-
'text block.unset'
|
|
7
|
-
> = ({action}) => {
|
|
8
|
-
const at = toSlateRange(
|
|
9
|
-
{
|
|
10
|
-
anchor: {path: action.at, offset: 0},
|
|
11
|
-
focus: {path: action.at, offset: 0},
|
|
12
|
-
},
|
|
13
|
-
action.editor,
|
|
14
|
-
)!
|
|
15
|
-
|
|
16
|
-
Transforms.unsetNodes(action.editor, action.props, {at})
|
|
17
|
-
}
|