Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155S2Ye8BRUakV7GHAsmYWu
41 lines
2.2 KiB
JavaScript
41 lines
2.2 KiB
JavaScript
// Cards and tiles: a rounded group of fields, a pressable tile, and the grid that lays tiles out.
|
|
import { define, lookup } from "../internals/register.js";
|
|
import { el } from "../internals/el.js";
|
|
import { setText } from "../internals/text.js";
|
|
|
|
/** Related fields on one rounded card. Takes handles, like a stack. */
|
|
define("card", "default", ({ children = [], gap = "md" } = {}) => {
|
|
const stack = lookup("region", "stack")({ children, gap });
|
|
stack.node.classList.add("cx-fieldcard");
|
|
return stack;
|
|
});
|
|
|
|
/** Tiles in rows. `pair` is two columns; `cards` fills the width with columns at least 190 wide. */
|
|
for (const kind of ["pair", "cards"]) {
|
|
define("grid", kind, ({ children = [] } = {}) => {
|
|
const stack = lookup("region", "stack")({ children, gap: "none" });
|
|
stack.node.classList.add("cx-grid", `cx-grid-${kind}`);
|
|
return stack;
|
|
});
|
|
}
|
|
|
|
const TONES = { ready: "Ready", needs: "Needs attention", skipped: "Skipped: not available" };
|
|
|
|
function tile(kind, { title, sub, onClick, state, label } = {}) {
|
|
if (state !== undefined && !TONES[state]) throw new RangeError(`Unknown tile state "${state}". Known: ${Object.keys(TONES).join(", ")}.`);
|
|
const head = el("span", { cls: "cx-tile-title", text: title });
|
|
const hint = el("span", { cls: "cx-tile-sub", text: sub });
|
|
const dot = state ? el("span", { cls: ["cx-tile-state", `cx-tile-${state}`], attrs: { title: TONES[state] } }) : null;
|
|
const node = el("button", { cls: ["cx-tile", `cx-tile-${kind}`, "cx-focusable"], attrs: { type: "button", "aria-label": label },
|
|
children: [dot, head, sub != null && sub !== "" ? hint : null].filter(Boolean) });
|
|
if (onClick) node.addEventListener("click", onClick);
|
|
return { node, setTitle: (v) => setText(head, v), setSub: (v) => setText(hint, v), destroy: () => node.remove() };
|
|
}
|
|
|
|
/** A whole-tile button: a title over one line of hint. */
|
|
define("tile", "action", (props = {}) => tile("action", props));
|
|
/** A node of the pipeline: title, one line of what is set, and a dot that says whether it is ready. */
|
|
define("tile", "node", (props = {}) => tile("node", { state: "ready", ...props }));
|
|
/** The empty slot at the end of a group: "+ Add". */
|
|
define("tile", "add", (props = {}) => tile("add", props));
|