@portabletext/editor 1.32.0 → 1.33.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 +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 +295 -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 +300 -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 +2 -2
- 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 +395 -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/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
|
@@ -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',
|
|
@@ -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
|
+
}
|