@portabletext/editor 3.0.7 → 3.0.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.
@@ -129,8 +129,6 @@ describe(parseBlock.name, () => {
129
129
  marks: [],
130
130
  },
131
131
  ],
132
- markDefs: [],
133
- style: 'normal',
134
132
  })
135
133
  })
136
134
 
@@ -160,8 +158,6 @@ describe(parseBlock.name, () => {
160
158
  marks: [],
161
159
  },
162
160
  ],
163
- markDefs: [],
164
- style: 'normal',
165
161
  })
166
162
  })
167
163
 
@@ -271,8 +267,6 @@ describe(parseBlock.name, () => {
271
267
  marks: ['em'],
272
268
  },
273
269
  ],
274
- markDefs: [],
275
- style: 'normal',
276
270
  })
277
271
  })
278
272
 
@@ -301,9 +295,7 @@ describe(parseBlock.name, () => {
301
295
  marks: [],
302
296
  },
303
297
  ],
304
- markDefs: [],
305
298
  listItem: 'bullet',
306
- style: 'normal',
307
299
  })
308
300
  })
309
301
 
@@ -332,8 +324,6 @@ describe(parseBlock.name, () => {
332
324
  marks: [],
333
325
  },
334
326
  ],
335
- markDefs: [],
336
- style: 'normal',
337
327
  })
338
328
  })
339
329
 
@@ -363,8 +353,6 @@ describe(parseBlock.name, () => {
363
353
  marks: [],
364
354
  },
365
355
  ],
366
- markDefs: [],
367
- style: 'normal',
368
356
  })
369
357
  })
370
358
 
@@ -398,8 +386,6 @@ describe(parseBlock.name, () => {
398
386
  marks: [],
399
387
  },
400
388
  ],
401
- markDefs: [],
402
- style: 'normal',
403
389
  })
404
390
  })
405
391
 
@@ -432,8 +418,6 @@ describe(parseBlock.name, () => {
432
418
  marks: [],
433
419
  },
434
420
  ],
435
- markDefs: [],
436
- style: 'normal',
437
421
  })
438
422
  })
439
423
  })
