/** * @module * Editing UI for `html:` blocks — a key/value attribute table. * * Previously this lived inside `createHtmlHandler(emmetAbbr)` and could have * closed over the parsed Emmet abbreviation (tag/type). It never did: the * editor only ever read `attrs` and reported back through `onCommit`, so * moving it out to a single standalone editor shared by every `html:*` block * type loses no behaviour. If a future revision needs the element tag, note * that a `BlockEditor` is not given the block type string — it would have to * come from `attrs` or from registering a distinct editor per type. */ import type { BlockEditor } from "./editor.type.js"; /** Editing UI for `html:` blocks — an editable attribute key/value table. */ export const htmlEditor: BlockEditor = (container, _raw, attrs, onCommit, onCancel, _toolbar) => { const entries = Object.entries(attrs); const rows: { key: string; valueInput: HTMLInputElement }[] = []; const wrapper = document.createElement("div"); function addRow(key: string, value: string) { const row = document.createElement("div"); row.style.display = "flex"; row.style.gap = "6px"; row.style.marginBottom = "4px"; const keyInput = document.createElement("input"); keyInput.type = "text"; keyInput.classList.add("edit-input"); keyInput.style.minHeight = "auto"; keyInput.style.flex = "0 0 35%"; keyInput.placeholder = "attribute"; keyInput.value = key; const valueInput = document.createElement("input"); valueInput.type = "text"; valueInput.classList.add("edit-input"); valueInput.style.minHeight = "auto"; valueInput.placeholder = "value"; valueInput.value = value; const entry = { key, valueInput }; rows.push(entry); keyInput.addEventListener("input", () => { entry.key = keyInput.value; }); const onKey = (e: KeyboardEvent) => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); commit(); } if (e.key === "Escape") { e.preventDefault(); onCancel(); } }; keyInput.addEventListener("keydown", onKey); valueInput.addEventListener("keydown", onKey); row.appendChild(keyInput); row.appendChild(valueInput); wrapper.appendChild(row); } for (const [k, v] of entries) { addRow(k, v); } if (entries.length === 0) { addRow("name", ""); } const addBtn = document.createElement("button"); addBtn.textContent = "+ Add attribute"; addBtn.style.fontSize = "0.8em"; addBtn.style.cursor = "pointer"; addBtn.style.background = "none"; addBtn.style.border = "none"; addBtn.style.padding = "2px 0"; addBtn.style.color = "inherit"; addBtn.style.opacity = "0.7"; addBtn.addEventListener("click", (e) => { e.stopPropagation(); addRow("", ""); const inputs = wrapper.querySelectorAll("input"); inputs[inputs.length - 2]?.focus(); }); function commit() { const newAttrs: Record = {}; for (const row of rows) { const k = row.key.trim(); if (k) newAttrs[k] = row.valueInput.value; } onCommit("", newAttrs); } container.innerHTML = ""; container.appendChild(wrapper); container.appendChild(addBtn); const firstValue = wrapper.querySelector("input:nth-child(2)"); firstValue?.focus(); };