@portabletext/editor 1.44.13 → 1.44.14
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 +41 -41
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/editor-provider.cjs +4474 -4454
- package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js +41 -41
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/editor-provider.js +4477 -4457
- package/lib/_chunks-es/editor-provider.js.map +1 -1
- package/lib/index.cjs +13 -13
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +16 -16
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/behavior-actions/behavior.action.annotation.add.ts +133 -0
- package/src/behavior-actions/behavior.action.annotation.remove.ts +150 -0
- package/src/behavior-actions/behavior.actions.ts +2 -4
- package/src/converters/converter.text-html.ts +14 -2
- package/src/converters/converter.text-plain.ts +14 -2
- package/src/editor/PortableTextEditor.tsx +1 -1
- package/src/editor/plugins/createWithEditableAPI.ts +1 -282
- package/src/index.ts +34 -34
package/package.json
CHANGED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type {Path} from '@sanity/types'
|
|
2
|
+
import {Editor, Node, Range, Text, Transforms} from 'slate'
|
|
3
|
+
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export type AddedAnnotationPaths = {
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated An annotation may be applied to multiple blocks, resulting
|
|
11
|
+
* in multiple `markDef`'s being created. Use `markDefPaths` instead.
|
|
12
|
+
*/
|
|
13
|
+
markDefPath: Path
|
|
14
|
+
markDefPaths: Array<Path>
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Does not return anything meaningful since an annotation
|
|
17
|
+
* can span multiple blocks and spans. If references the span closest
|
|
18
|
+
* to the focus point of the selection.
|
|
19
|
+
*/
|
|
20
|
+
spanPath: Path
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const addAnnotationActionImplementation: BehaviorActionImplementation<
|
|
24
|
+
'annotation.add',
|
|
25
|
+
AddedAnnotationPaths | undefined
|
|
26
|
+
> = ({context, action}) => {
|
|
27
|
+
const editor = action.editor
|
|
28
|
+
|
|
29
|
+
if (!editor.selection || Range.isCollapsed(editor.selection)) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let paths: AddedAnnotationPaths | undefined = undefined
|
|
34
|
+
let spanPath: Path | undefined
|
|
35
|
+
let markDefPath: Path | undefined
|
|
36
|
+
const markDefPaths: Path[] = []
|
|
37
|
+
|
|
38
|
+
const selectedBlocks = Editor.nodes(editor, {
|
|
39
|
+
at: editor.selection,
|
|
40
|
+
match: (node) => editor.isTextBlock(node),
|
|
41
|
+
reverse: Range.isBackward(editor.selection),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
for (const [block, blockPath] of selectedBlocks) {
|
|
45
|
+
if (block.children.length === 0) {
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (block.children.length === 1 && block.children[0].text === '') {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const annotationKey = context.keyGenerator()
|
|
54
|
+
const markDefs = block.markDefs ?? []
|
|
55
|
+
const existingMarkDef = markDefs.find(
|
|
56
|
+
(markDef) =>
|
|
57
|
+
markDef._type === action.annotation.name &&
|
|
58
|
+
markDef._key === annotationKey,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if (existingMarkDef === undefined) {
|
|
62
|
+
Transforms.setNodes(
|
|
63
|
+
editor,
|
|
64
|
+
{
|
|
65
|
+
markDefs: [
|
|
66
|
+
...markDefs,
|
|
67
|
+
{
|
|
68
|
+
_type: action.annotation.name,
|
|
69
|
+
_key: annotationKey,
|
|
70
|
+
...action.annotation.value,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{at: blockPath},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
markDefPath = [{_key: block._key}, 'markDefs', {_key: annotationKey}]
|
|
78
|
+
|
|
79
|
+
if (Range.isBackward(editor.selection)) {
|
|
80
|
+
markDefPaths.unshift(markDefPath)
|
|
81
|
+
} else {
|
|
82
|
+
markDefPaths.push(markDefPath)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Transforms.setNodes(editor, {}, {match: Text.isText, split: true})
|
|
87
|
+
|
|
88
|
+
const children = Node.children(editor, blockPath)
|
|
89
|
+
|
|
90
|
+
for (const [span, path] of children) {
|
|
91
|
+
if (!editor.isTextSpan(span)) {
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!Range.includes(editor.selection, path)) {
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const marks = span.marks ?? []
|
|
100
|
+
const existingSameTypeAnnotations = marks.filter((mark) =>
|
|
101
|
+
markDefs.some(
|
|
102
|
+
(markDef) =>
|
|
103
|
+
markDef._key === mark && markDef._type === action.annotation.name,
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
Transforms.setNodes(
|
|
108
|
+
editor,
|
|
109
|
+
{
|
|
110
|
+
marks: [
|
|
111
|
+
...marks.filter(
|
|
112
|
+
(mark) => !existingSameTypeAnnotations.includes(mark),
|
|
113
|
+
),
|
|
114
|
+
annotationKey,
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{at: path},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
spanPath = [{_key: block._key}, 'children', {_key: span._key}]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (markDefPath && spanPath) {
|
|
125
|
+
paths = {
|
|
126
|
+
markDefPath,
|
|
127
|
+
markDefPaths,
|
|
128
|
+
spanPath,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return paths
|
|
133
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type {PortableTextSpan} from '@sanity/types'
|
|
2
|
+
import {Editor, Node, Path, Range, Transforms} from 'slate'
|
|
3
|
+
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
|
+
|
|
5
|
+
export const removeAnnotationActionImplementation: BehaviorActionImplementation<
|
|
6
|
+
'annotation.remove'
|
|
7
|
+
> = ({action}) => {
|
|
8
|
+
const editor = action.editor
|
|
9
|
+
|
|
10
|
+
if (!editor.selection) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (Range.isCollapsed(editor.selection)) {
|
|
15
|
+
const [block, blockPath] = Editor.node(editor, editor.selection, {
|
|
16
|
+
depth: 1,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
if (!editor.isTextBlock(block)) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const markDefs = block.markDefs ?? []
|
|
24
|
+
const potentialAnnotations = markDefs.filter(
|
|
25
|
+
(markDef) => markDef._type === action.annotation.name,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const [selectedChild, selectedChildPath] = Editor.node(
|
|
29
|
+
editor,
|
|
30
|
+
editor.selection,
|
|
31
|
+
{
|
|
32
|
+
depth: 2,
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if (!editor.isTextSpan(selectedChild)) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const annotationToRemove = selectedChild.marks?.find((mark) =>
|
|
41
|
+
potentialAnnotations.some((markDef) => markDef._key === mark),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if (!annotationToRemove) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const previousSpansWithSameAnnotation: Array<
|
|
49
|
+
[span: PortableTextSpan, path: Path]
|
|
50
|
+
> = []
|
|
51
|
+
|
|
52
|
+
for (const [child, childPath] of Node.children(editor, blockPath, {
|
|
53
|
+
reverse: true,
|
|
54
|
+
})) {
|
|
55
|
+
if (!editor.isTextSpan(child)) {
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!Path.isBefore(childPath, selectedChildPath)) {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (child.marks?.includes(annotationToRemove)) {
|
|
64
|
+
previousSpansWithSameAnnotation.push([child, childPath])
|
|
65
|
+
} else {
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const nextSpansWithSameAnnotation: Array<
|
|
71
|
+
[span: PortableTextSpan, path: Path]
|
|
72
|
+
> = []
|
|
73
|
+
|
|
74
|
+
for (const [child, childPath] of Node.children(editor, blockPath)) {
|
|
75
|
+
if (!editor.isTextSpan(child)) {
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!Path.isAfter(childPath, selectedChildPath)) {
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (child.marks?.includes(annotationToRemove)) {
|
|
84
|
+
nextSpansWithSameAnnotation.push([child, childPath])
|
|
85
|
+
} else {
|
|
86
|
+
break
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const [child, childPath] of [
|
|
91
|
+
...previousSpansWithSameAnnotation,
|
|
92
|
+
[selectedChild, selectedChildPath] as const,
|
|
93
|
+
...nextSpansWithSameAnnotation,
|
|
94
|
+
]) {
|
|
95
|
+
Transforms.setNodes(
|
|
96
|
+
editor,
|
|
97
|
+
{
|
|
98
|
+
marks: child.marks?.filter((mark) => mark !== annotationToRemove),
|
|
99
|
+
},
|
|
100
|
+
{at: childPath},
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
Transforms.setNodes(
|
|
105
|
+
editor,
|
|
106
|
+
{},
|
|
107
|
+
{
|
|
108
|
+
match: (node) => editor.isTextSpan(node),
|
|
109
|
+
split: true,
|
|
110
|
+
hanging: true,
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const blocks = Editor.nodes(editor, {
|
|
115
|
+
at: editor.selection,
|
|
116
|
+
match: (node) => editor.isTextBlock(node),
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
for (const [block, blockPath] of blocks) {
|
|
120
|
+
const children = Node.children(editor, blockPath)
|
|
121
|
+
|
|
122
|
+
for (const [child, childPath] of children) {
|
|
123
|
+
if (!editor.isTextSpan(child)) {
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!Range.includes(editor.selection, childPath)) {
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const markDefs = block.markDefs ?? []
|
|
132
|
+
const marks = child.marks ?? []
|
|
133
|
+
const marksWithoutAnnotation = marks.filter((mark) => {
|
|
134
|
+
const markDef = markDefs.find((markDef) => markDef._key === mark)
|
|
135
|
+
return markDef?._type !== action.annotation.name
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
if (marksWithoutAnnotation.length !== marks.length) {
|
|
139
|
+
Transforms.setNodes(
|
|
140
|
+
editor,
|
|
141
|
+
{
|
|
142
|
+
marks: marksWithoutAnnotation,
|
|
143
|
+
},
|
|
144
|
+
{at: childPath},
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import {omit} from 'lodash'
|
|
2
2
|
import type {InternalBehaviorAction} from '../behaviors/behavior.types.action'
|
|
3
3
|
import type {EditorContext} from '../editor/editor-snapshot'
|
|
4
|
-
import {
|
|
5
|
-
addAnnotationActionImplementation,
|
|
6
|
-
removeAnnotationActionImplementation,
|
|
7
|
-
} from '../editor/plugins/createWithEditableAPI'
|
|
8
4
|
import {removeDecoratorActionImplementation} from '../editor/plugins/createWithPortableTextMarkModel'
|
|
9
5
|
import {
|
|
10
6
|
historyRedoActionImplementation,
|
|
@@ -12,6 +8,8 @@ import {
|
|
|
12
8
|
} from '../editor/plugins/createWithUndoRedo'
|
|
13
9
|
import {debugWithName} from '../internal-utils/debug'
|
|
14
10
|
import type {PickFromUnion} from '../type-utils'
|
|
11
|
+
import {addAnnotationActionImplementation} from './behavior.action.annotation.add'
|
|
12
|
+
import {removeAnnotationActionImplementation} from './behavior.action.annotation.remove'
|
|
15
13
|
import {blockSetBehaviorActionImplementation} from './behavior.action.block.set'
|
|
16
14
|
import {blockUnsetBehaviorActionImplementation} from './behavior.action.block.unset'
|
|
17
15
|
import {blurActionImplementation} from './behavior.action.blur'
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {htmlToBlocks} from '@portabletext/block-tools'
|
|
2
2
|
import {toHTML} from '@portabletext/to-html'
|
|
3
3
|
import type {PortableTextBlock} from '@sanity/types'
|
|
4
|
+
import {parseBlock} from '../internal-utils/parse-blocks'
|
|
4
5
|
import {sliceBlocks} from '../utils'
|
|
5
6
|
import {defineConverter} from './converter.types'
|
|
6
7
|
|
|
@@ -59,7 +60,18 @@ export const converterTextHtml = defineConverter({
|
|
|
59
60
|
},
|
|
60
61
|
) as Array<PortableTextBlock>
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
const parsedBlocks = blocks.flatMap((block) => {
|
|
64
|
+
const parsedBlock = parseBlock({
|
|
65
|
+
context: snapshot.context,
|
|
66
|
+
block,
|
|
67
|
+
options: {
|
|
68
|
+
refreshKeys: false,
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
return parsedBlock ? [parsedBlock] : []
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
if (parsedBlocks.length === 0) {
|
|
63
75
|
return {
|
|
64
76
|
type: 'deserialization.failure',
|
|
65
77
|
mimeType: 'text/html',
|
|
@@ -69,7 +81,7 @@ export const converterTextHtml = defineConverter({
|
|
|
69
81
|
|
|
70
82
|
return {
|
|
71
83
|
type: 'deserialization.success',
|
|
72
|
-
data:
|
|
84
|
+
data: parsedBlocks,
|
|
73
85
|
mimeType: 'text/html',
|
|
74
86
|
}
|
|
75
87
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {htmlToBlocks} from '@portabletext/block-tools'
|
|
2
2
|
import {isPortableTextTextBlock, type PortableTextBlock} from '@sanity/types'
|
|
3
|
+
import {parseBlock} from '../internal-utils/parse-blocks'
|
|
3
4
|
import {sliceBlocks} from '../utils'
|
|
4
5
|
import {defineConverter} from './converter.types'
|
|
5
6
|
|
|
@@ -80,7 +81,18 @@ export const converterTextPlain = defineConverter({
|
|
|
80
81
|
},
|
|
81
82
|
) as Array<PortableTextBlock>
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
const parsedBlocks = blocks.flatMap((block) => {
|
|
85
|
+
const parsedBlock = parseBlock({
|
|
86
|
+
context: snapshot.context,
|
|
87
|
+
block,
|
|
88
|
+
options: {
|
|
89
|
+
refreshKeys: false,
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
return parsedBlock ? [parsedBlock] : []
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
if (parsedBlocks.length === 0) {
|
|
84
96
|
return {
|
|
85
97
|
type: 'deserialization.failure',
|
|
86
98
|
mimeType: 'text/plain',
|
|
@@ -90,7 +102,7 @@ export const converterTextPlain = defineConverter({
|
|
|
90
102
|
|
|
91
103
|
return {
|
|
92
104
|
type: 'deserialization.success',
|
|
93
|
-
data:
|
|
105
|
+
data: parsedBlocks,
|
|
94
106
|
mimeType: 'text/plain',
|
|
95
107
|
}
|
|
96
108
|
},
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import {Subject} from 'rxjs'
|
|
16
16
|
import {Slate} from 'slate-react'
|
|
17
17
|
import {useEffectEvent} from 'use-effect-event'
|
|
18
|
+
import type {AddedAnnotationPaths} from '../behavior-actions/behavior.action.annotation.add'
|
|
18
19
|
import {debugWithName} from '../internal-utils/debug'
|
|
19
20
|
import {compileType} from '../internal-utils/schema'
|
|
20
21
|
import type {
|
|
@@ -34,7 +35,6 @@ import type {EditorActor} from './editor-machine'
|
|
|
34
35
|
import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
|
|
35
36
|
import {PortableTextEditorSelectionProvider} from './hooks/usePortableTextEditorSelection'
|
|
36
37
|
import {defaultKeyGenerator} from './key-generator'
|
|
37
|
-
import type {AddedAnnotationPaths} from './plugins/createWithEditableAPI'
|
|
38
38
|
|
|
39
39
|
const debug = debugWithName('component:PortableTextEditor')
|
|
40
40
|
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
type PortableTextBlock,
|
|
5
5
|
type PortableTextChild,
|
|
6
6
|
type PortableTextObject,
|
|
7
|
-
type PortableTextSpan,
|
|
8
7
|
type PortableTextTextBlock,
|
|
9
8
|
} from '@sanity/types'
|
|
10
9
|
import {
|
|
@@ -12,13 +11,12 @@ import {
|
|
|
12
11
|
Node,
|
|
13
12
|
Range,
|
|
14
13
|
Element as SlateElement,
|
|
15
|
-
Path as SlatePath,
|
|
16
14
|
Text,
|
|
17
15
|
Transforms,
|
|
18
16
|
} from 'slate'
|
|
19
17
|
import type {DOMNode} from 'slate-dom'
|
|
20
18
|
import {ReactEditor} from 'slate-react'
|
|
21
|
-
import
|
|
19
|
+
import {addAnnotationActionImplementation} from '../../behavior-actions/behavior.action.annotation.add'
|
|
22
20
|
import {debugWithName} from '../../internal-utils/debug'
|
|
23
21
|
import {toSlateRange} from '../../internal-utils/ranges'
|
|
24
22
|
import {
|
|
@@ -608,282 +606,3 @@ function isAnnotationActive({
|
|
|
608
606
|
return false
|
|
609
607
|
}
|
|
610
608
|
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* @public
|
|
614
|
-
*/
|
|
615
|
-
export type AddedAnnotationPaths = {
|
|
616
|
-
/**
|
|
617
|
-
* @deprecated An annotation may be applied to multiple blocks, resulting
|
|
618
|
-
* in multiple `markDef`'s being created. Use `markDefPaths` instead.
|
|
619
|
-
*/
|
|
620
|
-
markDefPath: Path
|
|
621
|
-
markDefPaths: Array<Path>
|
|
622
|
-
/**
|
|
623
|
-
* @deprecated Does not return anything meaningful since an annotation
|
|
624
|
-
* can span multiple blocks and spans. If references the span closest
|
|
625
|
-
* to the focus point of the selection.
|
|
626
|
-
*/
|
|
627
|
-
spanPath: Path
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
export const addAnnotationActionImplementation: BehaviorActionImplementation<
|
|
631
|
-
'annotation.add',
|
|
632
|
-
AddedAnnotationPaths | undefined
|
|
633
|
-
> = ({context, action}) => {
|
|
634
|
-
const editor = action.editor
|
|
635
|
-
|
|
636
|
-
if (!editor.selection || Range.isCollapsed(editor.selection)) {
|
|
637
|
-
return
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
let paths: AddedAnnotationPaths | undefined = undefined
|
|
641
|
-
let spanPath: Path | undefined
|
|
642
|
-
let markDefPath: Path | undefined
|
|
643
|
-
const markDefPaths: Path[] = []
|
|
644
|
-
|
|
645
|
-
const selectedBlocks = Editor.nodes(editor, {
|
|
646
|
-
at: editor.selection,
|
|
647
|
-
match: (node) => editor.isTextBlock(node),
|
|
648
|
-
reverse: Range.isBackward(editor.selection),
|
|
649
|
-
})
|
|
650
|
-
|
|
651
|
-
for (const [block, blockPath] of selectedBlocks) {
|
|
652
|
-
if (block.children.length === 0) {
|
|
653
|
-
continue
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
if (block.children.length === 1 && block.children[0].text === '') {
|
|
657
|
-
continue
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
const annotationKey = context.keyGenerator()
|
|
661
|
-
const markDefs = block.markDefs ?? []
|
|
662
|
-
const existingMarkDef = markDefs.find(
|
|
663
|
-
(markDef) =>
|
|
664
|
-
markDef._type === action.annotation.name &&
|
|
665
|
-
markDef._key === annotationKey,
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
if (existingMarkDef === undefined) {
|
|
669
|
-
Transforms.setNodes(
|
|
670
|
-
editor,
|
|
671
|
-
{
|
|
672
|
-
markDefs: [
|
|
673
|
-
...markDefs,
|
|
674
|
-
{
|
|
675
|
-
_type: action.annotation.name,
|
|
676
|
-
_key: annotationKey,
|
|
677
|
-
...action.annotation.value,
|
|
678
|
-
},
|
|
679
|
-
],
|
|
680
|
-
},
|
|
681
|
-
{at: blockPath},
|
|
682
|
-
)
|
|
683
|
-
|
|
684
|
-
markDefPath = [{_key: block._key}, 'markDefs', {_key: annotationKey}]
|
|
685
|
-
|
|
686
|
-
if (Range.isBackward(editor.selection)) {
|
|
687
|
-
markDefPaths.unshift(markDefPath)
|
|
688
|
-
} else {
|
|
689
|
-
markDefPaths.push(markDefPath)
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
Transforms.setNodes(editor, {}, {match: Text.isText, split: true})
|
|
694
|
-
|
|
695
|
-
const children = Node.children(editor, blockPath)
|
|
696
|
-
|
|
697
|
-
for (const [span, path] of children) {
|
|
698
|
-
if (!editor.isTextSpan(span)) {
|
|
699
|
-
continue
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
if (!Range.includes(editor.selection, path)) {
|
|
703
|
-
continue
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
const marks = span.marks ?? []
|
|
707
|
-
const existingSameTypeAnnotations = marks.filter((mark) =>
|
|
708
|
-
markDefs.some(
|
|
709
|
-
(markDef) =>
|
|
710
|
-
markDef._key === mark && markDef._type === action.annotation.name,
|
|
711
|
-
),
|
|
712
|
-
)
|
|
713
|
-
|
|
714
|
-
Transforms.setNodes(
|
|
715
|
-
editor,
|
|
716
|
-
{
|
|
717
|
-
marks: [
|
|
718
|
-
...marks.filter(
|
|
719
|
-
(mark) => !existingSameTypeAnnotations.includes(mark),
|
|
720
|
-
),
|
|
721
|
-
annotationKey,
|
|
722
|
-
],
|
|
723
|
-
},
|
|
724
|
-
{at: path},
|
|
725
|
-
)
|
|
726
|
-
|
|
727
|
-
spanPath = [{_key: block._key}, 'children', {_key: span._key}]
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
if (markDefPath && spanPath) {
|
|
732
|
-
paths = {
|
|
733
|
-
markDefPath,
|
|
734
|
-
markDefPaths,
|
|
735
|
-
spanPath,
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
return paths
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
export const removeAnnotationActionImplementation: BehaviorActionImplementation<
|
|
743
|
-
'annotation.remove'
|
|
744
|
-
> = ({action}) => {
|
|
745
|
-
const editor = action.editor
|
|
746
|
-
|
|
747
|
-
debug('Removing annotation', action.annotation.name)
|
|
748
|
-
|
|
749
|
-
if (!editor.selection) {
|
|
750
|
-
return
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
if (Range.isCollapsed(editor.selection)) {
|
|
754
|
-
const [block, blockPath] = Editor.node(editor, editor.selection, {
|
|
755
|
-
depth: 1,
|
|
756
|
-
})
|
|
757
|
-
|
|
758
|
-
if (!editor.isTextBlock(block)) {
|
|
759
|
-
return
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
const markDefs = block.markDefs ?? []
|
|
763
|
-
const potentialAnnotations = markDefs.filter(
|
|
764
|
-
(markDef) => markDef._type === action.annotation.name,
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
const [selectedChild, selectedChildPath] = Editor.node(
|
|
768
|
-
editor,
|
|
769
|
-
editor.selection,
|
|
770
|
-
{
|
|
771
|
-
depth: 2,
|
|
772
|
-
},
|
|
773
|
-
)
|
|
774
|
-
|
|
775
|
-
if (!editor.isTextSpan(selectedChild)) {
|
|
776
|
-
return
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
const annotationToRemove = selectedChild.marks?.find((mark) =>
|
|
780
|
-
potentialAnnotations.some((markDef) => markDef._key === mark),
|
|
781
|
-
)
|
|
782
|
-
|
|
783
|
-
if (!annotationToRemove) {
|
|
784
|
-
return
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
const previousSpansWithSameAnnotation: Array<
|
|
788
|
-
[span: PortableTextSpan, path: SlatePath]
|
|
789
|
-
> = []
|
|
790
|
-
|
|
791
|
-
for (const [child, childPath] of Node.children(editor, blockPath, {
|
|
792
|
-
reverse: true,
|
|
793
|
-
})) {
|
|
794
|
-
if (!editor.isTextSpan(child)) {
|
|
795
|
-
continue
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
if (!SlatePath.isBefore(childPath, selectedChildPath)) {
|
|
799
|
-
continue
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
if (child.marks?.includes(annotationToRemove)) {
|
|
803
|
-
previousSpansWithSameAnnotation.push([child, childPath])
|
|
804
|
-
} else {
|
|
805
|
-
break
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
const nextSpansWithSameAnnotation: Array<
|
|
810
|
-
[span: PortableTextSpan, path: SlatePath]
|
|
811
|
-
> = []
|
|
812
|
-
|
|
813
|
-
for (const [child, childPath] of Node.children(editor, blockPath)) {
|
|
814
|
-
if (!editor.isTextSpan(child)) {
|
|
815
|
-
continue
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
if (!SlatePath.isAfter(childPath, selectedChildPath)) {
|
|
819
|
-
continue
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
if (child.marks?.includes(annotationToRemove)) {
|
|
823
|
-
nextSpansWithSameAnnotation.push([child, childPath])
|
|
824
|
-
} else {
|
|
825
|
-
break
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
for (const [child, childPath] of [
|
|
830
|
-
...previousSpansWithSameAnnotation,
|
|
831
|
-
[selectedChild, selectedChildPath] as const,
|
|
832
|
-
...nextSpansWithSameAnnotation,
|
|
833
|
-
]) {
|
|
834
|
-
Transforms.setNodes(
|
|
835
|
-
editor,
|
|
836
|
-
{
|
|
837
|
-
marks: child.marks?.filter((mark) => mark !== annotationToRemove),
|
|
838
|
-
},
|
|
839
|
-
{at: childPath},
|
|
840
|
-
)
|
|
841
|
-
}
|
|
842
|
-
} else {
|
|
843
|
-
Transforms.setNodes(
|
|
844
|
-
editor,
|
|
845
|
-
{},
|
|
846
|
-
{
|
|
847
|
-
match: (node) => editor.isTextSpan(node),
|
|
848
|
-
split: true,
|
|
849
|
-
hanging: true,
|
|
850
|
-
},
|
|
851
|
-
)
|
|
852
|
-
|
|
853
|
-
const blocks = Editor.nodes(editor, {
|
|
854
|
-
at: editor.selection,
|
|
855
|
-
match: (node) => editor.isTextBlock(node),
|
|
856
|
-
})
|
|
857
|
-
|
|
858
|
-
for (const [block, blockPath] of blocks) {
|
|
859
|
-
const children = Node.children(editor, blockPath)
|
|
860
|
-
|
|
861
|
-
for (const [child, childPath] of children) {
|
|
862
|
-
if (!editor.isTextSpan(child)) {
|
|
863
|
-
continue
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if (!Range.includes(editor.selection, childPath)) {
|
|
867
|
-
continue
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
const markDefs = block.markDefs ?? []
|
|
871
|
-
const marks = child.marks ?? []
|
|
872
|
-
const marksWithoutAnnotation = marks.filter((mark) => {
|
|
873
|
-
const markDef = markDefs.find((markDef) => markDef._key === mark)
|
|
874
|
-
return markDef?._type !== action.annotation.name
|
|
875
|
-
})
|
|
876
|
-
|
|
877
|
-
if (marksWithoutAnnotation.length !== marks.length) {
|
|
878
|
-
Transforms.setNodes(
|
|
879
|
-
editor,
|
|
880
|
-
{
|
|
881
|
-
marks: marksWithoutAnnotation,
|
|
882
|
-
},
|
|
883
|
-
{at: childPath},
|
|
884
|
-
)
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}
|