import { Menu } from 'obsidian'; import type { TodoItem } from '../core/types'; export interface TodoItemCallbacks { onToggle: (todo: TodoItem) => void; onMoveClick: (todo: TodoItem) => void; onClick: (todo: TodoItem) => void; } /** * Collect all line numbers for a todo and its descendants (for drag data). */ export function collectChildLineNumbers(todo: TodoItem): number[] { const lines: number[] = []; for (const child of todo.children) { lines.push(child.lineNumber); lines.push(...collectChildLineNumbers(child)); } return lines; } /** * Creates a DOM element for a todo item with checkbox, right-click menu, * drag support, and recursive child rendering. */ export function createTodoItemEl( container: HTMLElement, todo: TodoItem, callbacks: TodoItemCallbacks ): HTMLElement { const itemEl = container.createEl('li', { cls: 'todo-tracker-item' }); itemEl.dataset.lineNumber = String(todo.lineNumber); // Checkbox const checkboxEl = itemEl.createEl('input', { type: 'checkbox' }); checkboxEl.checked = todo.completed; checkboxEl.addEventListener('change', () => { callbacks.onToggle(todo); }); // Text content — click to navigate to editor const textEl = itemEl.createEl('span', { cls: 'todo-tracker-item-text' }); textEl.setText(todo.text); if (todo.completed) { textEl.addClass('todo-tracker-item-completed'); } textEl.addEventListener('click', (evt) => { evt.stopPropagation(); callbacks.onClick(todo); }); // Right-click context menu itemEl.addEventListener('contextmenu', (evt) => { evt.preventDefault(); evt.stopPropagation(); const menu = new Menu(); menu.addItem((item) => { item .setTitle('Move to...') .setIcon('arrow-right') .onClick(() => { callbacks.onMoveClick(todo); }); }); menu.showAtMouseEvent(evt); }); // Render children recursively if (todo.children.length > 0) { const nestedList = itemEl.createEl('ul', { cls: 'todo-tracker-nested-list' }); for (const child of todo.children) { createTodoItemEl(nestedList, child, callbacks); } } return itemEl; }