80 lines
2.0 KiB
TypeScript
80 lines
2.0 KiB
TypeScript
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;
|
|
}
|