11import { Fragment , Node , Schema } from 'prosemirror-model' ;
2- import { Command , TextSelection } from 'prosemirror-state' ;
3- import { findChildrenByType , findParentNodeOfType } from 'prosemirror-utils' ;
2+ import { Command , TextSelection , Transaction } from 'prosemirror-state' ;
43
54import type { ActionSpec } from '../../../core' ;
5+ import { get$Cursor , isNodeSelection } from '../../../utils/selection' ;
66import { pType } from '../../base/BaseSchema' ;
77
88import { checkboxInputType , checkboxLabelType , checkboxType } from './utils' ;
@@ -14,42 +14,48 @@ const createCheckbox = (schema: Schema, content?: Fragment | Node | Node[]) =>
1414 ] ) ;
1515
1616export const addCheckboxCmd : Command = ( state , dispatch ) => {
17- const paragraph = findParentNodeOfType ( pType ( state . schema ) ) ( state . selection ) ;
18- const checkboxParent = findParentNodeOfType ( checkboxType ( state . schema ) ) ( state . selection ) ;
19- const parent = paragraph || checkboxParent ;
20-
21- if ( ! parent ) return false ;
22-
23- const checkboxChild = findChildrenByType ( parent . node , checkboxLabelType ( state . schema ) ) ;
24-
25- if ( checkboxChild . length ) {
26- if ( dispatch ) {
27- const { tr} = state ;
28-
29- tr . insert ( parent . pos + parent . node . nodeSize , createCheckbox ( state . schema , undefined ) ) ;
17+ function insertCheckbox ( tr : Transaction , pos : number , content ?: Fragment ) : Transaction {
18+ tr . insert ( pos , createCheckbox ( state . schema , content ) ) ;
19+ return tr . setSelection ( TextSelection . create ( tr . doc , pos + 3 ) ) ; // move cursor inside checkbox
20+ }
3021
31- tr . setSelection ( new TextSelection ( tr . doc . resolve ( tr . selection . $from . after ( ) + 4 ) ) ) ;
22+ if ( isNodeSelection ( state . selection ) && rootOrNonComplex ( state . selection . node ) ) {
23+ const pos = state . selection . to ;
24+ dispatch ?.( insertCheckbox ( state . tr , pos ) . scrollIntoView ( ) ) ;
25+ return true ;
26+ }
3227
33- dispatch ( tr ) ;
34- }
28+ const $cursor = get$Cursor ( state . selection ) ;
29+ if ( ! $cursor ) return false ;
3530
31+ const inCheckbox =
32+ $cursor . parent . type === checkboxLabelType ( state . schema ) &&
33+ $cursor . node ( $cursor . depth - 1 ) . type === checkboxType ( state . schema ) ;
34+ if ( inCheckbox ) {
35+ const pos = $cursor . after ( $cursor . depth - 1 ) ;
36+ dispatch ?.( insertCheckbox ( state . tr , pos ) . scrollIntoView ( ) ) ;
3637 return true ;
3738 }
3839
39- const { tr} = state ;
40-
41- if ( dispatch ) {
42- tr . replaceWith (
43- parent . pos ,
44- parent . pos + parent . node . nodeSize ,
45- createCheckbox ( state . schema , parent . node . content ) ,
46- ) ;
40+ if ( ! rootOrNonComplex ( $cursor . parent ) ) return false ;
4741
48- tr . setSelection ( new TextSelection ( tr . doc . resolve ( tr . selection . $from . after ( ) - 1 ) ) ) ;
42+ if ( ! dispatch ) return true ;
4943
50- dispatch ?.( tr ) ;
44+ const { tr} = state ;
45+ const inParagraph = $cursor . parent . type === pType ( state . schema ) ;
46+
47+ if ( inParagraph ) {
48+ const from = $cursor . before ( ) ,
49+ to = $cursor . after ( ) ;
50+ // replace para with checkbox with same content
51+ tr . replaceWith ( from , to , createCheckbox ( state . schema , $cursor . parent . content ) ) ;
52+ tr . setSelection ( TextSelection . create ( tr . doc , $cursor . pos + 2 ) ) ; // save cursor position in text
53+ } else {
54+ const pos = $cursor . after ( ) ;
55+ insertCheckbox ( tr , pos ) ;
5156 }
5257
58+ dispatch ( tr . scrollIntoView ( ) ) ;
5359 return true ;
5460} ;
5561
@@ -59,3 +65,8 @@ export const addCheckbox = (): ActionSpec => {
5965 run : addCheckboxCmd ,
6066 } ;
6167} ;
68+
69+ function rootOrNonComplex ( node : Node ) : boolean {
70+ const { complex} = node . type . spec ;
71+ return ! complex || complex === 'root' ;
72+ }
0 commit comments