Skip to content

Commit da57ad8

Browse files
committed
PR review
1 parent c1cb147 commit da57ad8

2 files changed

Lines changed: 83 additions & 53 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
},
2424
"scripts": {
2525
"dev": "vite",
26-
"build": "tsc && vite build"
26+
"build": "vite build"
2727
},
2828
"author": {
2929
"name": "CodeX",

src/index.ts

Lines changed: 82 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
*/
2431
interface 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
*/
3342
export 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

Comments
 (0)