@@ -144,45 +144,10 @@ export function parseTextBlock({
144
144
  const _key =
145
145
  typeof block._key === 'string' ? block._key : context.keyGenerator()
146
146
 
147
- const unparsedMarkDefs: Array<unknown> = Array.isArray(block.markDefs)
148
- ? block.markDefs
149
- : []
150
- const markDefKeyMap = new Map<string, string>()
151
- const markDefs = unparsedMarkDefs.flatMap((markDef) => {
152
- if (!isTypedObject(markDef)) {
153
- return []
154
- }
155
-
156
- const schemaType = context.schema.annotations.find(
157
- ({name}) => name === markDef._type,
158
- )
159
-
160
- if (!schemaType) {
161
- return []
162
- }
163
-
164
- if (typeof markDef._key !== 'string') {
165
- // If the `markDef` doesn't have a `_key` then we don't know what spans
166
- // it belongs to and therefore we have to discard it.
167
- return []
168
- }
169
-
170
- const parsedAnnotation = parseObject({
171
- object: markDef,
172
- context: {
173
- schemaType,
174
- keyGenerator: context.keyGenerator,
175
- },
176
- options,
177
- })
178
-
179
- if (!parsedAnnotation) {
180
- return []
181
- }
182
-
183
- markDefKeyMap.set(markDef._key, parsedAnnotation._key)
184
-
185
- return [parsedAnnotation]
147
+ const {markDefs, markDefKeyMap} = parseMarkDefs({
148
+ context,
149
+ markDefs: block.markDefs,
150
+ options,
186
151
  })
187
152
 
188
153
  const unparsedChildren: Array<unknown> = Array.isArray(block.children)
@@ -249,25 +214,20 @@ export function parseTextBlock({
249
214
  _type: context.schema.block.name,
250
215
  _key,
251
216
  children: normalizedChildren,
252
- markDefs: options.removeUnusedMarkDefs
253
- ? markDefs.filter((markDef) => marks.includes(markDef._key))
254
- : markDefs,
255
217
  ...customFields,
256
218
  }
257
219
 
220
+ if (typeof block.markDefs === 'object' && block.markDefs !== null) {
221
+ parsedBlock.markDefs = options.removeUnusedMarkDefs
222
+ ? markDefs.filter((markDef) => marks.includes(markDef._key))
223
+ : markDefs
224
+ }
225
+
258
226
  if (
259
227
  typeof block.style === 'string' &&
260
228
  context.schema.styles.find((style) => style.name === block.style)
261
229
  ) {
262
230
  parsedBlock.style = block.style
263
- } else {
264
- const defaultStyle = context.schema.styles.at(0)?.name
265
-
266
- if (defaultStyle !== undefined) {
267
- parsedBlock.style = defaultStyle
268
- } else {
269
- console.error('Expected default style')
270
- }
271
231
  }
272
232
 
273
233
  if (
@@ -284,6 +244,66 @@ export function parseTextBlock({
284
244
  return parsedBlock
285
245
  }
286
246
 
247
+ export function parseMarkDefs({
248
+ context,
249
+ markDefs,
250
+ options,
251
+ }: {
252
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>
253
+ markDefs: unknown
254
+ options: {validateFields: boolean}
255
+ }): {
256
+ markDefs: Array<PortableTextObject>
257
+ markDefKeyMap: Map<string, string>
258
+ } {
259
+ const unparsedMarkDefs: Array<unknown> = Array.isArray(markDefs)
260
+ ? markDefs
261
+ : []
262
+ const markDefKeyMap = new Map<string, string>()
263
+
264
+ const parsedMarkDefs = unparsedMarkDefs.flatMap((markDef) => {
265
+ if (!isTypedObject(markDef)) {
266
+ return []
267
+ }
268
+
269
+ const schemaType = context.schema.annotations.find(
270
+ ({name}) => name === markDef._type,
271
+ )
272
+
273
+ if (!schemaType) {
274
+ return []
275
+ }
276
+
277
+ if (typeof markDef._key !== 'string') {
278
+ // If the `markDef` doesn't have a `_key` then we don't know what spans
279
+ // it belongs to and therefore we have to discard it.
280
+ return []
281
+ }
282
+
283
+ const parsedAnnotation = parseObject({
284
+ object: markDef,
285
+ context: {
286
+ schemaType,
287
+ keyGenerator: context.keyGenerator,
288
+ },
289
+ options,
290
+ })
291
+
292
+ if (!parsedAnnotation) {
293
+ return []
294
+ }
295
+
296
+ markDefKeyMap.set(markDef._key, parsedAnnotation._key)
297
+
298
+ return [parsedAnnotation]
299
+ })
300
+
301
+ return {
302
+ markDefs: parsedMarkDefs,
303
+ markDefKeyMap,
304
+ }
305
+ }
306
+
287
307
  export function parseChild({
288
308
  child,
289
309
  context,
@@ -8,7 +8,7 @@ import {getTextBlockText} from './util.get-text-block-text'
8
8
  */
9
9
  export function isEmptyTextBlock(
10
10
  context: Pick<EditorContext, 'schema'>,
11
- block: PortableTextBlock,
11
+ block: PortableTextBlock | unknown,
12
12
  ) {
13
13
  if (!isTextBlock(context, block)) {
14
14
  return false
@@ -1,212 +0,0 @@
1
- import type {EditorSchema} from '../editor/editor-schema'
2
- import {createPairRegex} from '../internal-utils/get-text-to-emphasize'
3
- import {getFocusTextBlock} from '../selectors/selector.get-focus-text-block'
4
- import {getPreviousInlineObject} from '../selectors/selector.get-previous-inline-object'
5
- import {getSelectionStartPoint} from '../selectors/selector.get-selection-start-point'
6
- import {getBlockTextBefore} from '../selectors/selector.get-text-before'
7
- import type {BlockOffset} from '../types/block-offset'
8
- import {spanSelectionPointToBlockOffset} from '../utils/util.block-offset'
9
- import {blockOffsetsToSelection} from '../utils/util.block-offsets-to-selection'
10
- import {childSelectionPointToBlockOffset} from '../utils/util.child-selection-point-to-block-offset'
11
- import {effect, execute} from './behavior.types.action'
12
- import {defineBehavior} from './behavior.types.behavior'
13
-
14
- export function createDecoratorPairBehavior(config: {
15
- decorator: ({schema}: {schema: EditorSchema}) => string | undefined
16
- pair: {char: string; amount: number}
17
- onDecorate: (offset: BlockOffset) => void
18
- }) {
19
- if (config.pair.amount < 1) {
20
- console.warn(
21
- `The amount of characters in the pair should be greater than 0`,
22
- )
23
- }
24
-
25
- const pairRegex = createPairRegex(config.pair.char, config.pair.amount)
26
- const regEx = new RegExp(`(${pairRegex})$`)
27
-
28
- return defineBehavior({
29
- on: 'insert.text',
30
- guard: ({snapshot, event}) => {
31
- if (config.pair.amount < 1) {
32
- return false
33
- }
34
-
35
- const decorator = config.decorator({schema: snapshot.context.schema})
36
-
37
- if (decorator === undefined) {
38
- return false
39
- }
40
-
41
- const focusTextBlock = getFocusTextBlock(snapshot)
42
- const selectionStartPoint = getSelectionStartPoint(snapshot)
43
- const selectionStartOffset = selectionStartPoint
44
- ? spanSelectionPointToBlockOffset({
45
- context: {
46
- schema: snapshot.context.schema,
47
- value: snapshot.context.value,
48
- },
49
- selectionPoint: selectionStartPoint,
50
- })
51
- : undefined
52
-
53
- if (!focusTextBlock || !selectionStartOffset) {
54
- return false
55
- }
56
-
57
- const textBefore = getBlockTextBefore(snapshot)
58
- const newText = `${textBefore}${event.text}`
59
- const textToDecorate = newText.match(regEx)?.at(0)
60
-
61
- if (textToDecorate === undefined) {
62
- return false
63
- }
64
-
65
- const prefixOffsets = {
66
- anchor: {
67
- path: focusTextBlock.path,
68
- // Example: "foo **bar**".length - "**bar**".length = 4
69
- offset: newText.length - textToDecorate.length,
70
- },
71
- focus: {
72
- path: focusTextBlock.path,
73
- // Example: "foo **bar**".length - "**bar**".length + "*".length * 2 = 6
74
- offset:
75
- newText.length -
76
- textToDecorate.length +
77
- config.pair.char.length * config.pair.amount,
78
- },
79
- }
80
-
81
- const suffixOffsets = {
82
- anchor: {
83
- path: focusTextBlock.path,
84
- // Example: "foo **bar*|" (10) + "*".length - 2 = 9
85
- offset:
86
- selectionStartOffset.offset +
87
- event.text.length -
88
- config.pair.char.length * config.pair.amount,
89
- },
90
- focus: {
91
- path: focusTextBlock.path,
92
- // Example: "foo **bar*|" (10) + "*".length = 11
93
- offset: selectionStartOffset.offset + event.text.length,
94
- },
95
- }
96
-
97
- // If the prefix is more than one character, then we need to check if
98
- // there is an inline object inside it
99
- if (prefixOffsets.focus.offset - prefixOffsets.anchor.offset > 1) {
100
- const prefixSelection = blockOffsetsToSelection({
101
- context: snapshot.context,
102
- offsets: prefixOffsets,
103
- })
104
- const inlineObjectBeforePrefixFocus = getPreviousInlineObject({
105
- ...snapshot,
106
- context: {
107
- ...snapshot.context,
108
- selection: prefixSelection
109
- ? {
110
- anchor: prefixSelection.focus,
111
- focus: prefixSelection.focus,
112
- }
113
- : null,
114
- },
115
- })
116
- const inlineObjectBeforePrefixFocusOffset =
117
- inlineObjectBeforePrefixFocus
118
- ? childSelectionPointToBlockOffset({
119
- context: {
120
- schema: snapshot.context.schema,
121
- value: snapshot.context.value,
122
- },
123
- selectionPoint: {
124
- path: inlineObjectBeforePrefixFocus.path,
125
- offset: 0,
126
- },
127
- })
128
- : undefined
129
-
130
- if (
131
- inlineObjectBeforePrefixFocusOffset &&
132
- inlineObjectBeforePrefixFocusOffset.offset >
133
- prefixOffsets.anchor.offset &&
134
- inlineObjectBeforePrefixFocusOffset.offset <
135
- prefixOffsets.focus.offset
136
- ) {
137
- return false
138
- }
139
- }
140
-
141
- // If the suffix is more than one character, then we need to check if
142
- // there is an inline object inside it
143
- if (suffixOffsets.focus.offset - suffixOffsets.anchor.offset > 1) {
144
- const previousInlineObject = getPreviousInlineObject(snapshot)
145
- const previousInlineObjectOffset = previousInlineObject
146
- ? childSelectionPointToBlockOffset({
147
- context: {
148
- schema: snapshot.context.schema,
149
- value: snapshot.context.value,
150
- },
151
- selectionPoint: {
152
- path: previousInlineObject.path,
153
- offset: 0,
154
- },
155
- })
156
- : undefined
157
-
158
- if (
159
- previousInlineObjectOffset &&
160
- previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&
161
- previousInlineObjectOffset.offset < suffixOffsets.focus.offset
162
- ) {
163
- return false
164
- }
165
- }
166
-
167
- return {
168
- prefixOffsets,
169
- suffixOffsets,
170
- decorator,
171
- }
172
- },
173
- actions: [
174
- // Insert the text as usual in its own undo step
175
- ({event}) => [execute(event)],
176
- (_, {prefixOffsets, suffixOffsets, decorator}) => [
177
- // Decorate the text between the prefix and suffix
178
- execute({
179
- type: 'decorator.add',
180
- decorator,
181
- at: {
182
- anchor: prefixOffsets.focus,
183
- focus: suffixOffsets.anchor,
184
- },
185
- }),
186
- // Delete the suffix
187
- execute({
188
- type: 'delete.text',
189
- at: suffixOffsets,
190
- }),
191
- // Delete the prefix
192
- execute({
193
- type: 'delete.text',
194
- at: prefixOffsets,
195
- }),
196
- // Toggle the decorator off so the next inserted text isn't emphasized
197
- execute({
198
- type: 'decorator.remove',
199
- decorator,
200
- }),
201
- effect(() => {
202
- config.onDecorate({
203
- ...suffixOffsets.anchor,
204
- offset:
205
- suffixOffsets.anchor.offset -
206
- (prefixOffsets.focus.offset - prefixOffsets.anchor.offset),
207
- })
208
- }),
209
- ],
210
- ],
211
- })
212
- }