import { ITEM_SEPARATOR } from "../block/identify.js"; import { joinItemLines, splitItemHead } from "../block/list-item.js"; import type { BlockEditor } from "./editor.type.js"; export const listEditor: BlockEditor = (container, raw, attrs, onCommit, onCancel, toolbar) => { let isOrdered = attrs["list-type"] === "ordered"; const items = raw.split(ITEM_SEPARATOR).map(splitItemHead); const texts = items.map(([head]) => head); const tails = items.map(([, tail]) => tail); // List type toggle in the popup toolbar const selector = document.createElement("div"); selector.classList.add("heading-selector"); const ulBtn = document.createElement("button"); ulBtn.textContent = "• List"; if (!isOrdered) ulBtn.classList.add("active"); const olBtn = document.createElement("button"); olBtn.textContent = "1. List"; if (isOrdered) olBtn.classList.add("active"); ulBtn.addEventListener("click", (e) => { e.stopPropagation(); isOrdered = false; ulBtn.classList.add("active"); olBtn.classList.remove("active"); rebuild(); }); olBtn.addEventListener("click", (e) => { e.stopPropagation(); isOrdered = true; olBtn.classList.add("active"); ulBtn.classList.remove("active"); rebuild(); }); selector.appendChild(ulBtn); selector.appendChild(olBtn); const deleteBtn = toolbar.querySelector(".danger"); toolbar.insertBefore(selector, deleteBtn); const wrapper = document.createElement("div"); function rebuild() { wrapper.innerHTML = ""; for (let i = 0; i < texts.length; i++) { const row = document.createElement("div"); row.style.display = "flex"; row.style.alignItems = "center"; row.style.gap = "6px"; row.style.marginBottom = "4px"; const marker = document.createElement("span"); marker.style.fontSize = "0.85em"; marker.style.opacity = "0.5"; marker.style.minWidth = "1.5em"; marker.textContent = isOrdered ? `${i + 1}.` : "•"; const input = document.createElement("input"); input.type = "text"; input.classList.add("edit-input"); input.style.minHeight = "auto"; input.value = texts[i]; input.addEventListener("input", () => { texts[i] = input.value; }); input.addEventListener("keydown", (e) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); commit(); } else if (e.key === "Escape") { e.preventDefault(); onCancel(); } else if (e.key === "Enter" && !e.metaKey && !e.ctrlKey) { e.preventDefault(); texts.splice(i + 1, 0, ""); tails.splice(i + 1, 0, []); rebuild(); const inputs = wrapper.querySelectorAll('input[type="text"]'); inputs[i + 1]?.focus(); } else if (e.key === "Backspace" && input.value === "" && texts.length > 1) { e.preventDefault(); texts.splice(i, 1); tails.splice(i, 1); rebuild(); const inputs = wrapper.querySelectorAll('input[type="text"]'); inputs[Math.max(0, i - 1)]?.focus(); } }); row.appendChild(marker); row.appendChild(input); wrapper.appendChild(row); } } function commit() { const kept = texts .map((text, i) => [text, tails[i]] as const) .filter(([text]) => text.trim() !== ""); if (kept.length === 0) return onCancel(); onCommit(kept.map(([text, tail]) => joinItemLines(text, tail)).join(ITEM_SEPARATOR), { ...attrs, "list-type": isOrdered ? "ordered" : "unordered", }); } rebuild(); container.innerHTML = ""; container.appendChild(wrapper); const firstInput = wrapper.querySelector('input[type="text"]'); firstInput?.focus(); };