export interface ItemGroup { type: "group"; items: Item[]; } export type Icon = | "spinner" | "check-circle" | "gyazo" | "ocr" | "calendar" | "trim" | "exclamation-triangle" | `caret-${"up" | "down" | "left" | "right"}` | `align-${"left" | "center" | "justify" | "right"}` | "copy" | "cut" | "clipboard" | "expand" | "strikethrough" | "i-cursor" | "undo" | "redo" | "times" | "slash" | "ban" | "markdown" | "link" | "unlink" | "bold" | "strikethrough" | "highlighter" | "remove-format" | "italic" | "marker" | "google" | "code" | "search" | "language" | "underline"; export type Item = | string | { type: Icon | [Icon, Icon]; } | { type: "text"; text: string } | ItemGroup; export const makeGroup = (...items: Item[]): HTMLSpanElement | undefined => { const nodes = items.flatMap((item) => { if (typeof item === "string") { return [makeItem(item)]; } if (Array.isArray(item.type)) { return [makeIcon(item.type)]; } switch (item.type) { case "text": return [makeItem(item.text)]; case "group": { const group = makeGroup(...item.items); return group ? [group] : []; } default: return [makeIcon(item.type)]; } }); if (nodes.length === 0) return; if (nodes.length === 1) return nodes[0]; const span = document.createElement("span"); span.classList.add("item-group"); span.append(...nodes); return span; }; const makeItem = (child: string | Node): HTMLSpanElement => { const span = document.createElement("span"); span.classList.add("item"); span.append(child); return span; }; const makeIcon = (icon: Icon | [Icon, Icon]): HTMLSpanElement => { if (Array.isArray(icon)) { const stack = document.createElement("span"); stack.classList.add("fa-stack"); const icon1 = makeIconBase(icon[0]); icon1.classList.add("fa-stack-1x"); const icon2 = makeIconBase(icon[1]); icon2.classList.add("fa-stack-1x"); stack.append(icon1, icon2); return makeItem(stack); } return makeItem(makeIconBase(icon)); }; const makeIconBase = (icon: Icon): HTMLElement => { const i = document.createElement("i"); switch (icon) { case "spinner": i.classList.add("fa", "fa-spinner"); break; case "check-circle": case "gyazo": case "ocr": case "calendar": case "trim": i.classList.add("kamon", `kamon-${icon}`); break; case "markdown": case "google": i.classList.add("fab", `fa-${icon}`); break; case "copy": case "clipboard": i.classList.add("far", `fa-${icon}`); break; default: i.classList.add("fas", `fa-${icon}`); break; } return i; };