import { ITEM_SEPARATOR } from "../block/identify.js"; import { joinItemLines, splitItemHead } from "../block/list-item.js"; import type { BlockEditor } from "./editor.type.js"; export const taskListEditor: BlockEditor = (container, raw, _attrs, onCommit, onCancel) => { const items = raw.split(ITEM_SEPARATOR); const parsed = items.map((item) => { const [head, tail] = splitItemHead(item); return { checked: /^\[x\] /i.test(head), text: head.replace(/^\[([ xX])\] /, ""), tail, }; }); const wrapper = document.createElement("div"); function rebuild() { wrapper.innerHTML = ""; for (let i = 0; i < parsed.length; i++) { const row = document.createElement("div"); row.style.display = "flex"; row.style.alignItems = "center"; row.style.gap = "6px"; row.style.marginBottom = "4px"; const cb = document.createElement("input"); cb.type = "checkbox"; cb.checked = parsed[i].checked; cb.addEventListener("change", () => { parsed[i].checked = cb.checked; }); const input = document.createElement("input"); input.type = "text"; input.classList.add("edit-input"); input.style.minHeight = "auto"; input.value = parsed[i].text; input.addEventListener("input", () => { parsed[i].text = input.value; }); input.addEventListener("keydown", (e) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); commit(); } if (e.key === "Escape") { e.preventDefault(); onCancel(); } if (e.key === "Enter" && !e.metaKey && !e.ctrlKey) { e.preventDefault(); parsed.splice(i + 1, 0, { checked: false, text: "", tail: [] }); rebuild(); const inputs = wrapper.querySelectorAll('input[type="text"]'); inputs[i + 1]?.focus(); } if (e.key === "Backspace" && input.value === "" && parsed.length > 1) { e.preventDefault(); parsed.splice(i, 1); rebuild(); const inputs = wrapper.querySelectorAll('input[type="text"]'); const focusIdx = Math.max(0, i - 1); inputs[focusIdx]?.focus(); } }); row.appendChild(cb); row.appendChild(input); wrapper.appendChild(row); } } function commit() { const filtered = parsed.filter((p) => p.text.trim() !== ""); if (filtered.length === 0) return onCancel(); const newRaw = filtered .map((p) => joinItemLines(`[${p.checked ? "x" : " "}] ${p.text}`, p.tail)) .join(ITEM_SEPARATOR); onCommit(newRaw); } rebuild(); container.innerHTML = ""; container.appendChild(wrapper); const firstInput = wrapper.querySelector('input[type="text"]'); firstInput?.focus(); };