1
- import { Fragment , type NodeRange , type NodeType , Slice } from 'prosemirror-model' ;
1
+ import { Fragment , type Node , type NodeRange , type NodeType , Slice } from 'prosemirror-model' ;
2
2
import { wrapInList } from 'prosemirror-schema-list' ;
3
3
import type { Command , Transaction } from 'prosemirror-state' ;
4
4
import { ReplaceAroundStep , liftTarget } from 'prosemirror-transform' ;
@@ -30,11 +30,17 @@ export const joinPrevList = joinPreviousBlock({
30
30
sinks list items deeper.
31
31
*/
32
32
const sink = ( tr : Transaction , range : NodeRange , itemType : NodeType ) => {
33
- const before = tr . mapping . map ( range . start ) ;
34
- const after = tr . mapping . map ( range . end ) ;
35
- const startIndex = tr . mapping . map ( range . startIndex ) ;
36
-
33
+ console . warn ( 'sink' , '=========>>>' ) ;
34
+ const before = range . start ;
35
+ const after = range . end ;
36
+ const startIndex = range . startIndex ;
37
37
const parent = range . parent ;
38
+
39
+ console . log ( 'before' , before ) ;
40
+ console . log ( 'after' , after ) ;
41
+ console . log ( 'startIndex' , startIndex ) ;
42
+ console . log ( 'parent' , parent ) ;
43
+
38
44
const nodeBefore = parent . child ( startIndex - 1 ) ;
39
45
40
46
const nestedBefore = nodeBefore . lastChild && nodeBefore . lastChild . type === parent . type ;
@@ -56,65 +62,135 @@ const sink = (tr: Transaction, range: NodeRange, itemType: NodeType) => {
56
62
true ,
57
63
) ,
58
64
) ;
65
+
66
+ // After sinking, lift any nested <li> children back out
67
+ const from = range . start ;
68
+ const $movedPos = tr . doc . resolve ( from ) ;
69
+ const movedItem = $movedPos . nodeAfter ;
70
+
71
+ if ( movedItem ) {
72
+ movedItem . forEach ( ( child , offset ) => {
73
+ if ( child . type === parent . type ) {
74
+ const nestedStart = from + offset + 1 ;
75
+ const nestedEnd = nestedStart + child . nodeSize ;
76
+ const $liStart = tr . doc . resolve ( nestedStart + 1 ) ;
77
+ const $liEnd = tr . doc . resolve ( nestedEnd - 1 ) ;
78
+ const liftRange = $liStart . blockRange ( $liEnd , ( node ) => node . type === itemType ) ;
79
+
80
+ if ( liftRange ) {
81
+ const targetDepth = liftTarget ( liftRange ) ;
82
+ if ( targetDepth !== null ) {
83
+ tr . lift ( liftRange , targetDepth ) ;
84
+ }
85
+ }
86
+ }
87
+ } ) ;
88
+ }
89
+
59
90
return true ;
60
91
} ;
61
92
93
+ const isListItemNode = ( node : Node , itemType : NodeType ) =>
94
+ node . childCount > 0 && node . firstChild ! . type === itemType ;
95
+
96
+ /**
97
+ * Returns a map of list item positions that should be transformed (e.g., sink or lift).
98
+ */
99
+ function getListItemsToTransform (
100
+ tr : Transaction ,
101
+ itemType : NodeType ,
102
+ {
103
+ start,
104
+ end,
105
+ from,
106
+ to,
107
+ } : {
108
+ start : number ;
109
+ end : number ;
110
+ from : number ;
111
+ to : number ;
112
+ } ,
113
+ ) : Map < number , number > {
114
+ // console.warn('getListItemsToTransform', start, end, from, to);
115
+ const listItemsPoses = new Map < number , number > ( ) ;
116
+ let pos = start ;
117
+
118
+ while ( pos <= end ) {
119
+ const node = tr . doc . nodeAt ( pos ) ;
120
+
121
+ // console.log('pos', pos);
122
+ // console.log('node', node?.type.name);
123
+
124
+ if ( node ?. type === itemType ) {
125
+ // console.log('list pos ----->: ', pos, pos + node.nodeSize);
126
+ const isBeetwwen =
127
+ ( pos <= from && pos + node . nodeSize >= from ) ||
128
+ ( pos <= to && pos + node . nodeSize >= to ) ;
129
+ if ( isBeetwwen ) {
130
+ // console.warn(isBeetwwen);
131
+ listItemsPoses . set ( pos , pos + node . nodeSize ) ;
132
+ } else {
133
+ // console.log(isBeetwwen, pos, pos + node.nodeSize, 'from:to', from, to);
134
+ }
135
+ }
136
+
137
+ pos ++ ;
138
+ }
139
+
140
+ return listItemsPoses ;
141
+ }
142
+
62
143
export function sinkOnlySelectedListItem ( itemType : NodeType ) : Command {
63
144
return ( { tr, selection} , dispatch ) => {
64
- const { $from, $to} = selection ;
65
- const selectionRange = $from . blockRange (
66
- $to ,
67
- ( node ) => node . childCount > 0 && node . firstChild ! . type === itemType ,
145
+ const { $from, $to, from, to} = selection ;
146
+ const listItemSelectionRange = $from . blockRange ( $to , ( node ) =>
147
+ isListItemNode ( node , itemType ) ,
68
148
) ;
69
- if ( ! selectionRange ) {
70
- return false ;
71
- }
72
-
73
- const { startIndex, parent, start, end} = selectionRange ;
74
- if ( startIndex === 0 ) {
75
- return false ;
76
- }
77
149
78
- const nodeBefore = parent . child ( startIndex - 1 ) ;
79
- if ( nodeBefore . type !== itemType ) {
150
+ if ( ! listItemSelectionRange ) {
80
151
return false ;
81
152
}
82
153
83
154
if ( dispatch ) {
84
- // lifts following list items sequentially to prepare correct nesting structure
85
- let currentEnd = end - 1 ;
86
- while ( currentEnd > start ) {
87
- const selectionEnd = tr . mapping . map ( $to . pos ) ;
88
-
89
- const $candidateBlockEnd = tr . doc . resolve ( currentEnd ) ;
90
- const candidateBlockStartPos = $candidateBlockEnd . before ( $candidateBlockEnd . depth ) ;
91
- const $candidateBlockStart = tr . doc . resolve ( candidateBlockStartPos ) ;
92
- const candidateBlockRange = $candidateBlockStart . blockRange ( $candidateBlockEnd ) ;
93
-
94
- if ( candidateBlockRange ?. start ) {
95
- const $rangeStart = tr . doc . resolve ( candidateBlockRange . start ) ;
96
- const shouldLift =
97
- candidateBlockRange . start > selectionEnd && isListNode ( $rangeStart . parent ) ;
98
-
99
- if ( shouldLift ) {
100
- currentEnd = candidateBlockRange . start ;
101
-
102
- const targetDepth = liftTarget ( candidateBlockRange ) ;
103
- if ( targetDepth !== null ) {
104
- tr . lift ( candidateBlockRange , targetDepth ) ;
105
- }
106
- }
107
- }
155
+ const { start, end} = listItemSelectionRange ;
156
+ const listItemsPoses = getListItemsToTransform ( tr , itemType , {
157
+ start,
158
+ end,
159
+ from,
160
+ to,
161
+ } ) ;
108
162
109
- currentEnd -- ;
110
- }
163
+ console . warn ( listItemsPoses , 'start: end' , start , end ) ;
164
+
165
+ for ( const [ startPos , endPos ] of listItemsPoses ) {
166
+ const mappedStart = tr . mapping . map ( startPos ) ;
167
+ const nodeStart = tr . doc . nodeAt ( mappedStart ) ;
111
168
112
- // sinks the selected list item deeper into the list hierarchy
113
- sink ( tr , selectionRange , itemType ) ;
169
+ const mappedEnd = tr . mapping . map ( endPos ) ;
170
+ // const nodeEnd = tr.doc.nodeAt(mappedEnd );
114
171
172
+ console . log ( 'startPos ---->' , startPos ) ;
173
+ console . log ( 'endPos ---->' , endPos ) ;
174
+
175
+ console . log ( 'mapped startPos ---->' , mappedStart ) ;
176
+ console . log ( 'mapped endPos ---->' , mappedEnd ) ;
177
+
178
+ console . log ( 'nodeStart ---->' , nodeStart ?. type . name ) ;
179
+
180
+ const $mappedStart = tr . doc . resolve ( mappedStart ) ;
181
+ const $mappedEnd = tr . doc . resolve ( mappedEnd ) ;
182
+ const range = $mappedStart . blockRange ( $mappedEnd ) ;
183
+
184
+ if ( range ) {
185
+ console . log ( 'sink ---->' , range . start , range . end , range ) ;
186
+ sink ( tr , range , itemType ) ;
187
+ }
188
+ }
115
189
dispatch ( tr . scrollIntoView ( ) ) ;
190
+
116
191
return true ;
117
192
}
193
+
118
194
return true ;
119
195
} ;
120
196
}
0 commit comments