@portabletext/editor 1.34.0 → 1.35.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 +57 -118
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/behavior.markdown.cjs +27 -67
- package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
- package/lib/_chunks-cjs/plugin.event-listener.cjs +53 -71
- package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.get-text-before.cjs +5 -7
- package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-active-style.cjs +22 -36
- package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +68 -153
- package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
- package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
- package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js +57 -118
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/behavior.markdown.js +27 -67
- package/lib/_chunks-es/behavior.markdown.js.map +1 -1
- package/lib/_chunks-es/plugin.event-listener.js +53 -71
- package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
- package/lib/_chunks-es/selector.get-text-before.js +5 -7
- package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
- package/lib/_chunks-es/selector.is-active-style.js +22 -36
- package/lib/_chunks-es/selector.is-active-style.js.map +1 -1
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js +68 -153
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
- package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
- package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
- package/lib/behaviors/index.cjs +18 -48
- package/lib/behaviors/index.cjs.map +1 -1
- package/lib/behaviors/index.d.cts +28 -16
- package/lib/behaviors/index.d.ts +28 -16
- package/lib/behaviors/index.js +18 -48
- package/lib/behaviors/index.js.map +1 -1
- package/lib/index.d.cts +132 -71
- package/lib/index.d.ts +132 -71
- package/lib/plugins/index.cjs +182 -186
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +147 -82
- package/lib/plugins/index.d.ts +147 -82
- package/lib/plugins/index.js +182 -186
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.cjs +22 -50
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +9 -200
- package/lib/selectors/index.d.ts +9 -200
- package/lib/selectors/index.js +22 -50
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +15 -7
- package/lib/utils/index.d.ts +15 -7
- package/lib/utils/index.js.map +1 -1
- package/package.json +6 -6
- package/src/behaviors/behavior.code-editor.ts +6 -6
- package/src/behaviors/behavior.core.annotations.ts +5 -4
- package/src/behaviors/behavior.core.block-objects.ts +17 -17
- package/src/behaviors/behavior.core.decorators.ts +12 -8
- package/src/behaviors/behavior.core.insert-break.ts +27 -29
- package/src/behaviors/behavior.core.lists.ts +19 -19
- package/src/behaviors/behavior.decorator-pair.ts +200 -0
- package/src/behaviors/behavior.default.ts +35 -30
- package/src/behaviors/behavior.emoji-picker.ts +12 -12
- package/src/behaviors/behavior.links.ts +7 -7
- package/src/behaviors/behavior.markdown.ts +41 -42
- package/src/behaviors/behavior.types.ts +15 -18
- package/src/behaviors/index.ts +0 -1
- package/src/converters/converter.json.ts +6 -6
- package/src/converters/converter.portable-text.deserialize.test.ts +28 -26
- package/src/converters/converter.portable-text.ts +6 -6
- package/src/converters/converter.text-html.deserialize.test.ts +17 -15
- package/src/converters/converter.text-html.serialize.test.ts +57 -53
- package/src/converters/converter.text-html.ts +14 -10
- package/src/converters/converter.text-plain.test.ts +17 -15
- package/src/converters/converter.text-plain.ts +15 -11
- package/src/converters/converter.types.ts +8 -7
- package/src/editor/editor-machine.ts +6 -1
- package/src/editor/plugins/create-with-event-listeners.ts +0 -5
- package/src/index.ts +3 -3
- package/src/internal-utils/get-text-to-emphasize.ts +29 -7
- package/src/plugins/plugin.decorator-shortcut.ts +235 -0
- package/src/plugins/plugin.markdown.tsx +56 -8
- package/src/plugins/plugin.one-line.tsx +17 -17
- package/src/selectors/selector.get-active-list-item.ts +4 -4
- package/src/selectors/selector.get-active-style.ts +6 -6
- package/src/selectors/selector.get-anchor-block.ts +5 -5
- package/src/selectors/selector.get-anchor-child.ts +5 -5
- package/src/selectors/selector.get-anchor-span.ts +2 -2
- package/src/selectors/selector.get-anchor-text-block.ts +2 -2
- package/src/selectors/selector.get-block-offsets.ts +8 -7
- package/src/selectors/selector.get-caret-word-selection.ts +19 -16
- package/src/selectors/selector.get-next-inline-object.ts +4 -4
- package/src/selectors/selector.get-previous-inline-object.ts +4 -4
- package/src/selectors/selector.get-selected-slice.ts +7 -4
- package/src/selectors/selector.get-selected-spans.ts +9 -9
- package/src/selectors/selector.get-selection-end-point.ts +5 -5
- package/src/selectors/selector.get-selection-start-point.ts +5 -5
- package/src/selectors/selector.get-selection-text.ts +2 -2
- package/src/selectors/selector.get-selection.ts +2 -2
- package/src/selectors/selector.get-text-before.ts +8 -8
- package/src/selectors/selector.get-trimmed-selection.ts +15 -13
- package/src/selectors/selector.get-value.ts +4 -4
- package/src/selectors/selector.is-at-the-end-of-block.ts +6 -3
- package/src/selectors/selector.is-at-the-start-of-block.ts +3 -3
- package/src/selectors/selector.is-overlapping-selection.ts +8 -6
- package/src/selectors/selector.is-selection-collapsed.ts +6 -5
- package/src/selectors/selector.is-selection-expanded.ts +2 -2
- package/src/selectors/selectors.ts +59 -59
- package/src/types/block-offset.ts +9 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/util.block-offset.ts +1 -1
- package/src/utils/util.block-offsets-to-selection.ts +1 -1
- package/src/utils/util.child-selection-point-to-block-offset.ts +1 -1
- package/src/behaviors/behavior.markdown-emphasis.ts +0 -437
|
@@ -3,17 +3,16 @@ import {defineBehavior, raise} from './behavior.types'
|
|
|
3
3
|
|
|
4
4
|
const breakingAtTheEndOfTextBlock = defineBehavior({
|
|
5
5
|
on: 'insert.break',
|
|
6
|
-
guard: ({
|
|
7
|
-
const focusTextBlock = selectors.getFocusTextBlock(
|
|
8
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
6
|
+
guard: ({snapshot}) => {
|
|
7
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
8
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
9
9
|
|
|
10
|
-
if (!context.selection || !focusTextBlock || !selectionCollapsed) {
|
|
10
|
+
if (!snapshot.context.selection || !focusTextBlock || !selectionCollapsed) {
|
|
11
11
|
return false
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const atTheEndOfBlock =
|
|
15
|
-
|
|
16
|
-
})
|
|
14
|
+
const atTheEndOfBlock =
|
|
15
|
+
selectors.isAtTheEndOfBlock(focusTextBlock)(snapshot)
|
|
17
16
|
|
|
18
17
|
const focusListItem = focusTextBlock.node.listItem
|
|
19
18
|
const focusLevel = focusTextBlock.node.level
|
|
@@ -25,16 +24,16 @@ const breakingAtTheEndOfTextBlock = defineBehavior({
|
|
|
25
24
|
return false
|
|
26
25
|
},
|
|
27
26
|
actions: [
|
|
28
|
-
({
|
|
27
|
+
({snapshot}, {focusListItem, focusLevel}) => [
|
|
29
28
|
raise({
|
|
30
29
|
type: 'insert.block',
|
|
31
30
|
block: {
|
|
32
|
-
_type: context.schema.block.name,
|
|
33
|
-
_key: context.keyGenerator(),
|
|
31
|
+
_type: snapshot.context.schema.block.name,
|
|
32
|
+
_key: snapshot.context.keyGenerator(),
|
|
34
33
|
children: [
|
|
35
34
|
{
|
|
36
|
-
_key: context.keyGenerator(),
|
|
37
|
-
_type: context.schema.span.name,
|
|
35
|
+
_key: snapshot.context.keyGenerator(),
|
|
36
|
+
_type: snapshot.context.schema.span.name,
|
|
38
37
|
text: '',
|
|
39
38
|
marks: [],
|
|
40
39
|
},
|
|
@@ -42,7 +41,7 @@ const breakingAtTheEndOfTextBlock = defineBehavior({
|
|
|
42
41
|
markDefs: [],
|
|
43
42
|
listItem: focusListItem,
|
|
44
43
|
level: focusLevel,
|
|
45
|
-
style: context.schema.styles[0]?.value,
|
|
44
|
+
style: snapshot.context.schema.styles[0]?.value,
|
|
46
45
|
},
|
|
47
46
|
placement: 'after',
|
|
48
47
|
}),
|
|
@@ -52,35 +51,34 @@ const breakingAtTheEndOfTextBlock = defineBehavior({
|
|
|
52
51
|
|
|
53
52
|
const breakingAtTheStartOfTextBlock = defineBehavior({
|
|
54
53
|
on: 'insert.break',
|
|
55
|
-
guard: ({
|
|
56
|
-
const focusTextBlock = selectors.getFocusTextBlock(
|
|
57
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
54
|
+
guard: ({snapshot}) => {
|
|
55
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
56
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
58
57
|
|
|
59
|
-
if (!context.selection || !focusTextBlock || !selectionCollapsed) {
|
|
58
|
+
if (!snapshot.context.selection || !focusTextBlock || !selectionCollapsed) {
|
|
60
59
|
return false
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
const focusSpan = selectors.getFocusSpan(
|
|
62
|
+
const focusSpan = selectors.getFocusSpan(snapshot)
|
|
64
63
|
|
|
65
64
|
const focusDecorators = focusSpan?.node.marks?.filter(
|
|
66
65
|
(mark) =>
|
|
67
|
-
context.schema.decorators.some(
|
|
66
|
+
snapshot.context.schema.decorators.some(
|
|
68
67
|
(decorator) => decorator.value === mark,
|
|
69
68
|
) ?? [],
|
|
70
69
|
)
|
|
71
70
|
const focusAnnotations =
|
|
72
71
|
focusSpan?.node.marks?.filter(
|
|
73
72
|
(mark) =>
|
|
74
|
-
!context.schema.decorators.some(
|
|
73
|
+
!snapshot.context.schema.decorators.some(
|
|
75
74
|
(decorator) => decorator.value === mark,
|
|
76
75
|
),
|
|
77
76
|
) ?? []
|
|
78
77
|
const focusListItem = focusTextBlock.node.listItem
|
|
79
78
|
const focusLevel = focusTextBlock.node.level
|
|
80
79
|
|
|
81
|
-
const atTheStartOfBlock =
|
|
82
|
-
|
|
83
|
-
})
|
|
80
|
+
const atTheStartOfBlock =
|
|
81
|
+
selectors.isAtTheStartOfBlock(focusTextBlock)(snapshot)
|
|
84
82
|
|
|
85
83
|
if (atTheStartOfBlock) {
|
|
86
84
|
return {focusAnnotations, focusDecorators, focusListItem, focusLevel}
|
|
@@ -90,25 +88,25 @@ const breakingAtTheStartOfTextBlock = defineBehavior({
|
|
|
90
88
|
},
|
|
91
89
|
actions: [
|
|
92
90
|
(
|
|
93
|
-
{
|
|
91
|
+
{snapshot},
|
|
94
92
|
{focusAnnotations, focusDecorators, focusListItem, focusLevel},
|
|
95
93
|
) => [
|
|
96
94
|
raise({
|
|
97
95
|
type: 'insert.block',
|
|
98
96
|
block: {
|
|
99
|
-
_key: context.keyGenerator(),
|
|
100
|
-
_type: context.schema.block.name,
|
|
97
|
+
_key: snapshot.context.keyGenerator(),
|
|
98
|
+
_type: snapshot.context.schema.block.name,
|
|
101
99
|
children: [
|
|
102
100
|
{
|
|
103
|
-
_key: context.keyGenerator(),
|
|
104
|
-
_type: context.schema.span.name,
|
|
101
|
+
_key: snapshot.context.keyGenerator(),
|
|
102
|
+
_type: snapshot.context.schema.span.name,
|
|
105
103
|
marks: focusAnnotations.length === 0 ? focusDecorators : [],
|
|
106
104
|
text: '',
|
|
107
105
|
},
|
|
108
106
|
],
|
|
109
107
|
listItem: focusListItem,
|
|
110
108
|
level: focusLevel,
|
|
111
|
-
style: context.schema.styles[0]?.value,
|
|
109
|
+
style: snapshot.context.schema.styles[0]?.value,
|
|
112
110
|
},
|
|
113
111
|
placement: 'before',
|
|
114
112
|
}),
|
|
@@ -8,10 +8,10 @@ const MAX_LIST_LEVEL = 10
|
|
|
8
8
|
|
|
9
9
|
const clearListOnBackspace = defineBehavior({
|
|
10
10
|
on: 'delete.backward',
|
|
11
|
-
guard: ({
|
|
12
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
13
|
-
const focusTextBlock = selectors.getFocusTextBlock(
|
|
14
|
-
const focusSpan = selectors.getFocusSpan(
|
|
11
|
+
guard: ({snapshot}) => {
|
|
12
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
13
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
14
|
+
const focusSpan = selectors.getFocusSpan(snapshot)
|
|
15
15
|
|
|
16
16
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
17
17
|
return false
|
|
@@ -19,7 +19,7 @@ const clearListOnBackspace = defineBehavior({
|
|
|
19
19
|
|
|
20
20
|
const atTheBeginningOfBLock =
|
|
21
21
|
focusTextBlock.node.children[0]._key === focusSpan.node._key &&
|
|
22
|
-
context.selection?.focus.offset === 0
|
|
22
|
+
snapshot.context.selection?.focus.offset === 0
|
|
23
23
|
|
|
24
24
|
if (atTheBeginningOfBLock && focusTextBlock.node.level === 1) {
|
|
25
25
|
return {focusTextBlock}
|
|
@@ -40,10 +40,10 @@ const clearListOnBackspace = defineBehavior({
|
|
|
40
40
|
|
|
41
41
|
const unindentListOnBackspace = defineBehavior({
|
|
42
42
|
on: 'delete.backward',
|
|
43
|
-
guard: ({
|
|
44
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
45
|
-
const focusTextBlock = selectors.getFocusTextBlock(
|
|
46
|
-
const focusSpan = selectors.getFocusSpan(
|
|
43
|
+
guard: ({snapshot}) => {
|
|
44
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
45
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
46
|
+
const focusSpan = selectors.getFocusSpan(snapshot)
|
|
47
47
|
|
|
48
48
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
49
49
|
return false
|
|
@@ -51,7 +51,7 @@ const unindentListOnBackspace = defineBehavior({
|
|
|
51
51
|
|
|
52
52
|
const atTheBeginningOfBLock =
|
|
53
53
|
focusTextBlock.node.children[0]._key === focusSpan.node._key &&
|
|
54
|
-
context.selection?.focus.offset === 0
|
|
54
|
+
snapshot.context.selection?.focus.offset === 0
|
|
55
55
|
|
|
56
56
|
if (
|
|
57
57
|
atTheBeginningOfBLock &&
|
|
@@ -76,9 +76,9 @@ const unindentListOnBackspace = defineBehavior({
|
|
|
76
76
|
|
|
77
77
|
const clearListOnEnter = defineBehavior({
|
|
78
78
|
on: 'insert.break',
|
|
79
|
-
guard: ({
|
|
80
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
81
|
-
const focusListBlock = selectors.getFocusListBlock(
|
|
79
|
+
guard: ({snapshot}) => {
|
|
80
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
81
|
+
const focusListBlock = selectors.getFocusListBlock(snapshot)
|
|
82
82
|
|
|
83
83
|
if (
|
|
84
84
|
!selectionCollapsed ||
|
|
@@ -103,15 +103,15 @@ const clearListOnEnter = defineBehavior({
|
|
|
103
103
|
|
|
104
104
|
const indentListOnTab = defineBehavior({
|
|
105
105
|
on: 'key.down',
|
|
106
|
-
guard: ({
|
|
106
|
+
guard: ({snapshot, event}) => {
|
|
107
107
|
const isTab = isHotkey('Tab', event.keyboardEvent)
|
|
108
108
|
|
|
109
109
|
if (!isTab) {
|
|
110
110
|
return false
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
const selectedBlocks = selectors.getSelectedBlocks(
|
|
114
|
-
const guards = createGuards(context)
|
|
113
|
+
const selectedBlocks = selectors.getSelectedBlocks(snapshot)
|
|
114
|
+
const guards = createGuards(snapshot.context)
|
|
115
115
|
const selectedListBlocks = selectedBlocks.flatMap((block) =>
|
|
116
116
|
guards.isListBlock(block.node)
|
|
117
117
|
? [
|
|
@@ -148,15 +148,15 @@ const indentListOnTab = defineBehavior({
|
|
|
148
148
|
|
|
149
149
|
const unindentListOnShiftTab = defineBehavior({
|
|
150
150
|
on: 'key.down',
|
|
151
|
-
guard: ({
|
|
151
|
+
guard: ({snapshot, event}) => {
|
|
152
152
|
const isShiftTab = isHotkey('Shift+Tab', event.keyboardEvent)
|
|
153
153
|
|
|
154
154
|
if (!isShiftTab) {
|
|
155
155
|
return false
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const selectedBlocks = selectors.getSelectedBlocks(
|
|
159
|
-
const guards = createGuards(context)
|
|
158
|
+
const selectedBlocks = selectors.getSelectedBlocks(snapshot)
|
|
159
|
+
const guards = createGuards(snapshot.context)
|
|
160
160
|
const selectedListBlocks = selectedBlocks.flatMap((block) =>
|
|
161
161
|
guards.isListBlock(block.node)
|
|
162
162
|
? [
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {createPairRegex} from '../internal-utils/get-text-to-emphasize'
|
|
2
|
+
import * as selectors from '../selectors'
|
|
3
|
+
import type {BlockOffset} from '../types/block-offset'
|
|
4
|
+
import * as utils from '../utils'
|
|
5
|
+
import {defineBehavior} from './behavior.types'
|
|
6
|
+
|
|
7
|
+
export function createDecoratorPairBehavior(config: {
|
|
8
|
+
decorator: ({schema}: {schema: selectors.EditorSchema}) => string | undefined
|
|
9
|
+
pair: {char: string; amount: number}
|
|
10
|
+
onDecorate: (offset: BlockOffset) => void
|
|
11
|
+
}) {
|
|
12
|
+
if (config.pair.amount < 1) {
|
|
13
|
+
console.warn(
|
|
14
|
+
`The amount of characters in the pair should be greater than 0`,
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const pairRegex = createPairRegex(config.pair.char, config.pair.amount)
|
|
19
|
+
const regEx = new RegExp(`(${pairRegex})$`)
|
|
20
|
+
|
|
21
|
+
return defineBehavior({
|
|
22
|
+
on: 'insert.text',
|
|
23
|
+
guard: ({snapshot, event}) => {
|
|
24
|
+
if (config.pair.amount < 1) {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const decorator = config.decorator({schema: snapshot.context.schema})
|
|
29
|
+
|
|
30
|
+
if (decorator === undefined) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
35
|
+
const selectionStartPoint = selectors.getSelectionStartPoint(snapshot)
|
|
36
|
+
const selectionStartOffset = selectionStartPoint
|
|
37
|
+
? utils.spanSelectionPointToBlockOffset({
|
|
38
|
+
value: snapshot.context.value,
|
|
39
|
+
selectionPoint: selectionStartPoint,
|
|
40
|
+
})
|
|
41
|
+
: undefined
|
|
42
|
+
|
|
43
|
+
if (!focusTextBlock || !selectionStartOffset) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const textBefore = selectors.getBlockTextBefore(snapshot)
|
|
48
|
+
const newText = `${textBefore}${event.text}`
|
|
49
|
+
const textToDecorate = newText.match(regEx)?.at(0)
|
|
50
|
+
|
|
51
|
+
if (textToDecorate === undefined) {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const prefixOffsets = {
|
|
56
|
+
anchor: {
|
|
57
|
+
path: focusTextBlock.path,
|
|
58
|
+
// Example: "foo **bar**".length - "**bar**".length = 4
|
|
59
|
+
offset: newText.length - textToDecorate.length,
|
|
60
|
+
},
|
|
61
|
+
focus: {
|
|
62
|
+
path: focusTextBlock.path,
|
|
63
|
+
// Example: "foo **bar**".length - "**bar**".length + "*".length * 2 = 6
|
|
64
|
+
offset:
|
|
65
|
+
newText.length -
|
|
66
|
+
textToDecorate.length +
|
|
67
|
+
config.pair.char.length * config.pair.amount,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const suffixOffsets = {
|
|
72
|
+
anchor: {
|
|
73
|
+
path: focusTextBlock.path,
|
|
74
|
+
// Example: "foo **bar*|" (10) + "*".length - 2 = 9
|
|
75
|
+
offset:
|
|
76
|
+
selectionStartOffset.offset +
|
|
77
|
+
event.text.length -
|
|
78
|
+
config.pair.char.length * config.pair.amount,
|
|
79
|
+
},
|
|
80
|
+
focus: {
|
|
81
|
+
path: focusTextBlock.path,
|
|
82
|
+
// Example: "foo **bar*|" (10) + "*".length = 11
|
|
83
|
+
offset: selectionStartOffset.offset + event.text.length,
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// If the prefix is more than one character, then we need to check if
|
|
88
|
+
// there is an inline object inside it
|
|
89
|
+
if (prefixOffsets.focus.offset - prefixOffsets.anchor.offset > 1) {
|
|
90
|
+
const prefixSelection = utils.blockOffsetsToSelection({
|
|
91
|
+
value: snapshot.context.value,
|
|
92
|
+
offsets: prefixOffsets,
|
|
93
|
+
})
|
|
94
|
+
const inlineObjectBeforePrefixFocus = selectors.getPreviousInlineObject(
|
|
95
|
+
{
|
|
96
|
+
context: {
|
|
97
|
+
...snapshot.context,
|
|
98
|
+
selection: prefixSelection
|
|
99
|
+
? {
|
|
100
|
+
anchor: prefixSelection.focus,
|
|
101
|
+
focus: prefixSelection.focus,
|
|
102
|
+
}
|
|
103
|
+
: null,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
)
|
|
107
|
+
const inlineObjectBeforePrefixFocusOffset =
|
|
108
|
+
inlineObjectBeforePrefixFocus
|
|
109
|
+
? utils.childSelectionPointToBlockOffset({
|
|
110
|
+
value: snapshot.context.value,
|
|
111
|
+
selectionPoint: {
|
|
112
|
+
path: inlineObjectBeforePrefixFocus.path,
|
|
113
|
+
offset: 0,
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
: undefined
|
|
117
|
+
|
|
118
|
+
if (
|
|
119
|
+
inlineObjectBeforePrefixFocusOffset &&
|
|
120
|
+
inlineObjectBeforePrefixFocusOffset.offset >
|
|
121
|
+
prefixOffsets.anchor.offset &&
|
|
122
|
+
inlineObjectBeforePrefixFocusOffset.offset <
|
|
123
|
+
prefixOffsets.focus.offset
|
|
124
|
+
) {
|
|
125
|
+
return false
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// If the suffix is more than one character, then we need to check if
|
|
130
|
+
// there is an inline object inside it
|
|
131
|
+
if (suffixOffsets.focus.offset - suffixOffsets.anchor.offset > 1) {
|
|
132
|
+
const previousInlineObject = selectors.getPreviousInlineObject(snapshot)
|
|
133
|
+
const previousInlineObjectOffset = previousInlineObject
|
|
134
|
+
? utils.childSelectionPointToBlockOffset({
|
|
135
|
+
value: snapshot.context.value,
|
|
136
|
+
selectionPoint: {
|
|
137
|
+
path: previousInlineObject.path,
|
|
138
|
+
offset: 0,
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
: undefined
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
previousInlineObjectOffset &&
|
|
145
|
+
previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&
|
|
146
|
+
previousInlineObjectOffset.offset < suffixOffsets.focus.offset
|
|
147
|
+
) {
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
prefixOffsets,
|
|
154
|
+
suffixOffsets,
|
|
155
|
+
decorator,
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
actions: [
|
|
159
|
+
// Insert the text as usual in its own undo step
|
|
160
|
+
({event}) => [event],
|
|
161
|
+
(_, {prefixOffsets, suffixOffsets, decorator}) => [
|
|
162
|
+
// Decorate the text between the prefix and suffix
|
|
163
|
+
{
|
|
164
|
+
type: 'decorator.add',
|
|
165
|
+
decorator,
|
|
166
|
+
offsets: {
|
|
167
|
+
anchor: prefixOffsets.focus,
|
|
168
|
+
focus: suffixOffsets.anchor,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
// Delete the suffix
|
|
172
|
+
{
|
|
173
|
+
type: 'delete.text',
|
|
174
|
+
...suffixOffsets,
|
|
175
|
+
},
|
|
176
|
+
// Delete the prefix
|
|
177
|
+
{
|
|
178
|
+
type: 'delete.text',
|
|
179
|
+
...prefixOffsets,
|
|
180
|
+
},
|
|
181
|
+
// Toggle the decorator off so the next inserted text isn't emphasized
|
|
182
|
+
{
|
|
183
|
+
type: 'decorator.remove',
|
|
184
|
+
decorator,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
type: 'effect',
|
|
188
|
+
effect: () => {
|
|
189
|
+
config.onDecorate({
|
|
190
|
+
...suffixOffsets.anchor,
|
|
191
|
+
offset:
|
|
192
|
+
suffixOffsets.anchor.offset -
|
|
193
|
+
(prefixOffsets.focus.offset - prefixOffsets.anchor.offset),
|
|
194
|
+
})
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
],
|
|
199
|
+
})
|
|
200
|
+
}
|
|
@@ -3,8 +3,8 @@ import {defineBehavior, raise} from './behavior.types'
|
|
|
3
3
|
|
|
4
4
|
const toggleAnnotationOff = defineBehavior({
|
|
5
5
|
on: 'annotation.toggle',
|
|
6
|
-
guard: ({
|
|
7
|
-
selectors.isActiveAnnotation(event.annotation.name)(
|
|
6
|
+
guard: ({snapshot, event}) =>
|
|
7
|
+
selectors.isActiveAnnotation(event.annotation.name)(snapshot),
|
|
8
8
|
actions: [
|
|
9
9
|
({event}) => [
|
|
10
10
|
raise({type: 'annotation.remove', annotation: event.annotation}),
|
|
@@ -14,8 +14,8 @@ const toggleAnnotationOff = defineBehavior({
|
|
|
14
14
|
|
|
15
15
|
const toggleAnnotationOn = defineBehavior({
|
|
16
16
|
on: 'annotation.toggle',
|
|
17
|
-
guard: ({
|
|
18
|
-
!selectors.isActiveAnnotation(event.annotation.name)(
|
|
17
|
+
guard: ({snapshot, event}) =>
|
|
18
|
+
!selectors.isActiveAnnotation(event.annotation.name)(snapshot),
|
|
19
19
|
actions: [
|
|
20
20
|
({event}) => [
|
|
21
21
|
raise({type: 'annotation.add', annotation: event.annotation}),
|
|
@@ -25,8 +25,8 @@ const toggleAnnotationOn = defineBehavior({
|
|
|
25
25
|
|
|
26
26
|
const toggleDecoratorOff = defineBehavior({
|
|
27
27
|
on: 'decorator.toggle',
|
|
28
|
-
guard: ({
|
|
29
|
-
selectors.isActiveDecorator(event.decorator)(
|
|
28
|
+
guard: ({snapshot, event}) =>
|
|
29
|
+
selectors.isActiveDecorator(event.decorator)(snapshot),
|
|
30
30
|
actions: [
|
|
31
31
|
({event}) => [
|
|
32
32
|
raise({type: 'decorator.remove', decorator: event.decorator}),
|
|
@@ -36,8 +36,8 @@ const toggleDecoratorOff = defineBehavior({
|
|
|
36
36
|
|
|
37
37
|
const toggleDecoratorOn = defineBehavior({
|
|
38
38
|
on: 'decorator.toggle',
|
|
39
|
-
guard: ({
|
|
40
|
-
!selectors.isActiveDecorator(event.decorator)(
|
|
39
|
+
guard: ({snapshot, event}) =>
|
|
40
|
+
!selectors.isActiveDecorator(event.decorator)(snapshot),
|
|
41
41
|
actions: [
|
|
42
42
|
({event}) => [raise({type: 'decorator.add', decorator: event.decorator})],
|
|
43
43
|
],
|
|
@@ -45,8 +45,8 @@ const toggleDecoratorOn = defineBehavior({
|
|
|
45
45
|
|
|
46
46
|
const toggleListItemOff = defineBehavior({
|
|
47
47
|
on: 'list item.toggle',
|
|
48
|
-
guard: ({
|
|
49
|
-
selectors.isActiveListItem(event.listItem)(
|
|
48
|
+
guard: ({snapshot, event}) =>
|
|
49
|
+
selectors.isActiveListItem(event.listItem)(snapshot),
|
|
50
50
|
actions: [
|
|
51
51
|
({event}) => [
|
|
52
52
|
raise({
|
|
@@ -59,8 +59,8 @@ const toggleListItemOff = defineBehavior({
|
|
|
59
59
|
|
|
60
60
|
const toggleListItemOn = defineBehavior({
|
|
61
61
|
on: 'list item.toggle',
|
|
62
|
-
guard: ({
|
|
63
|
-
!selectors.isActiveListItem(event.listItem)(
|
|
62
|
+
guard: ({snapshot, event}) =>
|
|
63
|
+
!selectors.isActiveListItem(event.listItem)(snapshot),
|
|
64
64
|
actions: [
|
|
65
65
|
({event}) => [
|
|
66
66
|
raise({
|
|
@@ -73,30 +73,35 @@ const toggleListItemOn = defineBehavior({
|
|
|
73
73
|
|
|
74
74
|
const toggleStyleOff = defineBehavior({
|
|
75
75
|
on: 'style.toggle',
|
|
76
|
-
guard: ({
|
|
76
|
+
guard: ({snapshot, event}) => selectors.isActiveStyle(event.style)(snapshot),
|
|
77
77
|
actions: [({event}) => [raise({type: 'style.remove', style: event.style})]],
|
|
78
78
|
})
|
|
79
79
|
|
|
80
80
|
const toggleStyleOn = defineBehavior({
|
|
81
81
|
on: 'style.toggle',
|
|
82
|
-
guard: ({
|
|
82
|
+
guard: ({snapshot, event}) => !selectors.isActiveStyle(event.style)(snapshot),
|
|
83
83
|
actions: [({event}) => [raise({type: 'style.add', style: event.style})]],
|
|
84
84
|
})
|
|
85
85
|
|
|
86
86
|
const raiseDeserializationSuccessOrFailure = defineBehavior({
|
|
87
87
|
on: 'deserialize',
|
|
88
|
-
guard: ({
|
|
89
|
-
const deserializeEvents = context.converters.flatMap(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
guard: ({snapshot, event}) => {
|
|
89
|
+
const deserializeEvents = snapshot.context.converters.flatMap(
|
|
90
|
+
(converter) => {
|
|
91
|
+
const data = event.dataTransfer.getData(converter.mimeType)
|
|
92
|
+
|
|
93
|
+
if (!data) {
|
|
94
|
+
return []
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return [
|
|
98
|
+
converter.deserialize({
|
|
99
|
+
snapshot,
|
|
100
|
+
event: {type: 'deserialize', data},
|
|
101
|
+
}),
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
)
|
|
100
105
|
|
|
101
106
|
const firstSuccess = deserializeEvents.find(
|
|
102
107
|
(deserializeEvent) => deserializeEvent.type === 'deserialization.success',
|
|
@@ -142,13 +147,13 @@ const raiseInsertBlocks = defineBehavior({
|
|
|
142
147
|
|
|
143
148
|
const raiseSerializationSuccessOrFailure = defineBehavior({
|
|
144
149
|
on: 'serialize',
|
|
145
|
-
guard: ({
|
|
146
|
-
if (context.converters.length === 0) {
|
|
150
|
+
guard: ({snapshot, event}) => {
|
|
151
|
+
if (snapshot.context.converters.length === 0) {
|
|
147
152
|
return false
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
const serializeEvents = context.converters.map((converter) =>
|
|
151
|
-
converter.serialize({
|
|
155
|
+
const serializeEvents = snapshot.context.converters.map((converter) =>
|
|
156
|
+
converter.serialize({snapshot, event}),
|
|
152
157
|
)
|
|
153
158
|
|
|
154
159
|
if (serializeEvents.length === 0) {
|
|
@@ -39,7 +39,7 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
39
39
|
return [
|
|
40
40
|
defineBehavior({
|
|
41
41
|
on: 'insert.text',
|
|
42
|
-
guard: ({
|
|
42
|
+
guard: ({snapshot, event}) => {
|
|
43
43
|
if (event.text === ':') {
|
|
44
44
|
return false
|
|
45
45
|
}
|
|
@@ -50,8 +50,8 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
50
50
|
return {emojis: []}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const focusBlock = selectors.getFocusTextBlock(
|
|
54
|
-
const textBefore = selectors.getBlockTextBefore(
|
|
53
|
+
const focusBlock = selectors.getFocusTextBlock(snapshot)
|
|
54
|
+
const textBefore = selectors.getBlockTextBefore(snapshot)
|
|
55
55
|
const emojiKeyword = `${textBefore}${event.text}`.match(
|
|
56
56
|
incompleteEmojiRegEx,
|
|
57
57
|
)?.[1]
|
|
@@ -80,7 +80,7 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
80
80
|
}),
|
|
81
81
|
defineBehavior({
|
|
82
82
|
on: 'insert.text',
|
|
83
|
-
guard: ({
|
|
83
|
+
guard: ({snapshot, event}) => {
|
|
84
84
|
const isColon = event.text === ':'
|
|
85
85
|
|
|
86
86
|
if (!isColon) {
|
|
@@ -94,8 +94,8 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
94
94
|
? config.parseMatch({match: matches[selectedIndex]})
|
|
95
95
|
: undefined
|
|
96
96
|
|
|
97
|
-
const focusBlock = selectors.getFocusTextBlock(
|
|
98
|
-
const textBefore = selectors.getBlockTextBefore(
|
|
97
|
+
const focusBlock = selectors.getFocusTextBlock(snapshot)
|
|
98
|
+
const textBefore = selectors.getBlockTextBefore(snapshot)
|
|
99
99
|
const emojiKeyword = `${textBefore}:`.match(emojiRegEx)?.[1]
|
|
100
100
|
|
|
101
101
|
if (!focusBlock || emojiKeyword === undefined) {
|
|
@@ -149,7 +149,7 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
149
149
|
}),
|
|
150
150
|
defineBehavior({
|
|
151
151
|
on: 'key.down',
|
|
152
|
-
guard: ({
|
|
152
|
+
guard: ({snapshot, event}) => {
|
|
153
153
|
const matches = emojiPickerActor.getSnapshot().context.matches
|
|
154
154
|
|
|
155
155
|
if (matches.length === 0) {
|
|
@@ -177,8 +177,8 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
177
177
|
return false
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
const focusBlock = selectors.getFocusTextBlock(
|
|
181
|
-
const textBefore = selectors.getBlockTextBefore(
|
|
180
|
+
const focusBlock = selectors.getFocusTextBlock(snapshot)
|
|
181
|
+
const textBefore = selectors.getBlockTextBefore(snapshot)
|
|
182
182
|
const emojiKeyword = textBefore.match(incompleteEmojiRegEx)?.[1]
|
|
183
183
|
|
|
184
184
|
if (!focusBlock || emojiKeyword === undefined) {
|
|
@@ -286,7 +286,7 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
286
286
|
}),
|
|
287
287
|
defineBehavior({
|
|
288
288
|
on: 'delete.backward',
|
|
289
|
-
guard: ({
|
|
289
|
+
guard: ({snapshot, event}) => {
|
|
290
290
|
if (event.unit !== 'character') {
|
|
291
291
|
return false
|
|
292
292
|
}
|
|
@@ -297,8 +297,8 @@ export function createEmojiPickerBehaviors<TEmojiMatch>(
|
|
|
297
297
|
return false
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
const focusBlock = selectors.getFocusTextBlock(
|
|
301
|
-
const textBefore = selectors.getBlockTextBefore(
|
|
300
|
+
const focusBlock = selectors.getFocusTextBlock(snapshot)
|
|
301
|
+
const textBefore = selectors.getBlockTextBefore(snapshot)
|
|
302
302
|
const emojiKeyword = textBefore
|
|
303
303
|
.slice(0, textBefore.length - 1)
|
|
304
304
|
.match(incompleteEmojiRegEx)?.[1]
|
|
@@ -19,13 +19,13 @@ export type LinkBehaviorsConfig = {
|
|
|
19
19
|
export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
20
20
|
const pasteLinkOnSelection = defineBehavior({
|
|
21
21
|
on: 'paste',
|
|
22
|
-
guard: ({
|
|
23
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
22
|
+
guard: ({snapshot, event}) => {
|
|
23
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
24
24
|
const text = event.data.getData('text/plain')
|
|
25
25
|
const url = looksLikeUrl(text) ? text : undefined
|
|
26
26
|
const annotation =
|
|
27
27
|
url !== undefined
|
|
28
|
-
? config.linkAnnotation?.({url, schema: context.schema})
|
|
28
|
+
? config.linkAnnotation?.({url, schema: snapshot.context.schema})
|
|
29
29
|
: undefined
|
|
30
30
|
|
|
31
31
|
if (annotation && !selectionCollapsed) {
|
|
@@ -45,9 +45,9 @@ export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
|
45
45
|
})
|
|
46
46
|
const pasteLinkAtCaret = defineBehavior({
|
|
47
47
|
on: 'paste',
|
|
48
|
-
guard: ({
|
|
49
|
-
const focusSpan = selectors.getFocusSpan(
|
|
50
|
-
const selectionCollapsed = selectors.isSelectionCollapsed(
|
|
48
|
+
guard: ({snapshot, event}) => {
|
|
49
|
+
const focusSpan = selectors.getFocusSpan(snapshot)
|
|
50
|
+
const selectionCollapsed = selectors.isSelectionCollapsed(snapshot)
|
|
51
51
|
|
|
52
52
|
if (!focusSpan || !selectionCollapsed) {
|
|
53
53
|
return false
|
|
@@ -57,7 +57,7 @@ export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
|
57
57
|
const url = looksLikeUrl(text) ? text : undefined
|
|
58
58
|
const annotation =
|
|
59
59
|
url !== undefined
|
|
60
|
-
? config.linkAnnotation?.({url, schema: context.schema})
|
|
60
|
+
? config.linkAnnotation?.({url, schema: snapshot.context.schema})
|
|
61
61
|
: undefined
|
|
62
62
|
|
|
63
63
|
if (url && annotation && selectionCollapsed) {
|