@@ -8,7 +8,7 @@ import './polyfills.js';
88 * @property {string } text - item label
99 * @property {boolean } checked - is the item checked
1010 */
11- interface ChecklistItem {
11+ export interface ChecklistItem {
1212 text : string ;
1313 checked : boolean ;
1414}
@@ -17,10 +17,17 @@ interface ChecklistItem {
1717 * @typedef {object } ChecklistData
1818 * @property {ChecklistItem[] } items - checklist elements
1919 */
20- interface ChecklistData {
20+ export interface ChecklistData {
2121 items : ChecklistItem [ ] ;
2222}
2323
24+ // TODO: Add strong type for the EditorJs API
25+ /**
26+ * @typedef {object } ChecklistOptions
27+ * @property {ChecklistData } data - checklist option data
28+ * @property {any } api - Editor.js API
29+ * @property {boolean } readOnly - read only mode flag
30+ */
2431interface ChecklistOptions {
2532 data : ChecklistData ;
2633 api : any ;
@@ -29,11 +36,17 @@ interface ChecklistOptions {
2936
3037/**
3138 * Checklist Tool for the Editor.js 2.0
39+ *
40+ * @typedef {object } CheckList API
3241 */
3342export default class Checklist {
43+ /** @private HTML nodes */
3444 private _elements : { wrapper : HTMLElement | null ; items : HTMLElement [ ] } ;
45+ /** @private read only flag */
3546 private readOnly : boolean ;
47+ /** @private Editor.js API */
3648 private api : any ;
49+ /** @private checklist option data */
3750 private data : ChecklistData ;
3851
3952 /**
@@ -153,7 +166,7 @@ export default class Checklist {
153166 this . data . items . forEach ( item => {
154167 const newItem = this . createChecklistItem ( item ) ;
155168
156- if ( this . _elements . wrapper ) {
169+ if ( this . _elements . wrapper ) {
157170 this . _elements . wrapper . appendChild ( newItem ) ;
158171 }
159172 } ) ;
@@ -182,7 +195,7 @@ export default class Checklist {
182195 } , false ) ;
183196
184197 this . _elements . wrapper . addEventListener ( 'click' , ( event : MouseEvent ) => {
185- this . toggleCheckbox ( event ) ;
198+ this . toggleCheckbox ( this . _elements . wrapper , event ) ;
186199 } ) ;
187200
188201 return this . _elements . wrapper ;
@@ -197,8 +210,8 @@ export default class Checklist {
197210 /**
198211 * @type {ChecklistItem[] }
199212 */
200- let items : ChecklistItem [ ] = this . items . map ( ( itemEl : HTMLElement ) => {
201- const input = this . getItemInput ( itemEl ) ;
213+ let items : ChecklistItem [ ] = this . items . filter ( itemEl => this . getItemInput ( itemEl ) !== null ) . map ( ( itemEl ) => {
214+ const input = this . getItemInput ( itemEl ) as HTMLElement ;
202215
203216 return {
204217 text : getHTML ( input ) ,
@@ -230,17 +243,24 @@ export default class Checklist {
230243 /**
231244 * Toggle checklist item state
232245 *
246+ * @param {HTMLElement | null } target - element receiving the event
233247 * @param {MouseEvent } event - click
234248 * @returns {void }
235249 */
236- toggleCheckbox ( event : MouseEvent ) : void {
237- const checkListItem = ( event . target as HTMLElement ) . closest ( `.${ this . CSS . item } ` ) as HTMLElement ;
238- const checkbox = checkListItem . querySelector ( `.${ this . CSS . checkboxContainer } ` ) as HTMLElement ;
239-
240- if ( checkbox . contains ( event . target as Node ) ) {
241- checkListItem . classList . toggle ( this . CSS . itemChecked ) ;
242- checkbox . classList . add ( this . CSS . noHover ) ;
243- checkbox . addEventListener ( 'mouseleave' , ( ) => this . removeSpecialHoverBehavior ( checkbox ) , { once : true } ) ;
250+ toggleCheckbox ( target : HTMLElement | null , event : MouseEvent ) : void {
251+
252+ if ( target ) {
253+ const checkListItem = target . closest ( `.${ this . CSS . item } ` ) ;
254+
255+ if ( checkListItem ) {
256+ const checkbox = checkListItem . querySelector < HTMLElement > ( `.${ this . CSS . checkboxContainer } ` ) ;
257+
258+ if ( checkbox && checkbox . contains ( event . target as Node ) ) {
259+ checkListItem . classList . toggle ( this . CSS . itemChecked ) ;
260+ checkbox . classList . add ( this . CSS . noHover ) ;
261+ checkbox . addEventListener ( 'mouseleave' , ( ) => this . removeSpecialHoverBehavior ( checkbox ) , { once : true } ) ;
262+ }
263+ }
244264 }
245265 }
246266
@@ -282,53 +302,60 @@ export default class Checklist {
282302 event . preventDefault ( ) ;
283303
284304 const items = this . items ;
285- const currentItem = document . activeElement ?. closest ( `.${ this . CSS . item } ` ) as HTMLElement ;
286- const currentItemIndex = items . indexOf ( currentItem ) ;
305+ const currentItem = document . activeElement ?. closest ( `.${ this . CSS . item } ` ) ;
306+ const currentItemIndex = items . indexOf ( < HTMLElement > currentItem ) ;
287307 const isLastItem = currentItemIndex === items . length - 1 ;
288308
289309 /**
290310 * Prevent checklist item generation if it's the last item and it's empty
291311 * and get out of checklist
292312 */
293- if ( isLastItem ) {
294- const currentItemText = getHTML ( this . getItemInput ( currentItem ) ) ;
295- const isEmptyItem = currentItemText . length === 0 ;
313+ if ( isLastItem && currentItem ) {
314+ const itemInput = this . getItemInput ( currentItem )
296315
297- if ( isEmptyItem ) {
298- const currentBlockIndex = this . api . blocks . getCurrentBlockIndex ( ) ;
316+ if ( itemInput ) {
317+ const currentItemText = getHTML ( itemInput ) ;
318+ const isEmptyItem = currentItemText . length === 0 ;
299319
300- currentItem . remove ( ) ;
320+ if ( isEmptyItem ) {
321+ const currentBlockIndex = this . api . blocks . getCurrentBlockIndex ( ) ;
301322
302- this . api . blocks . insert ( ) ;
303- this . api . caret . setToBlock ( currentBlockIndex + 1 ) ;
323+ currentItem . remove ( ) ;
304324
305- return ;
325+ this . api . blocks . insert ( ) ;
326+ this . api . caret . setToBlock ( currentBlockIndex + 1 ) ;
327+
328+ return ;
329+ }
306330 }
307- }
331+ /**
332+ * Cut content after caret
333+ */
334+ const fragmentAfterCaret = extractContentAfterCaret ( ) ;
335+ const htmlAfterCaret = fragmentToHtml ( fragmentAfterCaret ) ;
308336
309- /**
310- * Cut content after caret
311- */
312- const fragmentAfterCaret = extractContentAfterCaret ( ) ;
313- const htmlAfterCaret = fragmentToHtml ( fragmentAfterCaret ) ;
337+ /**
338+ * Create new checklist item
339+ */
340+ const newItem = this . createChecklistItem ( {
341+ text : htmlAfterCaret ,
342+ checked : false ,
343+ } ) ;
314344
315- /**
316- * Create new checklist item
317- */
318- const newItem = this . createChecklistItem ( {
319- text : htmlAfterCaret ,
320- checked : false ,
321- } ) ;
345+ /**
346+ * Insert new checklist item as sibling to currently selected item
347+ */
348+ this . _elements . wrapper ?. insertBefore ( newItem , currentItem . nextSibling ) ;
322349
323- /**
324- * Insert new checklist item as sibling to currently selected item
325- */
326- this . _elements . wrapper ?. insertBefore ( newItem , currentItem . nextSibling ) ;
350+ /**
351+ * Move caret to contentEditable textField of new checklist item
352+ */
353+ const newItemInput = this . getItemInput ( newItem )
327354
328- /**
329- * Move caret to contentEditable textField of new checklist item
330- */
331- moveCaret ( this . getItemInput ( newItem ) , true ) ;
355+ if ( newItemInput ) {
356+ moveCaret ( newItemInput , true ) ;
357+ }
358+ }
332359 }
333360
334361 /**
@@ -360,13 +387,16 @@ export default class Checklist {
360387 */
361388 const fragmentAfterCaret = extractContentAfterCaret ( ) ;
362389 const prevItemInput = this . getItemInput ( prevItem ) ;
363- const prevItemChildNodesLength = prevItemInput . childNodes . length ;
364390
365- prevItemInput . appendChild ( fragmentAfterCaret ) ;
391+ if ( prevItemInput ) {
392+ const prevItemChildNodesLength = prevItemInput . childNodes . length ;
393+
394+ prevItemInput . appendChild ( fragmentAfterCaret ) ;
366395
367- moveCaret ( prevItemInput , undefined , prevItemChildNodesLength ) ;
396+ moveCaret ( prevItemInput , undefined , prevItemChildNodesLength ) ;
368397
369- currentItem . remove ( ) ;
398+ currentItem . remove ( ) ;
399+ }
370400 }
371401
372402 /**
@@ -415,7 +445,7 @@ export default class Checklist {
415445 * @param {Element } el - item wrapper
416446 * @returns {Element }
417447 */
418- getItemInput ( el : HTMLElement ) : HTMLElement {
419- return el . querySelector ( `.${ this . CSS . textField } ` ) as HTMLElement ;
448+ getItemInput ( el : HTMLElement | Element ) : HTMLElement | null {
449+ return el . querySelector ( `.${ this . CSS . textField } ` ) ;
420450 }
421451}
0 commit comments