/// /// /// import { Item, makeGroup } from "./item.ts"; import { Cursor, Selection, takeStores } from "../scrapbox-userscript-std/dom.ts"; import type { Scrapbox } from "../scrapbox-jp%2Ftypes/userscript.ts"; declare const scrapbox: Scrapbox; const { cursor, selection } = takeStores(); /** ボタンの設定 */ export interface Button { /** .status-bar > div につけるclass name */ className?: string; /** ボタンに表示するアイテム * * 空文字を渡すと非表示になる * * 函数系は、カーソルが動くたびに呼び出される */ display: Item | Item[] | ((props: Omit) => Item | Item[]); onClick: (props: ClickProps) => void; /** ボタンを表示するページの種類 * * トップページやstreamなど、特殊なページで使いたいボタンがあれば指定する * * @default "page" */ context?: Context; }; export interface ClickProps { cursor: Cursor; selection: Selection; setDisplay: (...items: Item[]) => void; } export type Context = "page" | "stream" | "list"; /** ボタンの描画情報 */ export interface ButtonComponent { status: HTMLDivElement; context: Context; update: () => void; } export const makeButton = (init: Button): ButtonComponent => { const { className, display, onClick, context = "page" } = init; const status = document.createElement("div"); if (className) status.classList.add(className); if (!isContext(context)) status.style.display = "none"; const setDisplay = (...items: Item[]) => { // 空文字の場合は非表示にする if (items.length === 1 && items[0] === "") { status.style.display = "none"; return; } status.textContent = ""; const group = makeGroup(...items); if (group) status.append(group); }; const itemList = typeof display === "function" ? display({ cursor, selection }) : display; setDisplay(...(Array.isArray(itemList) ? itemList : [itemList])); status.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); onClick({ cursor, selection, setDisplay }); }); const update = () => { if (isContext(context)) { status.removeAttribute("style"); } else { status.style.display = "none"; } if (typeof display === "function") { const itemList = display({ cursor, selection }); setDisplay(...(Array.isArray(itemList) ? itemList : [itemList])); } }; return { status, context, update }; }; const isContext = (context: Context): boolean => context !== "stream" ? scrapbox.Layout === context : scrapbox.Layout === "list" && location.pathname.startsWith("/stream");