@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -79,15 +79,15 @@
|
|
|
79
79
|
"slate-react": "0.112.1",
|
|
80
80
|
"use-effect-event": "^1.0.2",
|
|
81
81
|
"xstate": "^5.19.2",
|
|
82
|
-
"@portabletext/block-tools": "1.1.
|
|
82
|
+
"@portabletext/block-tools": "1.1.8",
|
|
83
83
|
"@portabletext/patches": "1.1.3"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@portabletext/toolkit": "^2.0.17",
|
|
87
87
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
88
88
|
"@sanity/pkg-utils": "^7.0.4",
|
|
89
|
-
"@sanity/schema": "^3.75.
|
|
90
|
-
"@sanity/types": "^3.75.
|
|
89
|
+
"@sanity/schema": "^3.75.1",
|
|
90
|
+
"@sanity/types": "^3.75.1",
|
|
91
91
|
"@testing-library/jest-dom": "^6.6.3",
|
|
92
92
|
"@testing-library/react": "^16.2.0",
|
|
93
93
|
"@types/debug": "^4.1.12",
|
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
"racejar": "1.2.0"
|
|
116
116
|
},
|
|
117
117
|
"peerDependencies": {
|
|
118
|
-
"@sanity/schema": "^3.75.
|
|
119
|
-
"@sanity/types": "^3.75.
|
|
118
|
+
"@sanity/schema": "^3.75.1",
|
|
119
|
+
"@sanity/types": "^3.75.1",
|
|
120
120
|
"react": "^16.9 || ^17 || ^18 || ^19",
|
|
121
121
|
"rxjs": "^7.8.1"
|
|
122
122
|
},
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {Transforms} from 'slate'
|
|
1
|
+
import {Editor, Transforms, type Element as SlateElement} from 'slate'
|
|
2
|
+
import {parseBlock} from '../internal-utils/parse-blocks'
|
|
2
3
|
import {toSlateRange} from '../internal-utils/ranges'
|
|
4
|
+
import {fromSlateValue, toSlateValue} from '../internal-utils/values'
|
|
5
|
+
import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
|
|
3
6
|
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
7
|
|
|
5
8
|
export const blockSetBehaviorActionImplementation: BehaviorActionImplementation<
|
|
6
9
|
'block.set'
|
|
7
|
-
> = ({action}) => {
|
|
10
|
+
> = ({context, action}) => {
|
|
8
11
|
const location = toSlateRange(
|
|
9
12
|
{
|
|
10
13
|
anchor: {path: action.at, offset: 0},
|
|
@@ -14,10 +17,50 @@ export const blockSetBehaviorActionImplementation: BehaviorActionImplementation<
|
|
|
14
17
|
)
|
|
15
18
|
|
|
16
19
|
if (!location) {
|
|
17
|
-
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Unable to convert ${JSON.stringify(action.at)} into a Slate Range`,
|
|
22
|
+
)
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
const
|
|
25
|
+
const blockEntry = Editor.node(action.editor, location, {depth: 1})
|
|
26
|
+
const block = blockEntry?.[0]
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
if (!block) {
|
|
29
|
+
throw new Error(`Unable to find block at ${JSON.stringify(action.at)}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parsedBlock = fromSlateValue(
|
|
33
|
+
[block],
|
|
34
|
+
context.schema.block.name,
|
|
35
|
+
KEY_TO_VALUE_ELEMENT.get(action.editor),
|
|
36
|
+
).at(0)
|
|
37
|
+
|
|
38
|
+
if (!parsedBlock) {
|
|
39
|
+
throw new Error(`Unable to parse block at ${JSON.stringify(action.at)}`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const {_type, ...filteredProps} = action.props
|
|
43
|
+
|
|
44
|
+
const updatedBlock = parseBlock({
|
|
45
|
+
context,
|
|
46
|
+
block: {
|
|
47
|
+
...parsedBlock,
|
|
48
|
+
...filteredProps,
|
|
49
|
+
},
|
|
50
|
+
options: {refreshKeys: false},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (!updatedBlock) {
|
|
54
|
+
throw new Error(`Unable to update block at ${JSON.stringify(action.at)}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const slateBlock = toSlateValue([updatedBlock], {
|
|
58
|
+
schemaTypes: context.schema,
|
|
59
|
+
})?.at(0) as SlateElement | undefined
|
|
60
|
+
|
|
61
|
+
if (!slateBlock) {
|
|
62
|
+
throw new Error(`Unable to convert block to Slate value`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Transforms.setNodes(action.editor, slateBlock, {at: location})
|
|
23
66
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {omit} from 'lodash'
|
|
2
|
+
import {Editor, Transforms} from 'slate'
|
|
3
|
+
import {isTextBlock, parseBlock} from '../internal-utils/parse-blocks'
|
|
2
4
|
import {toSlateRange} from '../internal-utils/ranges'
|
|
5
|
+
import {fromSlateValue} from '../internal-utils/values'
|
|
6
|
+
import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
|
|
3
7
|
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
8
|
|
|
5
9
|
export const blockUnsetBehaviorActionImplementation: BehaviorActionImplementation<
|
|
6
10
|
'block.unset'
|
|
7
|
-
> = ({action}) => {
|
|
11
|
+
> = ({context, action}) => {
|
|
8
12
|
const location = toSlateRange(
|
|
9
13
|
{
|
|
10
14
|
anchor: {path: action.at, offset: 0},
|
|
@@ -14,8 +18,78 @@ export const blockUnsetBehaviorActionImplementation: BehaviorActionImplementatio
|
|
|
14
18
|
)
|
|
15
19
|
|
|
16
20
|
if (!location) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Unable to convert ${JSON.stringify(action.at)} into a Slate Range`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const blockEntry = Editor.node(action.editor, location, {depth: 1})
|
|
27
|
+
const block = blockEntry?.[0]
|
|
28
|
+
|
|
29
|
+
if (!block) {
|
|
30
|
+
throw new Error(`Unable to find block at ${JSON.stringify(action.at)}`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const parsedBlock = fromSlateValue(
|
|
34
|
+
[block],
|
|
35
|
+
context.schema.block.name,
|
|
36
|
+
KEY_TO_VALUE_ELEMENT.get(action.editor),
|
|
37
|
+
).at(0)
|
|
38
|
+
|
|
39
|
+
if (!parsedBlock) {
|
|
40
|
+
throw new Error(`Unable to parse block at ${JSON.stringify(action.at)}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isTextBlock(context.schema, parsedBlock)) {
|
|
44
|
+
const propsToRemove = action.props.filter((prop) => prop !== '_type')
|
|
45
|
+
|
|
46
|
+
const updatedTextBlock = parseBlock({
|
|
47
|
+
context,
|
|
48
|
+
block: omit(parsedBlock, propsToRemove),
|
|
49
|
+
options: {refreshKeys: false},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if (!updatedTextBlock) {
|
|
53
|
+
throw new Error(`Unable to update block at ${JSON.stringify(action.at)}`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const propsToSet: Record<string, unknown> = {}
|
|
57
|
+
|
|
58
|
+
for (const prop of propsToRemove) {
|
|
59
|
+
if (!(prop in updatedTextBlock)) {
|
|
60
|
+
propsToSet[prop] = undefined
|
|
61
|
+
} else {
|
|
62
|
+
propsToSet[prop] = (updatedTextBlock as Record<string, unknown>)[prop]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Transforms.setNodes(action.editor, propsToSet, {at: location})
|
|
67
|
+
|
|
17
68
|
return
|
|
18
69
|
}
|
|
19
70
|
|
|
20
|
-
|
|
71
|
+
const updatedBlockObject = parseBlock({
|
|
72
|
+
context,
|
|
73
|
+
block: omit(
|
|
74
|
+
parsedBlock,
|
|
75
|
+
action.props.filter((prop) => prop !== '_type'),
|
|
76
|
+
),
|
|
77
|
+
options: {refreshKeys: false},
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (!updatedBlockObject) {
|
|
81
|
+
throw new Error(`Unable to update block at ${JSON.stringify(action.at)}`)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const {_type, _key, ...props} = updatedBlockObject
|
|
85
|
+
|
|
86
|
+
Transforms.setNodes(
|
|
87
|
+
action.editor,
|
|
88
|
+
{
|
|
89
|
+
_type,
|
|
90
|
+
_key,
|
|
91
|
+
value: props,
|
|
92
|
+
},
|
|
93
|
+
{at: location},
|
|
94
|
+
)
|
|
21
95
|
}
|
|
@@ -11,20 +11,42 @@ export const decoratorAddActionImplementation: BehaviorActionImplementation<
|
|
|
11
11
|
> = ({context, action}) => {
|
|
12
12
|
const editor = action.editor
|
|
13
13
|
const mark = action.decorator
|
|
14
|
-
const selection = action.selection
|
|
15
|
-
? (toSlateRange(action.selection, action.editor) ?? editor.selection)
|
|
16
|
-
: editor.selection
|
|
17
|
-
|
|
18
|
-
if (!selection) {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
14
|
const value = fromSlateValue(
|
|
23
15
|
editor.children,
|
|
24
16
|
context.schema.block.name,
|
|
25
17
|
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
26
18
|
)
|
|
27
19
|
|
|
20
|
+
const manualAnchor = action.offsets?.anchor
|
|
21
|
+
? utils.blockOffsetToSpanSelectionPoint({
|
|
22
|
+
value,
|
|
23
|
+
blockOffset: action.offsets.anchor,
|
|
24
|
+
direction: 'backward',
|
|
25
|
+
})
|
|
26
|
+
: undefined
|
|
27
|
+
const manualFocus = action.offsets?.focus
|
|
28
|
+
? utils.blockOffsetToSpanSelectionPoint({
|
|
29
|
+
value,
|
|
30
|
+
blockOffset: action.offsets.focus,
|
|
31
|
+
direction: 'forward',
|
|
32
|
+
})
|
|
33
|
+
: undefined
|
|
34
|
+
const manualSelection =
|
|
35
|
+
manualAnchor && manualFocus
|
|
36
|
+
? {
|
|
37
|
+
anchor: manualAnchor,
|
|
38
|
+
focus: manualFocus,
|
|
39
|
+
}
|
|
40
|
+
: undefined
|
|
41
|
+
|
|
42
|
+
const selection = manualSelection
|
|
43
|
+
? (toSlateRange(manualSelection, action.editor) ?? editor.selection)
|
|
44
|
+
: editor.selection
|
|
45
|
+
|
|
46
|
+
if (!selection) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
28
50
|
const editorSelection = toPortableTextRange(value, selection, context.schema)
|
|
29
51
|
const anchorOffset = editorSelection
|
|
30
52
|
? utils.spanSelectionPointToBlockOffset({
|
|
@@ -48,8 +48,6 @@ import {
|
|
|
48
48
|
removeStyleActionImplementation,
|
|
49
49
|
toggleStyleActionImplementation,
|
|
50
50
|
} from './behavior.action.style'
|
|
51
|
-
import {textBlockSetActionImplementation} from './behavior.action.text-block.set'
|
|
52
|
-
import {textBlockUnsetActionImplementation} from './behavior.action.text-block.unset'
|
|
53
51
|
|
|
54
52
|
export type BehaviorActionImplementationContext = Pick<
|
|
55
53
|
EditorContext,
|
|
@@ -266,8 +264,6 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
|
|
|
266
264
|
'style.toggle': toggleStyleActionImplementation,
|
|
267
265
|
'style.add': addStyleActionImplementation,
|
|
268
266
|
'style.remove': removeStyleActionImplementation,
|
|
269
|
-
'text block.set': textBlockSetActionImplementation,
|
|
270
|
-
'text block.unset': textBlockUnsetActionImplementation,
|
|
271
267
|
}
|
|
272
268
|
|
|
273
269
|
export function performAction({
|
|
@@ -600,25 +596,12 @@ function performDefaultAction({
|
|
|
600
596
|
})
|
|
601
597
|
break
|
|
602
598
|
}
|
|
603
|
-
|
|
599
|
+
default: {
|
|
604
600
|
behaviorActionImplementations['style.toggle']({
|
|
605
601
|
context,
|
|
606
602
|
action,
|
|
607
603
|
})
|
|
608
604
|
break
|
|
609
605
|
}
|
|
610
|
-
case 'text block.set': {
|
|
611
|
-
behaviorActionImplementations['text block.set']({
|
|
612
|
-
context,
|
|
613
|
-
action,
|
|
614
|
-
})
|
|
615
|
-
break
|
|
616
|
-
}
|
|
617
|
-
default: {
|
|
618
|
-
behaviorActionImplementations['text block.unset']({
|
|
619
|
-
context,
|
|
620
|
-
action,
|
|
621
|
-
})
|
|
622
|
-
}
|
|
623
606
|
}
|
|
624
607
|
}
|
|
@@ -30,7 +30,7 @@ const clearListOnBackspace = defineBehavior({
|
|
|
30
30
|
actions: [
|
|
31
31
|
(_, {focusTextBlock}) => [
|
|
32
32
|
raise({
|
|
33
|
-
type: '
|
|
33
|
+
type: 'block.unset',
|
|
34
34
|
props: ['listItem', 'level'],
|
|
35
35
|
at: focusTextBlock.path,
|
|
36
36
|
}),
|
|
@@ -66,8 +66,8 @@ const unindentListOnBackspace = defineBehavior({
|
|
|
66
66
|
actions: [
|
|
67
67
|
(_, {focusTextBlock, level}) => [
|
|
68
68
|
raise({
|
|
69
|
-
type: '
|
|
70
|
-
level,
|
|
69
|
+
type: 'block.set',
|
|
70
|
+
props: {level},
|
|
71
71
|
at: focusTextBlock.path,
|
|
72
72
|
}),
|
|
73
73
|
],
|
|
@@ -93,7 +93,7 @@ const clearListOnEnter = defineBehavior({
|
|
|
93
93
|
actions: [
|
|
94
94
|
(_, {focusListBlock}) => [
|
|
95
95
|
raise({
|
|
96
|
-
type: '
|
|
96
|
+
type: 'block.unset',
|
|
97
97
|
props: ['listItem', 'level'],
|
|
98
98
|
at: focusListBlock.path,
|
|
99
99
|
}),
|
|
@@ -133,11 +133,13 @@ const indentListOnTab = defineBehavior({
|
|
|
133
133
|
(_, {selectedListBlocks}) =>
|
|
134
134
|
selectedListBlocks.map((selectedListBlock) =>
|
|
135
135
|
raise({
|
|
136
|
-
type: '
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
type: 'block.set',
|
|
137
|
+
props: {
|
|
138
|
+
level: Math.min(
|
|
139
|
+
MAX_LIST_LEVEL,
|
|
140
|
+
Math.max(1, selectedListBlock.node.level + 1),
|
|
141
|
+
),
|
|
142
|
+
},
|
|
141
143
|
at: selectedListBlock.path,
|
|
142
144
|
}),
|
|
143
145
|
),
|
|
@@ -176,11 +178,13 @@ const unindentListOnShiftTab = defineBehavior({
|
|
|
176
178
|
(_, {selectedListBlocks}) =>
|
|
177
179
|
selectedListBlocks.map((selectedListBlock) =>
|
|
178
180
|
raise({
|
|
179
|
-
type: '
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
type: 'block.set',
|
|
182
|
+
props: {
|
|
183
|
+
level: Math.min(
|
|
184
|
+
MAX_LIST_LEVEL,
|
|
185
|
+
Math.max(1, selectedListBlock.node.level - 1),
|
|
186
|
+
),
|
|
187
|
+
},
|
|
184
188
|
at: selectedListBlock.path,
|
|
185
189
|
}),
|
|
186
190
|
),
|
|
@@ -79,10 +79,6 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
79
79
|
return false
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if (event.text !== '*' && event.text !== '_') {
|
|
83
|
-
return false
|
|
84
|
-
}
|
|
85
|
-
|
|
86
82
|
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
87
83
|
const selectionStartPoint = selectors.getSelectionStartPoint({context})
|
|
88
84
|
const selectionStartOffset = selectionStartPoint
|
|
@@ -104,44 +100,33 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
104
100
|
const prefixOffsets = {
|
|
105
101
|
anchor: {
|
|
106
102
|
path: focusTextBlock.path,
|
|
107
|
-
|
|
103
|
+
// Example: "foo *bar*".length - "*bar*".length = 4
|
|
104
|
+
offset: `${textBefore}${event.text}`.length - textToItalic.length,
|
|
108
105
|
},
|
|
109
106
|
focus: {
|
|
110
107
|
path: focusTextBlock.path,
|
|
111
|
-
|
|
108
|
+
// Example: "foo *bar*".length - "*bar*".length + 1 = 5
|
|
109
|
+
offset:
|
|
110
|
+
`${textBefore}${event.text}`.length - textToItalic.length + 1,
|
|
112
111
|
},
|
|
113
112
|
}
|
|
114
113
|
const suffixOffsets = {
|
|
115
114
|
anchor: {
|
|
116
115
|
path: focusTextBlock.path,
|
|
117
|
-
|
|
116
|
+
// Example: "foo *bar|" (8) + "*".length - 1 = 8
|
|
117
|
+
offset: selectionStartOffset.offset + event.text.length - 1,
|
|
118
118
|
},
|
|
119
119
|
focus: {
|
|
120
120
|
path: focusTextBlock.path,
|
|
121
|
-
|
|
121
|
+
// Example: "foo *bar|" (8) + "*".length = 9
|
|
122
|
+
offset: selectionStartOffset.offset + event.text.length,
|
|
122
123
|
},
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
const anchor = utils.blockOffsetToSpanSelectionPoint({
|
|
126
|
-
value: context.value,
|
|
127
|
-
blockOffset: prefixOffsets.focus,
|
|
128
|
-
direction: 'backward',
|
|
129
|
-
})
|
|
130
|
-
const focus = utils.blockOffsetToSpanSelectionPoint({
|
|
131
|
-
value: context.value,
|
|
132
|
-
blockOffset: suffixOffsets.anchor,
|
|
133
|
-
direction: 'forward',
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
if (!anchor || !focus) {
|
|
137
|
-
return false
|
|
138
|
-
}
|
|
139
|
-
|
|
140
126
|
return {
|
|
141
127
|
prefixOffsets,
|
|
142
128
|
suffixOffsets,
|
|
143
129
|
decorator: italicDecorator,
|
|
144
|
-
selection: {anchor, focus},
|
|
145
130
|
}
|
|
146
131
|
}
|
|
147
132
|
|
|
@@ -151,35 +136,85 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
151
136
|
const prefixOffsets = {
|
|
152
137
|
anchor: {
|
|
153
138
|
path: focusTextBlock.path,
|
|
154
|
-
|
|
139
|
+
// Example: "foo **bar**".length - "**bar**".length = 4
|
|
140
|
+
offset: `${textBefore}${event.text}`.length - textToBold.length,
|
|
155
141
|
},
|
|
156
142
|
focus: {
|
|
157
143
|
path: focusTextBlock.path,
|
|
158
|
-
|
|
144
|
+
// Example: "foo **bar**".length - "**bar**".length + 2 = 6
|
|
145
|
+
offset:
|
|
146
|
+
`${textBefore}${event.text}`.length - textToBold.length + 2,
|
|
159
147
|
},
|
|
160
148
|
}
|
|
149
|
+
|
|
150
|
+
const prefixSelection = utils.blockOffsetsToSelection({
|
|
151
|
+
value: context.value,
|
|
152
|
+
offsets: prefixOffsets,
|
|
153
|
+
})
|
|
154
|
+
const inlineObjectBeforePrefixFocus =
|
|
155
|
+
selectors.getPreviousInlineObject({
|
|
156
|
+
context: {
|
|
157
|
+
...context,
|
|
158
|
+
selection: prefixSelection
|
|
159
|
+
? {
|
|
160
|
+
anchor: prefixSelection.focus,
|
|
161
|
+
focus: prefixSelection.focus,
|
|
162
|
+
}
|
|
163
|
+
: null,
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
const inlineObjectBeforePrefixFocusOffset =
|
|
167
|
+
inlineObjectBeforePrefixFocus
|
|
168
|
+
? utils.childSelectionPointToBlockOffset({
|
|
169
|
+
value: context.value,
|
|
170
|
+
selectionPoint: {
|
|
171
|
+
path: inlineObjectBeforePrefixFocus.path,
|
|
172
|
+
offset: 0,
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
: undefined
|
|
176
|
+
|
|
177
|
+
if (
|
|
178
|
+
inlineObjectBeforePrefixFocusOffset &&
|
|
179
|
+
inlineObjectBeforePrefixFocusOffset.offset >
|
|
180
|
+
prefixOffsets.anchor.offset &&
|
|
181
|
+
inlineObjectBeforePrefixFocusOffset.offset <
|
|
182
|
+
prefixOffsets.focus.offset
|
|
183
|
+
) {
|
|
184
|
+
return false
|
|
185
|
+
}
|
|
186
|
+
|
|
161
187
|
const suffixOffsets = {
|
|
162
188
|
anchor: {
|
|
163
189
|
path: focusTextBlock.path,
|
|
164
|
-
|
|
190
|
+
// Example: "foo **bar*|" (10) + "*".length - 2 = 9
|
|
191
|
+
offset: selectionStartOffset.offset + event.text.length - 2,
|
|
165
192
|
},
|
|
166
193
|
focus: {
|
|
167
194
|
path: focusTextBlock.path,
|
|
168
|
-
|
|
195
|
+
// Example: "foo **bar*|" (10) + "*".length = 11
|
|
196
|
+
offset: selectionStartOffset.offset + event.text.length,
|
|
169
197
|
},
|
|
170
198
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
direction: 'backward',
|
|
175
|
-
})
|
|
176
|
-
const focus = utils.blockOffsetToSpanSelectionPoint({
|
|
177
|
-
value: context.value,
|
|
178
|
-
blockOffset: suffixOffsets.anchor,
|
|
179
|
-
direction: 'forward',
|
|
199
|
+
|
|
200
|
+
const previousInlineObject = selectors.getPreviousInlineObject({
|
|
201
|
+
context,
|
|
180
202
|
})
|
|
203
|
+
const previousInlineObjectOffset = previousInlineObject
|
|
204
|
+
? utils.childSelectionPointToBlockOffset({
|
|
205
|
+
value: context.value,
|
|
206
|
+
selectionPoint: {
|
|
207
|
+
path: previousInlineObject.path,
|
|
208
|
+
offset: 0,
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
: undefined
|
|
181
212
|
|
|
182
|
-
if (
|
|
213
|
+
if (
|
|
214
|
+
previousInlineObjectOffset &&
|
|
215
|
+
previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&
|
|
216
|
+
previousInlineObjectOffset.offset < suffixOffsets.focus.offset
|
|
217
|
+
) {
|
|
183
218
|
return false
|
|
184
219
|
}
|
|
185
220
|
|
|
@@ -187,7 +222,6 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
187
222
|
prefixOffsets,
|
|
188
223
|
suffixOffsets,
|
|
189
224
|
decorator: boldDecorator,
|
|
190
|
-
selection: {anchor, focus},
|
|
191
225
|
}
|
|
192
226
|
}
|
|
193
227
|
|
|
@@ -195,11 +229,14 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
195
229
|
},
|
|
196
230
|
actions: [
|
|
197
231
|
({event}) => [event],
|
|
198
|
-
(_, {prefixOffsets, suffixOffsets, decorator
|
|
232
|
+
(_, {prefixOffsets, suffixOffsets, decorator}) => [
|
|
199
233
|
{
|
|
200
234
|
type: 'decorator.add',
|
|
201
235
|
decorator,
|
|
202
|
-
|
|
236
|
+
offsets: {
|
|
237
|
+
anchor: prefixOffsets.focus,
|
|
238
|
+
focus: suffixOffsets.anchor,
|
|
239
|
+
},
|
|
203
240
|
},
|
|
204
241
|
{
|
|
205
242
|
type: 'delete.text',
|
|
@@ -209,6 +246,10 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
209
246
|
type: 'delete.text',
|
|
210
247
|
...prefixOffsets,
|
|
211
248
|
},
|
|
249
|
+
{
|
|
250
|
+
type: 'decorator.remove',
|
|
251
|
+
decorator,
|
|
252
|
+
},
|
|
212
253
|
{
|
|
213
254
|
type: 'effect',
|
|
214
255
|
effect: () => {
|
|
@@ -127,13 +127,13 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
127
127
|
],
|
|
128
128
|
(_, {focusTextBlock, style}) => [
|
|
129
129
|
{
|
|
130
|
-
type: '
|
|
130
|
+
type: 'block.unset',
|
|
131
131
|
props: ['listItem', 'level'],
|
|
132
132
|
at: focusTextBlock.path,
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
|
-
type: '
|
|
136
|
-
style,
|
|
135
|
+
type: 'block.set',
|
|
136
|
+
props: {style},
|
|
137
137
|
at: focusTextBlock.path,
|
|
138
138
|
},
|
|
139
139
|
{
|
|
@@ -326,13 +326,13 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
326
326
|
({event}) => [event],
|
|
327
327
|
(_, {focusTextBlock, style, level}) => [
|
|
328
328
|
{
|
|
329
|
-
type: '
|
|
329
|
+
type: 'block.unset',
|
|
330
330
|
props: ['listItem', 'level'],
|
|
331
331
|
at: focusTextBlock.path,
|
|
332
332
|
},
|
|
333
333
|
{
|
|
334
|
-
type: '
|
|
335
|
-
style,
|
|
334
|
+
type: 'block.set',
|
|
335
|
+
props: {style},
|
|
336
336
|
at: focusTextBlock.path,
|
|
337
337
|
},
|
|
338
338
|
{
|
|
@@ -379,8 +379,8 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
379
379
|
actions: [
|
|
380
380
|
(_, {defaultStyle, focusTextBlock}) => [
|
|
381
381
|
{
|
|
382
|
-
type: '
|
|
383
|
-
style: defaultStyle,
|
|
382
|
+
type: 'block.set',
|
|
383
|
+
props: {style: defaultStyle},
|
|
384
384
|
at: focusTextBlock.path,
|
|
385
385
|
},
|
|
386
386
|
],
|
|
@@ -464,10 +464,12 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
464
464
|
({event}) => [event],
|
|
465
465
|
(_, {focusTextBlock, style, listItem, listItemLength}) => [
|
|
466
466
|
{
|
|
467
|
-
type: '
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
467
|
+
type: 'block.set',
|
|
468
|
+
props: {
|
|
469
|
+
listItem,
|
|
470
|
+
level: 1,
|
|
471
|
+
style,
|
|
472
|
+
},
|
|
471
473
|
at: focusTextBlock.path,
|
|
472
474
|
},
|
|
473
475
|
{
|
|
@@ -38,7 +38,7 @@ export type SyntheticBehaviorEvent =
|
|
|
38
38
|
| {
|
|
39
39
|
type: 'block.set'
|
|
40
40
|
at: [KeyedSegment]
|
|
41
|
-
|
|
41
|
+
props: Record<string, unknown>
|
|
42
42
|
}
|
|
43
43
|
| {
|
|
44
44
|
type: 'block.unset'
|
|
@@ -57,7 +57,7 @@ export type SyntheticBehaviorEvent =
|
|
|
57
57
|
| {
|
|
58
58
|
type: 'decorator.add'
|
|
59
59
|
decorator: string
|
|
60
|
-
|
|
60
|
+
offsets?: {anchor: BlockOffset; focus: BlockOffset}
|
|
61
61
|
}
|
|
62
62
|
| {
|
|
63
63
|
type: 'decorator.remove'
|
|
@@ -195,18 +195,6 @@ export type SyntheticBehaviorEvent =
|
|
|
195
195
|
type: 'style.toggle'
|
|
196
196
|
style: string
|
|
197
197
|
}
|
|
198
|
-
| {
|
|
199
|
-
type: 'text block.set'
|
|
200
|
-
at: [KeyedSegment]
|
|
201
|
-
level?: number
|
|
202
|
-
listItem?: string
|
|
203
|
-
style?: string
|
|
204
|
-
}
|
|
205
|
-
| {
|
|
206
|
-
type: 'text block.unset'
|
|
207
|
-
at: [KeyedSegment]
|
|
208
|
-
props: Array<'level' | 'listItem' | 'style'>
|
|
209
|
-
}
|
|
210
198
|
| (PickFromUnion<
|
|
211
199
|
ConverterEvent,
|
|
212
200
|
'type',
|
|
@@ -412,6 +412,7 @@ describe(converterPortableText.deserialize, () => {
|
|
|
412
412
|
},
|
|
413
413
|
],
|
|
414
414
|
markDefs: [],
|
|
415
|
+
level: 1,
|
|
415
416
|
style: 'normal',
|
|
416
417
|
},
|
|
417
418
|
])
|
|
@@ -568,7 +569,7 @@ describe(converterPortableText.deserialize, () => {
|
|
|
568
569
|
{
|
|
569
570
|
_type: 'span',
|
|
570
571
|
text: 'foo',
|
|
571
|
-
marks: ['
|
|
572
|
+
marks: ['k1'],
|
|
572
573
|
},
|
|
573
574
|
{
|
|
574
575
|
_type: 'span',
|
|
@@ -578,7 +579,7 @@ describe(converterPortableText.deserialize, () => {
|
|
|
578
579
|
],
|
|
579
580
|
markDefs: [
|
|
580
581
|
{
|
|
581
|
-
_key: '
|
|
582
|
+
_key: 'k1',
|
|
582
583
|
_type: 'link',
|
|
583
584
|
href: 'https://example.com',
|
|
584
585
|
},
|