@portabletext/editor 1.13.0 → 1.14.1
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/README.md +1 -1
- package/lib/_chunks-cjs/selector.get-text-before.cjs +320 -0
- package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -0
- package/lib/_chunks-es/selector.get-text-before.js +321 -0
- package/lib/_chunks-es/selector.get-text-before.js.map +1 -0
- package/lib/{index.esm.js → index.cjs} +1703 -1431
- package/lib/index.cjs.map +1 -0
- package/lib/{index.d.mts → index.d.cts} +4038 -313
- package/lib/index.d.ts +4038 -313
- package/lib/index.js +1724 -1407
- package/lib/index.js.map +1 -1
- package/lib/selectors/index.cjs +35 -0
- package/lib/selectors/index.cjs.map +1 -0
- package/lib/selectors/index.d.cts +243 -0
- package/lib/selectors/index.d.ts +243 -0
- package/lib/selectors/index.js +36 -0
- package/lib/selectors/index.js.map +1 -0
- package/package.json +21 -13
- package/src/editor/Editable.tsx +1 -1
- package/src/editor/PortableTextEditor.tsx +19 -4
- package/src/editor/__tests__/handleClick.test.tsx +4 -4
- package/src/editor/behavior/behavior.action.insert-block-object.ts +1 -1
- package/src/editor/behavior/behavior.action.insert-break.ts +3 -3
- package/src/editor/behavior/behavior.action.insert-inline-object.ts +58 -0
- package/src/editor/behavior/behavior.action.insert-span.ts +1 -1
- package/src/editor/behavior/behavior.action.list-item.ts +100 -0
- package/src/editor/behavior/behavior.action.style.ts +108 -0
- package/src/editor/behavior/behavior.action.text-block.set.ts +25 -0
- package/src/editor/behavior/behavior.action.text-block.unset.ts +17 -0
- package/src/editor/behavior/behavior.actions.ts +178 -109
- package/src/editor/behavior/behavior.code-editor.ts +30 -40
- package/src/editor/behavior/behavior.core.block-objects.ts +26 -26
- package/src/editor/behavior/behavior.core.decorators.ts +9 -6
- package/src/editor/behavior/behavior.core.lists.ts +139 -17
- package/src/editor/behavior/behavior.core.ts +5 -2
- package/src/editor/behavior/behavior.guards.ts +28 -0
- package/src/editor/behavior/behavior.links.ts +7 -7
- package/src/editor/behavior/behavior.markdown.ts +68 -79
- package/src/editor/behavior/behavior.types.ts +86 -60
- package/src/editor/{use-editor.ts → create-editor.ts} +13 -8
- package/src/editor/editor-event-listener.tsx +2 -2
- package/src/editor/editor-machine.ts +54 -15
- package/src/editor/editor-provider.tsx +5 -5
- package/src/editor/editor-selector.ts +49 -0
- package/src/editor/editor-snapshot.ts +22 -0
- package/src/editor/get-value.ts +11 -0
- package/src/editor/plugins/create-with-event-listeners.ts +93 -5
- package/src/editor/plugins/createWithEditableAPI.ts +69 -20
- package/src/editor/plugins/createWithHotKeys.ts +0 -54
- package/src/editor/plugins/createWithPortableTextBlockStyle.ts +1 -55
- package/src/editor/plugins/with-plugins.ts +4 -8
- package/src/editor/{behavior/behavior.utils.block-offset.test.ts → utils/utils.block-offset.test.ts} +1 -1
- package/src/editor/{behavior/behavior.utils.block-offset.ts → utils/utils.block-offset.ts} +1 -8
- package/src/editor/{behavior/behavior.utils.reverse-selection.ts → utils/utils.reverse-selection.ts} +3 -5
- package/src/editor/utils/utils.ts +21 -0
- package/src/index.ts +13 -13
- package/src/selectors/index.ts +15 -0
- package/src/selectors/selector.get-active-list-item.ts +37 -0
- package/src/{editor/behavior/behavior.utils.get-selection-text.ts → selectors/selector.get-selection-text.ts} +10 -15
- package/src/selectors/selector.get-text-before.ts +41 -0
- package/src/selectors/selectors.ts +329 -0
- package/src/types/editor.ts +0 -60
- package/src/utils/is-hotkey.test.ts +2 -0
- package/src/utils/operationToPatches.ts +5 -0
- package/src/utils/paths.ts +4 -11
- package/src/utils/ranges.ts +3 -3
- package/lib/index.esm.js.map +0 -1
- package/lib/index.mjs +0 -7541
- package/lib/index.mjs.map +0 -1
- package/src/editor/behavior/behavior.utils.ts +0 -218
- package/src/editor/behavior/behavior.utilts.get-text-before.ts +0 -31
- package/src/editor/plugins/createWithPortableTextLists.ts +0 -172
- /package/src/editor/{behavior/behavior.utils.get-start-point.ts → utils/utils.get-start-point.ts} +0 -0
- /package/src/editor/{behavior/behavior.utils.is-keyed-segment.ts → utils/utils.is-keyed-segment.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Editor, Path, Text as SlateText, Transforms
|
|
1
|
+
import {Editor, Path, Text as SlateText, Transforms} from 'slate'
|
|
2
2
|
import type {
|
|
3
3
|
PortableTextMemberSchemaTypes,
|
|
4
4
|
PortableTextSlateEditor,
|
|
@@ -50,60 +50,6 @@ export function createWithPortableTextBlockStyle(
|
|
|
50
50
|
normalizeNode(nodeEntry)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
editor.pteHasBlockStyle = (style: string): boolean => {
|
|
54
|
-
if (!editor.selection) {
|
|
55
|
-
return false
|
|
56
|
-
}
|
|
57
|
-
const selectedBlocks = [
|
|
58
|
-
...Editor.nodes(editor, {
|
|
59
|
-
at: editor.selection,
|
|
60
|
-
match: (node) => editor.isTextBlock(node) && node.style === style,
|
|
61
|
-
}),
|
|
62
|
-
]
|
|
63
|
-
if (selectedBlocks.length > 0) {
|
|
64
|
-
return true
|
|
65
|
-
}
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
editor.pteToggleBlockStyle = (blockStyle: string): void => {
|
|
70
|
-
if (!editor.selection) {
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
const selectedBlocks = [
|
|
74
|
-
...Editor.nodes(editor, {
|
|
75
|
-
at: editor.selection,
|
|
76
|
-
match: (node) => editor.isTextBlock(node),
|
|
77
|
-
}),
|
|
78
|
-
]
|
|
79
|
-
selectedBlocks.forEach(([node, path]) => {
|
|
80
|
-
if (editor.isTextBlock(node) && node.style === blockStyle) {
|
|
81
|
-
debug(`Unsetting block style '${blockStyle}'`)
|
|
82
|
-
Transforms.setNodes(
|
|
83
|
-
editor,
|
|
84
|
-
{...node, style: defaultStyle} as Partial<Node>,
|
|
85
|
-
{
|
|
86
|
-
at: path,
|
|
87
|
-
},
|
|
88
|
-
)
|
|
89
|
-
} else {
|
|
90
|
-
if (blockStyle) {
|
|
91
|
-
debug(`Setting style '${blockStyle}'`)
|
|
92
|
-
} else {
|
|
93
|
-
debug('Setting default style', defaultStyle)
|
|
94
|
-
}
|
|
95
|
-
Transforms.setNodes(
|
|
96
|
-
editor,
|
|
97
|
-
{
|
|
98
|
-
...node,
|
|
99
|
-
style: blockStyle || defaultStyle,
|
|
100
|
-
} as Partial<Node>,
|
|
101
|
-
{at: path},
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
})
|
|
105
|
-
editor.onChange()
|
|
106
|
-
}
|
|
107
53
|
return editor
|
|
108
54
|
}
|
|
109
55
|
}
|
|
@@ -8,7 +8,6 @@ import {createWithObjectKeys} from './createWithObjectKeys'
|
|
|
8
8
|
import {createWithPatches} from './createWithPatches'
|
|
9
9
|
import {createWithPlaceholderBlock} from './createWithPlaceholderBlock'
|
|
10
10
|
import {createWithPortableTextBlockStyle} from './createWithPortableTextBlockStyle'
|
|
11
|
-
import {createWithPortableTextLists} from './createWithPortableTextLists'
|
|
12
11
|
import {createWithPortableTextMarkModel} from './createWithPortableTextMarkModel'
|
|
13
12
|
import {createWithPortableTextSelections} from './createWithPortableTextSelections'
|
|
14
13
|
import {createWithSchemaTypes} from './createWithSchemaTypes'
|
|
@@ -46,7 +45,6 @@ export const withPlugins = <T extends Editor>(
|
|
|
46
45
|
subscriptions: options.subscriptions,
|
|
47
46
|
})
|
|
48
47
|
const withMaxBlocks = createWithMaxBlocks(editorActor)
|
|
49
|
-
const withPortableTextLists = createWithPortableTextLists(schemaTypes)
|
|
50
48
|
const withUndoRedo = createWithUndoRedo({
|
|
51
49
|
editorActor,
|
|
52
50
|
blockSchemaType: schemaTypes.block,
|
|
@@ -82,12 +80,10 @@ export const withPlugins = <T extends Editor>(
|
|
|
82
80
|
withObjectKeys(
|
|
83
81
|
withPortableTextMarkModel(
|
|
84
82
|
withPortableTextBlockStyle(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
withUndoRedo(withPatches(withPortableTextSelections(e))),
|
|
90
|
-
),
|
|
83
|
+
withPlaceholderBlock(
|
|
84
|
+
withUtils(
|
|
85
|
+
withMaxBlocks(
|
|
86
|
+
withUndoRedo(withPatches(withPortableTextSelections(e))),
|
|
91
87
|
),
|
|
92
88
|
),
|
|
93
89
|
),
|
package/src/editor/{behavior/behavior.utils.block-offset.test.ts → utils/utils.block-offset.test.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type {PortableTextBlock} from '@sanity/types'
|
|
2
2
|
import {expect, test} from 'vitest'
|
|
3
|
-
import {blockOffsetToSpanSelectionPoint} from './
|
|
3
|
+
import {blockOffsetToSpanSelectionPoint} from './utils.block-offset'
|
|
4
4
|
|
|
5
5
|
test(blockOffsetToSpanSelectionPoint.name, () => {
|
|
6
6
|
const value: Array<PortableTextBlock> = [
|
|
@@ -4,14 +4,7 @@ import {
|
|
|
4
4
|
type KeyedSegment,
|
|
5
5
|
type PortableTextBlock,
|
|
6
6
|
} from '@sanity/types'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @alpha
|
|
10
|
-
*/
|
|
11
|
-
export type BlockOffset = {
|
|
12
|
-
path: [KeyedSegment]
|
|
13
|
-
offset: number
|
|
14
|
-
}
|
|
7
|
+
import type {BlockOffset} from '../behavior/behavior.types'
|
|
15
8
|
|
|
16
9
|
export function blockOffsetToSpanSelectionPoint({
|
|
17
10
|
value,
|
package/src/editor/{behavior/behavior.utils.reverse-selection.ts → utils/utils.reverse-selection.ts}
RENAMED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type {EditorSelection} from '../../types/editor'
|
|
2
2
|
|
|
3
|
-
export function reverseSelection(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
3
|
+
export function reverseSelection(
|
|
4
|
+
selection: NonNullable<EditorSelection>,
|
|
5
|
+
): NonNullable<EditorSelection> {
|
|
8
6
|
if (selection.backward) {
|
|
9
7
|
return {
|
|
10
8
|
anchor: selection.focus,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isPortableTextSpan,
|
|
3
|
+
isPortableTextTextBlock,
|
|
4
|
+
type PortableTextBlock,
|
|
5
|
+
type PortableTextTextBlock,
|
|
6
|
+
} from '@sanity/types'
|
|
7
|
+
|
|
8
|
+
export function isEmptyTextBlock(block: PortableTextBlock) {
|
|
9
|
+
if (!isPortableTextTextBlock(block)) {
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const onlyText = block.children.every(isPortableTextSpan)
|
|
14
|
+
const blockText = getTextBlockText(block)
|
|
15
|
+
|
|
16
|
+
return onlyText && blockText === ''
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getTextBlockText(block: PortableTextTextBlock) {
|
|
20
|
+
return block.children.map((child) => child.text ?? '').join('')
|
|
21
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export type {Patch} from '@portabletext/patches'
|
|
2
2
|
export type {PortableTextBlock, PortableTextChild} from '@sanity/types'
|
|
3
|
+
export {
|
|
4
|
+
createCodeEditorBehaviors,
|
|
5
|
+
type CodeEditorBehaviorsConfig,
|
|
6
|
+
} from './editor/behavior/behavior.code-editor'
|
|
3
7
|
export {coreBehavior, coreBehaviors} from './editor/behavior/behavior.core'
|
|
4
8
|
export {
|
|
5
9
|
createLinkBehaviors,
|
|
@@ -9,22 +13,18 @@ export {
|
|
|
9
13
|
createMarkdownBehaviors,
|
|
10
14
|
type MarkdownBehaviorsConfig,
|
|
11
15
|
} from './editor/behavior/behavior.markdown'
|
|
12
|
-
export {
|
|
13
|
-
createCodeEditorBehaviors,
|
|
14
|
-
type CodeEditorBehaviorsConfig,
|
|
15
|
-
} from './editor/behavior/behavior.code-editor'
|
|
16
16
|
export {
|
|
17
17
|
defineBehavior,
|
|
18
18
|
type Behavior,
|
|
19
19
|
type BehaviorActionIntend,
|
|
20
20
|
type BehaviorActionIntendSet,
|
|
21
|
-
type BehaviorContext,
|
|
22
21
|
type BehaviorEvent,
|
|
23
22
|
type BehaviorGuard,
|
|
24
23
|
type OmitFromUnion,
|
|
25
24
|
type PickFromUnion,
|
|
25
|
+
type BlockOffset,
|
|
26
26
|
} from './editor/behavior/behavior.types'
|
|
27
|
-
export type {
|
|
27
|
+
export type {Editor, EditorConfig, EditorEvent} from './editor/create-editor'
|
|
28
28
|
export type {SlateEditor} from './editor/create-slate-editor'
|
|
29
29
|
export {
|
|
30
30
|
defineSchema,
|
|
@@ -46,20 +46,20 @@ export {
|
|
|
46
46
|
} from './editor/editor-machine'
|
|
47
47
|
export {
|
|
48
48
|
EditorProvider,
|
|
49
|
-
|
|
49
|
+
useEditor,
|
|
50
50
|
type EditorProviderProps,
|
|
51
51
|
} from './editor/editor-provider'
|
|
52
|
+
export {
|
|
53
|
+
useEditorSelector,
|
|
54
|
+
type EditorSelector,
|
|
55
|
+
type EditorSelectorSnapshot,
|
|
56
|
+
} from './editor/editor-selector'
|
|
57
|
+
export type {EditorContext, EditorSnapshot} from './editor/editor-snapshot'
|
|
52
58
|
export {usePortableTextEditor} from './editor/hooks/usePortableTextEditor'
|
|
53
59
|
export {usePortableTextEditorSelection} from './editor/hooks/usePortableTextEditorSelection'
|
|
54
60
|
export {defaultKeyGenerator as keyGenerator} from './editor/key-generator'
|
|
55
61
|
export type {AddedAnnotationPaths} from './editor/plugins/createWithEditableAPI'
|
|
56
62
|
export {PortableTextEditor} from './editor/PortableTextEditor'
|
|
57
63
|
export type {PortableTextEditorProps} from './editor/PortableTextEditor'
|
|
58
|
-
export {
|
|
59
|
-
useEditor,
|
|
60
|
-
type Editor,
|
|
61
|
-
type EditorConfig,
|
|
62
|
-
type EditorEvent,
|
|
63
|
-
} from './editor/use-editor'
|
|
64
64
|
export * from './types/editor'
|
|
65
65
|
export type {HotkeyOptions} from './types/options'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
EditorSelector,
|
|
3
|
+
EditorSelectorSnapshot,
|
|
4
|
+
} from '../editor/editor-selector'
|
|
5
|
+
export type {EditorContext, EditorSnapshot} from '../editor/editor-snapshot'
|
|
6
|
+
export type {
|
|
7
|
+
EditorSelection,
|
|
8
|
+
EditorSelectionPoint,
|
|
9
|
+
PortableTextMemberSchemaTypes,
|
|
10
|
+
} from '../types/editor'
|
|
11
|
+
|
|
12
|
+
export {getActiveListItem} from './selector.get-active-list-item'
|
|
13
|
+
export {getSelectionText} from './selector.get-selection-text'
|
|
14
|
+
export {getBlockTextBefore} from './selector.get-text-before'
|
|
15
|
+
export * from './selectors'
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {PortableTextListBlock} from '@sanity/types'
|
|
2
|
+
import {createGuards} from '../editor/behavior/behavior.guards'
|
|
3
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
4
|
+
import {getSelectedBlocks} from './selectors'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @alpha
|
|
8
|
+
*/
|
|
9
|
+
export const getActiveListItem: EditorSelector<
|
|
10
|
+
PortableTextListBlock['listItem'] | undefined
|
|
11
|
+
> = ({context}) => {
|
|
12
|
+
if (!context.selection) {
|
|
13
|
+
return undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const guards = createGuards(context)
|
|
17
|
+
const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)
|
|
18
|
+
const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)
|
|
19
|
+
|
|
20
|
+
const firstTextBlock = selectedTextBlocks.at(0)
|
|
21
|
+
|
|
22
|
+
if (!firstTextBlock) {
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const firstListItem = firstTextBlock.listItem
|
|
27
|
+
|
|
28
|
+
if (!firstListItem) {
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (selectedTextBlocks.every((block) => block.listItem === firstListItem)) {
|
|
33
|
+
return firstListItem
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from '@sanity/types'
|
|
6
|
-
import type {EditorSelection} from '../../types/editor'
|
|
7
|
-
import {isKeyedSegment} from './behavior.utils.is-keyed-segment'
|
|
8
|
-
import {reverseSelection} from './behavior.utils.reverse-selection'
|
|
1
|
+
import {isPortableTextSpan, isPortableTextTextBlock} from '@sanity/types'
|
|
2
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
3
|
+
import {isKeyedSegment} from '../editor/utils/utils.is-keyed-segment'
|
|
4
|
+
import {reverseSelection} from '../editor/utils/utils.reverse-selection'
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
value: Array<PortableTextBlock>
|
|
15
|
-
selection: NonNullable<EditorSelection>
|
|
16
|
-
}): string {
|
|
6
|
+
/**
|
|
7
|
+
* @alpha
|
|
8
|
+
*/
|
|
9
|
+
export const getSelectionText: EditorSelector<string> = ({context}) => {
|
|
17
10
|
let text = ''
|
|
18
11
|
|
|
12
|
+
const {value, selection} = context
|
|
13
|
+
|
|
19
14
|
if (!value || !selection) {
|
|
20
15
|
return text
|
|
21
16
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
2
|
+
import {getStartPoint} from '../editor/utils/utils.get-start-point'
|
|
3
|
+
import {isKeyedSegment} from '../editor/utils/utils.is-keyed-segment'
|
|
4
|
+
import {reverseSelection} from '../editor/utils/utils.reverse-selection'
|
|
5
|
+
import {getSelectionText} from './selector.get-selection-text'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @alpha
|
|
9
|
+
*/
|
|
10
|
+
export const getBlockTextBefore: EditorSelector<string> = ({context}) => {
|
|
11
|
+
if (!context.selection) {
|
|
12
|
+
return ''
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const selection = context.selection.backward
|
|
16
|
+
? reverseSelection(context.selection)
|
|
17
|
+
: context.selection
|
|
18
|
+
const point = selection.anchor
|
|
19
|
+
const key = isKeyedSegment(point.path[0]) ? point.path[0]._key : undefined
|
|
20
|
+
|
|
21
|
+
const block = key
|
|
22
|
+
? context.value.find((block) => block._key === key)
|
|
23
|
+
: undefined
|
|
24
|
+
|
|
25
|
+
if (!block) {
|
|
26
|
+
return ''
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const startOfBlock = getStartPoint({node: block, path: [{_key: block._key}]})
|
|
30
|
+
|
|
31
|
+
return getSelectionText({
|
|
32
|
+
context: {
|
|
33
|
+
...context,
|
|
34
|
+
value: context.value,
|
|
35
|
+
selection: {
|
|
36
|
+
anchor: startOfBlock,
|
|
37
|
+
focus: point,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isKeySegment,
|
|
3
|
+
isPortableTextSpan,
|
|
4
|
+
isPortableTextTextBlock,
|
|
5
|
+
type KeyedSegment,
|
|
6
|
+
type PortableTextBlock,
|
|
7
|
+
type PortableTextListBlock,
|
|
8
|
+
type PortableTextObject,
|
|
9
|
+
type PortableTextSpan,
|
|
10
|
+
type PortableTextTextBlock,
|
|
11
|
+
} from '@sanity/types'
|
|
12
|
+
import {createGuards} from '../editor/behavior/behavior.guards'
|
|
13
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @alpha
|
|
17
|
+
*/
|
|
18
|
+
export const selectionIsCollapsed: EditorSelector<boolean> = ({context}) => {
|
|
19
|
+
return (
|
|
20
|
+
JSON.stringify(context.selection?.anchor.path) ===
|
|
21
|
+
JSON.stringify(context.selection?.focus.path) &&
|
|
22
|
+
context.selection?.anchor.offset === context.selection?.focus.offset
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @alpha
|
|
28
|
+
*/
|
|
29
|
+
export const getFocusBlock: EditorSelector<
|
|
30
|
+
{node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
31
|
+
> = ({context}) => {
|
|
32
|
+
const key = context.selection
|
|
33
|
+
? isKeySegment(context.selection.focus.path[0])
|
|
34
|
+
? context.selection.focus.path[0]._key
|
|
35
|
+
: undefined
|
|
36
|
+
: undefined
|
|
37
|
+
|
|
38
|
+
const node = key
|
|
39
|
+
? context.value.find((block) => block._key === key)
|
|
40
|
+
: undefined
|
|
41
|
+
|
|
42
|
+
return node && key ? {node, path: [{_key: key}]} : undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @alpha
|
|
47
|
+
*/
|
|
48
|
+
export const getFocusListBlock: EditorSelector<
|
|
49
|
+
{node: PortableTextListBlock; path: [KeyedSegment]} | undefined
|
|
50
|
+
> = ({context}) => {
|
|
51
|
+
const guards = createGuards(context)
|
|
52
|
+
const focusBlock = getFocusBlock({context})
|
|
53
|
+
|
|
54
|
+
return focusBlock && guards.isListBlock(focusBlock.node)
|
|
55
|
+
? {node: focusBlock.node, path: focusBlock.path}
|
|
56
|
+
: undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @alpha
|
|
61
|
+
*/
|
|
62
|
+
export const getFocusTextBlock: EditorSelector<
|
|
63
|
+
{node: PortableTextTextBlock; path: [KeyedSegment]} | undefined
|
|
64
|
+
> = ({context}) => {
|
|
65
|
+
const focusBlock = getFocusBlock({context})
|
|
66
|
+
|
|
67
|
+
return focusBlock && isPortableTextTextBlock(focusBlock.node)
|
|
68
|
+
? {node: focusBlock.node, path: focusBlock.path}
|
|
69
|
+
: undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @alpha
|
|
74
|
+
*/
|
|
75
|
+
export const getFocusBlockObject: EditorSelector<
|
|
76
|
+
{node: PortableTextObject; path: [KeyedSegment]} | undefined
|
|
77
|
+
> = ({context}) => {
|
|
78
|
+
const focusBlock = getFocusBlock({context})
|
|
79
|
+
|
|
80
|
+
return focusBlock && !isPortableTextTextBlock(focusBlock.node)
|
|
81
|
+
? {node: focusBlock.node, path: focusBlock.path}
|
|
82
|
+
: undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @alpha
|
|
87
|
+
*/
|
|
88
|
+
export const getFocusChild: EditorSelector<
|
|
89
|
+
| {
|
|
90
|
+
node: PortableTextObject | PortableTextSpan
|
|
91
|
+
path: [KeyedSegment, 'children', KeyedSegment]
|
|
92
|
+
}
|
|
93
|
+
| undefined
|
|
94
|
+
> = ({context}) => {
|
|
95
|
+
const focusBlock = getFocusTextBlock({context})
|
|
96
|
+
|
|
97
|
+
if (!focusBlock) {
|
|
98
|
+
return undefined
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const key = context.selection
|
|
102
|
+
? isKeySegment(context.selection.focus.path[2])
|
|
103
|
+
? context.selection.focus.path[2]._key
|
|
104
|
+
: undefined
|
|
105
|
+
: undefined
|
|
106
|
+
|
|
107
|
+
const node = key
|
|
108
|
+
? focusBlock.node.children.find((span) => span._key === key)
|
|
109
|
+
: undefined
|
|
110
|
+
|
|
111
|
+
return node && key
|
|
112
|
+
? {node, path: [...focusBlock.path, 'children', {_key: key}]}
|
|
113
|
+
: undefined
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @alpha
|
|
118
|
+
*/
|
|
119
|
+
export const getFocusSpan: EditorSelector<
|
|
120
|
+
| {node: PortableTextSpan; path: [KeyedSegment, 'children', KeyedSegment]}
|
|
121
|
+
| undefined
|
|
122
|
+
> = ({context}) => {
|
|
123
|
+
const focusChild = getFocusChild({context})
|
|
124
|
+
|
|
125
|
+
return focusChild && isPortableTextSpan(focusChild.node)
|
|
126
|
+
? {node: focusChild.node, path: focusChild.path}
|
|
127
|
+
: undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @alpha
|
|
132
|
+
*/
|
|
133
|
+
export const getFirstBlock: EditorSelector<
|
|
134
|
+
{node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
135
|
+
> = ({context}) => {
|
|
136
|
+
const node = context.value[0]
|
|
137
|
+
|
|
138
|
+
return node ? {node, path: [{_key: node._key}]} : undefined
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @alpha
|
|
143
|
+
*/
|
|
144
|
+
export const getLastBlock: EditorSelector<
|
|
145
|
+
{node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
146
|
+
> = ({context}) => {
|
|
147
|
+
const node = context.value[context.value.length - 1]
|
|
148
|
+
? context.value[context.value.length - 1]
|
|
149
|
+
: undefined
|
|
150
|
+
|
|
151
|
+
return node ? {node, path: [{_key: node._key}]} : undefined
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @alpha
|
|
156
|
+
*/
|
|
157
|
+
export const getSelectedBlocks: EditorSelector<
|
|
158
|
+
Array<{node: PortableTextBlock; path: [KeyedSegment]}>
|
|
159
|
+
> = ({context}) => {
|
|
160
|
+
if (!context.selection) {
|
|
161
|
+
return []
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const selectedBlocks: Array<{node: PortableTextBlock; path: [KeyedSegment]}> =
|
|
165
|
+
[]
|
|
166
|
+
const startKey = context.selection.backward
|
|
167
|
+
? isKeySegment(context.selection.focus.path[0])
|
|
168
|
+
? context.selection.focus.path[0]._key
|
|
169
|
+
: undefined
|
|
170
|
+
: isKeySegment(context.selection.anchor.path[0])
|
|
171
|
+
? context.selection.anchor.path[0]._key
|
|
172
|
+
: undefined
|
|
173
|
+
const endKey = context.selection.backward
|
|
174
|
+
? isKeySegment(context.selection.anchor.path[0])
|
|
175
|
+
? context.selection.anchor.path[0]._key
|
|
176
|
+
: undefined
|
|
177
|
+
: isKeySegment(context.selection.focus.path[0])
|
|
178
|
+
? context.selection.focus.path[0]._key
|
|
179
|
+
: undefined
|
|
180
|
+
|
|
181
|
+
if (!startKey || !endKey) {
|
|
182
|
+
return selectedBlocks
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const block of context.value) {
|
|
186
|
+
if (block._key === startKey) {
|
|
187
|
+
selectedBlocks.push({node: block, path: [{_key: block._key}]})
|
|
188
|
+
|
|
189
|
+
if (startKey === endKey) {
|
|
190
|
+
break
|
|
191
|
+
}
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (block._key === endKey) {
|
|
196
|
+
selectedBlocks.push({node: block, path: [{_key: block._key}]})
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (selectedBlocks.length > 0) {
|
|
201
|
+
selectedBlocks.push({node: block, path: [{_key: block._key}]})
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return selectedBlocks
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @alpha
|
|
210
|
+
*/
|
|
211
|
+
export const getSelectionStartBlock: EditorSelector<
|
|
212
|
+
| {
|
|
213
|
+
node: PortableTextBlock
|
|
214
|
+
path: [KeyedSegment]
|
|
215
|
+
}
|
|
216
|
+
| undefined
|
|
217
|
+
> = ({context}) => {
|
|
218
|
+
if (!context.selection) {
|
|
219
|
+
return undefined
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const key = context.selection.backward
|
|
223
|
+
? isKeySegment(context.selection.focus.path[0])
|
|
224
|
+
? context.selection.focus.path[0]._key
|
|
225
|
+
: undefined
|
|
226
|
+
: isKeySegment(context.selection.anchor.path[0])
|
|
227
|
+
? context.selection.anchor.path[0]._key
|
|
228
|
+
: undefined
|
|
229
|
+
|
|
230
|
+
const node = key
|
|
231
|
+
? context.value.find((block) => block._key === key)
|
|
232
|
+
: undefined
|
|
233
|
+
|
|
234
|
+
return node && key ? {node, path: [{_key: key}]} : undefined
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @alpha
|
|
239
|
+
*/
|
|
240
|
+
export const getSelectionEndBlock: EditorSelector<
|
|
241
|
+
| {
|
|
242
|
+
node: PortableTextBlock
|
|
243
|
+
path: [KeyedSegment]
|
|
244
|
+
}
|
|
245
|
+
| undefined
|
|
246
|
+
> = ({context}) => {
|
|
247
|
+
if (!context.selection) {
|
|
248
|
+
return undefined
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const key = context.selection.backward
|
|
252
|
+
? isKeySegment(context.selection.anchor.path[0])
|
|
253
|
+
? context.selection.anchor.path[0]._key
|
|
254
|
+
: undefined
|
|
255
|
+
: isKeySegment(context.selection.focus.path[0])
|
|
256
|
+
? context.selection.focus.path[0]._key
|
|
257
|
+
: undefined
|
|
258
|
+
|
|
259
|
+
const node = key
|
|
260
|
+
? context.value.find((block) => block._key === key)
|
|
261
|
+
: undefined
|
|
262
|
+
|
|
263
|
+
return node && key ? {node, path: [{_key: key}]} : undefined
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @alpha
|
|
268
|
+
*/
|
|
269
|
+
export const getPreviousBlock: EditorSelector<
|
|
270
|
+
{node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
271
|
+
> = ({context}) => {
|
|
272
|
+
let previousBlock: {node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
273
|
+
const selectionStartBlock = getSelectionStartBlock({context})
|
|
274
|
+
|
|
275
|
+
if (!selectionStartBlock) {
|
|
276
|
+
return undefined
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
let foundSelectionStartBlock = false
|
|
280
|
+
|
|
281
|
+
for (const block of context.value) {
|
|
282
|
+
if (block._key === selectionStartBlock.node._key) {
|
|
283
|
+
foundSelectionStartBlock = true
|
|
284
|
+
break
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
previousBlock = {node: block, path: [{_key: block._key}]}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (foundSelectionStartBlock && previousBlock) {
|
|
291
|
+
return previousBlock
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return undefined
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @alpha
|
|
299
|
+
*/
|
|
300
|
+
export const getNextBlock: EditorSelector<
|
|
301
|
+
{node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
302
|
+
> = ({context}) => {
|
|
303
|
+
let nextBlock: {node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
304
|
+
const selectionEndBlock = getSelectionEndBlock({context})
|
|
305
|
+
|
|
306
|
+
if (!selectionEndBlock) {
|
|
307
|
+
return undefined
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let foundSelectionEndBlock = false
|
|
311
|
+
|
|
312
|
+
for (const block of context.value) {
|
|
313
|
+
if (block._key === selectionEndBlock.node._key) {
|
|
314
|
+
foundSelectionEndBlock = true
|
|
315
|
+
continue
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (foundSelectionEndBlock) {
|
|
319
|
+
nextBlock = {node: block, path: [{_key: block._key}]}
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (foundSelectionEndBlock && nextBlock) {
|
|
325
|
+
return nextBlock
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return undefined
|
|
329
|
+
}
|