@portabletext/editor 1.1.3 → 1.1.5
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/index.d.mts +228 -30
- package/lib/index.d.ts +228 -30
- package/lib/index.esm.js +169 -112
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +169 -112
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +169 -112
- package/lib/index.mjs.map +1 -1
- package/package.json +24 -19
- package/src/editor/Editable.tsx +6 -11
- package/src/editor/PortableTextEditor.tsx +26 -30
- package/src/editor/__tests__/self-solving.test.tsx +1 -1
- package/src/editor/components/SlateContainer.tsx +2 -13
- package/src/editor/components/Synchronizer.tsx +0 -3
- package/src/editor/editor-machine.ts +7 -2
- package/src/editor/hooks/useSyncValue.ts +3 -5
- package/src/editor/key-generator.ts +6 -0
- package/src/editor/plugins/createWithEditableAPI.ts +8 -5
- package/src/editor/plugins/createWithHotKeys.ts +1 -0
- package/src/editor/plugins/createWithInsertBreak.ts +57 -9
- package/src/editor/plugins/createWithInsertData.ts +7 -4
- package/src/editor/plugins/createWithObjectKeys.ts +12 -5
- package/src/editor/plugins/createWithPatches.ts +0 -1
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +106 -23
- package/src/editor/plugins/createWithSchemaTypes.ts +3 -4
- package/src/editor/plugins/createWithUtils.ts +5 -4
- package/src/editor/plugins/index.ts +5 -13
- package/src/index.ts +2 -2
- package/src/types/options.ts +0 -1
- package/src/utils/__tests__/operationToPatches.test.ts +0 -2
- package/src/utils/__tests__/patchToOperations.test.ts +1 -2
- package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +0 -27
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
|
|
8
|
-
import type {PortableTextObject} from '@sanity/types'
|
|
8
|
+
import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
|
|
9
9
|
import {isEqual, uniq} from 'lodash'
|
|
10
10
|
import {Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
|
|
11
11
|
import type {
|
|
@@ -23,7 +23,6 @@ const debug = debugWithName('plugin:withPortableTextMarkModel')
|
|
|
23
23
|
export function createWithPortableTextMarkModel(
|
|
24
24
|
editorActor: EditorActor,
|
|
25
25
|
types: PortableTextMemberSchemaTypes,
|
|
26
|
-
keyGenerator: () => string,
|
|
27
26
|
): (editor: PortableTextSlateEditor) => PortableTextSlateEditor {
|
|
28
27
|
return function withPortableTextMarkModel(editor: PortableTextSlateEditor) {
|
|
29
28
|
const {apply, normalizeNode} = editor
|
|
@@ -288,14 +287,20 @@ export function createWithPortableTextMarkModel(
|
|
|
288
287
|
: false
|
|
289
288
|
|
|
290
289
|
if (selection && collapsedSelection) {
|
|
291
|
-
const [
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
290
|
+
const [_block, blockPath] = Editor.node(editor, selection, {
|
|
291
|
+
depth: 1,
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
const [span, spanPath] =
|
|
295
|
+
Array.from(
|
|
296
|
+
Editor.nodes(editor, {
|
|
297
|
+
mode: 'lowest',
|
|
298
|
+
at: selection.focus,
|
|
299
|
+
match: (n) => editor.isTextSpan(n),
|
|
300
|
+
voids: false,
|
|
301
|
+
}),
|
|
302
|
+
)[0] ?? ([undefined, undefined] as const)
|
|
303
|
+
|
|
299
304
|
const marks = span.marks ?? []
|
|
300
305
|
const marksWithoutAnnotations = marks.filter((mark) =>
|
|
301
306
|
decorators.includes(mark),
|
|
@@ -303,19 +308,97 @@ export function createWithPortableTextMarkModel(
|
|
|
303
308
|
const spanHasAnnotations =
|
|
304
309
|
marks.length > marksWithoutAnnotations.length
|
|
305
310
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
311
|
+
const spanIsEmpty = span.text.length === 0
|
|
312
|
+
|
|
313
|
+
const atTheBeginningOfSpan = selection.anchor.offset === 0
|
|
314
|
+
const atTheEndOfSpan = selection.anchor.offset === span.text.length
|
|
315
|
+
|
|
316
|
+
let previousSpan: PortableTextSpan | undefined
|
|
317
|
+
let nextSpan: PortableTextSpan | undefined
|
|
318
|
+
|
|
319
|
+
for (const [child, childPath] of Node.children(editor, blockPath, {
|
|
320
|
+
reverse: true,
|
|
321
|
+
})) {
|
|
322
|
+
if (!editor.isTextSpan(child)) {
|
|
323
|
+
continue
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (Path.isBefore(childPath, spanPath)) {
|
|
327
|
+
previousSpan = child
|
|
328
|
+
break
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
for (const [child, childPath] of Node.children(editor, blockPath)) {
|
|
333
|
+
if (!editor.isTextSpan(child)) {
|
|
334
|
+
continue
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (Path.isAfter(childPath, spanPath)) {
|
|
338
|
+
nextSpan = child
|
|
339
|
+
break
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const previousSpanHasSameAnnotation = previousSpan
|
|
344
|
+
? previousSpan.marks?.some(
|
|
345
|
+
(mark) => !decorators.includes(mark) && marks.includes(mark),
|
|
346
|
+
)
|
|
347
|
+
: false
|
|
348
|
+
const previousSpanHasSameMarks = previousSpan
|
|
349
|
+
? previousSpan.marks?.every((mark) => marks.includes(mark))
|
|
350
|
+
: false
|
|
351
|
+
const nextSpanHasSameAnnotation = nextSpan
|
|
352
|
+
? nextSpan.marks?.some(
|
|
353
|
+
(mark) => !decorators.includes(mark) && marks.includes(mark),
|
|
354
|
+
)
|
|
355
|
+
: false
|
|
356
|
+
const nextSpanHasSameMarks = nextSpan
|
|
357
|
+
? nextSpan.marks?.every((mark) => marks.includes(mark))
|
|
358
|
+
: false
|
|
359
|
+
|
|
360
|
+
if (spanHasAnnotations && !spanIsEmpty) {
|
|
361
|
+
if (atTheBeginningOfSpan) {
|
|
362
|
+
if (previousSpanHasSameMarks) {
|
|
363
|
+
Transforms.insertNodes(editor, {
|
|
364
|
+
_type: 'span',
|
|
365
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
366
|
+
text: op.text,
|
|
367
|
+
marks: previousSpan?.marks ?? [],
|
|
368
|
+
})
|
|
369
|
+
} else if (previousSpanHasSameAnnotation) {
|
|
370
|
+
apply(op)
|
|
371
|
+
} else {
|
|
372
|
+
Transforms.insertNodes(editor, {
|
|
373
|
+
_type: 'span',
|
|
374
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
375
|
+
text: op.text,
|
|
376
|
+
marks: [],
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
return
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (atTheEndOfSpan) {
|
|
383
|
+
if (nextSpanHasSameMarks) {
|
|
384
|
+
Transforms.insertNodes(editor, {
|
|
385
|
+
_type: 'span',
|
|
386
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
387
|
+
text: op.text,
|
|
388
|
+
marks: nextSpan?.marks ?? [],
|
|
389
|
+
})
|
|
390
|
+
} else if (nextSpanHasSameAnnotation) {
|
|
391
|
+
apply(op)
|
|
392
|
+
} else {
|
|
393
|
+
Transforms.insertNodes(editor, {
|
|
394
|
+
_type: 'span',
|
|
395
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
396
|
+
text: op.text,
|
|
397
|
+
marks: [],
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
return
|
|
401
|
+
}
|
|
319
402
|
}
|
|
320
403
|
}
|
|
321
404
|
}
|
|
@@ -22,11 +22,9 @@ const debug = debugWithName('plugin:withSchemaTypes')
|
|
|
22
22
|
export function createWithSchemaTypes({
|
|
23
23
|
editorActor,
|
|
24
24
|
schemaTypes,
|
|
25
|
-
keyGenerator,
|
|
26
25
|
}: {
|
|
27
26
|
editorActor: EditorActor
|
|
28
27
|
schemaTypes: PortableTextMemberSchemaTypes
|
|
29
|
-
keyGenerator: () => string
|
|
30
28
|
}) {
|
|
31
29
|
return function withSchemaTypes(
|
|
32
30
|
editor: PortableTextSlateEditor,
|
|
@@ -73,7 +71,8 @@ export function createWithSchemaTypes({
|
|
|
73
71
|
if (node._type === undefined && path.length === 2) {
|
|
74
72
|
debug('Setting span type on text node without a type')
|
|
75
73
|
const span = node as PortableTextSpan
|
|
76
|
-
const key =
|
|
74
|
+
const key =
|
|
75
|
+
span._key || editorActor.getSnapshot().context.keyGenerator()
|
|
77
76
|
editorActor.send({type: 'normalizing'})
|
|
78
77
|
Transforms.setNodes(
|
|
79
78
|
editor,
|
|
@@ -87,7 +86,7 @@ export function createWithSchemaTypes({
|
|
|
87
86
|
// catches cases when the children are missing keys but excludes it when the normalize is running the node as the editor object
|
|
88
87
|
if (node._key === undefined && (path.length === 1 || path.length === 2)) {
|
|
89
88
|
debug('Setting missing key on child node without a key')
|
|
90
|
-
const key = keyGenerator()
|
|
89
|
+
const key = editorActor.getSnapshot().context.keyGenerator()
|
|
91
90
|
editorActor.send({type: 'normalizing'})
|
|
92
91
|
Transforms.setNodes(editor, {_key: key}, {at: path})
|
|
93
92
|
editorActor.send({type: 'done normalizing'})
|
|
@@ -5,13 +5,14 @@ import type {
|
|
|
5
5
|
} from '../../types/editor'
|
|
6
6
|
import {debugWithName} from '../../utils/debug'
|
|
7
7
|
import {toSlateValue} from '../../utils/values'
|
|
8
|
+
import type {EditorActor} from '../editor-machine'
|
|
8
9
|
import type {PortableTextEditor} from '../PortableTextEditor'
|
|
9
10
|
|
|
10
11
|
const debug = debugWithName('plugin:withUtils')
|
|
11
12
|
|
|
12
13
|
interface Options {
|
|
14
|
+
editorActor: EditorActor
|
|
13
15
|
schemaTypes: PortableTextMemberSchemaTypes
|
|
14
|
-
keyGenerator: () => string
|
|
15
16
|
portableTextEditor: PortableTextEditor
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
@@ -19,8 +20,8 @@ interface Options {
|
|
|
19
20
|
*
|
|
20
21
|
*/
|
|
21
22
|
export function createWithUtils({
|
|
23
|
+
editorActor,
|
|
22
24
|
schemaTypes,
|
|
23
|
-
keyGenerator,
|
|
24
25
|
portableTextEditor,
|
|
25
26
|
}: Options) {
|
|
26
27
|
return function withUtils(
|
|
@@ -79,13 +80,13 @@ export function createWithUtils({
|
|
|
79
80
|
[
|
|
80
81
|
{
|
|
81
82
|
_type: schemaTypes.block.name,
|
|
82
|
-
_key: keyGenerator(),
|
|
83
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
83
84
|
style: schemaTypes.styles[0].value || 'normal',
|
|
84
85
|
markDefs: [],
|
|
85
86
|
children: [
|
|
86
87
|
{
|
|
87
88
|
_type: 'span',
|
|
88
|
-
_key: keyGenerator(),
|
|
89
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
89
90
|
text: '',
|
|
90
91
|
marks: options.decorators.filter((decorator) =>
|
|
91
92
|
schemaTypes.decorators.find(({value}) => value === decorator),
|
|
@@ -47,8 +47,7 @@ export const withPlugins = <T extends Editor>(
|
|
|
47
47
|
options: createEditorOptions,
|
|
48
48
|
): {editor: PortableTextSlateEditor; subscribe: () => () => void} => {
|
|
49
49
|
const e = editor as T & PortableTextSlateEditor
|
|
50
|
-
const {
|
|
51
|
-
options
|
|
50
|
+
const {portableTextEditor, patches$, readOnly, maxBlocks} = options
|
|
52
51
|
const {editorActor, schemaTypes} = portableTextEditor
|
|
53
52
|
e.subscriptions = []
|
|
54
53
|
if (e.destroy) {
|
|
@@ -63,24 +62,18 @@ export const withPlugins = <T extends Editor>(
|
|
|
63
62
|
})
|
|
64
63
|
}
|
|
65
64
|
const operationToPatches = createOperationToPatches(schemaTypes)
|
|
66
|
-
const withObjectKeys = createWithObjectKeys(
|
|
67
|
-
editorActor,
|
|
68
|
-
schemaTypes,
|
|
69
|
-
keyGenerator,
|
|
70
|
-
)
|
|
65
|
+
const withObjectKeys = createWithObjectKeys(editorActor, schemaTypes)
|
|
71
66
|
const withSchemaTypes = createWithSchemaTypes({
|
|
72
67
|
editorActor,
|
|
73
68
|
schemaTypes,
|
|
74
|
-
keyGenerator,
|
|
75
69
|
})
|
|
76
70
|
const withEditableAPI = createWithEditableAPI(
|
|
71
|
+
editorActor,
|
|
77
72
|
portableTextEditor,
|
|
78
73
|
schemaTypes,
|
|
79
|
-
keyGenerator,
|
|
80
74
|
)
|
|
81
75
|
const withPatches = createWithPatches({
|
|
82
76
|
editorActor,
|
|
83
|
-
keyGenerator,
|
|
84
77
|
patches$,
|
|
85
78
|
patchFunctions: operationToPatches,
|
|
86
79
|
readOnly,
|
|
@@ -96,7 +89,6 @@ export const withPlugins = <T extends Editor>(
|
|
|
96
89
|
const withPortableTextMarkModel = createWithPortableTextMarkModel(
|
|
97
90
|
editorActor,
|
|
98
91
|
schemaTypes,
|
|
99
|
-
keyGenerator,
|
|
100
92
|
)
|
|
101
93
|
const withPortableTextBlockStyle = createWithPortableTextBlockStyle(
|
|
102
94
|
editorActor,
|
|
@@ -105,10 +97,10 @@ export const withPlugins = <T extends Editor>(
|
|
|
105
97
|
|
|
106
98
|
const withPlaceholderBlock = createWithPlaceholderBlock()
|
|
107
99
|
|
|
108
|
-
const withInsertBreak = createWithInsertBreak(
|
|
100
|
+
const withInsertBreak = createWithInsertBreak(editorActor, schemaTypes)
|
|
109
101
|
|
|
110
102
|
const withUtils = createWithUtils({
|
|
111
|
-
|
|
103
|
+
editorActor,
|
|
112
104
|
schemaTypes,
|
|
113
105
|
portableTextEditor,
|
|
114
106
|
})
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type {Patch} from '@portabletext/patches'
|
|
2
2
|
export {PortableTextEditable} from './editor/Editable'
|
|
3
3
|
export type {PortableTextEditableProps} from './editor/Editable'
|
|
4
4
|
export {
|
|
@@ -8,7 +8,7 @@ export {
|
|
|
8
8
|
type PatchEvent,
|
|
9
9
|
} from './editor/editor-machine'
|
|
10
10
|
export {usePortableTextEditor} from './editor/hooks/usePortableTextEditor'
|
|
11
|
-
export {defaultKeyGenerator as keyGenerator} from './editor/
|
|
11
|
+
export {defaultKeyGenerator as keyGenerator} from './editor/key-generator'
|
|
12
12
|
export {usePortableTextEditorSelection} from './editor/hooks/usePortableTextEditorSelection'
|
|
13
13
|
export {PortableTextEditor} from './editor/PortableTextEditor'
|
|
14
14
|
export type {PortableTextEditorProps} from './editor/PortableTextEditor'
|
package/src/types/options.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {createEditor, type Descendant} from 'slate'
|
|
|
3
3
|
import {beforeEach, describe, expect, it} from 'vitest'
|
|
4
4
|
import {PortableTextEditor, type PortableTextEditorProps} from '../..'
|
|
5
5
|
import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
|
|
6
|
-
import {defaultKeyGenerator} from '../../editor/hooks/usePortableTextEditorKeyGenerator'
|
|
7
6
|
import {withPlugins} from '../../editor/plugins'
|
|
8
7
|
import {getPortableTextMemberSchemaTypes} from '../getPortableTextMemberSchemaTypes'
|
|
9
8
|
import {createOperationToPatches} from '../operationToPatches'
|
|
@@ -16,7 +15,6 @@ const {editor} = withPlugins(createEditor(), {
|
|
|
16
15
|
portableTextEditor: new PortableTextEditor({
|
|
17
16
|
schemaType,
|
|
18
17
|
} as PortableTextEditorProps),
|
|
19
|
-
keyGenerator: defaultKeyGenerator,
|
|
20
18
|
readOnly: false,
|
|
21
19
|
})
|
|
22
20
|
|
|
@@ -2,7 +2,7 @@ import type {Patch} from '@portabletext/patches'
|
|
|
2
2
|
import {noop} from 'lodash'
|
|
3
3
|
import {createEditor, type Descendant} from 'slate'
|
|
4
4
|
import {beforeEach, describe, expect, it} from 'vitest'
|
|
5
|
-
import {
|
|
5
|
+
import {PortableTextEditor} from '../..'
|
|
6
6
|
import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
|
|
7
7
|
import {withPlugins} from '../../editor/plugins'
|
|
8
8
|
import {createApplyPatch} from '../applyPatch'
|
|
@@ -16,7 +16,6 @@ const portableTextEditor = new PortableTextEditor({schemaType, onChange: noop})
|
|
|
16
16
|
|
|
17
17
|
const {editor} = withPlugins(createEditor(), {
|
|
18
18
|
portableTextEditor,
|
|
19
|
-
keyGenerator,
|
|
20
19
|
readOnly: false,
|
|
21
20
|
})
|
|
22
21
|
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {randomKey} from '@sanity/util/content'
|
|
2
|
-
import {createContext, useContext} from 'react'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @public
|
|
6
|
-
*/
|
|
7
|
-
export const defaultKeyGenerator = (): string => randomKey(12)
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A React context for sharing the editor's keyGenerator.
|
|
11
|
-
*/
|
|
12
|
-
export const PortableTextEditorKeyGeneratorContext =
|
|
13
|
-
createContext<() => string>(defaultKeyGenerator)
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Get the current editor selection from the React context.
|
|
17
|
-
*/
|
|
18
|
-
export const usePortableTextEditorKeyGenerator = (): (() => string) => {
|
|
19
|
-
const keyGenerator = useContext(PortableTextEditorKeyGeneratorContext)
|
|
20
|
-
|
|
21
|
-
if (keyGenerator === undefined) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`The \`usePortableTextEditorKeyGenerator\` hook must be used inside the <PortableTextEditor> component's context.`,
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
return keyGenerator
|
|
27
|
-
}
|