import { h, $ } from "sigpro" import { get, cls, isFn } from "./All.js" export const Editor = (p) => { const { value, class: extraClass } = p let editorRef = null const isSource = $(false) const source = $("") const notify = () => { if (!editorRef) return const html = editorRef.innerHTML if (isFn(value)) value(html) else p.onchange?.(html) } const exec = (cmd, val = null) => { if (!editorRef) return editorRef.focus() document.execCommand(cmd, false, val) notify() } const queryState = (cmd, val = null) => { if (!editorRef) return false try { if (cmd === 'formatBlock') { const sel = window.getSelection() if (!sel.rangeCount) return false let node = sel.getRangeAt(0).commonAncestorContainer while (node && node !== editorRef) { if (node.nodeType === 1 && node.tagName === val) return true node = node.parentNode } return false } return document.queryCommandState(cmd) } catch (e) { return false } } const toolbar = h("div", { class: "flex flex-wrap items-center gap-1 p-2 border-b border-base-300 bg-base-200" }, [ h("div", { class: "flex flex-wrap gap-1 flex-1" }, [ h("button", { type: "button", class: () => `btn btn-ghost btn-xs ${queryState('bold') ? 'btn-active' : ''}`, onclick: () => exec("bold") }, h("span", { class: "icon-[lucide--bold]" })), h("button", { type: "button", class: () => `btn btn-ghost btn-xs ${queryState('italic') ? 'btn-active' : ''}`, onclick: () => exec("italic") }, h("span", { class: "icon-[lucide--italic]" })), h("button", { type: "button", class: () => `btn btn-ghost btn-xs ${queryState('underline') ? 'btn-active' : ''}`, onclick: () => exec("underline") }, h("span", { class: "icon-[lucide--underline]" })), h("button", { type: "button", class: () => `btn btn-ghost btn-xs ${queryState('strikeThrough') ? 'btn-active' : ''}`, onclick: () => exec("strikeThrough") }, h("span", { class: "icon-[lucide--strikethrough]" })), h("span", { class: "w-px h-5 bg-base-300 mx-1" }), h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("insertUnorderedList") }, h("span", { class: "icon-[lucide--list]" })), h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("insertOrderedList") }, h("span", { class: "icon-[lucide--list-ordered]" })), h("span", { class: "w-px h-5 bg-base-300 mx-1" }), h("button", { type: "button", class: () => `btn btn-ghost btn-xs ${queryState('formatBlock', 'BLOCKQUOTE') ? 'btn-active' : ''}`, onclick: () => exec("formatBlock", queryState('formatBlock', 'BLOCKQUOTE') ? 'P' : 'BLOCKQUOTE') }, h("span", { class: "icon-[lucide--quote]" })), h("span", { class: "w-px h-5 bg-base-300 mx-1" }), h("select", { class: "select select-xs w-16", onchange: (e) => exec("fontSize", e.target.value), value: "3" }, [ h("option", { value: "1" }, "1"), h("option", { value: "2" }, "2"), h("option", { value: "3" }, "3"), h("option", { value: "4" }, "4"), h("option", { value: "5" }, "5"), h("option", { value: "6" }, "6"), h("option", { value: "7" }, "7"), ]), h("span", { class: "w-px h-5 bg-base-300 mx-1" }), h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("undo") }, h("span", { class: "icon-[lucide--undo-2]" })), h("button", { type: "button", class: "btn btn-ghost btn-xs", onclick: () => exec("redo") }, h("span", { class: "icon-[lucide--redo-2]" })), ]), h("div", { class: "flex" }, [ h("button", { type: "button", class: () => `btn btn-ghost btn-xs ${isSource() ? 'btn-active' : ''}`, onclick: () => { const wasSource = isSource() if (!wasSource) { source(editorRef?.innerHTML || "") } else { if (editorRef) { editorRef.innerHTML = source() notify() } } isSource(!wasSource) } }, h("span", { class: "icon-[lucide--code-2]" })) ]) ]) return h("div", { class: cls("border border-base-300 rounded-box bg-base-100 overflow-hidden", extraClass) }, [ toolbar, h("div", { class: "relative" }, [ h("div", { ref: el => { if (!editorRef && el) { editorRef = el el.innerHTML = get(value) || "" } }, style: () => `min-height:10rem;${isSource() ? 'display:none' : ''}`, class: "p-3 outline-none text-base-content [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:list-item [&_p]:m-0 [&_div]:m-0 [&_br]:content-[''] [&_br]:block [&_br]:h-[1em]", contenteditable: "true", oninput: notify, onpaste: () => setTimeout(notify, 0) }), h("textarea", { class: "w-full min-h-[10rem] p-3 outline-none font-mono text-sm bg-base-200 border-0", style: () => isSource() ? '' : 'display:none', value: source, oninput: (e) => source(e.target.value) }) ]) ]) }