33 BlockAddedEvent ,
44 BlockRemovedEvent ,
55 createDataKey ,
6- type DataKey ,
6+ type DataKey , DataNodeAddedEvent , DataNodeRemovedEvent ,
77 type EditorJSModel ,
88 EventAction ,
99 EventType ,
@@ -71,8 +71,6 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
7171
7272 /**
7373 * Inputs that bound to the model
74- *
75- * @todo handle inputs deletion — remove inputs from the map when they are removed from the DOM
7674 */
7775 #attachedInputs = new Map < DataKey , HTMLElement > ( ) ;
7876
@@ -103,6 +101,8 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
103101 this . #formattingAdapter = formattingAdapter ;
104102 this . #toolName = toolName ;
105103
104+ this . #model. addEventListener ( EventType . Changed , ( event : ModelEvents ) => this . #handleModelUpdate( event ) ) ;
105+
106106 eventBus . addEventListener ( `ui:${ BeforeInputUIEventName } ` , ( event : BeforeInputUIEvent ) => {
107107 this . #processDelegatedBeforeInput( event ) ;
108108 } ) ;
@@ -124,34 +124,67 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
124124
125125 this . #attachedInputs. set ( key , input ) ;
126126
127- this . #model. addEventListener ( EventType . Changed , ( event : ModelEvents ) => this . #handleModelUpdate( event , input , key ) ) ;
127+ this . #model. createDataNode (
128+ this . #config. userId ,
129+ this . #blockIndex,
130+ key ,
131+ {
132+ $t : 't' ,
133+ value : '' ,
134+ }
135+ ) ;
128136
129137 const builder = new IndexBuilder ( ) ;
130138
131139 builder . addBlockIndex ( this . #blockIndex) . addDataKey ( key ) ;
132140
133141 this . #caretAdapter. attachInput ( input , builder . build ( ) ) ;
134142
135- try {
136- const value = this . #model. getText ( this . #blockIndex, key ) ;
137- const fragments = this . #model. getFragments ( this . #blockIndex, key ) ;
143+ const value = this . #model. getText ( this . #blockIndex, key ) ;
144+ const fragments = this . #model. getFragments ( this . #blockIndex, key ) ;
138145
139- input . textContent = value ;
146+ input . textContent = value ;
147+
148+ fragments . forEach ( fragment => {
149+ this . #formattingAdapter. formatElementContent ( input , fragment ) ;
150+ } ) ;
151+ }
152+
153+ /**
154+ * Removes the input from the DOM by key
155+ *
156+ * @param keyRaw - key of the input to remove
157+ */
158+ public detachInput ( keyRaw : string ) : void {
159+ const key = createDataKey ( keyRaw ) ;
160+ const input = this . #attachedInputs. get ( key ) ;
140161
141- fragments . forEach ( fragment => {
142- this . #formattingAdapter. formatElementContent ( input , fragment ) ;
143- } ) ;
144- } catch ( _ ) {
145- // do nothing — TextNode is not created yet as there is no initial data in the model
162+ if ( ! input ) {
163+ return ;
146164 }
165+
166+ /**
167+ * @todo Let BlockTool handle DOM update
168+ */
169+ input . remove ( ) ;
170+ this . #caretAdapter. detachInput (
171+ new IndexBuilder ( )
172+ . addBlockIndex ( this . #blockIndex)
173+ . addDataKey ( key )
174+ . build ( )
175+ ) ;
176+
177+ this . #attachedInputs. delete ( key ) ;
178+
179+ this . #model. removeDataNode ( this . #config. userId , this . #blockIndex, key ) ;
147180 }
148181
149182 /**
150183 * Check current selection and find it across all attached inputs
151184 *
152185 * @returns tuple of data key and input element or null if no focused input is found
153186 */
154- #findFocusedInput( ) : [ DataKey , HTMLElement ] | null {
187+ #findFocusedInput( ) : [ DataKey , HTMLElement ] | null {
155188 const currentInput = Array . from ( this . #attachedInputs. entries ( ) ) . find ( ( [ _ , input ] ) => {
156189 /**
157190 * Case 1: Input is a native input — check if it has selection
@@ -421,7 +454,7 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
421454 this . #config. userId ,
422455 {
423456 name : this . #toolName,
424- data : {
457+ data : {
425458 [ key ] : {
426459 $t : 't' ,
427460 value : newValueAfter ,
@@ -451,35 +484,12 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
451484 *
452485 * @param event - model update event
453486 * @param input - input element
454- * @param key - data key input is attached to
455487 */
456- #handleModelUpdateForNativeInput( event : ModelEvents , input : HTMLInputElement | HTMLTextAreaElement , key : DataKey ) : void {
457- if ( ! ( event instanceof TextAddedEvent ) && ! ( event instanceof TextRemovedEvent ) ) {
458- return ;
459- }
460-
461- const { textRange, dataKey, blockIndex } = event . detail . index ;
462-
463- if ( textRange === undefined ) {
464- return ;
465- }
466-
467- /**
468- * Event is not related to the attached block
469- */
470- if ( blockIndex !== this . #blockIndex) {
471- return ;
472- }
473-
474- /**
475- * Event is not related to the attached data key
476- */
477- if ( dataKey !== key ) {
478- return ;
479- }
488+ #handleModelUpdateForNativeInput( event : ModelEvents , input : HTMLInputElement | HTMLTextAreaElement ) : void {
489+ const { textRange } = event . detail . index ;
480490
481491 const currentElement = input ;
482- const [ start , end ] = textRange ;
492+ const [ start , end ] = textRange ! ;
483493
484494 const action = event . detail . action ;
485495
@@ -519,31 +529,10 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
519529 * @param key - data key input is attached to
520530 */
521531 #handleModelUpdateForContentEditableElement( event : ModelEvents , input : HTMLElement , key : DataKey ) : void {
522- if ( ! ( event instanceof TextAddedEvent ) && ! ( event instanceof TextRemovedEvent ) ) {
523- return ;
524- }
525-
526- const { textRange, dataKey, blockIndex } = event . detail . index ;
527-
528- if ( blockIndex !== this . #blockIndex) {
529- return ;
530- }
531-
532- /**
533- * Event is not related to the attached data key
534- */
535- if ( dataKey !== key ) {
536- return ;
537- }
538-
539- if ( textRange === undefined ) {
540- return ;
541- }
542-
532+ const { textRange } = event . detail . index ;
543533 const action = event . detail . action ;
544534
545- const start = textRange [ 0 ] ;
546- const end = textRange [ 1 ] ;
535+ const [ start , end ] = textRange ! ;
547536
548537 const [ startNode , startOffset ] = getBoundaryPointByAbsoluteOffset ( input , start ) ;
549538 const [ endNode , endOffset ] = getBoundaryPointByAbsoluteOffset ( input , end ) ;
@@ -586,22 +575,51 @@ export class BlockToolAdapter implements BlockToolAdapterInterface {
586575 * Handles model update events and updates DOM
587576 *
588577 * @param event - model update event
589- * @param input - attached input element
590- * @param key - data key input is attached to
591578 */
592- #handleModelUpdate( event : ModelEvents , input : HTMLElement , key : DataKey ) : void {
579+ #handleModelUpdate( event : ModelEvents ) : void {
593580 if ( event instanceof BlockAddedEvent || event instanceof BlockRemovedEvent ) {
594581 if ( event . detail . index . blockIndex ! <= this . #blockIndex) {
595582 this . #blockIndex += event . detail . action === EventAction . Added ? 1 : - 1 ;
596583 }
584+
585+ return ;
586+ }
587+
588+ const { textRange, dataKey, blockIndex } = event . detail . index ;
589+
590+ if ( blockIndex !== this . #blockIndex) {
591+ return ;
592+ }
593+
594+
595+ if ( event instanceof DataNodeRemovedEvent ) {
596+ this . detachInput ( dataKey as string ) ;
597+
598+ return ;
599+ }
600+
601+ if ( event instanceof DataNodeAddedEvent ) {
602+ /**
603+ * @todo Decide how to handle this case as only BlockTool knows how to render an input
604+ */
605+ }
606+
607+ if ( ! ( event instanceof TextAddedEvent ) && ! ( event instanceof TextRemovedEvent ) ) {
608+ return ;
609+ }
610+
611+ const input = this . #attachedInputs. get ( dataKey ! ) ;
612+
613+ if ( ! input || textRange === undefined ) {
614+ return ;
597615 }
598616
599617 const isInputNative = isNativeInput ( input ) ;
600618
601619 if ( isInputNative === true ) {
602- this . #handleModelUpdateForNativeInput( event , input as HTMLInputElement | HTMLTextAreaElement , key ) ;
620+ this . #handleModelUpdateForNativeInput( event , input as HTMLInputElement | HTMLTextAreaElement ) ;
603621 } else {
604- this . #handleModelUpdateForContentEditableElement( event , input , key ) ;
622+ this . #handleModelUpdateForContentEditableElement( event , input , dataKey ! ) ;
605623 }
606624 } ;
607625}
0 commit comments