@portabletext/editor 1.32.0 → 1.33.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/lib/_chunks-cjs/behavior.core.cjs +4 -4
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/behavior.markdown.cjs +19 -11
- package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
- package/lib/_chunks-cjs/plugin.event-listener.cjs +127 -88
- package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs +97 -0
- package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs.map +1 -0
- package/lib/_chunks-cjs/{parse-blocks.cjs → util.block-offsets-to-selection.cjs} +21 -2
- package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -0
- package/lib/_chunks-cjs/util.reverse-selection.cjs +11 -0
- package/lib/_chunks-cjs/util.reverse-selection.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js +1 -1
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/behavior.markdown.js +18 -11
- package/lib/_chunks-es/behavior.markdown.js.map +1 -1
- package/lib/_chunks-es/plugin.event-listener.js +127 -87
- package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
- package/lib/_chunks-es/selector.get-trimmed-selection.js +100 -0
- package/lib/_chunks-es/selector.get-trimmed-selection.js.map +1 -0
- package/lib/_chunks-es/{parse-blocks.js → util.block-offsets-to-selection.js} +21 -1
- package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -0
- package/lib/_chunks-es/util.reverse-selection.js +11 -0
- package/lib/_chunks-es/util.reverse-selection.js.map +1 -1
- package/lib/behaviors/index.d.cts +1 -0
- package/lib/behaviors/index.d.ts +1 -0
- package/lib/index.d.cts +60 -0
- package/lib/index.d.ts +60 -0
- package/lib/plugins/index.cjs +302 -3
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +74 -1
- package/lib/plugins/index.d.ts +74 -1
- package/lib/plugins/index.js +307 -4
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.cjs +51 -1
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +67 -0
- package/lib/selectors/index.d.ts +67 -0
- package/lib/selectors/index.js +53 -2
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.cjs +5 -4
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +16 -0
- package/lib/utils/index.d.ts +16 -0
- package/lib/utils/index.js +4 -3
- package/package.json +7 -7
- package/src/behavior-actions/behavior.action.decorator.add.ts +161 -0
- package/src/behavior-actions/behavior.action.delete.text.ts +54 -0
- package/src/behavior-actions/behavior.actions.ts +5 -43
- package/src/behaviors/behavior.markdown-emphasis.ts +392 -0
- package/src/behaviors/behavior.markdown.ts +11 -4
- package/src/behaviors/behavior.types.ts +1 -0
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +2 -97
- package/src/internal-utils/get-text-to-emphasize.test.ts +36 -0
- package/src/internal-utils/get-text-to-emphasize.ts +18 -0
- package/src/plugins/plugin.markdown.tsx +11 -1
- package/src/selectors/index.ts +5 -0
- package/src/selectors/selector.get-anchor-block.ts +22 -0
- package/src/selectors/selector.get-anchor-child.ts +36 -0
- package/src/selectors/selector.get-anchor-span.ts +18 -0
- package/src/selectors/selector.get-anchor-text-block.ts +20 -0
- package/src/selectors/selector.get-trimmed-selection.test.ts +658 -0
- package/src/selectors/selector.get-trimmed-selection.ts +175 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/util.block-offsets-to-selection.ts +36 -0
- package/lib/_chunks-cjs/parse-blocks.cjs.map +0 -1
- package/lib/_chunks-cjs/util.is-empty-text-block.cjs +0 -14
- package/lib/_chunks-cjs/util.is-empty-text-block.cjs.map +0 -1
- package/lib/_chunks-es/parse-blocks.js.map +0 -1
- package/lib/_chunks-es/util.is-empty-text-block.js +0 -15
- package/lib/_chunks-es/util.is-empty-text-block.js.map +0 -1
|
@@ -86,6 +86,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
86
86
|
return false
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
const previousInlineObject = selectors.getPreviousInlineObject({context})
|
|
89
90
|
const blockOffset = spanSelectionPointToBlockOffset({
|
|
90
91
|
value: context.value,
|
|
91
92
|
selectionPoint: {
|
|
@@ -98,7 +99,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
98
99
|
},
|
|
99
100
|
})
|
|
100
101
|
|
|
101
|
-
if (!blockOffset) {
|
|
102
|
+
if (previousInlineObject || !blockOffset) {
|
|
102
103
|
return false
|
|
103
104
|
}
|
|
104
105
|
|
|
@@ -173,6 +174,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
173
174
|
return false
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
const previousInlineObject = selectors.getPreviousInlineObject({context})
|
|
176
178
|
const textBefore = getBlockTextBefore({context})
|
|
177
179
|
const hrBlockOffsets = {
|
|
178
180
|
anchor: {
|
|
@@ -185,7 +187,10 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
185
187
|
},
|
|
186
188
|
}
|
|
187
189
|
|
|
188
|
-
if (
|
|
190
|
+
if (
|
|
191
|
+
!previousInlineObject &&
|
|
192
|
+
textBefore === `${hrCharacter}${hrCharacter}`
|
|
193
|
+
) {
|
|
189
194
|
return {hrObject, focusBlock, hrCharacter, hrBlockOffsets}
|
|
190
195
|
}
|
|
191
196
|
|
|
@@ -290,6 +295,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
290
295
|
return false
|
|
291
296
|
}
|
|
292
297
|
|
|
298
|
+
const previousInlineObject = selectors.getPreviousInlineObject({context})
|
|
293
299
|
const blockText = getTextBlockText(focusTextBlock.node)
|
|
294
300
|
const markdownHeadingSearch = /^#+/.exec(blockText)
|
|
295
301
|
const level = markdownHeadingSearch
|
|
@@ -297,7 +303,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
297
303
|
: undefined
|
|
298
304
|
const caretAtTheEndOfHeading = blockOffset.offset === level
|
|
299
305
|
|
|
300
|
-
if (!caretAtTheEndOfHeading) {
|
|
306
|
+
if (previousInlineObject || !caretAtTheEndOfHeading) {
|
|
301
307
|
return false
|
|
302
308
|
}
|
|
303
309
|
|
|
@@ -397,6 +403,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
397
403
|
return false
|
|
398
404
|
}
|
|
399
405
|
|
|
406
|
+
const previousInlineObject = selectors.getPreviousInlineObject({context})
|
|
400
407
|
const blockOffset = spanSelectionPointToBlockOffset({
|
|
401
408
|
value: context.value,
|
|
402
409
|
selectionPoint: {
|
|
@@ -409,7 +416,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
409
416
|
},
|
|
410
417
|
})
|
|
411
418
|
|
|
412
|
-
if (!blockOffset) {
|
|
419
|
+
if (previousInlineObject || !blockOffset) {
|
|
413
420
|
return false
|
|
414
421
|
}
|
|
415
422
|
|
|
@@ -8,6 +8,7 @@ import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
|
|
|
8
8
|
import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
|
|
9
9
|
import {isEqual, uniq} from 'lodash'
|
|
10
10
|
import {Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
|
|
11
|
+
import {decoratorAddActionImplementation} from '../../behavior-actions/behavior.action.decorator.add'
|
|
11
12
|
import type {BehaviorActionImplementation} from '../../behavior-actions/behavior.actions'
|
|
12
13
|
import {debugWithName} from '../../internal-utils/debug'
|
|
13
14
|
import {getNextSpan, getPreviousSpan} from '../../internal-utils/sibling-utils'
|
|
@@ -657,102 +658,6 @@ export function createWithPortableTextMarkModel(
|
|
|
657
658
|
}
|
|
658
659
|
}
|
|
659
660
|
|
|
660
|
-
export const addDecoratorActionImplementation: BehaviorActionImplementation<
|
|
661
|
-
'decorator.add'
|
|
662
|
-
> = ({action}) => {
|
|
663
|
-
const editor = action.editor
|
|
664
|
-
const mark = action.decorator
|
|
665
|
-
|
|
666
|
-
if (editor.selection) {
|
|
667
|
-
if (Range.isExpanded(editor.selection)) {
|
|
668
|
-
// Split if needed
|
|
669
|
-
Transforms.setNodes(
|
|
670
|
-
editor,
|
|
671
|
-
{},
|
|
672
|
-
{match: Text.isText, split: true, hanging: true},
|
|
673
|
-
)
|
|
674
|
-
// Use new selection
|
|
675
|
-
const splitTextNodes = Range.isRange(editor.selection)
|
|
676
|
-
? [
|
|
677
|
-
...Editor.nodes(editor, {
|
|
678
|
-
at: editor.selection,
|
|
679
|
-
match: Text.isText,
|
|
680
|
-
}),
|
|
681
|
-
]
|
|
682
|
-
: []
|
|
683
|
-
const shouldRemoveMark =
|
|
684
|
-
splitTextNodes.length > 1 &&
|
|
685
|
-
splitTextNodes.every((node) => node[0].marks?.includes(mark))
|
|
686
|
-
|
|
687
|
-
if (shouldRemoveMark) {
|
|
688
|
-
editor.removeMark(mark)
|
|
689
|
-
} else {
|
|
690
|
-
splitTextNodes.forEach(([node, path]) => {
|
|
691
|
-
const marks = [
|
|
692
|
-
...(Array.isArray(node.marks) ? node.marks : []).filter(
|
|
693
|
-
(eMark: string) => eMark !== mark,
|
|
694
|
-
),
|
|
695
|
-
mark,
|
|
696
|
-
]
|
|
697
|
-
Transforms.setNodes(
|
|
698
|
-
editor,
|
|
699
|
-
{marks},
|
|
700
|
-
{at: path, match: Text.isText, split: true, hanging: true},
|
|
701
|
-
)
|
|
702
|
-
})
|
|
703
|
-
}
|
|
704
|
-
} else {
|
|
705
|
-
const [block, blockPath] = Editor.node(editor, editor.selection, {
|
|
706
|
-
depth: 1,
|
|
707
|
-
})
|
|
708
|
-
const lonelyEmptySpan =
|
|
709
|
-
editor.isTextBlock(block) &&
|
|
710
|
-
block.children.length === 1 &&
|
|
711
|
-
editor.isTextSpan(block.children[0]) &&
|
|
712
|
-
block.children[0].text === ''
|
|
713
|
-
? block.children[0]
|
|
714
|
-
: undefined
|
|
715
|
-
|
|
716
|
-
if (lonelyEmptySpan) {
|
|
717
|
-
const existingMarks = lonelyEmptySpan.marks ?? []
|
|
718
|
-
const existingMarksWithoutDecorator = existingMarks.filter(
|
|
719
|
-
(existingMark) => existingMark !== mark,
|
|
720
|
-
)
|
|
721
|
-
|
|
722
|
-
Transforms.setNodes(
|
|
723
|
-
editor,
|
|
724
|
-
{
|
|
725
|
-
marks:
|
|
726
|
-
existingMarks.length === existingMarksWithoutDecorator.length
|
|
727
|
-
? [...existingMarks, mark]
|
|
728
|
-
: existingMarksWithoutDecorator,
|
|
729
|
-
},
|
|
730
|
-
{
|
|
731
|
-
at: blockPath,
|
|
732
|
-
match: (node) => editor.isTextSpan(node),
|
|
733
|
-
},
|
|
734
|
-
)
|
|
735
|
-
} else {
|
|
736
|
-
const existingMarks: string[] =
|
|
737
|
-
{
|
|
738
|
-
...(Editor.marks(editor) || {}),
|
|
739
|
-
}.marks || []
|
|
740
|
-
const marks = {
|
|
741
|
-
...(Editor.marks(editor) || {}),
|
|
742
|
-
marks: [...existingMarks, mark],
|
|
743
|
-
}
|
|
744
|
-
editor.marks = marks as Text
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
if (editor.selection) {
|
|
749
|
-
// Reselect
|
|
750
|
-
const selection = editor.selection
|
|
751
|
-
editor.selection = {...selection}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
661
|
export const removeDecoratorActionImplementation: BehaviorActionImplementation<
|
|
757
662
|
'decorator.remove'
|
|
758
663
|
> = ({action}) => {
|
|
@@ -892,7 +797,7 @@ export const toggleDecoratorActionImplementation: BehaviorActionImplementation<
|
|
|
892
797
|
},
|
|
893
798
|
})
|
|
894
799
|
} else {
|
|
895
|
-
|
|
800
|
+
decoratorAddActionImplementation({
|
|
896
801
|
context,
|
|
897
802
|
action: {
|
|
898
803
|
type: 'decorator.add',
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {expect, test} from 'vitest'
|
|
2
|
+
import {getTextToBold, getTextToItalic} from './get-text-to-emphasize'
|
|
3
|
+
|
|
4
|
+
test(getTextToItalic.name, () => {
|
|
5
|
+
expect(getTextToItalic('Hello *world*')).toBe('*world*')
|
|
6
|
+
expect(getTextToItalic('Hello _world_')).toBe('_world_')
|
|
7
|
+
expect(getTextToItalic('*Hello*world*')).toBe('*world*')
|
|
8
|
+
expect(getTextToItalic('_Hello_world_')).toBe('_world_')
|
|
9
|
+
|
|
10
|
+
expect(getTextToItalic('Hello *world')).toBe(undefined)
|
|
11
|
+
expect(getTextToItalic('Hello world*')).toBe(undefined)
|
|
12
|
+
expect(getTextToItalic('Hello *world* *')).toBe(undefined)
|
|
13
|
+
|
|
14
|
+
expect(getTextToItalic('_Hello*world_')).toBe('_Hello*world_')
|
|
15
|
+
expect(getTextToItalic('*Hello_world*')).toBe('*Hello_world*')
|
|
16
|
+
|
|
17
|
+
expect(getTextToItalic('*hello\nworld*')).toBe(undefined)
|
|
18
|
+
expect(getTextToItalic('_hello\nworld_')).toBe(undefined)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test(getTextToBold.name, () => {
|
|
22
|
+
expect(getTextToBold('Hello **world**')).toBe('**world**')
|
|
23
|
+
expect(getTextToBold('Hello __world__')).toBe('__world__')
|
|
24
|
+
expect(getTextToBold('**Hello**world**')).toBe('**world**')
|
|
25
|
+
expect(getTextToBold('__Hello__world__')).toBe('__world__')
|
|
26
|
+
|
|
27
|
+
expect(getTextToBold('Hello **world')).toBe(undefined)
|
|
28
|
+
expect(getTextToBold('Hello world**')).toBe(undefined)
|
|
29
|
+
expect(getTextToBold('Hello **world** **')).toBe(undefined)
|
|
30
|
+
|
|
31
|
+
expect(getTextToBold('__Hello**world__')).toBe('__Hello**world__')
|
|
32
|
+
expect(getTextToBold('**Hello__world**')).toBe('**Hello__world**')
|
|
33
|
+
|
|
34
|
+
expect(getTextToBold('**hello\nworld**')).toBe(undefined)
|
|
35
|
+
expect(getTextToBold('__hello\nworld__')).toBe(undefined)
|
|
36
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const asteriskPairRegex = '(?<!\\*)\\*(?!\\s)([^*\\n]+?)(?<!\\s)\\*(?!\\*)'
|
|
2
|
+
const underscorePairRegex = '(?<!_)_(?!\\s)([^_\\n]+?)(?<!\\s)_(?!_)'
|
|
3
|
+
const italicRegex = new RegExp(`(${asteriskPairRegex}|${underscorePairRegex})$`)
|
|
4
|
+
|
|
5
|
+
const doubleAsteriskPairRegex =
|
|
6
|
+
'(?<!\\*)\\*\\*(?!\\s)([^*\\n]+?)(?<!\\s)\\*\\*(?!\\*)'
|
|
7
|
+
const doubleUnderscorePairRegex = '(?<!_)__(?!\\s)([^_\\n]+?)(?<!\\s)__(?!_)'
|
|
8
|
+
const boldRegex = new RegExp(
|
|
9
|
+
`(${doubleAsteriskPairRegex}|${doubleUnderscorePairRegex})$`,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
export function getTextToItalic(text: string) {
|
|
13
|
+
return text.match(italicRegex)?.at(0)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getTextToBold(text: string) {
|
|
17
|
+
return text.match(boldRegex)?.at(0)
|
|
18
|
+
}
|
|
@@ -3,12 +3,17 @@ import {
|
|
|
3
3
|
createMarkdownBehaviors,
|
|
4
4
|
type MarkdownBehaviorsConfig,
|
|
5
5
|
} from '../behaviors/behavior.markdown'
|
|
6
|
+
import {
|
|
7
|
+
useMarkdownEmphasisBehaviors,
|
|
8
|
+
type MarkdownEmphasisBehaviorsConfig,
|
|
9
|
+
} from '../behaviors/behavior.markdown-emphasis'
|
|
6
10
|
import {useEditor} from '../editor/editor-provider'
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* @beta
|
|
10
14
|
*/
|
|
11
|
-
export type MarkdownPluginConfig = MarkdownBehaviorsConfig
|
|
15
|
+
export type MarkdownPluginConfig = MarkdownBehaviorsConfig &
|
|
16
|
+
MarkdownEmphasisBehaviorsConfig
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* @beta
|
|
@@ -25,6 +30,10 @@ export type MarkdownPluginConfig = MarkdownBehaviorsConfig
|
|
|
25
30
|
* <EditorProvider>
|
|
26
31
|
* <MarkdownPlugin
|
|
27
32
|
* config={{
|
|
33
|
+
* boldDecorator: ({schema}) =>
|
|
34
|
+
* schema.decorators.find((decorator) => decorator.value === 'strong')?.value,
|
|
35
|
+
* italicDecorator: ({schema}) =>
|
|
36
|
+
* schema.decorators.find((decorator) => decorator.value === 'em')?.value,
|
|
28
37
|
* horizontalRuleObject: ({schema}) => {
|
|
29
38
|
* const name = schema.blockObjects.find(
|
|
30
39
|
* (object) => object.name === 'break',
|
|
@@ -51,6 +60,7 @@ export type MarkdownPluginConfig = MarkdownBehaviorsConfig
|
|
|
51
60
|
*/
|
|
52
61
|
export function MarkdownPlugin(props: {config: MarkdownPluginConfig}) {
|
|
53
62
|
const editor = useEditor()
|
|
63
|
+
useMarkdownEmphasisBehaviors({config: props.config})
|
|
54
64
|
|
|
55
65
|
useEffect(() => {
|
|
56
66
|
const behaviors = createMarkdownBehaviors(props.config)
|
package/src/selectors/index.ts
CHANGED
|
@@ -9,6 +9,10 @@ export type {
|
|
|
9
9
|
export {getActiveAnnotations} from './selector.get-active-annotations'
|
|
10
10
|
export {getActiveListItem} from './selector.get-active-list-item'
|
|
11
11
|
export {getActiveStyle} from './selector.get-active-style'
|
|
12
|
+
export {getAnchorBlock} from './selector.get-anchor-block'
|
|
13
|
+
export {getAnchorChild} from './selector.get-anchor-child'
|
|
14
|
+
export {getAnchorSpan} from './selector.get-anchor-span'
|
|
15
|
+
export {getAnchorTextBlock} from './selector.get-anchor-text-block'
|
|
12
16
|
export {getBlockOffsets} from './selector.get-block-offsets'
|
|
13
17
|
export {getCaretWordSelection} from './selector.get-caret-word-selection'
|
|
14
18
|
export {getNextInlineObject} from './selector.get-next-inline-object'
|
|
@@ -20,6 +24,7 @@ export {getSelectionEndPoint} from './selector.get-selection-end-point'
|
|
|
20
24
|
export {getSelectionStartPoint} from './selector.get-selection-start-point'
|
|
21
25
|
export {getSelectionText} from './selector.get-selection-text'
|
|
22
26
|
export {getBlockTextBefore} from './selector.get-text-before'
|
|
27
|
+
export {getTrimmedSelection} from './selector.get-trimmed-selection'
|
|
23
28
|
export {getValue} from './selector.get-value'
|
|
24
29
|
export {isActiveAnnotation} from './selector.is-active-annotation'
|
|
25
30
|
export {isActiveDecorator} from './selector.is-active-decorator'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type {KeyedSegment, PortableTextBlock} from '@sanity/types'
|
|
2
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
3
|
+
import {isKeyedSegment} from '../utils'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export const getAnchorBlock: EditorSelector<
|
|
9
|
+
{node: PortableTextBlock; path: [KeyedSegment]} | undefined
|
|
10
|
+
> = ({context}) => {
|
|
11
|
+
const key = context.selection
|
|
12
|
+
? isKeyedSegment(context.selection.anchor.path[0])
|
|
13
|
+
? context.selection.anchor.path[0]._key
|
|
14
|
+
: undefined
|
|
15
|
+
: undefined
|
|
16
|
+
|
|
17
|
+
const node = key
|
|
18
|
+
? context.value.find((block) => block._key === key)
|
|
19
|
+
: undefined
|
|
20
|
+
|
|
21
|
+
return node && key ? {node, path: [{_key: key}]} : undefined
|
|
22
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type {KeyedSegment} from '@portabletext/patches'
|
|
2
|
+
import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
|
|
3
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
4
|
+
import {isKeyedSegment} from '../utils'
|
|
5
|
+
import {getAnchorTextBlock} from './selector.get-anchor-text-block'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export const getAnchorChild: EditorSelector<
|
|
11
|
+
| {
|
|
12
|
+
node: PortableTextObject | PortableTextSpan
|
|
13
|
+
path: [KeyedSegment, 'children', KeyedSegment]
|
|
14
|
+
}
|
|
15
|
+
| undefined
|
|
16
|
+
> = ({context}) => {
|
|
17
|
+
const anchorBlock = getAnchorTextBlock({context})
|
|
18
|
+
|
|
19
|
+
if (!anchorBlock) {
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const key = context.selection
|
|
24
|
+
? isKeyedSegment(context.selection.anchor.path[2])
|
|
25
|
+
? context.selection.anchor.path[2]._key
|
|
26
|
+
: undefined
|
|
27
|
+
: undefined
|
|
28
|
+
|
|
29
|
+
const node = key
|
|
30
|
+
? anchorBlock.node.children.find((span) => span._key === key)
|
|
31
|
+
: undefined
|
|
32
|
+
|
|
33
|
+
return node && key
|
|
34
|
+
? {node, path: [...anchorBlock.path, 'children', {_key: key}]}
|
|
35
|
+
: undefined
|
|
36
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type {KeyedSegment} from '@portabletext/patches'
|
|
2
|
+
import {isPortableTextSpan, type PortableTextSpan} from '@sanity/types'
|
|
3
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
4
|
+
import {getAnchorChild} from './selector.get-anchor-child'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export const getAnchorSpan: EditorSelector<
|
|
10
|
+
| {node: PortableTextSpan; path: [KeyedSegment, 'children', KeyedSegment]}
|
|
11
|
+
| undefined
|
|
12
|
+
> = ({context}) => {
|
|
13
|
+
const anchorChild = getAnchorChild({context})
|
|
14
|
+
|
|
15
|
+
return anchorChild && isPortableTextSpan(anchorChild.node)
|
|
16
|
+
? {node: anchorChild.node, path: anchorChild.path}
|
|
17
|
+
: undefined
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isPortableTextTextBlock,
|
|
3
|
+
type KeyedSegment,
|
|
4
|
+
type PortableTextTextBlock,
|
|
5
|
+
} from '@sanity/types'
|
|
6
|
+
import type {EditorSelector} from '../editor/editor-selector'
|
|
7
|
+
import {getAnchorBlock} from './selector.get-anchor-block'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export const getAnchorTextBlock: EditorSelector<
|
|
13
|
+
{node: PortableTextTextBlock; path: [KeyedSegment]} | undefined
|
|
14
|
+
> = ({context}) => {
|
|
15
|
+
const anchorBlock = getAnchorBlock({context})
|
|
16
|
+
|
|
17
|
+
return anchorBlock && isPortableTextTextBlock(anchorBlock.node)
|
|
18
|
+
? {node: anchorBlock.node, path: anchorBlock.path}
|
|
19
|
+
: undefined
|
|
20
|
+
}
|