@portabletext/editor 1.50.8 → 1.52.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/{util.slice-blocks.cjs → selection-point.cjs} +26 -18
- package/lib/_chunks-cjs/selection-point.cjs.map +1 -0
- package/lib/_chunks-cjs/selector.get-text-before.cjs +13 -10
- package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +46 -46
- package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.is-selection-expanded.cjs +21 -17
- package/lib/_chunks-cjs/selector.is-selection-expanded.cjs.map +1 -1
- package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs +10 -10
- package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs.map +1 -1
- package/lib/_chunks-cjs/util.is-equal-selection-points.cjs +5 -5
- package/lib/_chunks-cjs/util.is-equal-selection-points.cjs.map +1 -1
- package/lib/_chunks-cjs/util.merge-text-blocks.cjs +3 -3
- package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
- package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs +7 -14
- package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs.map +1 -1
- package/lib/_chunks-es/{util.slice-blocks.js → selection-point.js} +26 -18
- package/lib/_chunks-es/selection-point.js.map +1 -0
- package/lib/_chunks-es/selector.get-text-before.js +13 -10
- package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
- package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +21 -21
- package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
- package/lib/_chunks-es/selector.is-selection-expanded.js +14 -10
- package/lib/_chunks-es/selector.is-selection-expanded.js.map +1 -1
- package/lib/_chunks-es/util.child-selection-point-to-block-offset.js +2 -2
- package/lib/_chunks-es/util.child-selection-point-to-block-offset.js.map +1 -1
- package/lib/_chunks-es/util.is-equal-selection-points.js +1 -1
- package/lib/_chunks-es/util.merge-text-blocks.js +1 -1
- package/lib/_chunks-es/util.selection-point-to-block-offset.js +4 -11
- package/lib/_chunks-es/util.selection-point-to-block-offset.js.map +1 -1
- package/lib/behaviors/index.cjs.map +1 -1
- package/lib/behaviors/index.d.cts +25 -2010
- package/lib/behaviors/index.d.ts +25 -2010
- package/lib/behaviors/index.js.map +1 -1
- package/lib/index.cjs +515 -393
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +361 -34
- package/lib/index.d.ts +361 -34
- package/lib/index.js +471 -349
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.cjs +11 -11
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +32 -1986
- package/lib/plugins/index.d.ts +32 -1986
- package/lib/plugins/index.js +1 -1
- package/lib/selectors/index.cjs +11 -7
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +2 -2648
- package/lib/selectors/index.d.ts +2 -2648
- package/lib/selectors/index.js +7 -3
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.cjs +25 -14
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +0 -2647
- package/lib/utils/index.d.ts +0 -2647
- package/lib/utils/index.js +14 -3
- package/lib/utils/index.js.map +1 -1
- package/package.json +14 -14
- package/src/behaviors/behavior.abstract.delete.ts +0 -2
- package/src/behaviors/behavior.abstract.insert.ts +8 -8
- package/src/behaviors/behavior.abstract.ts +0 -113
- package/src/behaviors/behavior.core.block-element.ts +9 -3
- package/src/behaviors/behavior.core.dnd.ts +328 -1
- package/src/behaviors/behavior.perform-event.ts +10 -0
- package/src/behaviors/behavior.types.action.ts +2 -0
- package/src/behaviors/behavior.types.event.ts +5 -0
- package/src/behaviors/behavior.types.guard.ts +2 -0
- package/src/converters/converter.portable-text.ts +2 -7
- package/src/converters/converter.text-html.ts +1 -3
- package/src/converters/converter.text-plain.ts +3 -5
- package/src/editor/Editable.tsx +6 -133
- package/src/editor/editor-machine.ts +15 -10
- package/src/editor/editor-selector.ts +0 -2
- package/src/editor/editor-snapshot.ts +0 -18
- package/src/internal-utils/create-test-snapshot.ts +0 -2
- package/src/internal-utils/event-position.ts +42 -30
- package/src/internal-utils/selection-block-keys.ts +7 -7
- package/src/internal-utils/selection-elements.ts +108 -0
- package/src/internal-utils/selection-focus-text.ts +13 -9
- package/src/internal-utils/selection-text.ts +9 -78
- package/src/internal-utils/terse-pt.test.ts +108 -26
- package/src/internal-utils/terse-pt.ts +132 -14
- package/src/operations/behavior.operation.decorator.add.ts +0 -2
- package/src/operations/behavior.operation.delete.ts +18 -13
- package/src/operations/behavior.operation.insert.block.ts +5 -1
- package/src/selection/selection-point.ts +22 -0
- package/src/selectors/selector.get-anchor-block.ts +6 -6
- package/src/selectors/selector.get-anchor-child.ts +6 -6
- package/src/selectors/selector.get-selected-spans.ts +16 -19
- package/src/selectors/selector.get-selected-text-blocks.ts +11 -19
- package/src/selectors/selector.get-selection-end-block.ts +30 -0
- package/src/selectors/selector.get-selection-start-block.ts +30 -0
- package/src/selectors/selector.get-text-before.ts +15 -16
- package/src/selectors/selector.get-trimmed-selection.ts +15 -21
- package/src/selectors/selector.is-point-after-selection.ts +11 -19
- package/src/selectors/selector.is-point-before-selection.ts +11 -19
- package/src/selectors/selectors.ts +23 -39
- package/src/utils/util.block-offset.ts +6 -7
- package/src/utils/util.child-selection-point-to-block-offset.ts +6 -7
- package/src/utils/util.selection-point-to-block-offset.ts +5 -6
- package/src/utils/util.slice-blocks.ts +11 -20
- package/lib/_chunks-cjs/util.slice-blocks.cjs.map +0 -1
- package/lib/_chunks-es/util.slice-blocks.js.map +0 -1
- package/src/internal-utils/inline-object-selection.ts +0 -115
package/src/editor/Editable.tsx
CHANGED
|
@@ -23,18 +23,14 @@ import {
|
|
|
23
23
|
type RenderElementProps,
|
|
24
24
|
type RenderLeafProps,
|
|
25
25
|
} from 'slate-react'
|
|
26
|
-
import {getCompoundClientRect} from '../internal-utils/compound-client-rect'
|
|
27
26
|
import {debugWithName} from '../internal-utils/debug'
|
|
28
|
-
import {getDragSelection} from '../internal-utils/drag-selection'
|
|
29
27
|
import {getEventPosition} from '../internal-utils/event-position'
|
|
30
28
|
import {parseBlocks} from '../internal-utils/parse-blocks'
|
|
31
29
|
import {toSlateRange} from '../internal-utils/ranges'
|
|
32
30
|
import {normalizeSelection} from '../internal-utils/selection'
|
|
33
|
-
import {getSelectionDomNodes} from '../internal-utils/selection-elements'
|
|
34
31
|
import {slateRangeToSelection} from '../internal-utils/slate-utils'
|
|
35
32
|
import {fromSlateValue} from '../internal-utils/values'
|
|
36
33
|
import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
|
|
37
|
-
import * as selectors from '../selectors'
|
|
38
34
|
import type {
|
|
39
35
|
EditorSelection,
|
|
40
36
|
OnCopyFn,
|
|
@@ -50,13 +46,10 @@ import type {
|
|
|
50
46
|
ScrollSelectionIntoViewFunction,
|
|
51
47
|
} from '../types/editor'
|
|
52
48
|
import type {HotkeyOptions} from '../types/options'
|
|
53
|
-
import {isSelectionCollapsed} from '../utils'
|
|
54
|
-
import {getSelectionEndPoint} from '../utils/util.get-selection-end-point'
|
|
55
49
|
import {RenderElement} from './components/render-element'
|
|
56
50
|
import {RenderLeaf} from './components/render-leaf'
|
|
57
51
|
import {RenderText, type RenderTextProps} from './components/render-text'
|
|
58
52
|
import {EditorActorContext} from './editor-actor-context'
|
|
59
|
-
import {getEditorSnapshot} from './editor-selector'
|
|
60
53
|
import {usePortableTextEditor} from './hooks/usePortableTextEditor'
|
|
61
54
|
import {createWithHotkeys} from './plugins/createWithHotKeys'
|
|
62
55
|
import {PortableTextEditor} from './PortableTextEditor'
|
|
@@ -811,131 +804,9 @@ export const PortableTextEditable = forwardRef<
|
|
|
811
804
|
return
|
|
812
805
|
}
|
|
813
806
|
|
|
814
|
-
const snapshot = getEditorSnapshot({
|
|
815
|
-
editorActorSnapshot: editorActor.getSnapshot(),
|
|
816
|
-
slateEditorInstance: slateEditor,
|
|
817
|
-
})
|
|
818
|
-
const dragSelection = getDragSelection({
|
|
819
|
-
eventSelection: position.selection,
|
|
820
|
-
snapshot,
|
|
821
|
-
})
|
|
822
|
-
|
|
823
|
-
const selectingEntireBlocks = selectors.isSelectingEntireBlocks({
|
|
824
|
-
...snapshot,
|
|
825
|
-
context: {
|
|
826
|
-
...snapshot.context,
|
|
827
|
-
selection: dragSelection,
|
|
828
|
-
},
|
|
829
|
-
})
|
|
830
|
-
|
|
831
|
-
const dragGhost = document.createElement('div')
|
|
832
|
-
|
|
833
|
-
const draggedDomNodes = getSelectionDomNodes({
|
|
834
|
-
snapshot: {
|
|
835
|
-
...snapshot,
|
|
836
|
-
context: {
|
|
837
|
-
...snapshot.context,
|
|
838
|
-
selection: dragSelection,
|
|
839
|
-
},
|
|
840
|
-
},
|
|
841
|
-
slateEditor,
|
|
842
|
-
})
|
|
843
|
-
|
|
844
|
-
if (selectingEntireBlocks) {
|
|
845
|
-
// Clone the DOM Nodes so they won't be visually clipped by scroll-containers etc.
|
|
846
|
-
const clonedBlockNodes = draggedDomNodes.blockNodes.map((node) =>
|
|
847
|
-
node.cloneNode(true),
|
|
848
|
-
)
|
|
849
|
-
|
|
850
|
-
for (const block of clonedBlockNodes) {
|
|
851
|
-
if (block instanceof HTMLElement) {
|
|
852
|
-
block.style.position = 'relative'
|
|
853
|
-
}
|
|
854
|
-
dragGhost.appendChild(block)
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// A custom drag ghost element can be configured using this data attribute
|
|
858
|
-
const customGhost = dragGhost.querySelector(
|
|
859
|
-
'[data-pt-drag-ghost-element]',
|
|
860
|
-
)
|
|
861
|
-
if (customGhost) {
|
|
862
|
-
dragGhost.replaceChildren(customGhost)
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
// Setting the `data-dragged` attribute so the consumer can style the element while it’s dragged
|
|
866
|
-
dragGhost.setAttribute('data-dragged', '')
|
|
867
|
-
|
|
868
|
-
dragGhost.style.position = 'absolute'
|
|
869
|
-
dragGhost.style.left = '-99999px'
|
|
870
|
-
dragGhost.style.boxSizing = 'border-box'
|
|
871
|
-
document.body.appendChild(dragGhost)
|
|
872
|
-
|
|
873
|
-
if (customGhost) {
|
|
874
|
-
const customGhostRect = customGhost.getBoundingClientRect()
|
|
875
|
-
const x = event.clientX - customGhostRect.left
|
|
876
|
-
const y = event.clientY - customGhostRect.top
|
|
877
|
-
dragGhost.style.width = `${customGhostRect.width}px`
|
|
878
|
-
dragGhost.style.height = `${customGhostRect.height}px`
|
|
879
|
-
event.dataTransfer.setDragImage(dragGhost, x, y)
|
|
880
|
-
} else {
|
|
881
|
-
const blocksDomRect = getCompoundClientRect(
|
|
882
|
-
draggedDomNodes.blockNodes,
|
|
883
|
-
)
|
|
884
|
-
const x = event.clientX - blocksDomRect.left
|
|
885
|
-
const y = event.clientY - blocksDomRect.top
|
|
886
|
-
dragGhost.style.width = `${blocksDomRect.width}px`
|
|
887
|
-
dragGhost.style.height = `${blocksDomRect.height}px`
|
|
888
|
-
event.dataTransfer.setDragImage(dragGhost, x, y)
|
|
889
|
-
}
|
|
890
|
-
} else {
|
|
891
|
-
const clonedChildNodes = draggedDomNodes.childNodes.map((node) =>
|
|
892
|
-
node.cloneNode(true),
|
|
893
|
-
)
|
|
894
|
-
|
|
895
|
-
for (const child of clonedChildNodes) {
|
|
896
|
-
dragGhost.appendChild(child)
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
dragGhost.style.position = 'absolute'
|
|
900
|
-
dragGhost.style.left = '-99999px'
|
|
901
|
-
dragGhost.style.boxSizing = 'border-box'
|
|
902
|
-
document.body.appendChild(dragGhost)
|
|
903
|
-
|
|
904
|
-
const childrenDomRect = getCompoundClientRect(
|
|
905
|
-
draggedDomNodes.childNodes,
|
|
906
|
-
)
|
|
907
|
-
const x = event.clientX - childrenDomRect.left
|
|
908
|
-
const y = event.clientY - childrenDomRect.top
|
|
909
|
-
dragGhost.style.width = `${childrenDomRect.width}px`
|
|
910
|
-
dragGhost.style.height = `${childrenDomRect.height}px`
|
|
911
|
-
|
|
912
|
-
event.dataTransfer.setDragImage(dragGhost, x, y)
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// Select drag selection
|
|
916
|
-
// If the selection is expanded then we just select the end of the
|
|
917
|
-
// selection
|
|
918
|
-
editorActor.send({
|
|
919
|
-
type: 'behavior event',
|
|
920
|
-
behaviorEvent: {
|
|
921
|
-
type: 'select',
|
|
922
|
-
at: isSelectionCollapsed(dragSelection)
|
|
923
|
-
? dragSelection
|
|
924
|
-
: {
|
|
925
|
-
anchor: getSelectionEndPoint(dragSelection),
|
|
926
|
-
focus: getSelectionEndPoint(dragSelection),
|
|
927
|
-
backward: false,
|
|
928
|
-
},
|
|
929
|
-
},
|
|
930
|
-
editor: slateEditor,
|
|
931
|
-
})
|
|
932
|
-
|
|
933
807
|
editorActor.send({
|
|
934
808
|
type: 'dragstart',
|
|
935
|
-
origin:
|
|
936
|
-
selection: dragSelection,
|
|
937
|
-
},
|
|
938
|
-
ghost: dragGhost,
|
|
809
|
+
origin: position,
|
|
939
810
|
})
|
|
940
811
|
|
|
941
812
|
editorActor.send({
|
|
@@ -943,11 +814,11 @@ export const PortableTextEditable = forwardRef<
|
|
|
943
814
|
behaviorEvent: {
|
|
944
815
|
type: 'drag.dragstart',
|
|
945
816
|
originEvent: {
|
|
817
|
+
clientX: event.clientX,
|
|
818
|
+
clientY: event.clientY,
|
|
946
819
|
dataTransfer: event.dataTransfer,
|
|
947
820
|
},
|
|
948
|
-
position
|
|
949
|
-
selection: dragSelection,
|
|
950
|
-
},
|
|
821
|
+
position,
|
|
951
822
|
},
|
|
952
823
|
editor: slateEditor,
|
|
953
824
|
})
|
|
@@ -1079,6 +950,7 @@ export const PortableTextEditable = forwardRef<
|
|
|
1079
950
|
originEvent: {
|
|
1080
951
|
dataTransfer: event.dataTransfer,
|
|
1081
952
|
},
|
|
953
|
+
dragOrigin: editorActor.getSnapshot().context.internalDrag?.origin,
|
|
1082
954
|
position,
|
|
1083
955
|
},
|
|
1084
956
|
editor: slateEditor,
|
|
@@ -1117,6 +989,7 @@ export const PortableTextEditable = forwardRef<
|
|
|
1117
989
|
originEvent: {
|
|
1118
990
|
dataTransfer: event.dataTransfer,
|
|
1119
991
|
},
|
|
992
|
+
dragOrigin: editorActor.getSnapshot().context.internalDrag?.origin,
|
|
1120
993
|
position,
|
|
1121
994
|
},
|
|
1122
995
|
editor: slateEditor,
|
|
@@ -68,7 +68,6 @@ type InternalPatchEvent = NamespaceEvent<PatchEvent, 'internal'> & {
|
|
|
68
68
|
* @internal
|
|
69
69
|
*/
|
|
70
70
|
export type EditorActor = ActorRefFrom<typeof editorMachine>
|
|
71
|
-
export type HasTag = ReturnType<EditorActor['getSnapshot']>['hasTag']
|
|
72
71
|
|
|
73
72
|
/**
|
|
74
73
|
* @internal
|
|
@@ -115,10 +114,14 @@ export type InternalEditorEvent =
|
|
|
115
114
|
}
|
|
116
115
|
| MutationEvent
|
|
117
116
|
| InternalPatchEvent
|
|
117
|
+
| {
|
|
118
|
+
type: 'set drag ghost'
|
|
119
|
+
ghost: HTMLElement
|
|
120
|
+
}
|
|
118
121
|
| {
|
|
119
122
|
type: 'dragstart'
|
|
120
|
-
origin: Pick<EventPosition, 'selection'>
|
|
121
123
|
ghost?: HTMLElement
|
|
124
|
+
origin: Pick<EventPosition, 'selection'>
|
|
122
125
|
}
|
|
123
126
|
| {type: 'dragend'}
|
|
124
127
|
| {type: 'drop'}
|
|
@@ -149,9 +152,9 @@ export const editorMachine = setup({
|
|
|
149
152
|
selection: EditorSelection
|
|
150
153
|
initialValue: Array<PortableTextBlock> | undefined
|
|
151
154
|
internalDrag?: {
|
|
152
|
-
ghost?: HTMLElement
|
|
153
155
|
origin: Pick<EventPosition, 'selection'>
|
|
154
156
|
}
|
|
157
|
+
dragGhost?: HTMLElement
|
|
155
158
|
slateEditor?: PortableTextSlateEditor
|
|
156
159
|
},
|
|
157
160
|
events: {} as InternalEditorEvent,
|
|
@@ -274,10 +277,9 @@ export const editorMachine = setup({
|
|
|
274
277
|
keyGenerator: context.keyGenerator,
|
|
275
278
|
readOnly: self.getSnapshot().matches({'edit mode': 'read only'}),
|
|
276
279
|
schema: context.schema,
|
|
277
|
-
hasTag: (tag) => self.getSnapshot().hasTag(tag),
|
|
278
|
-
internalDrag: context.internalDrag,
|
|
279
280
|
}),
|
|
280
281
|
nativeEvent: event.nativeEvent,
|
|
282
|
+
sendBack: (event) => self.send(event),
|
|
281
283
|
})
|
|
282
284
|
} catch (error) {
|
|
283
285
|
console.error(
|
|
@@ -324,6 +326,9 @@ export const editorMachine = setup({
|
|
|
324
326
|
emit(({event}) => ({...event, type: 'selection'})),
|
|
325
327
|
],
|
|
326
328
|
},
|
|
329
|
+
'set drag ghost': {
|
|
330
|
+
actions: assign({dragGhost: ({event}) => event.ghost}),
|
|
331
|
+
},
|
|
327
332
|
},
|
|
328
333
|
type: 'parallel',
|
|
329
334
|
states: {
|
|
@@ -429,7 +434,6 @@ export const editorMachine = setup({
|
|
|
429
434
|
actions: [
|
|
430
435
|
assign({
|
|
431
436
|
internalDrag: ({event}) => ({
|
|
432
|
-
ghost: event.ghost,
|
|
433
437
|
origin: event.origin,
|
|
434
438
|
}),
|
|
435
439
|
}),
|
|
@@ -497,20 +501,21 @@ export const editorMachine = setup({
|
|
|
497
501
|
debug('exit: edit mode->editable->dragging internally')
|
|
498
502
|
},
|
|
499
503
|
({context}) => {
|
|
500
|
-
if (context.
|
|
504
|
+
if (context.dragGhost) {
|
|
501
505
|
try {
|
|
502
|
-
context.
|
|
503
|
-
context.
|
|
506
|
+
context.dragGhost.parentNode?.removeChild(
|
|
507
|
+
context.dragGhost,
|
|
504
508
|
)
|
|
505
509
|
} catch (error) {
|
|
506
510
|
console.error(
|
|
507
511
|
new Error(
|
|
508
|
-
`Removing the
|
|
512
|
+
`Removing the drag ghost failed due to: ${error.message}`,
|
|
509
513
|
),
|
|
510
514
|
)
|
|
511
515
|
}
|
|
512
516
|
}
|
|
513
517
|
},
|
|
518
|
+
assign({dragGhost: undefined}),
|
|
514
519
|
assign({internalDrag: undefined}),
|
|
515
520
|
],
|
|
516
521
|
tags: ['dragging internally'],
|
|
@@ -86,8 +86,6 @@ export function getEditorSnapshot({
|
|
|
86
86
|
markState: slateEditorInstance.markState,
|
|
87
87
|
schema: editorActorSnapshot.context.schema,
|
|
88
88
|
}),
|
|
89
|
-
hasTag: (tag) => editorActorSnapshot.hasTag(tag),
|
|
90
|
-
internalDrag: editorActorSnapshot.context.internalDrag,
|
|
91
89
|
},
|
|
92
90
|
}
|
|
93
91
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type {PortableTextBlock} from '@sanity/types'
|
|
2
2
|
import type {Converter} from '../converters/converter.types'
|
|
3
|
-
import type {EventPosition} from '../internal-utils/event-position'
|
|
4
3
|
import {slateRangeToSelection} from '../internal-utils/slate-utils'
|
|
5
4
|
import type {EditorSelection, PortableTextSlateEditor} from '../types/editor'
|
|
6
|
-
import type {HasTag} from './editor-machine'
|
|
7
5
|
import type {EditorSchema} from './editor-schema'
|
|
8
6
|
import {getActiveAnnotations} from './get-active-annotations'
|
|
9
7
|
import {getActiveDecorators} from './get-active-decorators'
|
|
@@ -32,12 +30,6 @@ export type EditorSnapshot = {
|
|
|
32
30
|
beta: {
|
|
33
31
|
activeAnnotations: Array<string>
|
|
34
32
|
activeDecorators: Array<string>
|
|
35
|
-
hasTag: HasTag
|
|
36
|
-
internalDrag:
|
|
37
|
-
| {
|
|
38
|
-
origin: Pick<EventPosition, 'selection'>
|
|
39
|
-
}
|
|
40
|
-
| undefined
|
|
41
33
|
}
|
|
42
34
|
}
|
|
43
35
|
|
|
@@ -47,20 +39,12 @@ export function createEditorSnapshot({
|
|
|
47
39
|
keyGenerator,
|
|
48
40
|
readOnly,
|
|
49
41
|
schema,
|
|
50
|
-
hasTag,
|
|
51
|
-
internalDrag,
|
|
52
42
|
}: {
|
|
53
43
|
converters: Array<Converter>
|
|
54
44
|
editor: PortableTextSlateEditor
|
|
55
45
|
keyGenerator: () => string
|
|
56
46
|
readOnly: boolean
|
|
57
47
|
schema: EditorSchema
|
|
58
|
-
hasTag: HasTag
|
|
59
|
-
internalDrag:
|
|
60
|
-
| {
|
|
61
|
-
origin: Pick<EventPosition, 'selection'>
|
|
62
|
-
}
|
|
63
|
-
| undefined
|
|
64
48
|
}) {
|
|
65
49
|
const selection = editor.selection
|
|
66
50
|
? slateRangeToSelection({
|
|
@@ -91,8 +75,6 @@ export function createEditorSnapshot({
|
|
|
91
75
|
markState: editor.markState,
|
|
92
76
|
schema,
|
|
93
77
|
}),
|
|
94
|
-
hasTag,
|
|
95
|
-
internalDrag,
|
|
96
78
|
},
|
|
97
79
|
} satisfies EditorSnapshot
|
|
98
80
|
}
|
|
@@ -19,8 +19,6 @@ export function createTestSnapshot(snapshot: {
|
|
|
19
19
|
beta: {
|
|
20
20
|
activeAnnotations: snapshot.beta?.activeAnnotations ?? [],
|
|
21
21
|
activeDecorators: snapshot.beta?.activeDecorators ?? [],
|
|
22
|
-
hasTag: snapshot.beta?.hasTag ?? (() => false),
|
|
23
|
-
internalDrag: undefined,
|
|
24
22
|
},
|
|
25
23
|
}
|
|
26
24
|
}
|
|
@@ -2,6 +2,7 @@ import {Editor, type BaseRange, type Node} from 'slate'
|
|
|
2
2
|
import {DOMEditor, isDOMNode} from 'slate-dom'
|
|
3
3
|
import type {EditorSchema, EditorSelection} from '..'
|
|
4
4
|
import type {EditorActor} from '../editor/editor-machine'
|
|
5
|
+
import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
|
|
5
6
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
6
7
|
import * as utils from '../utils'
|
|
7
8
|
import {
|
|
@@ -34,82 +35,93 @@ export function getEventPosition({
|
|
|
34
35
|
return undefined
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
const
|
|
38
|
+
const eventNode = getEventNode({slateEditor, event})
|
|
38
39
|
|
|
39
|
-
if (!
|
|
40
|
+
if (!eventNode) {
|
|
40
41
|
return undefined
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
const
|
|
44
|
+
const eventBlock = getNodeBlock({
|
|
44
45
|
editor: slateEditor,
|
|
45
46
|
schema: editorActor.getSnapshot().context.schema,
|
|
46
|
-
node,
|
|
47
|
+
node: eventNode,
|
|
47
48
|
})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const eventPositionBlock = getEventPositionBlock({
|
|
50
|
+
node: eventNode,
|
|
51
|
+
slateEditor,
|
|
52
|
+
event,
|
|
53
|
+
})
|
|
54
|
+
const eventSelection = getEventSelection({
|
|
51
55
|
schema: editorActor.getSnapshot().context.schema,
|
|
52
56
|
slateEditor,
|
|
53
57
|
event,
|
|
54
58
|
})
|
|
55
59
|
|
|
56
|
-
if (
|
|
60
|
+
if (
|
|
61
|
+
eventBlock &&
|
|
62
|
+
eventPositionBlock &&
|
|
63
|
+
!eventSelection &&
|
|
64
|
+
!Editor.isEditor(eventNode)
|
|
65
|
+
) {
|
|
66
|
+
// If we for some reason can't find the event selection, then we default to
|
|
67
|
+
// selecting the entire block that the event originates from.
|
|
57
68
|
return {
|
|
58
|
-
block:
|
|
69
|
+
block: eventPositionBlock,
|
|
59
70
|
isEditor: false,
|
|
60
71
|
selection: {
|
|
61
72
|
anchor: utils.getBlockStartPoint({
|
|
62
73
|
context: editorActor.getSnapshot().context,
|
|
63
74
|
block: {
|
|
64
|
-
node:
|
|
65
|
-
path: [{_key:
|
|
75
|
+
node: eventBlock,
|
|
76
|
+
path: [{_key: eventBlock._key}],
|
|
66
77
|
},
|
|
67
78
|
}),
|
|
68
79
|
focus: utils.getBlockEndPoint({
|
|
69
80
|
context: editorActor.getSnapshot().context,
|
|
70
81
|
block: {
|
|
71
|
-
node:
|
|
72
|
-
path: [{_key:
|
|
82
|
+
node: eventBlock,
|
|
83
|
+
path: [{_key: eventBlock._key}],
|
|
73
84
|
},
|
|
74
85
|
}),
|
|
75
86
|
},
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
|
|
79
|
-
if (!
|
|
90
|
+
if (!eventPositionBlock || !eventSelection) {
|
|
80
91
|
return undefined
|
|
81
92
|
}
|
|
82
93
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
: undefined
|
|
94
|
+
const eventSelectionFocusBlockKey = getBlockKeyFromSelectionPoint(
|
|
95
|
+
eventSelection.focus,
|
|
96
|
+
)
|
|
87
97
|
|
|
88
|
-
if (
|
|
98
|
+
if (eventSelectionFocusBlockKey === undefined) {
|
|
89
99
|
return undefined
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
if (
|
|
93
|
-
utils.isSelectionCollapsed(
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
utils.isSelectionCollapsed(eventSelection) &&
|
|
104
|
+
eventBlock &&
|
|
105
|
+
eventSelectionFocusBlockKey !== eventBlock._key
|
|
96
106
|
) {
|
|
107
|
+
// If the event block and event selection somehow don't match, then the
|
|
108
|
+
// event block takes precedence.
|
|
97
109
|
return {
|
|
98
|
-
block:
|
|
110
|
+
block: eventPositionBlock,
|
|
99
111
|
isEditor: false,
|
|
100
112
|
selection: {
|
|
101
113
|
anchor: utils.getBlockStartPoint({
|
|
102
114
|
context: editorActor.getSnapshot().context,
|
|
103
115
|
block: {
|
|
104
|
-
node:
|
|
105
|
-
path: [{_key:
|
|
116
|
+
node: eventBlock,
|
|
117
|
+
path: [{_key: eventBlock._key}],
|
|
106
118
|
},
|
|
107
119
|
}),
|
|
108
120
|
focus: utils.getBlockEndPoint({
|
|
109
121
|
context: editorActor.getSnapshot().context,
|
|
110
122
|
block: {
|
|
111
|
-
node:
|
|
112
|
-
path: [{_key:
|
|
123
|
+
node: eventBlock,
|
|
124
|
+
path: [{_key: eventBlock._key}],
|
|
113
125
|
},
|
|
114
126
|
}),
|
|
115
127
|
},
|
|
@@ -117,9 +129,9 @@ export function getEventPosition({
|
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
return {
|
|
120
|
-
block:
|
|
121
|
-
isEditor: Editor.isEditor(
|
|
122
|
-
selection,
|
|
132
|
+
block: eventPositionBlock,
|
|
133
|
+
isEditor: Editor.isEditor(eventNode),
|
|
134
|
+
selection: eventSelection,
|
|
123
135
|
}
|
|
124
136
|
}
|
|
125
137
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
+
import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
|
|
1
2
|
import type {EditorSelection} from '../types/editor'
|
|
2
|
-
import {isKeyedSegment} from '../utils'
|
|
3
3
|
|
|
4
4
|
export function getSelectionBlockKeys(selection: EditorSelection) {
|
|
5
5
|
if (!selection) {
|
|
6
6
|
return undefined
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
) {
|
|
9
|
+
const anchorBlockKey = getBlockKeyFromSelectionPoint(selection.anchor)
|
|
10
|
+
const focusBlockKey = getBlockKeyFromSelectionPoint(selection.focus)
|
|
11
|
+
|
|
12
|
+
if (anchorBlockKey === undefined || focusBlockKey === undefined) {
|
|
13
13
|
return undefined
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
return {
|
|
17
|
-
anchor:
|
|
18
|
-
focus:
|
|
17
|
+
anchor: anchorBlockKey,
|
|
18
|
+
focus: focusBlockKey,
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -1,9 +1,96 @@
|
|
|
1
1
|
import {Editor} from 'slate'
|
|
2
2
|
import {DOMEditor} from 'slate-dom'
|
|
3
3
|
import type {EditorSnapshot} from '..'
|
|
4
|
+
import type {BehaviorEvent} from '../behaviors'
|
|
5
|
+
import type {PickFromUnion} from '../type-utils'
|
|
4
6
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
5
7
|
import {toSlateRange} from './ranges'
|
|
6
8
|
|
|
9
|
+
export type EditorDom = {
|
|
10
|
+
getBlockNodes: (snapshot: EditorSnapshot) => Array<Node>
|
|
11
|
+
getChildNodes: (snapshot: EditorSnapshot) => Array<Node>
|
|
12
|
+
/**
|
|
13
|
+
* Let the Editor set the drag ghost. This is to be sure that it will get
|
|
14
|
+
* properly removed again when the drag ends.
|
|
15
|
+
*/
|
|
16
|
+
setDragGhost: ({
|
|
17
|
+
event,
|
|
18
|
+
ghost,
|
|
19
|
+
}: {
|
|
20
|
+
event: PickFromUnion<BehaviorEvent, 'type', 'drag.dragstart'>
|
|
21
|
+
ghost: {
|
|
22
|
+
element: HTMLElement
|
|
23
|
+
x: number
|
|
24
|
+
y: number
|
|
25
|
+
}
|
|
26
|
+
}) => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createEditorDom(
|
|
30
|
+
sendBack: (event: {type: 'set drag ghost'; ghost: HTMLElement}) => void,
|
|
31
|
+
slateEditor: PortableTextSlateEditor,
|
|
32
|
+
): EditorDom {
|
|
33
|
+
return {
|
|
34
|
+
getBlockNodes: (snapshot) => getBlockNodes(slateEditor, snapshot),
|
|
35
|
+
getChildNodes: (snapshot) => getChildNodes(slateEditor, snapshot),
|
|
36
|
+
setDragGhost: ({event, ghost}) => setDragGhost({sendBack, event, ghost}),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getBlockNodes(
|
|
41
|
+
slateEditor: PortableTextSlateEditor,
|
|
42
|
+
snapshot: EditorSnapshot,
|
|
43
|
+
) {
|
|
44
|
+
if (!snapshot.context.selection) {
|
|
45
|
+
return []
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const range = toSlateRange(snapshot.context.selection, slateEditor)
|
|
49
|
+
|
|
50
|
+
if (!range) {
|
|
51
|
+
return []
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const blockEntries = Array.from(
|
|
55
|
+
Editor.nodes(slateEditor, {
|
|
56
|
+
at: range,
|
|
57
|
+
mode: 'highest',
|
|
58
|
+
match: (n) => !Editor.isEditor(n),
|
|
59
|
+
}),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return blockEntries.map(([blockNode]) =>
|
|
63
|
+
DOMEditor.toDOMNode(slateEditor, blockNode),
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getChildNodes(
|
|
68
|
+
slateEditor: PortableTextSlateEditor,
|
|
69
|
+
snapshot: EditorSnapshot,
|
|
70
|
+
) {
|
|
71
|
+
if (!snapshot.context.selection) {
|
|
72
|
+
return []
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const range = toSlateRange(snapshot.context.selection, slateEditor)
|
|
76
|
+
|
|
77
|
+
if (!range) {
|
|
78
|
+
return []
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const childEntries = Array.from(
|
|
82
|
+
Editor.nodes(slateEditor, {
|
|
83
|
+
at: range,
|
|
84
|
+
mode: 'lowest',
|
|
85
|
+
match: (n) => !Editor.isEditor(n),
|
|
86
|
+
}),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return childEntries.map(([childNode]) =>
|
|
90
|
+
DOMEditor.toDOMNode(slateEditor, childNode),
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
7
94
|
export type SelectionDomNodes = {
|
|
8
95
|
blockNodes: Array<Node>
|
|
9
96
|
childNodes: Array<Node>
|
|
@@ -59,3 +146,24 @@ export function getSelectionDomNodes({
|
|
|
59
146
|
),
|
|
60
147
|
}
|
|
61
148
|
}
|
|
149
|
+
|
|
150
|
+
function setDragGhost({
|
|
151
|
+
sendBack,
|
|
152
|
+
event,
|
|
153
|
+
ghost,
|
|
154
|
+
}: {
|
|
155
|
+
sendBack: (event: {type: 'set drag ghost'; ghost: HTMLElement}) => void
|
|
156
|
+
event: PickFromUnion<BehaviorEvent, 'type', 'drag.dragstart'>
|
|
157
|
+
ghost: {
|
|
158
|
+
element: HTMLElement
|
|
159
|
+
x: number
|
|
160
|
+
y: number
|
|
161
|
+
}
|
|
162
|
+
}) {
|
|
163
|
+
event.originEvent.dataTransfer.setDragImage(ghost.element, ghost.x, ghost.y)
|
|
164
|
+
|
|
165
|
+
sendBack({
|
|
166
|
+
type: 'set drag ghost',
|
|
167
|
+
ghost: ghost.element,
|
|
168
|
+
})
|
|
169
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
|
|
2
2
|
import type {PortableTextBlock} from '@sanity/types'
|
|
3
|
+
import {
|
|
4
|
+
getBlockKeyFromSelectionPoint,
|
|
5
|
+
getChildKeyFromSelectionPoint,
|
|
6
|
+
} from '../selection/selection-point'
|
|
3
7
|
import type {EditorSelection} from '../types/editor'
|
|
4
|
-
import {isKeyedSegment} from '../utils'
|
|
5
8
|
|
|
6
9
|
export function getSelectionFocusText(
|
|
7
10
|
value: Array<PortableTextBlock> | undefined,
|
|
@@ -11,20 +14,21 @@ export function getSelectionFocusText(
|
|
|
11
14
|
return undefined
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
const focusBlockKey = getBlockKeyFromSelectionPoint(selection.focus)
|
|
18
|
+
const focusChildKey = getChildKeyFromSelectionPoint(selection.focus)
|
|
19
|
+
|
|
20
|
+
if (focusBlockKey === undefined || focusChildKey === undefined) {
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
14
24
|
let text: string | undefined
|
|
15
25
|
|
|
16
26
|
for (const block of value) {
|
|
17
27
|
if (isPortableTextBlock(block)) {
|
|
18
|
-
if (
|
|
19
|
-
isKeyedSegment(selection.focus.path[0]) &&
|
|
20
|
-
block._key === selection.focus.path[0]._key
|
|
21
|
-
) {
|
|
28
|
+
if (block._key === focusBlockKey) {
|
|
22
29
|
for (const child of block.children) {
|
|
23
30
|
if (isPortableTextSpan(child)) {
|
|
24
|
-
if (
|
|
25
|
-
isKeyedSegment(selection.focus.path[2]) &&
|
|
26
|
-
child._key === selection.focus.path[2]._key
|
|
27
|
-
) {
|
|
31
|
+
if (child._key === focusChildKey) {
|
|
28
32
|
text = child.text
|
|
29
33
|
break
|
|
30
34
|
}
|