klods-js 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +10 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/klods.umd.js +1 -1
- package/dist/klods.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
codeBlock: () => codeBlock,
|
|
37
37
|
content: () => content,
|
|
38
38
|
el: () => el,
|
|
39
|
+
fill: () => fill,
|
|
39
40
|
footer: () => footer,
|
|
40
41
|
grid: () => grid,
|
|
41
42
|
header: () => header,
|
|
@@ -48,6 +49,7 @@ __export(index_exports, {
|
|
|
48
49
|
navList: () => navList,
|
|
49
50
|
page: () => page,
|
|
50
51
|
prose: () => prose,
|
|
52
|
+
push: () => push,
|
|
51
53
|
raw: () => raw,
|
|
52
54
|
row: () => row,
|
|
53
55
|
sidebar: () => sidebar,
|
|
@@ -312,7 +314,8 @@ var page = builder({
|
|
|
312
314
|
base: "klods-page",
|
|
313
315
|
modifiers: {
|
|
314
316
|
sidebar: "klods-page--with-sidebar",
|
|
315
|
-
sidebarRight: "klods-page--sidebar-right"
|
|
317
|
+
sidebarRight: "klods-page--sidebar-right",
|
|
318
|
+
stickyHeader: "klods-page--sticky-header"
|
|
316
319
|
}
|
|
317
320
|
});
|
|
318
321
|
var header = builder({ tag: "header", base: "klods-header" });
|
|
@@ -353,6 +356,10 @@ var spread = builder({ tag: "div", base: "klods-spread" });
|
|
|
353
356
|
function text(value) {
|
|
354
357
|
return new KlodsNode("span", {}, [String(value)]);
|
|
355
358
|
}
|
|
359
|
+
|
|
360
|
+
// src/utilities.ts
|
|
361
|
+
var push = builder({ tag: "span", base: "klods-push" });
|
|
362
|
+
var fill = builder({ tag: "div", base: "klods-fill" });
|
|
356
363
|
// Annotate the CommonJS export names for ESM import in node:
|
|
357
364
|
0 && (module.exports = {
|
|
358
365
|
KlodsNode,
|
|
@@ -371,6 +378,7 @@ function text(value) {
|
|
|
371
378
|
codeBlock,
|
|
372
379
|
content,
|
|
373
380
|
el,
|
|
381
|
+
fill,
|
|
374
382
|
footer,
|
|
375
383
|
grid,
|
|
376
384
|
header,
|
|
@@ -383,6 +391,7 @@ function text(value) {
|
|
|
383
391
|
navList,
|
|
384
392
|
page,
|
|
385
393
|
prose,
|
|
394
|
+
push,
|
|
386
395
|
raw,
|
|
387
396
|
row,
|
|
388
397
|
sidebar,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/components.ts","../src/layout.ts"],"sourcesContent":["// Public API surface for the `klods` package.\n\nexport * from \"./components.js\";\nexport * from \"./core.js\";\nexport * from \"./layout.js\";\n","// Core — a tiny VDOM-ish node with two faces:\n// - .render(target?) → HTMLElement (uses real DOM)\n// - .toString() → HTML string (works in Node / Rails / SSR)\n//\n// Both are produced from the same KlodsNode tree, so the docs site can show the\n// TS source, the rendered HTML and the live preview from one source of truth.\n\nexport type KlodsChild = string | number | bigint | boolean | null | undefined | KlodsNode | Node | KlodsChild[];\n\nexport type KlodsAttrs = {\n class?: string | string[] | Record<string, boolean | undefined> | undefined;\n id?: string | undefined;\n style?: string | Partial<CSSStyleDeclaration> | undefined;\n [key: `data-${string}`]: string | number | boolean | undefined;\n [key: `aria-${string}`]: string | number | boolean | undefined;\n // Allow any other HTML attribute or DOM event handler (onClick, onInput, …).\n [key: string]: unknown;\n};\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst RAW = Symbol(\"klods.raw\");\n\ntype RawHtml = { [RAW]: true; html: string };\n\n/** Mark a string as already-escaped HTML; pass it as a child to inject as-is. */\nexport function raw(html: string): RawHtml {\n return { [RAW]: true, html };\n}\n\nfunction isRaw(value: unknown): value is RawHtml {\n return typeof value === \"object\" && value !== null && (value as { [RAW]?: unknown })[RAW] === true;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\n/** Normalise a `class` value (string | array | record) into a clean string. */\nexport function classNames(input: KlodsAttrs[\"class\"]): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return input.trim();\n if (Array.isArray(input)) return input.filter(Boolean).join(\" \").trim();\n return Object.entries(input)\n .filter(([, v]) => Boolean(v))\n .map(([k]) => k)\n .join(\" \")\n .trim();\n}\n\n/** Merge two class values (used internally to combine builder defaults + user-supplied). */\nexport function mergeClasses(...inputs: Array<KlodsAttrs[\"class\"]>): string {\n return inputs\n .map((c) => classNames(c))\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction styleToString(style: string | Partial<CSSStyleDeclaration>): string {\n if (typeof style === \"string\") return style;\n return Object.entries(style)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`)\n .join(\";\");\n}\n\nfunction flattenChildren(children: KlodsChild | KlodsChild[]): Array<Exclude<KlodsChild, KlodsChild[]>> {\n const out: Array<Exclude<KlodsChild, KlodsChild[]>> = [];\n const stack: KlodsChild[] = Array.isArray(children) ? [...children] : [children];\n while (stack.length) {\n const next = stack.shift();\n if (Array.isArray(next)) stack.unshift(...next);\n else if (next !== null && next !== undefined && next !== false && next !== true) out.push(next);\n }\n return out;\n}\n\nexport class KlodsNode {\n readonly tag: string;\n readonly attrs: KlodsAttrs;\n readonly children: KlodsChild[];\n\n constructor(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []) {\n this.tag = tag;\n this.attrs = attrs;\n this.children = Array.isArray(children) ? children : [children];\n }\n\n /** Render to a real DOM element. If `target` is given, append to it. */\n render(target?: Element | null): HTMLElement {\n const el = document.createElement(this.tag);\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) el.setAttribute(\"class\", cls);\n continue;\n }\n if (name === \"style\") {\n el.setAttribute(\"style\", styleToString(value as string | Partial<CSSStyleDeclaration>));\n continue;\n }\n if (name.startsWith(\"on\") && typeof value === \"function\") {\n el.addEventListener(name.slice(2).toLowerCase(), value as EventListener);\n continue;\n }\n if (value === true) {\n el.setAttribute(name, \"\");\n continue;\n }\n el.setAttribute(name, String(value));\n }\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) el.appendChild(child.render());\n else if (typeof child === \"object\" && child !== null && \"nodeType\" in child) {\n el.appendChild(child as Node);\n } else if (isRaw(child)) {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = child.html;\n el.appendChild(tpl.content);\n } else {\n el.appendChild(document.createTextNode(String(child)));\n }\n }\n if (target) target.appendChild(el);\n return el;\n }\n\n /** Render to a string of HTML. */\n toString(): string {\n const parts: string[] = [`<${this.tag}`];\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name.startsWith(\"on\") && typeof value === \"function\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) parts.push(` class=\"${escapeAttr(cls)}\"`);\n continue;\n }\n if (name === \"style\") {\n parts.push(` style=\"${escapeAttr(styleToString(value as string | Partial<CSSStyleDeclaration>))}\"`);\n continue;\n }\n if (value === true) {\n parts.push(` ${name}`);\n continue;\n }\n parts.push(` ${name}=\"${escapeAttr(String(value))}\"`);\n }\n if (VOID_TAGS.has(this.tag)) {\n parts.push(\" />\");\n return parts.join(\"\");\n }\n parts.push(\">\");\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) parts.push(child.toString());\n else if (isRaw(child)) parts.push(child.html);\n else if (typeof child === \"object\" && child !== null && \"outerHTML\" in child) {\n parts.push((child as Element).outerHTML);\n } else {\n parts.push(escapeHtml(String(child)));\n }\n }\n parts.push(`</${this.tag}>`);\n return parts.join(\"\");\n }\n}\n\n/**\n * Generic element builder. Most consumers use the named builders (page, header, …)\n * rather than calling `el` directly, but it's exported as an escape hatch.\n */\nexport function el(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []): KlodsNode {\n return new KlodsNode(tag, attrs, children);\n}\n\n/**\n * Factory that produces a typed builder for a tag with a base class. Modifier\n * props are converted into `--modifier` BEM classes and stripped from the output\n * attributes; everything else passes through untouched, so consumers can attach\n * arbitrary `id`, `data-*`, `aria-*`, event handlers, `style`, etc.\n */\nexport function builder<P extends Record<string, unknown> = Record<never, never>>(options: {\n tag: string;\n base: string;\n /** Map of prop name → class (or function returning a class) when the prop is truthy. */\n modifiers?: { [K in keyof P]?: string | ((value: P[K]) => string | undefined) };\n}): (props?: (P & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]) => KlodsNode {\n const { tag, base, modifiers = {} } = options;\n const modifierMap = modifiers as Record<string, string | ((value: unknown) => string | undefined) | undefined>;\n return (props, children) => {\n const userProps = (props ?? {}) as P & KlodsAttrs;\n const modClasses: string[] = [];\n const passthrough: KlodsAttrs = {};\n for (const [key, value] of Object.entries(userProps)) {\n const m = modifierMap[key];\n if (m !== undefined) {\n if (typeof m === \"function\") {\n const c = m(value);\n if (c) modClasses.push(c);\n } else if (value) {\n modClasses.push(m);\n }\n } else {\n passthrough[key] = value;\n }\n }\n const finalClass = mergeClasses(base, ...modClasses, userProps.class);\n const resolvedChildren =\n children !== undefined ? children : ((userProps.children as KlodsChild | KlodsChild[] | undefined) ?? []);\n delete passthrough.children;\n return new KlodsNode(tag, { ...passthrough, class: finalClass || undefined }, resolvedChildren);\n };\n}\n","// First wave of components: nav, card, button, badge, alert, prose helpers.\n// All match the BEM classes shipped by klods-css.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, el, KlodsNode } from \"./core.js\";\n\n// ── Nav ──────────────────────────────────────────────────────────────────\nexport const nav = builder({ tag: \"nav\", base: \"klods-nav\" });\nexport const navList = builder({ tag: \"ul\", base: \"klods-nav__list\" });\n\nexport type NavLinkProps = {\n href?: string;\n active?: boolean;\n};\nconst navLinkBuilder = builder<NavLinkProps>({\n tag: \"a\",\n base: \"klods-nav__link\",\n modifiers: { active: \"klods-nav__link--active\" },\n});\nexport function navLink(props?: (NavLinkProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Wrap each link in <li> so it's idiomatic inside <ul class=\"klods-nav__list\">.\n return el(\"li\", {}, [navLinkBuilder(props ?? null, children)]);\n}\n\n// ── Card ─────────────────────────────────────────────────────────────────\nexport type CardProps = {\n elevated?: boolean;\n};\nexport const card = builder<CardProps>({\n tag: \"div\",\n base: \"klods-card\",\n modifiers: { elevated: \"klods-card--elevated\" },\n});\nexport const cardTitle = builder({ tag: \"h3\", base: \"klods-card__title\" });\nexport const cardBody = builder({ tag: \"div\", base: \"klods-card__body\" });\nexport const cardFooter = builder({ tag: \"div\", base: \"klods-card__footer\" });\n\n// ── Button ───────────────────────────────────────────────────────────────\nexport type ButtonProps = {\n variant?: \"default\" | \"primary\" | \"danger\" | \"ghost\";\n type?: \"button\" | \"submit\" | \"reset\";\n};\nconst buttonBase = builder<ButtonProps>({\n tag: \"button\",\n base: \"klods-button\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-button--${v}` : undefined),\n },\n});\nexport function button(props?: (ButtonProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Default `type=\"button\"` so it never accidentally submits a form.\n const merged = { type: \"button\", ...(props ?? {}) } as ButtonProps & KlodsAttrs;\n return buttonBase(merged, children);\n}\n\n// ── Badge ────────────────────────────────────────────────────────────────\nexport type BadgeProps = {\n variant?: \"default\" | \"accent\" | \"success\" | \"danger\";\n};\nexport const badge = builder<BadgeProps>({\n tag: \"span\",\n base: \"klods-badge\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-badge--${v}` : undefined),\n },\n});\n\n// ── Alert ────────────────────────────────────────────────────────────────\nexport type AlertProps = {\n variant?: \"default\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\nconst alertBase = builder<AlertProps>({\n tag: \"div\",\n base: \"klods-alert\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-alert--${v}` : undefined),\n },\n});\nexport function alert(props?: (AlertProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // role=alert by default for assistive tech, overridable.\n const merged = { role: \"alert\", ...(props ?? {}) } as AlertProps & KlodsAttrs;\n return alertBase(merged, children);\n}\n\n// ── Prose helpers ────────────────────────────────────────────────────────\nexport const prose = builder({ tag: \"div\", base: \"klods-prose\" });\nexport const muted = builder({ tag: \"span\", base: \"klods-muted\" });\nexport const lead = builder({ tag: \"p\", base: \"klods-lead\" });\n\n// ── Table ────────────────────────────────────────────────────────────────\nexport type TableProps = {\n striped?: boolean;\n dense?: boolean;\n};\nexport const table = builder<TableProps>({\n tag: \"table\",\n base: \"klods-table\",\n modifiers: {\n striped: \"klods-table--striped\",\n dense: \"klods-table--dense\",\n },\n});\nexport const thead = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"thead\", attrs ?? {}, children);\nexport const tbody = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tbody\", attrs ?? {}, children);\nexport const tr = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tr\", attrs ?? {}, children);\nexport const th = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"th\", attrs ?? {}, children);\nexport const td = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"td\", attrs ?? {}, children);\n\n// ── Code ─────────────────────────────────────────────────────────────────\nexport function codeBlock(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"pre\", attrs ?? {}, el(\"code\", {}, content));\n}\nexport function inlineCode(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"code\", attrs ?? {}, content);\n}\n\n// ── Box ──────────────────────────────────────────────────────────────────\nexport const box = builder({ tag: \"div\", base: \"klods-box\" });\n","// Layout builders — the four corners and the \"I-always-forget\" utilities.\n// Each builder is a thin wrapper around `builder()` from core.ts.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, KlodsNode } from \"./core.js\";\n\n// ── Page ─────────────────────────────────────────────────────────────────\nexport type PageProps = {\n /** Render with a sidebar column. */\n sidebar?: boolean;\n /** Place the sidebar on the right (only meaningful with `sidebar: true`). */\n sidebarRight?: boolean;\n};\n\nexport const page = builder<PageProps>({\n tag: \"div\",\n base: \"klods-page\",\n modifiers: {\n sidebar: \"klods-page--with-sidebar\",\n sidebarRight: \"klods-page--sidebar-right\",\n },\n});\n\n// ── Page slots ───────────────────────────────────────────────────────────\nexport const header = builder({ tag: \"header\", base: \"klods-header\" });\nexport const sidebar = builder({ tag: \"aside\", base: \"klods-sidebar\" });\n\nexport type ContentProps = {\n /** Cap content width to --klods-content-max and centre it. */\n narrow?: boolean;\n};\nexport const content = builder<ContentProps>({\n tag: \"main\",\n base: \"klods-content\",\n modifiers: { narrow: \"klods-content--narrow\" },\n});\n\nexport const footer = builder({ tag: \"footer\", base: \"klods-footer\" });\n\n// ── Layout utilities ─────────────────────────────────────────────────────\ntype GapProp = { gap?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 };\n\nconst gapModifier = (prefix: string) => (v: number | undefined) =>\n v === undefined ? undefined : `${prefix}--gap-${v}`;\n\nexport const stack = builder<GapProp>({\n tag: \"div\",\n base: \"klods-stack\",\n modifiers: { gap: gapModifier(\"klods-stack\") },\n});\n\nexport const cluster = builder<GapProp>({\n tag: \"div\",\n base: \"klods-cluster\",\n modifiers: { gap: gapModifier(\"klods-cluster\") },\n});\n\nexport const row = builder<GapProp>({\n tag: \"div\",\n base: \"klods-row\",\n modifiers: { gap: gapModifier(\"klods-row\") },\n});\n\nexport type GridProps = GapProp & {\n cols?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Auto-fit responsive columns; pair with `--klods-grid-min` if you want a custom minimum. */\n fit?: boolean;\n};\nexport const grid = builder<GridProps>({\n tag: \"div\",\n base: \"klods-grid\",\n modifiers: {\n gap: gapModifier(\"klods-grid\"),\n cols: (v) => (v === undefined ? undefined : `klods-grid--cols-${v}`),\n fit: \"klods-grid--fit\",\n },\n});\n\nexport const center = builder({ tag: \"div\", base: \"klods-center\" });\nexport const spread = builder({ tag: \"div\", base: \"klods-spread\" });\n\n// ── Convenience: empty fragment-ish wrapper for quick text + nodes ──────\nexport function text(value: string | number): KlodsNode {\n // Wrap loose text in a span so it composes anywhere a KlodsNode is expected.\n return new KlodsNode(\"span\", {}, [String(value)]);\n}\n\n// Re-export attribute / child types so consumers can extend a builder neatly.\nexport type { KlodsAttrs, KlodsChild };\n"],"mappings":"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","names":["stack","el","content"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/components.ts","../src/layout.ts","../src/utilities.ts"],"sourcesContent":["// Public API surface for the `klods` package.\n\nexport * from \"./components.js\";\nexport * from \"./core.js\";\nexport * from \"./layout.js\";\nexport * from \"./utilities.js\";\n","// Core — a tiny VDOM-ish node with two faces:\n// - .render(target?) → HTMLElement (uses real DOM)\n// - .toString() → HTML string (works in Node / Rails / SSR)\n//\n// Both are produced from the same KlodsNode tree, so the docs site can show the\n// TS source, the rendered HTML and the live preview from one source of truth.\n\nexport type KlodsChild = string | number | bigint | boolean | null | undefined | KlodsNode | Node | KlodsChild[];\n\nexport type KlodsAttrs = {\n class?: string | string[] | Record<string, boolean | undefined> | undefined;\n id?: string | undefined;\n style?: string | Partial<CSSStyleDeclaration> | undefined;\n [key: `data-${string}`]: string | number | boolean | undefined;\n [key: `aria-${string}`]: string | number | boolean | undefined;\n // Allow any other HTML attribute or DOM event handler (onClick, onInput, …).\n [key: string]: unknown;\n};\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst RAW = Symbol(\"klods.raw\");\n\ntype RawHtml = { [RAW]: true; html: string };\n\n/** Mark a string as already-escaped HTML; pass it as a child to inject as-is. */\nexport function raw(html: string): RawHtml {\n return { [RAW]: true, html };\n}\n\nfunction isRaw(value: unknown): value is RawHtml {\n return typeof value === \"object\" && value !== null && (value as { [RAW]?: unknown })[RAW] === true;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\n/** Normalise a `class` value (string | array | record) into a clean string. */\nexport function classNames(input: KlodsAttrs[\"class\"]): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return input.trim();\n if (Array.isArray(input)) return input.filter(Boolean).join(\" \").trim();\n return Object.entries(input)\n .filter(([, v]) => Boolean(v))\n .map(([k]) => k)\n .join(\" \")\n .trim();\n}\n\n/** Merge two class values (used internally to combine builder defaults + user-supplied). */\nexport function mergeClasses(...inputs: Array<KlodsAttrs[\"class\"]>): string {\n return inputs\n .map((c) => classNames(c))\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction styleToString(style: string | Partial<CSSStyleDeclaration>): string {\n if (typeof style === \"string\") return style;\n return Object.entries(style)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`)\n .join(\";\");\n}\n\nfunction flattenChildren(children: KlodsChild | KlodsChild[]): Array<Exclude<KlodsChild, KlodsChild[]>> {\n const out: Array<Exclude<KlodsChild, KlodsChild[]>> = [];\n const stack: KlodsChild[] = Array.isArray(children) ? [...children] : [children];\n while (stack.length) {\n const next = stack.shift();\n if (Array.isArray(next)) stack.unshift(...next);\n else if (next !== null && next !== undefined && next !== false && next !== true) out.push(next);\n }\n return out;\n}\n\nexport class KlodsNode {\n readonly tag: string;\n readonly attrs: KlodsAttrs;\n readonly children: KlodsChild[];\n\n constructor(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []) {\n this.tag = tag;\n this.attrs = attrs;\n this.children = Array.isArray(children) ? children : [children];\n }\n\n /** Render to a real DOM element. If `target` is given, append to it. */\n render(target?: Element | null): HTMLElement {\n const el = document.createElement(this.tag);\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) el.setAttribute(\"class\", cls);\n continue;\n }\n if (name === \"style\") {\n el.setAttribute(\"style\", styleToString(value as string | Partial<CSSStyleDeclaration>));\n continue;\n }\n if (name.startsWith(\"on\") && typeof value === \"function\") {\n el.addEventListener(name.slice(2).toLowerCase(), value as EventListener);\n continue;\n }\n if (value === true) {\n el.setAttribute(name, \"\");\n continue;\n }\n el.setAttribute(name, String(value));\n }\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) el.appendChild(child.render());\n else if (typeof child === \"object\" && child !== null && \"nodeType\" in child) {\n el.appendChild(child as Node);\n } else if (isRaw(child)) {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = child.html;\n el.appendChild(tpl.content);\n } else {\n el.appendChild(document.createTextNode(String(child)));\n }\n }\n if (target) target.appendChild(el);\n return el;\n }\n\n /** Render to a string of HTML. */\n toString(): string {\n const parts: string[] = [`<${this.tag}`];\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name.startsWith(\"on\") && typeof value === \"function\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) parts.push(` class=\"${escapeAttr(cls)}\"`);\n continue;\n }\n if (name === \"style\") {\n parts.push(` style=\"${escapeAttr(styleToString(value as string | Partial<CSSStyleDeclaration>))}\"`);\n continue;\n }\n if (value === true) {\n parts.push(` ${name}`);\n continue;\n }\n parts.push(` ${name}=\"${escapeAttr(String(value))}\"`);\n }\n if (VOID_TAGS.has(this.tag)) {\n parts.push(\" />\");\n return parts.join(\"\");\n }\n parts.push(\">\");\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) parts.push(child.toString());\n else if (isRaw(child)) parts.push(child.html);\n else if (typeof child === \"object\" && child !== null && \"outerHTML\" in child) {\n parts.push((child as Element).outerHTML);\n } else {\n parts.push(escapeHtml(String(child)));\n }\n }\n parts.push(`</${this.tag}>`);\n return parts.join(\"\");\n }\n}\n\n/**\n * Generic element builder. Most consumers use the named builders (page, header, …)\n * rather than calling `el` directly, but it's exported as an escape hatch.\n */\nexport function el(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []): KlodsNode {\n return new KlodsNode(tag, attrs, children);\n}\n\n/**\n * Factory that produces a typed builder for a tag with a base class. Modifier\n * props are converted into `--modifier` BEM classes and stripped from the output\n * attributes; everything else passes through untouched, so consumers can attach\n * arbitrary `id`, `data-*`, `aria-*`, event handlers, `style`, etc.\n */\nexport function builder<P extends Record<string, unknown> = Record<never, never>>(options: {\n tag: string;\n base: string;\n /** Map of prop name → class (or function returning a class) when the prop is truthy. */\n modifiers?: { [K in keyof P]?: string | ((value: P[K]) => string | undefined) };\n}): (props?: (P & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]) => KlodsNode {\n const { tag, base, modifiers = {} } = options;\n const modifierMap = modifiers as Record<string, string | ((value: unknown) => string | undefined) | undefined>;\n return (props, children) => {\n const userProps = (props ?? {}) as P & KlodsAttrs;\n const modClasses: string[] = [];\n const passthrough: KlodsAttrs = {};\n for (const [key, value] of Object.entries(userProps)) {\n const m = modifierMap[key];\n if (m !== undefined) {\n if (typeof m === \"function\") {\n const c = m(value);\n if (c) modClasses.push(c);\n } else if (value) {\n modClasses.push(m);\n }\n } else {\n passthrough[key] = value;\n }\n }\n const finalClass = mergeClasses(base, ...modClasses, userProps.class);\n const resolvedChildren =\n children !== undefined ? children : ((userProps.children as KlodsChild | KlodsChild[] | undefined) ?? []);\n delete passthrough.children;\n return new KlodsNode(tag, { ...passthrough, class: finalClass || undefined }, resolvedChildren);\n };\n}\n","// First wave of components: nav, card, button, badge, alert, prose helpers.\n// All match the BEM classes shipped by klods-css.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, el, KlodsNode } from \"./core.js\";\n\n// ── Nav ──────────────────────────────────────────────────────────────────\nexport const nav = builder({ tag: \"nav\", base: \"klods-nav\" });\nexport const navList = builder({ tag: \"ul\", base: \"klods-nav__list\" });\n\nexport type NavLinkProps = {\n href?: string;\n active?: boolean;\n};\nconst navLinkBuilder = builder<NavLinkProps>({\n tag: \"a\",\n base: \"klods-nav__link\",\n modifiers: { active: \"klods-nav__link--active\" },\n});\nexport function navLink(props?: (NavLinkProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Wrap each link in <li> so it's idiomatic inside <ul class=\"klods-nav__list\">.\n return el(\"li\", {}, [navLinkBuilder(props ?? null, children)]);\n}\n\n// ── Card ─────────────────────────────────────────────────────────────────\nexport type CardProps = {\n elevated?: boolean;\n};\nexport const card = builder<CardProps>({\n tag: \"div\",\n base: \"klods-card\",\n modifiers: { elevated: \"klods-card--elevated\" },\n});\nexport const cardTitle = builder({ tag: \"h3\", base: \"klods-card__title\" });\nexport const cardBody = builder({ tag: \"div\", base: \"klods-card__body\" });\nexport const cardFooter = builder({ tag: \"div\", base: \"klods-card__footer\" });\n\n// ── Button ───────────────────────────────────────────────────────────────\nexport type ButtonProps = {\n variant?: \"default\" | \"primary\" | \"danger\" | \"ghost\";\n type?: \"button\" | \"submit\" | \"reset\";\n};\nconst buttonBase = builder<ButtonProps>({\n tag: \"button\",\n base: \"klods-button\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-button--${v}` : undefined),\n },\n});\nexport function button(props?: (ButtonProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Default `type=\"button\"` so it never accidentally submits a form.\n const merged = { type: \"button\", ...(props ?? {}) } as ButtonProps & KlodsAttrs;\n return buttonBase(merged, children);\n}\n\n// ── Badge ────────────────────────────────────────────────────────────────\nexport type BadgeProps = {\n variant?: \"default\" | \"accent\" | \"success\" | \"danger\";\n};\nexport const badge = builder<BadgeProps>({\n tag: \"span\",\n base: \"klods-badge\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-badge--${v}` : undefined),\n },\n});\n\n// ── Alert ────────────────────────────────────────────────────────────────\nexport type AlertProps = {\n variant?: \"default\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\nconst alertBase = builder<AlertProps>({\n tag: \"div\",\n base: \"klods-alert\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-alert--${v}` : undefined),\n },\n});\nexport function alert(props?: (AlertProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // role=alert by default for assistive tech, overridable.\n const merged = { role: \"alert\", ...(props ?? {}) } as AlertProps & KlodsAttrs;\n return alertBase(merged, children);\n}\n\n// ── Prose helpers ────────────────────────────────────────────────────────\nexport const prose = builder({ tag: \"div\", base: \"klods-prose\" });\nexport const muted = builder({ tag: \"span\", base: \"klods-muted\" });\nexport const lead = builder({ tag: \"p\", base: \"klods-lead\" });\n\n// ── Table ────────────────────────────────────────────────────────────────\nexport type TableProps = {\n striped?: boolean;\n dense?: boolean;\n};\nexport const table = builder<TableProps>({\n tag: \"table\",\n base: \"klods-table\",\n modifiers: {\n striped: \"klods-table--striped\",\n dense: \"klods-table--dense\",\n },\n});\nexport const thead = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"thead\", attrs ?? {}, children);\nexport const tbody = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tbody\", attrs ?? {}, children);\nexport const tr = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tr\", attrs ?? {}, children);\nexport const th = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"th\", attrs ?? {}, children);\nexport const td = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"td\", attrs ?? {}, children);\n\n// ── Code ─────────────────────────────────────────────────────────────────\nexport function codeBlock(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"pre\", attrs ?? {}, el(\"code\", {}, content));\n}\nexport function inlineCode(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"code\", attrs ?? {}, content);\n}\n\n// ── Box ──────────────────────────────────────────────────────────────────\nexport const box = builder({ tag: \"div\", base: \"klods-box\" });\n","// Layout builders — the four corners and the \"I-always-forget\" utilities.\n// Each builder is a thin wrapper around `builder()` from core.ts.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, KlodsNode } from \"./core.js\";\n\n// ── Page ─────────────────────────────────────────────────────────────────\nexport type PageProps = {\n /** Render with a sidebar column. */\n sidebar?: boolean;\n /** Place the sidebar on the right (only meaningful with `sidebar: true`). */\n sidebarRight?: boolean;\n /** Keep the header pinned to the top of the viewport while the page scrolls. */\n stickyHeader?: boolean;\n};\n\nexport const page = builder<PageProps>({\n tag: \"div\",\n base: \"klods-page\",\n modifiers: {\n sidebar: \"klods-page--with-sidebar\",\n sidebarRight: \"klods-page--sidebar-right\",\n stickyHeader: \"klods-page--sticky-header\",\n },\n});\n\n// ── Page slots ───────────────────────────────────────────────────────────\nexport const header = builder({ tag: \"header\", base: \"klods-header\" });\nexport const sidebar = builder({ tag: \"aside\", base: \"klods-sidebar\" });\n\nexport type ContentProps = {\n /** Cap content width to --klods-content-max and centre it. */\n narrow?: boolean;\n};\nexport const content = builder<ContentProps>({\n tag: \"main\",\n base: \"klods-content\",\n modifiers: { narrow: \"klods-content--narrow\" },\n});\n\nexport const footer = builder({ tag: \"footer\", base: \"klods-footer\" });\n\n// ── Layout utilities ─────────────────────────────────────────────────────\ntype GapProp = { gap?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 };\n\nconst gapModifier = (prefix: string) => (v: number | undefined) =>\n v === undefined ? undefined : `${prefix}--gap-${v}`;\n\nexport const stack = builder<GapProp>({\n tag: \"div\",\n base: \"klods-stack\",\n modifiers: { gap: gapModifier(\"klods-stack\") },\n});\n\nexport const cluster = builder<GapProp>({\n tag: \"div\",\n base: \"klods-cluster\",\n modifiers: { gap: gapModifier(\"klods-cluster\") },\n});\n\nexport const row = builder<GapProp>({\n tag: \"div\",\n base: \"klods-row\",\n modifiers: { gap: gapModifier(\"klods-row\") },\n});\n\nexport type GridProps = GapProp & {\n cols?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Auto-fit responsive columns; pair with `--klods-grid-min` if you want a custom minimum. */\n fit?: boolean;\n};\nexport const grid = builder<GridProps>({\n tag: \"div\",\n base: \"klods-grid\",\n modifiers: {\n gap: gapModifier(\"klods-grid\"),\n cols: (v) => (v === undefined ? undefined : `klods-grid--cols-${v}`),\n fit: \"klods-grid--fit\",\n },\n});\n\nexport const center = builder({ tag: \"div\", base: \"klods-center\" });\nexport const spread = builder({ tag: \"div\", base: \"klods-spread\" });\n\n// ── Convenience: empty fragment-ish wrapper for quick text + nodes ──────\nexport function text(value: string | number): KlodsNode {\n // Wrap loose text in a span so it composes anywhere a KlodsNode is expected.\n return new KlodsNode(\"span\", {}, [String(value)]);\n}\n\n// Re-export attribute / child types so consumers can extend a builder neatly.\nexport type { KlodsAttrs, KlodsChild };\n","// Utility builders — thin wrappers over the most-reached-for klods-css utility classes.\n\nimport { builder } from \"./core.js\";\n\n// ── Push ─────────────────────────────────────────────────────────────────\n// Renders a <span class=\"klods-push\">.\n// Pushes siblings to the end of a flex/grid row by consuming all remaining\n// inline space (margin-inline-start: auto).\nexport const push = builder({ tag: \"span\", base: \"klods-push\" });\n\n// ── Fill ─────────────────────────────────────────────────────────────────\n// Renders a <div class=\"klods-fill\">.\n// Grows to fill available flex/grid space (flex: 1 1 auto).\n// Useful as a wrapper when you need one slot to absorb leftover room —\n// e.g. equal-width side groups in a header to centre a middle item.\nexport const fill = builder({ tag: \"div\", base: \"klods-fill\" });\n"],"mappings":"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","names":["stack","el","content"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -97,6 +97,8 @@ type PageProps = {
|
|
|
97
97
|
sidebar?: boolean;
|
|
98
98
|
/** Place the sidebar on the right (only meaningful with `sidebar: true`). */
|
|
99
99
|
sidebarRight?: boolean;
|
|
100
|
+
/** Keep the header pinned to the top of the viewport while the page scrolls. */
|
|
101
|
+
stickyHeader?: boolean;
|
|
100
102
|
};
|
|
101
103
|
declare const page: (props?: (PageProps & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
102
104
|
declare const header: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
@@ -127,4 +129,7 @@ declare const center: (props?: (Record<never, never> & KlodsAttrs) | null | unde
|
|
|
127
129
|
declare const spread: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
128
130
|
declare function text(value: string | number): KlodsNode;
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
declare const push: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
133
|
+
declare const fill: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
134
|
+
|
|
135
|
+
export { type AlertProps, type BadgeProps, type ButtonProps, type CardProps, type ContentProps, type GridProps, type KlodsAttrs, type KlodsChild, KlodsNode, type NavLinkProps, type PageProps, type TableProps, alert, badge, box, builder, button, card, cardBody, cardFooter, cardTitle, center, classNames, cluster, codeBlock, content, el, fill, footer, grid, header, inlineCode, lead, mergeClasses, muted, nav, navLink, navList, page, prose, push, raw, row, sidebar, spread, stack, table, tbody, td, text, th, thead, tr };
|
package/dist/index.d.ts
CHANGED
|
@@ -97,6 +97,8 @@ type PageProps = {
|
|
|
97
97
|
sidebar?: boolean;
|
|
98
98
|
/** Place the sidebar on the right (only meaningful with `sidebar: true`). */
|
|
99
99
|
sidebarRight?: boolean;
|
|
100
|
+
/** Keep the header pinned to the top of the viewport while the page scrolls. */
|
|
101
|
+
stickyHeader?: boolean;
|
|
100
102
|
};
|
|
101
103
|
declare const page: (props?: (PageProps & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
102
104
|
declare const header: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
@@ -127,4 +129,7 @@ declare const center: (props?: (Record<never, never> & KlodsAttrs) | null | unde
|
|
|
127
129
|
declare const spread: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
128
130
|
declare function text(value: string | number): KlodsNode;
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
declare const push: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
133
|
+
declare const fill: (props?: (Record<never, never> & KlodsAttrs) | null | undefined, children?: KlodsChild | KlodsChild[]) => KlodsNode;
|
|
134
|
+
|
|
135
|
+
export { type AlertProps, type BadgeProps, type ButtonProps, type CardProps, type ContentProps, type GridProps, type KlodsAttrs, type KlodsChild, KlodsNode, type NavLinkProps, type PageProps, type TableProps, alert, badge, box, builder, button, card, cardBody, cardFooter, cardTitle, center, classNames, cluster, codeBlock, content, el, fill, footer, grid, header, inlineCode, lead, mergeClasses, muted, nav, navLink, navList, page, prose, push, raw, row, sidebar, spread, stack, table, tbody, td, text, th, thead, tr };
|
package/dist/index.js
CHANGED
|
@@ -247,7 +247,8 @@ var page = builder({
|
|
|
247
247
|
base: "klods-page",
|
|
248
248
|
modifiers: {
|
|
249
249
|
sidebar: "klods-page--with-sidebar",
|
|
250
|
-
sidebarRight: "klods-page--sidebar-right"
|
|
250
|
+
sidebarRight: "klods-page--sidebar-right",
|
|
251
|
+
stickyHeader: "klods-page--sticky-header"
|
|
251
252
|
}
|
|
252
253
|
});
|
|
253
254
|
var header = builder({ tag: "header", base: "klods-header" });
|
|
@@ -288,6 +289,10 @@ var spread = builder({ tag: "div", base: "klods-spread" });
|
|
|
288
289
|
function text(value) {
|
|
289
290
|
return new KlodsNode("span", {}, [String(value)]);
|
|
290
291
|
}
|
|
292
|
+
|
|
293
|
+
// src/utilities.ts
|
|
294
|
+
var push = builder({ tag: "span", base: "klods-push" });
|
|
295
|
+
var fill = builder({ tag: "div", base: "klods-fill" });
|
|
291
296
|
export {
|
|
292
297
|
KlodsNode,
|
|
293
298
|
alert,
|
|
@@ -305,6 +310,7 @@ export {
|
|
|
305
310
|
codeBlock,
|
|
306
311
|
content,
|
|
307
312
|
el,
|
|
313
|
+
fill,
|
|
308
314
|
footer,
|
|
309
315
|
grid,
|
|
310
316
|
header,
|
|
@@ -317,6 +323,7 @@ export {
|
|
|
317
323
|
navList,
|
|
318
324
|
page,
|
|
319
325
|
prose,
|
|
326
|
+
push,
|
|
320
327
|
raw,
|
|
321
328
|
row,
|
|
322
329
|
sidebar,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../src/components.ts","../src/layout.ts"],"sourcesContent":["// Core — a tiny VDOM-ish node with two faces:\n// - .render(target?) → HTMLElement (uses real DOM)\n// - .toString() → HTML string (works in Node / Rails / SSR)\n//\n// Both are produced from the same KlodsNode tree, so the docs site can show the\n// TS source, the rendered HTML and the live preview from one source of truth.\n\nexport type KlodsChild = string | number | bigint | boolean | null | undefined | KlodsNode | Node | KlodsChild[];\n\nexport type KlodsAttrs = {\n class?: string | string[] | Record<string, boolean | undefined> | undefined;\n id?: string | undefined;\n style?: string | Partial<CSSStyleDeclaration> | undefined;\n [key: `data-${string}`]: string | number | boolean | undefined;\n [key: `aria-${string}`]: string | number | boolean | undefined;\n // Allow any other HTML attribute or DOM event handler (onClick, onInput, …).\n [key: string]: unknown;\n};\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst RAW = Symbol(\"klods.raw\");\n\ntype RawHtml = { [RAW]: true; html: string };\n\n/** Mark a string as already-escaped HTML; pass it as a child to inject as-is. */\nexport function raw(html: string): RawHtml {\n return { [RAW]: true, html };\n}\n\nfunction isRaw(value: unknown): value is RawHtml {\n return typeof value === \"object\" && value !== null && (value as { [RAW]?: unknown })[RAW] === true;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\n/** Normalise a `class` value (string | array | record) into a clean string. */\nexport function classNames(input: KlodsAttrs[\"class\"]): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return input.trim();\n if (Array.isArray(input)) return input.filter(Boolean).join(\" \").trim();\n return Object.entries(input)\n .filter(([, v]) => Boolean(v))\n .map(([k]) => k)\n .join(\" \")\n .trim();\n}\n\n/** Merge two class values (used internally to combine builder defaults + user-supplied). */\nexport function mergeClasses(...inputs: Array<KlodsAttrs[\"class\"]>): string {\n return inputs\n .map((c) => classNames(c))\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction styleToString(style: string | Partial<CSSStyleDeclaration>): string {\n if (typeof style === \"string\") return style;\n return Object.entries(style)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`)\n .join(\";\");\n}\n\nfunction flattenChildren(children: KlodsChild | KlodsChild[]): Array<Exclude<KlodsChild, KlodsChild[]>> {\n const out: Array<Exclude<KlodsChild, KlodsChild[]>> = [];\n const stack: KlodsChild[] = Array.isArray(children) ? [...children] : [children];\n while (stack.length) {\n const next = stack.shift();\n if (Array.isArray(next)) stack.unshift(...next);\n else if (next !== null && next !== undefined && next !== false && next !== true) out.push(next);\n }\n return out;\n}\n\nexport class KlodsNode {\n readonly tag: string;\n readonly attrs: KlodsAttrs;\n readonly children: KlodsChild[];\n\n constructor(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []) {\n this.tag = tag;\n this.attrs = attrs;\n this.children = Array.isArray(children) ? children : [children];\n }\n\n /** Render to a real DOM element. If `target` is given, append to it. */\n render(target?: Element | null): HTMLElement {\n const el = document.createElement(this.tag);\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) el.setAttribute(\"class\", cls);\n continue;\n }\n if (name === \"style\") {\n el.setAttribute(\"style\", styleToString(value as string | Partial<CSSStyleDeclaration>));\n continue;\n }\n if (name.startsWith(\"on\") && typeof value === \"function\") {\n el.addEventListener(name.slice(2).toLowerCase(), value as EventListener);\n continue;\n }\n if (value === true) {\n el.setAttribute(name, \"\");\n continue;\n }\n el.setAttribute(name, String(value));\n }\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) el.appendChild(child.render());\n else if (typeof child === \"object\" && child !== null && \"nodeType\" in child) {\n el.appendChild(child as Node);\n } else if (isRaw(child)) {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = child.html;\n el.appendChild(tpl.content);\n } else {\n el.appendChild(document.createTextNode(String(child)));\n }\n }\n if (target) target.appendChild(el);\n return el;\n }\n\n /** Render to a string of HTML. */\n toString(): string {\n const parts: string[] = [`<${this.tag}`];\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name.startsWith(\"on\") && typeof value === \"function\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) parts.push(` class=\"${escapeAttr(cls)}\"`);\n continue;\n }\n if (name === \"style\") {\n parts.push(` style=\"${escapeAttr(styleToString(value as string | Partial<CSSStyleDeclaration>))}\"`);\n continue;\n }\n if (value === true) {\n parts.push(` ${name}`);\n continue;\n }\n parts.push(` ${name}=\"${escapeAttr(String(value))}\"`);\n }\n if (VOID_TAGS.has(this.tag)) {\n parts.push(\" />\");\n return parts.join(\"\");\n }\n parts.push(\">\");\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) parts.push(child.toString());\n else if (isRaw(child)) parts.push(child.html);\n else if (typeof child === \"object\" && child !== null && \"outerHTML\" in child) {\n parts.push((child as Element).outerHTML);\n } else {\n parts.push(escapeHtml(String(child)));\n }\n }\n parts.push(`</${this.tag}>`);\n return parts.join(\"\");\n }\n}\n\n/**\n * Generic element builder. Most consumers use the named builders (page, header, …)\n * rather than calling `el` directly, but it's exported as an escape hatch.\n */\nexport function el(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []): KlodsNode {\n return new KlodsNode(tag, attrs, children);\n}\n\n/**\n * Factory that produces a typed builder for a tag with a base class. Modifier\n * props are converted into `--modifier` BEM classes and stripped from the output\n * attributes; everything else passes through untouched, so consumers can attach\n * arbitrary `id`, `data-*`, `aria-*`, event handlers, `style`, etc.\n */\nexport function builder<P extends Record<string, unknown> = Record<never, never>>(options: {\n tag: string;\n base: string;\n /** Map of prop name → class (or function returning a class) when the prop is truthy. */\n modifiers?: { [K in keyof P]?: string | ((value: P[K]) => string | undefined) };\n}): (props?: (P & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]) => KlodsNode {\n const { tag, base, modifiers = {} } = options;\n const modifierMap = modifiers as Record<string, string | ((value: unknown) => string | undefined) | undefined>;\n return (props, children) => {\n const userProps = (props ?? {}) as P & KlodsAttrs;\n const modClasses: string[] = [];\n const passthrough: KlodsAttrs = {};\n for (const [key, value] of Object.entries(userProps)) {\n const m = modifierMap[key];\n if (m !== undefined) {\n if (typeof m === \"function\") {\n const c = m(value);\n if (c) modClasses.push(c);\n } else if (value) {\n modClasses.push(m);\n }\n } else {\n passthrough[key] = value;\n }\n }\n const finalClass = mergeClasses(base, ...modClasses, userProps.class);\n const resolvedChildren =\n children !== undefined ? children : ((userProps.children as KlodsChild | KlodsChild[] | undefined) ?? []);\n delete passthrough.children;\n return new KlodsNode(tag, { ...passthrough, class: finalClass || undefined }, resolvedChildren);\n };\n}\n","// First wave of components: nav, card, button, badge, alert, prose helpers.\n// All match the BEM classes shipped by klods-css.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, el, KlodsNode } from \"./core.js\";\n\n// ── Nav ──────────────────────────────────────────────────────────────────\nexport const nav = builder({ tag: \"nav\", base: \"klods-nav\" });\nexport const navList = builder({ tag: \"ul\", base: \"klods-nav__list\" });\n\nexport type NavLinkProps = {\n href?: string;\n active?: boolean;\n};\nconst navLinkBuilder = builder<NavLinkProps>({\n tag: \"a\",\n base: \"klods-nav__link\",\n modifiers: { active: \"klods-nav__link--active\" },\n});\nexport function navLink(props?: (NavLinkProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Wrap each link in <li> so it's idiomatic inside <ul class=\"klods-nav__list\">.\n return el(\"li\", {}, [navLinkBuilder(props ?? null, children)]);\n}\n\n// ── Card ─────────────────────────────────────────────────────────────────\nexport type CardProps = {\n elevated?: boolean;\n};\nexport const card = builder<CardProps>({\n tag: \"div\",\n base: \"klods-card\",\n modifiers: { elevated: \"klods-card--elevated\" },\n});\nexport const cardTitle = builder({ tag: \"h3\", base: \"klods-card__title\" });\nexport const cardBody = builder({ tag: \"div\", base: \"klods-card__body\" });\nexport const cardFooter = builder({ tag: \"div\", base: \"klods-card__footer\" });\n\n// ── Button ───────────────────────────────────────────────────────────────\nexport type ButtonProps = {\n variant?: \"default\" | \"primary\" | \"danger\" | \"ghost\";\n type?: \"button\" | \"submit\" | \"reset\";\n};\nconst buttonBase = builder<ButtonProps>({\n tag: \"button\",\n base: \"klods-button\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-button--${v}` : undefined),\n },\n});\nexport function button(props?: (ButtonProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Default `type=\"button\"` so it never accidentally submits a form.\n const merged = { type: \"button\", ...(props ?? {}) } as ButtonProps & KlodsAttrs;\n return buttonBase(merged, children);\n}\n\n// ── Badge ────────────────────────────────────────────────────────────────\nexport type BadgeProps = {\n variant?: \"default\" | \"accent\" | \"success\" | \"danger\";\n};\nexport const badge = builder<BadgeProps>({\n tag: \"span\",\n base: \"klods-badge\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-badge--${v}` : undefined),\n },\n});\n\n// ── Alert ────────────────────────────────────────────────────────────────\nexport type AlertProps = {\n variant?: \"default\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\nconst alertBase = builder<AlertProps>({\n tag: \"div\",\n base: \"klods-alert\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-alert--${v}` : undefined),\n },\n});\nexport function alert(props?: (AlertProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // role=alert by default for assistive tech, overridable.\n const merged = { role: \"alert\", ...(props ?? {}) } as AlertProps & KlodsAttrs;\n return alertBase(merged, children);\n}\n\n// ── Prose helpers ────────────────────────────────────────────────────────\nexport const prose = builder({ tag: \"div\", base: \"klods-prose\" });\nexport const muted = builder({ tag: \"span\", base: \"klods-muted\" });\nexport const lead = builder({ tag: \"p\", base: \"klods-lead\" });\n\n// ── Table ────────────────────────────────────────────────────────────────\nexport type TableProps = {\n striped?: boolean;\n dense?: boolean;\n};\nexport const table = builder<TableProps>({\n tag: \"table\",\n base: \"klods-table\",\n modifiers: {\n striped: \"klods-table--striped\",\n dense: \"klods-table--dense\",\n },\n});\nexport const thead = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"thead\", attrs ?? {}, children);\nexport const tbody = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tbody\", attrs ?? {}, children);\nexport const tr = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tr\", attrs ?? {}, children);\nexport const th = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"th\", attrs ?? {}, children);\nexport const td = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"td\", attrs ?? {}, children);\n\n// ── Code ─────────────────────────────────────────────────────────────────\nexport function codeBlock(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"pre\", attrs ?? {}, el(\"code\", {}, content));\n}\nexport function inlineCode(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"code\", attrs ?? {}, content);\n}\n\n// ── Box ──────────────────────────────────────────────────────────────────\nexport const box = builder({ tag: \"div\", base: \"klods-box\" });\n","// Layout builders — the four corners and the \"I-always-forget\" utilities.\n// Each builder is a thin wrapper around `builder()` from core.ts.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, KlodsNode } from \"./core.js\";\n\n// ── Page ─────────────────────────────────────────────────────────────────\nexport type PageProps = {\n /** Render with a sidebar column. */\n sidebar?: boolean;\n /** Place the sidebar on the right (only meaningful with `sidebar: true`). */\n sidebarRight?: boolean;\n};\n\nexport const page = builder<PageProps>({\n tag: \"div\",\n base: \"klods-page\",\n modifiers: {\n sidebar: \"klods-page--with-sidebar\",\n sidebarRight: \"klods-page--sidebar-right\",\n },\n});\n\n// ── Page slots ───────────────────────────────────────────────────────────\nexport const header = builder({ tag: \"header\", base: \"klods-header\" });\nexport const sidebar = builder({ tag: \"aside\", base: \"klods-sidebar\" });\n\nexport type ContentProps = {\n /** Cap content width to --klods-content-max and centre it. */\n narrow?: boolean;\n};\nexport const content = builder<ContentProps>({\n tag: \"main\",\n base: \"klods-content\",\n modifiers: { narrow: \"klods-content--narrow\" },\n});\n\nexport const footer = builder({ tag: \"footer\", base: \"klods-footer\" });\n\n// ── Layout utilities ─────────────────────────────────────────────────────\ntype GapProp = { gap?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 };\n\nconst gapModifier = (prefix: string) => (v: number | undefined) =>\n v === undefined ? undefined : `${prefix}--gap-${v}`;\n\nexport const stack = builder<GapProp>({\n tag: \"div\",\n base: \"klods-stack\",\n modifiers: { gap: gapModifier(\"klods-stack\") },\n});\n\nexport const cluster = builder<GapProp>({\n tag: \"div\",\n base: \"klods-cluster\",\n modifiers: { gap: gapModifier(\"klods-cluster\") },\n});\n\nexport const row = builder<GapProp>({\n tag: \"div\",\n base: \"klods-row\",\n modifiers: { gap: gapModifier(\"klods-row\") },\n});\n\nexport type GridProps = GapProp & {\n cols?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Auto-fit responsive columns; pair with `--klods-grid-min` if you want a custom minimum. */\n fit?: boolean;\n};\nexport const grid = builder<GridProps>({\n tag: \"div\",\n base: \"klods-grid\",\n modifiers: {\n gap: gapModifier(\"klods-grid\"),\n cols: (v) => (v === undefined ? undefined : `klods-grid--cols-${v}`),\n fit: \"klods-grid--fit\",\n },\n});\n\nexport const center = builder({ tag: \"div\", base: \"klods-center\" });\nexport const spread = builder({ tag: \"div\", base: \"klods-spread\" });\n\n// ── Convenience: empty fragment-ish wrapper for quick text + nodes ──────\nexport function text(value: string | number): KlodsNode {\n // Wrap loose text in a span so it composes anywhere a KlodsNode is expected.\n return new KlodsNode(\"span\", {}, [String(value)]);\n}\n\n// Re-export attribute / child types so consumers can extend a builder neatly.\nexport type { KlodsAttrs, KlodsChild };\n"],"mappings":"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","names":["stack","el","content"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/components.ts","../src/layout.ts","../src/utilities.ts"],"sourcesContent":["// Core — a tiny VDOM-ish node with two faces:\n// - .render(target?) → HTMLElement (uses real DOM)\n// - .toString() → HTML string (works in Node / Rails / SSR)\n//\n// Both are produced from the same KlodsNode tree, so the docs site can show the\n// TS source, the rendered HTML and the live preview from one source of truth.\n\nexport type KlodsChild = string | number | bigint | boolean | null | undefined | KlodsNode | Node | KlodsChild[];\n\nexport type KlodsAttrs = {\n class?: string | string[] | Record<string, boolean | undefined> | undefined;\n id?: string | undefined;\n style?: string | Partial<CSSStyleDeclaration> | undefined;\n [key: `data-${string}`]: string | number | boolean | undefined;\n [key: `aria-${string}`]: string | number | boolean | undefined;\n // Allow any other HTML attribute or DOM event handler (onClick, onInput, …).\n [key: string]: unknown;\n};\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst RAW = Symbol(\"klods.raw\");\n\ntype RawHtml = { [RAW]: true; html: string };\n\n/** Mark a string as already-escaped HTML; pass it as a child to inject as-is. */\nexport function raw(html: string): RawHtml {\n return { [RAW]: true, html };\n}\n\nfunction isRaw(value: unknown): value is RawHtml {\n return typeof value === \"object\" && value !== null && (value as { [RAW]?: unknown })[RAW] === true;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\n/** Normalise a `class` value (string | array | record) into a clean string. */\nexport function classNames(input: KlodsAttrs[\"class\"]): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return input.trim();\n if (Array.isArray(input)) return input.filter(Boolean).join(\" \").trim();\n return Object.entries(input)\n .filter(([, v]) => Boolean(v))\n .map(([k]) => k)\n .join(\" \")\n .trim();\n}\n\n/** Merge two class values (used internally to combine builder defaults + user-supplied). */\nexport function mergeClasses(...inputs: Array<KlodsAttrs[\"class\"]>): string {\n return inputs\n .map((c) => classNames(c))\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction styleToString(style: string | Partial<CSSStyleDeclaration>): string {\n if (typeof style === \"string\") return style;\n return Object.entries(style)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`)\n .join(\";\");\n}\n\nfunction flattenChildren(children: KlodsChild | KlodsChild[]): Array<Exclude<KlodsChild, KlodsChild[]>> {\n const out: Array<Exclude<KlodsChild, KlodsChild[]>> = [];\n const stack: KlodsChild[] = Array.isArray(children) ? [...children] : [children];\n while (stack.length) {\n const next = stack.shift();\n if (Array.isArray(next)) stack.unshift(...next);\n else if (next !== null && next !== undefined && next !== false && next !== true) out.push(next);\n }\n return out;\n}\n\nexport class KlodsNode {\n readonly tag: string;\n readonly attrs: KlodsAttrs;\n readonly children: KlodsChild[];\n\n constructor(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []) {\n this.tag = tag;\n this.attrs = attrs;\n this.children = Array.isArray(children) ? children : [children];\n }\n\n /** Render to a real DOM element. If `target` is given, append to it. */\n render(target?: Element | null): HTMLElement {\n const el = document.createElement(this.tag);\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) el.setAttribute(\"class\", cls);\n continue;\n }\n if (name === \"style\") {\n el.setAttribute(\"style\", styleToString(value as string | Partial<CSSStyleDeclaration>));\n continue;\n }\n if (name.startsWith(\"on\") && typeof value === \"function\") {\n el.addEventListener(name.slice(2).toLowerCase(), value as EventListener);\n continue;\n }\n if (value === true) {\n el.setAttribute(name, \"\");\n continue;\n }\n el.setAttribute(name, String(value));\n }\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) el.appendChild(child.render());\n else if (typeof child === \"object\" && child !== null && \"nodeType\" in child) {\n el.appendChild(child as Node);\n } else if (isRaw(child)) {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = child.html;\n el.appendChild(tpl.content);\n } else {\n el.appendChild(document.createTextNode(String(child)));\n }\n }\n if (target) target.appendChild(el);\n return el;\n }\n\n /** Render to a string of HTML. */\n toString(): string {\n const parts: string[] = [`<${this.tag}`];\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name.startsWith(\"on\") && typeof value === \"function\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) parts.push(` class=\"${escapeAttr(cls)}\"`);\n continue;\n }\n if (name === \"style\") {\n parts.push(` style=\"${escapeAttr(styleToString(value as string | Partial<CSSStyleDeclaration>))}\"`);\n continue;\n }\n if (value === true) {\n parts.push(` ${name}`);\n continue;\n }\n parts.push(` ${name}=\"${escapeAttr(String(value))}\"`);\n }\n if (VOID_TAGS.has(this.tag)) {\n parts.push(\" />\");\n return parts.join(\"\");\n }\n parts.push(\">\");\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) parts.push(child.toString());\n else if (isRaw(child)) parts.push(child.html);\n else if (typeof child === \"object\" && child !== null && \"outerHTML\" in child) {\n parts.push((child as Element).outerHTML);\n } else {\n parts.push(escapeHtml(String(child)));\n }\n }\n parts.push(`</${this.tag}>`);\n return parts.join(\"\");\n }\n}\n\n/**\n * Generic element builder. Most consumers use the named builders (page, header, …)\n * rather than calling `el` directly, but it's exported as an escape hatch.\n */\nexport function el(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []): KlodsNode {\n return new KlodsNode(tag, attrs, children);\n}\n\n/**\n * Factory that produces a typed builder for a tag with a base class. Modifier\n * props are converted into `--modifier` BEM classes and stripped from the output\n * attributes; everything else passes through untouched, so consumers can attach\n * arbitrary `id`, `data-*`, `aria-*`, event handlers, `style`, etc.\n */\nexport function builder<P extends Record<string, unknown> = Record<never, never>>(options: {\n tag: string;\n base: string;\n /** Map of prop name → class (or function returning a class) when the prop is truthy. */\n modifiers?: { [K in keyof P]?: string | ((value: P[K]) => string | undefined) };\n}): (props?: (P & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]) => KlodsNode {\n const { tag, base, modifiers = {} } = options;\n const modifierMap = modifiers as Record<string, string | ((value: unknown) => string | undefined) | undefined>;\n return (props, children) => {\n const userProps = (props ?? {}) as P & KlodsAttrs;\n const modClasses: string[] = [];\n const passthrough: KlodsAttrs = {};\n for (const [key, value] of Object.entries(userProps)) {\n const m = modifierMap[key];\n if (m !== undefined) {\n if (typeof m === \"function\") {\n const c = m(value);\n if (c) modClasses.push(c);\n } else if (value) {\n modClasses.push(m);\n }\n } else {\n passthrough[key] = value;\n }\n }\n const finalClass = mergeClasses(base, ...modClasses, userProps.class);\n const resolvedChildren =\n children !== undefined ? children : ((userProps.children as KlodsChild | KlodsChild[] | undefined) ?? []);\n delete passthrough.children;\n return new KlodsNode(tag, { ...passthrough, class: finalClass || undefined }, resolvedChildren);\n };\n}\n","// First wave of components: nav, card, button, badge, alert, prose helpers.\n// All match the BEM classes shipped by klods-css.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, el, KlodsNode } from \"./core.js\";\n\n// ── Nav ──────────────────────────────────────────────────────────────────\nexport const nav = builder({ tag: \"nav\", base: \"klods-nav\" });\nexport const navList = builder({ tag: \"ul\", base: \"klods-nav__list\" });\n\nexport type NavLinkProps = {\n href?: string;\n active?: boolean;\n};\nconst navLinkBuilder = builder<NavLinkProps>({\n tag: \"a\",\n base: \"klods-nav__link\",\n modifiers: { active: \"klods-nav__link--active\" },\n});\nexport function navLink(props?: (NavLinkProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Wrap each link in <li> so it's idiomatic inside <ul class=\"klods-nav__list\">.\n return el(\"li\", {}, [navLinkBuilder(props ?? null, children)]);\n}\n\n// ── Card ─────────────────────────────────────────────────────────────────\nexport type CardProps = {\n elevated?: boolean;\n};\nexport const card = builder<CardProps>({\n tag: \"div\",\n base: \"klods-card\",\n modifiers: { elevated: \"klods-card--elevated\" },\n});\nexport const cardTitle = builder({ tag: \"h3\", base: \"klods-card__title\" });\nexport const cardBody = builder({ tag: \"div\", base: \"klods-card__body\" });\nexport const cardFooter = builder({ tag: \"div\", base: \"klods-card__footer\" });\n\n// ── Button ───────────────────────────────────────────────────────────────\nexport type ButtonProps = {\n variant?: \"default\" | \"primary\" | \"danger\" | \"ghost\";\n type?: \"button\" | \"submit\" | \"reset\";\n};\nconst buttonBase = builder<ButtonProps>({\n tag: \"button\",\n base: \"klods-button\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-button--${v}` : undefined),\n },\n});\nexport function button(props?: (ButtonProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Default `type=\"button\"` so it never accidentally submits a form.\n const merged = { type: \"button\", ...(props ?? {}) } as ButtonProps & KlodsAttrs;\n return buttonBase(merged, children);\n}\n\n// ── Badge ────────────────────────────────────────────────────────────────\nexport type BadgeProps = {\n variant?: \"default\" | \"accent\" | \"success\" | \"danger\";\n};\nexport const badge = builder<BadgeProps>({\n tag: \"span\",\n base: \"klods-badge\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-badge--${v}` : undefined),\n },\n});\n\n// ── Alert ────────────────────────────────────────────────────────────────\nexport type AlertProps = {\n variant?: \"default\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\nconst alertBase = builder<AlertProps>({\n tag: \"div\",\n base: \"klods-alert\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-alert--${v}` : undefined),\n },\n});\nexport function alert(props?: (AlertProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // role=alert by default for assistive tech, overridable.\n const merged = { role: \"alert\", ...(props ?? {}) } as AlertProps & KlodsAttrs;\n return alertBase(merged, children);\n}\n\n// ── Prose helpers ────────────────────────────────────────────────────────\nexport const prose = builder({ tag: \"div\", base: \"klods-prose\" });\nexport const muted = builder({ tag: \"span\", base: \"klods-muted\" });\nexport const lead = builder({ tag: \"p\", base: \"klods-lead\" });\n\n// ── Table ────────────────────────────────────────────────────────────────\nexport type TableProps = {\n striped?: boolean;\n dense?: boolean;\n};\nexport const table = builder<TableProps>({\n tag: \"table\",\n base: \"klods-table\",\n modifiers: {\n striped: \"klods-table--striped\",\n dense: \"klods-table--dense\",\n },\n});\nexport const thead = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"thead\", attrs ?? {}, children);\nexport const tbody = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tbody\", attrs ?? {}, children);\nexport const tr = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tr\", attrs ?? {}, children);\nexport const th = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"th\", attrs ?? {}, children);\nexport const td = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"td\", attrs ?? {}, children);\n\n// ── Code ─────────────────────────────────────────────────────────────────\nexport function codeBlock(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"pre\", attrs ?? {}, el(\"code\", {}, content));\n}\nexport function inlineCode(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"code\", attrs ?? {}, content);\n}\n\n// ── Box ──────────────────────────────────────────────────────────────────\nexport const box = builder({ tag: \"div\", base: \"klods-box\" });\n","// Layout builders — the four corners and the \"I-always-forget\" utilities.\n// Each builder is a thin wrapper around `builder()` from core.ts.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, KlodsNode } from \"./core.js\";\n\n// ── Page ─────────────────────────────────────────────────────────────────\nexport type PageProps = {\n /** Render with a sidebar column. */\n sidebar?: boolean;\n /** Place the sidebar on the right (only meaningful with `sidebar: true`). */\n sidebarRight?: boolean;\n /** Keep the header pinned to the top of the viewport while the page scrolls. */\n stickyHeader?: boolean;\n};\n\nexport const page = builder<PageProps>({\n tag: \"div\",\n base: \"klods-page\",\n modifiers: {\n sidebar: \"klods-page--with-sidebar\",\n sidebarRight: \"klods-page--sidebar-right\",\n stickyHeader: \"klods-page--sticky-header\",\n },\n});\n\n// ── Page slots ───────────────────────────────────────────────────────────\nexport const header = builder({ tag: \"header\", base: \"klods-header\" });\nexport const sidebar = builder({ tag: \"aside\", base: \"klods-sidebar\" });\n\nexport type ContentProps = {\n /** Cap content width to --klods-content-max and centre it. */\n narrow?: boolean;\n};\nexport const content = builder<ContentProps>({\n tag: \"main\",\n base: \"klods-content\",\n modifiers: { narrow: \"klods-content--narrow\" },\n});\n\nexport const footer = builder({ tag: \"footer\", base: \"klods-footer\" });\n\n// ── Layout utilities ─────────────────────────────────────────────────────\ntype GapProp = { gap?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 };\n\nconst gapModifier = (prefix: string) => (v: number | undefined) =>\n v === undefined ? undefined : `${prefix}--gap-${v}`;\n\nexport const stack = builder<GapProp>({\n tag: \"div\",\n base: \"klods-stack\",\n modifiers: { gap: gapModifier(\"klods-stack\") },\n});\n\nexport const cluster = builder<GapProp>({\n tag: \"div\",\n base: \"klods-cluster\",\n modifiers: { gap: gapModifier(\"klods-cluster\") },\n});\n\nexport const row = builder<GapProp>({\n tag: \"div\",\n base: \"klods-row\",\n modifiers: { gap: gapModifier(\"klods-row\") },\n});\n\nexport type GridProps = GapProp & {\n cols?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Auto-fit responsive columns; pair with `--klods-grid-min` if you want a custom minimum. */\n fit?: boolean;\n};\nexport const grid = builder<GridProps>({\n tag: \"div\",\n base: \"klods-grid\",\n modifiers: {\n gap: gapModifier(\"klods-grid\"),\n cols: (v) => (v === undefined ? undefined : `klods-grid--cols-${v}`),\n fit: \"klods-grid--fit\",\n },\n});\n\nexport const center = builder({ tag: \"div\", base: \"klods-center\" });\nexport const spread = builder({ tag: \"div\", base: \"klods-spread\" });\n\n// ── Convenience: empty fragment-ish wrapper for quick text + nodes ──────\nexport function text(value: string | number): KlodsNode {\n // Wrap loose text in a span so it composes anywhere a KlodsNode is expected.\n return new KlodsNode(\"span\", {}, [String(value)]);\n}\n\n// Re-export attribute / child types so consumers can extend a builder neatly.\nexport type { KlodsAttrs, KlodsChild };\n","// Utility builders — thin wrappers over the most-reached-for klods-css utility classes.\n\nimport { builder } from \"./core.js\";\n\n// ── Push ─────────────────────────────────────────────────────────────────\n// Renders a <span class=\"klods-push\">.\n// Pushes siblings to the end of a flex/grid row by consuming all remaining\n// inline space (margin-inline-start: auto).\nexport const push = builder({ tag: \"span\", base: \"klods-push\" });\n\n// ── Fill ─────────────────────────────────────────────────────────────────\n// Renders a <div class=\"klods-fill\">.\n// Grows to fill available flex/grid space (flex: 1 1 auto).\n// Useful as a wrapper when you need one slot to absorb leftover room —\n// e.g. equal-width side groups in a header to centre a middle item.\nexport const fill = builder({ tag: \"div\", base: \"klods-fill\" });\n"],"mappings":"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","names":["stack","el","content"]}
|
package/dist/klods.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var Klods=(()=>{var c=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var L=(t,e,o)=>e in t?c(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o;var R=(t,e)=>{for(var o in e)c(t,o,{get:e[o],enumerable:!0})},T=(t,e,o,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of j(e))!B.call(t,n)&&n!==o&&c(t,n,{get:()=>e[n],enumerable:!(s=$(e,n))||s.enumerable});return t};var _=t=>T(c({},"__esModule",{value:!0}),t);var p=(t,e,o)=>L(t,typeof e!="symbol"?e+"":e,o);var
|
|
1
|
+
"use strict";var Klods=(()=>{var c=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var L=(t,e,o)=>e in t?c(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o;var R=(t,e)=>{for(var o in e)c(t,o,{get:e[o],enumerable:!0})},T=(t,e,o,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of j(e))!B.call(t,n)&&n!==o&&c(t,n,{get:()=>e[n],enumerable:!(s=$(e,n))||s.enumerable});return t};var _=t=>T(c({},"__esModule",{value:!0}),t);var p=(t,e,o)=>L(t,typeof e!="symbol"?e+"":e,o);var Pt={};R(Pt,{KlodsNode:()=>l,alert:()=>U,badge:()=>J,box:()=>at,builder:()=>r,button:()=>z,card:()=>q,cardBody:()=>I,cardFooter:()=>V,cardTitle:()=>F,center:()=>kt,classNames:()=>u,cluster:()=>bt,codeBlock:()=>lt,content:()=>ft,el:()=>d,fill:()=>At,footer:()=>gt,grid:()=>mt,header:()=>pt,inlineCode:()=>it,lead:()=>tt,mergeClasses:()=>w,muted:()=>Y,nav:()=>M,navLink:()=>W,navList:()=>O,page:()=>ct,prose:()=>X,push:()=>Ct,raw:()=>E,row:()=>Kt,sidebar:()=>ut,spread:()=>xt,stack:()=>ht,table:()=>et,tbody:()=>st,td:()=>dt,text:()=>yt,th:()=>nt,thead:()=>ot,tr:()=>rt});var H=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]),v=Symbol("klods.raw");function E(t){return{[v]:!0,html:t}}function C(t){return typeof t=="object"&&t!==null&&t[v]===!0}function G(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function m(t){return t.replace(/&/g,"&").replace(/"/g,""")}function u(t){return t?typeof t=="string"?t.trim():Array.isArray(t)?t.filter(Boolean).join(" ").trim():Object.entries(t).filter(([,e])=>!!e).map(([e])=>e).join(" ").trim():""}function w(...t){return t.map(e=>u(e)).filter(Boolean).join(" ")}function A(t){return typeof t=="string"?t:Object.entries(t).filter(([,e])=>e!=null&&e!=="").map(([e,o])=>`${e.replace(/[A-Z]/g,s=>`-${s.toLowerCase()}`)}:${String(o)}`).join(";")}function P(t){let e=[],o=Array.isArray(t)?[...t]:[t];for(;o.length;){let s=o.shift();Array.isArray(s)?o.unshift(...s):s!=null&&s!==!1&&s!==!0&&e.push(s)}return e}var l=class t{constructor(e,o={},s=[]){p(this,"tag");p(this,"attrs");p(this,"children");this.tag=e,this.attrs=o,this.children=Array.isArray(s)?s:[s]}render(e){let o=document.createElement(this.tag);for(let[s,n]of Object.entries(this.attrs))if(!(n==null||n===!1)&&s!=="children"){if(s==="class"){let i=u(n);i&&o.setAttribute("class",i);continue}if(s==="style"){o.setAttribute("style",A(n));continue}if(s.startsWith("on")&&typeof n=="function"){o.addEventListener(s.slice(2).toLowerCase(),n);continue}if(n===!0){o.setAttribute(s,"");continue}o.setAttribute(s,String(n))}for(let s of P(this.children))if(s instanceof t)o.appendChild(s.render());else if(typeof s=="object"&&s!==null&&"nodeType"in s)o.appendChild(s);else if(C(s)){let n=document.createElement("template");n.innerHTML=s.html,o.appendChild(n.content)}else o.appendChild(document.createTextNode(String(s)));return e&&e.appendChild(o),o}toString(){let e=[`<${this.tag}`];for(let[o,s]of Object.entries(this.attrs))if(!(s==null||s===!1)&&o!=="children"&&!(o.startsWith("on")&&typeof s=="function")){if(o==="class"){let n=u(s);n&&e.push(` class="${m(n)}"`);continue}if(o==="style"){e.push(` style="${m(A(s))}"`);continue}if(s===!0){e.push(` ${o}`);continue}e.push(` ${o}="${m(String(s))}"`)}if(H.has(this.tag))return e.push(" />"),e.join("");e.push(">");for(let o of P(this.children))o instanceof t?e.push(o.toString()):C(o)?e.push(o.html):typeof o=="object"&&o!==null&&"outerHTML"in o?e.push(o.outerHTML):e.push(G(String(o)));return e.push(`</${this.tag}>`),e.join("")}};function d(t,e={},o=[]){return new l(t,e,o)}function r(t){let{tag:e,base:o,modifiers:s={}}=t,n=s;return(i,k)=>{let g=i??{},h=[],b={};for(let[x,K]of Object.entries(g)){let a=n[x];if(a!==void 0)if(typeof a=="function"){let y=a(K);y&&h.push(y)}else K&&h.push(a);else b[x]=K}let S=w(o,...h,g.class),N=k!==void 0?k:g.children??[];return delete b.children,new l(e,{...b,class:S||void 0},N)}}var M=r({tag:"nav",base:"klods-nav"}),O=r({tag:"ul",base:"klods-nav__list"}),D=r({tag:"a",base:"klods-nav__link",modifiers:{active:"klods-nav__link--active"}});function W(t,e){return d("li",{},[D(t??null,e)])}var q=r({tag:"div",base:"klods-card",modifiers:{elevated:"klods-card--elevated"}}),F=r({tag:"h3",base:"klods-card__title"}),I=r({tag:"div",base:"klods-card__body"}),V=r({tag:"div",base:"klods-card__footer"}),Z=r({tag:"button",base:"klods-button",modifiers:{variant:t=>t&&t!=="default"?`klods-button--${t}`:void 0}});function z(t,e){let o={type:"button",...t??{}};return Z(o,e)}var J=r({tag:"span",base:"klods-badge",modifiers:{variant:t=>t&&t!=="default"?`klods-badge--${t}`:void 0}}),Q=r({tag:"div",base:"klods-alert",modifiers:{variant:t=>t&&t!=="default"?`klods-alert--${t}`:void 0}});function U(t,e){let o={role:"alert",...t??{}};return Q(o,e)}var X=r({tag:"div",base:"klods-prose"}),Y=r({tag:"span",base:"klods-muted"}),tt=r({tag:"p",base:"klods-lead"}),et=r({tag:"table",base:"klods-table",modifiers:{striped:"klods-table--striped",dense:"klods-table--dense"}}),ot=(t,e)=>d("thead",t??{},e),st=(t,e)=>d("tbody",t??{},e),rt=(t,e)=>d("tr",t??{},e),nt=(t,e)=>d("th",t??{},e),dt=(t,e)=>d("td",t??{},e);function lt(t,e){return d("pre",t??{},d("code",{},e))}function it(t,e){return d("code",t??{},e)}var at=r({tag:"div",base:"klods-box"});var ct=r({tag:"div",base:"klods-page",modifiers:{sidebar:"klods-page--with-sidebar",sidebarRight:"klods-page--sidebar-right",stickyHeader:"klods-page--sticky-header"}}),pt=r({tag:"header",base:"klods-header"}),ut=r({tag:"aside",base:"klods-sidebar"}),ft=r({tag:"main",base:"klods-content",modifiers:{narrow:"klods-content--narrow"}}),gt=r({tag:"footer",base:"klods-footer"}),f=t=>e=>e===void 0?void 0:`${t}--gap-${e}`,ht=r({tag:"div",base:"klods-stack",modifiers:{gap:f("klods-stack")}}),bt=r({tag:"div",base:"klods-cluster",modifiers:{gap:f("klods-cluster")}}),Kt=r({tag:"div",base:"klods-row",modifiers:{gap:f("klods-row")}}),mt=r({tag:"div",base:"klods-grid",modifiers:{gap:f("klods-grid"),cols:t=>t===void 0?void 0:`klods-grid--cols-${t}`,fit:"klods-grid--fit"}}),kt=r({tag:"div",base:"klods-center"}),xt=r({tag:"div",base:"klods-spread"});function yt(t){return new l("span",{},[String(t)])}var Ct=r({tag:"span",base:"klods-push"}),At=r({tag:"div",base:"klods-fill"});return _(Pt);})();
|
|
2
2
|
//# sourceMappingURL=klods.umd.js.map
|
package/dist/klods.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/components.ts","../src/layout.ts"],"sourcesContent":["// Public API surface for the `klods` package.\n\nexport * from \"./components.js\";\nexport * from \"./core.js\";\nexport * from \"./layout.js\";\n","// Core — a tiny VDOM-ish node with two faces:\n// - .render(target?) → HTMLElement (uses real DOM)\n// - .toString() → HTML string (works in Node / Rails / SSR)\n//\n// Both are produced from the same KlodsNode tree, so the docs site can show the\n// TS source, the rendered HTML and the live preview from one source of truth.\n\nexport type KlodsChild = string | number | bigint | boolean | null | undefined | KlodsNode | Node | KlodsChild[];\n\nexport type KlodsAttrs = {\n class?: string | string[] | Record<string, boolean | undefined> | undefined;\n id?: string | undefined;\n style?: string | Partial<CSSStyleDeclaration> | undefined;\n [key: `data-${string}`]: string | number | boolean | undefined;\n [key: `aria-${string}`]: string | number | boolean | undefined;\n // Allow any other HTML attribute or DOM event handler (onClick, onInput, …).\n [key: string]: unknown;\n};\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst RAW = Symbol(\"klods.raw\");\n\ntype RawHtml = { [RAW]: true; html: string };\n\n/** Mark a string as already-escaped HTML; pass it as a child to inject as-is. */\nexport function raw(html: string): RawHtml {\n return { [RAW]: true, html };\n}\n\nfunction isRaw(value: unknown): value is RawHtml {\n return typeof value === \"object\" && value !== null && (value as { [RAW]?: unknown })[RAW] === true;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\n/** Normalise a `class` value (string | array | record) into a clean string. */\nexport function classNames(input: KlodsAttrs[\"class\"]): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return input.trim();\n if (Array.isArray(input)) return input.filter(Boolean).join(\" \").trim();\n return Object.entries(input)\n .filter(([, v]) => Boolean(v))\n .map(([k]) => k)\n .join(\" \")\n .trim();\n}\n\n/** Merge two class values (used internally to combine builder defaults + user-supplied). */\nexport function mergeClasses(...inputs: Array<KlodsAttrs[\"class\"]>): string {\n return inputs\n .map((c) => classNames(c))\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction styleToString(style: string | Partial<CSSStyleDeclaration>): string {\n if (typeof style === \"string\") return style;\n return Object.entries(style)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`)\n .join(\";\");\n}\n\nfunction flattenChildren(children: KlodsChild | KlodsChild[]): Array<Exclude<KlodsChild, KlodsChild[]>> {\n const out: Array<Exclude<KlodsChild, KlodsChild[]>> = [];\n const stack: KlodsChild[] = Array.isArray(children) ? [...children] : [children];\n while (stack.length) {\n const next = stack.shift();\n if (Array.isArray(next)) stack.unshift(...next);\n else if (next !== null && next !== undefined && next !== false && next !== true) out.push(next);\n }\n return out;\n}\n\nexport class KlodsNode {\n readonly tag: string;\n readonly attrs: KlodsAttrs;\n readonly children: KlodsChild[];\n\n constructor(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []) {\n this.tag = tag;\n this.attrs = attrs;\n this.children = Array.isArray(children) ? children : [children];\n }\n\n /** Render to a real DOM element. If `target` is given, append to it. */\n render(target?: Element | null): HTMLElement {\n const el = document.createElement(this.tag);\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) el.setAttribute(\"class\", cls);\n continue;\n }\n if (name === \"style\") {\n el.setAttribute(\"style\", styleToString(value as string | Partial<CSSStyleDeclaration>));\n continue;\n }\n if (name.startsWith(\"on\") && typeof value === \"function\") {\n el.addEventListener(name.slice(2).toLowerCase(), value as EventListener);\n continue;\n }\n if (value === true) {\n el.setAttribute(name, \"\");\n continue;\n }\n el.setAttribute(name, String(value));\n }\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) el.appendChild(child.render());\n else if (typeof child === \"object\" && child !== null && \"nodeType\" in child) {\n el.appendChild(child as Node);\n } else if (isRaw(child)) {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = child.html;\n el.appendChild(tpl.content);\n } else {\n el.appendChild(document.createTextNode(String(child)));\n }\n }\n if (target) target.appendChild(el);\n return el;\n }\n\n /** Render to a string of HTML. */\n toString(): string {\n const parts: string[] = [`<${this.tag}`];\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name.startsWith(\"on\") && typeof value === \"function\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) parts.push(` class=\"${escapeAttr(cls)}\"`);\n continue;\n }\n if (name === \"style\") {\n parts.push(` style=\"${escapeAttr(styleToString(value as string | Partial<CSSStyleDeclaration>))}\"`);\n continue;\n }\n if (value === true) {\n parts.push(` ${name}`);\n continue;\n }\n parts.push(` ${name}=\"${escapeAttr(String(value))}\"`);\n }\n if (VOID_TAGS.has(this.tag)) {\n parts.push(\" />\");\n return parts.join(\"\");\n }\n parts.push(\">\");\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) parts.push(child.toString());\n else if (isRaw(child)) parts.push(child.html);\n else if (typeof child === \"object\" && child !== null && \"outerHTML\" in child) {\n parts.push((child as Element).outerHTML);\n } else {\n parts.push(escapeHtml(String(child)));\n }\n }\n parts.push(`</${this.tag}>`);\n return parts.join(\"\");\n }\n}\n\n/**\n * Generic element builder. Most consumers use the named builders (page, header, …)\n * rather than calling `el` directly, but it's exported as an escape hatch.\n */\nexport function el(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []): KlodsNode {\n return new KlodsNode(tag, attrs, children);\n}\n\n/**\n * Factory that produces a typed builder for a tag with a base class. Modifier\n * props are converted into `--modifier` BEM classes and stripped from the output\n * attributes; everything else passes through untouched, so consumers can attach\n * arbitrary `id`, `data-*`, `aria-*`, event handlers, `style`, etc.\n */\nexport function builder<P extends Record<string, unknown> = Record<never, never>>(options: {\n tag: string;\n base: string;\n /** Map of prop name → class (or function returning a class) when the prop is truthy. */\n modifiers?: { [K in keyof P]?: string | ((value: P[K]) => string | undefined) };\n}): (props?: (P & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]) => KlodsNode {\n const { tag, base, modifiers = {} } = options;\n const modifierMap = modifiers as Record<string, string | ((value: unknown) => string | undefined) | undefined>;\n return (props, children) => {\n const userProps = (props ?? {}) as P & KlodsAttrs;\n const modClasses: string[] = [];\n const passthrough: KlodsAttrs = {};\n for (const [key, value] of Object.entries(userProps)) {\n const m = modifierMap[key];\n if (m !== undefined) {\n if (typeof m === \"function\") {\n const c = m(value);\n if (c) modClasses.push(c);\n } else if (value) {\n modClasses.push(m);\n }\n } else {\n passthrough[key] = value;\n }\n }\n const finalClass = mergeClasses(base, ...modClasses, userProps.class);\n const resolvedChildren =\n children !== undefined ? children : ((userProps.children as KlodsChild | KlodsChild[] | undefined) ?? []);\n delete passthrough.children;\n return new KlodsNode(tag, { ...passthrough, class: finalClass || undefined }, resolvedChildren);\n };\n}\n","// First wave of components: nav, card, button, badge, alert, prose helpers.\n// All match the BEM classes shipped by klods-css.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, el, KlodsNode } from \"./core.js\";\n\n// ── Nav ──────────────────────────────────────────────────────────────────\nexport const nav = builder({ tag: \"nav\", base: \"klods-nav\" });\nexport const navList = builder({ tag: \"ul\", base: \"klods-nav__list\" });\n\nexport type NavLinkProps = {\n href?: string;\n active?: boolean;\n};\nconst navLinkBuilder = builder<NavLinkProps>({\n tag: \"a\",\n base: \"klods-nav__link\",\n modifiers: { active: \"klods-nav__link--active\" },\n});\nexport function navLink(props?: (NavLinkProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Wrap each link in <li> so it's idiomatic inside <ul class=\"klods-nav__list\">.\n return el(\"li\", {}, [navLinkBuilder(props ?? null, children)]);\n}\n\n// ── Card ─────────────────────────────────────────────────────────────────\nexport type CardProps = {\n elevated?: boolean;\n};\nexport const card = builder<CardProps>({\n tag: \"div\",\n base: \"klods-card\",\n modifiers: { elevated: \"klods-card--elevated\" },\n});\nexport const cardTitle = builder({ tag: \"h3\", base: \"klods-card__title\" });\nexport const cardBody = builder({ tag: \"div\", base: \"klods-card__body\" });\nexport const cardFooter = builder({ tag: \"div\", base: \"klods-card__footer\" });\n\n// ── Button ───────────────────────────────────────────────────────────────\nexport type ButtonProps = {\n variant?: \"default\" | \"primary\" | \"danger\" | \"ghost\";\n type?: \"button\" | \"submit\" | \"reset\";\n};\nconst buttonBase = builder<ButtonProps>({\n tag: \"button\",\n base: \"klods-button\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-button--${v}` : undefined),\n },\n});\nexport function button(props?: (ButtonProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Default `type=\"button\"` so it never accidentally submits a form.\n const merged = { type: \"button\", ...(props ?? {}) } as ButtonProps & KlodsAttrs;\n return buttonBase(merged, children);\n}\n\n// ── Badge ────────────────────────────────────────────────────────────────\nexport type BadgeProps = {\n variant?: \"default\" | \"accent\" | \"success\" | \"danger\";\n};\nexport const badge = builder<BadgeProps>({\n tag: \"span\",\n base: \"klods-badge\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-badge--${v}` : undefined),\n },\n});\n\n// ── Alert ────────────────────────────────────────────────────────────────\nexport type AlertProps = {\n variant?: \"default\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\nconst alertBase = builder<AlertProps>({\n tag: \"div\",\n base: \"klods-alert\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-alert--${v}` : undefined),\n },\n});\nexport function alert(props?: (AlertProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // role=alert by default for assistive tech, overridable.\n const merged = { role: \"alert\", ...(props ?? {}) } as AlertProps & KlodsAttrs;\n return alertBase(merged, children);\n}\n\n// ── Prose helpers ────────────────────────────────────────────────────────\nexport const prose = builder({ tag: \"div\", base: \"klods-prose\" });\nexport const muted = builder({ tag: \"span\", base: \"klods-muted\" });\nexport const lead = builder({ tag: \"p\", base: \"klods-lead\" });\n\n// ── Table ────────────────────────────────────────────────────────────────\nexport type TableProps = {\n striped?: boolean;\n dense?: boolean;\n};\nexport const table = builder<TableProps>({\n tag: \"table\",\n base: \"klods-table\",\n modifiers: {\n striped: \"klods-table--striped\",\n dense: \"klods-table--dense\",\n },\n});\nexport const thead = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"thead\", attrs ?? {}, children);\nexport const tbody = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tbody\", attrs ?? {}, children);\nexport const tr = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tr\", attrs ?? {}, children);\nexport const th = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"th\", attrs ?? {}, children);\nexport const td = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"td\", attrs ?? {}, children);\n\n// ── Code ─────────────────────────────────────────────────────────────────\nexport function codeBlock(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"pre\", attrs ?? {}, el(\"code\", {}, content));\n}\nexport function inlineCode(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"code\", attrs ?? {}, content);\n}\n\n// ── Box ──────────────────────────────────────────────────────────────────\nexport const box = builder({ tag: \"div\", base: \"klods-box\" });\n","// Layout builders — the four corners and the \"I-always-forget\" utilities.\n// Each builder is a thin wrapper around `builder()` from core.ts.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, KlodsNode } from \"./core.js\";\n\n// ── Page ─────────────────────────────────────────────────────────────────\nexport type PageProps = {\n /** Render with a sidebar column. */\n sidebar?: boolean;\n /** Place the sidebar on the right (only meaningful with `sidebar: true`). */\n sidebarRight?: boolean;\n};\n\nexport const page = builder<PageProps>({\n tag: \"div\",\n base: \"klods-page\",\n modifiers: {\n sidebar: \"klods-page--with-sidebar\",\n sidebarRight: \"klods-page--sidebar-right\",\n },\n});\n\n// ── Page slots ───────────────────────────────────────────────────────────\nexport const header = builder({ tag: \"header\", base: \"klods-header\" });\nexport const sidebar = builder({ tag: \"aside\", base: \"klods-sidebar\" });\n\nexport type ContentProps = {\n /** Cap content width to --klods-content-max and centre it. */\n narrow?: boolean;\n};\nexport const content = builder<ContentProps>({\n tag: \"main\",\n base: \"klods-content\",\n modifiers: { narrow: \"klods-content--narrow\" },\n});\n\nexport const footer = builder({ tag: \"footer\", base: \"klods-footer\" });\n\n// ── Layout utilities ─────────────────────────────────────────────────────\ntype GapProp = { gap?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 };\n\nconst gapModifier = (prefix: string) => (v: number | undefined) =>\n v === undefined ? undefined : `${prefix}--gap-${v}`;\n\nexport const stack = builder<GapProp>({\n tag: \"div\",\n base: \"klods-stack\",\n modifiers: { gap: gapModifier(\"klods-stack\") },\n});\n\nexport const cluster = builder<GapProp>({\n tag: \"div\",\n base: \"klods-cluster\",\n modifiers: { gap: gapModifier(\"klods-cluster\") },\n});\n\nexport const row = builder<GapProp>({\n tag: \"div\",\n base: \"klods-row\",\n modifiers: { gap: gapModifier(\"klods-row\") },\n});\n\nexport type GridProps = GapProp & {\n cols?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Auto-fit responsive columns; pair with `--klods-grid-min` if you want a custom minimum. */\n fit?: boolean;\n};\nexport const grid = builder<GridProps>({\n tag: \"div\",\n base: \"klods-grid\",\n modifiers: {\n gap: gapModifier(\"klods-grid\"),\n cols: (v) => (v === undefined ? undefined : `klods-grid--cols-${v}`),\n fit: \"klods-grid--fit\",\n },\n});\n\nexport const center = builder({ tag: \"div\", base: \"klods-center\" });\nexport const spread = builder({ tag: \"div\", base: \"klods-spread\" });\n\n// ── Convenience: empty fragment-ish wrapper for quick text + nodes ──────\nexport function text(value: string | number): KlodsNode {\n // Wrap loose text in a span so it composes anywhere a KlodsNode is expected.\n return new KlodsNode(\"span\", {}, [String(value)]);\n}\n\n// Re-export attribute / child types so consumers can extend a builder neatly.\nexport type { KlodsAttrs, KlodsChild };\n"],"mappings":"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","names":["src_exports","__export","KlodsNode","alert","badge","box","builder","button","card","cardBody","cardFooter","cardTitle","center","classNames","cluster","codeBlock","content","el","footer","grid","header","inlineCode","lead","mergeClasses","muted","nav","navLink","navList","page","prose","raw","row","sidebar","spread","stack","table","tbody","td","text","th","thead","tr","VOID_TAGS","RAW","raw","html","isRaw","value","escapeHtml","str","escapeAttr","classNames","input","v","k","mergeClasses","inputs","c","styleToString","style","m","flattenChildren","children","out","stack","next","KlodsNode","_KlodsNode","tag","attrs","__publicField","target","el","name","cls","child","tpl","parts","builder","options","base","modifiers","modifierMap","props","userProps","modClasses","passthrough","key","finalClass","resolvedChildren","nav","builder","navList","navLinkBuilder","navLink","props","children","el","card","cardTitle","cardBody","cardFooter","buttonBase","v","button","merged","badge","alertBase","alert","prose","muted","lead","table","thead","attrs","tbody","tr","th","td","codeBlock","content","inlineCode","box","page","builder","header","sidebar","content","footer","gapModifier","prefix","v","stack","cluster","row","grid","center","spread","text","value","KlodsNode"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/components.ts","../src/layout.ts","../src/utilities.ts"],"sourcesContent":["// Public API surface for the `klods` package.\n\nexport * from \"./components.js\";\nexport * from \"./core.js\";\nexport * from \"./layout.js\";\nexport * from \"./utilities.js\";\n","// Core — a tiny VDOM-ish node with two faces:\n// - .render(target?) → HTMLElement (uses real DOM)\n// - .toString() → HTML string (works in Node / Rails / SSR)\n//\n// Both are produced from the same KlodsNode tree, so the docs site can show the\n// TS source, the rendered HTML and the live preview from one source of truth.\n\nexport type KlodsChild = string | number | bigint | boolean | null | undefined | KlodsNode | Node | KlodsChild[];\n\nexport type KlodsAttrs = {\n class?: string | string[] | Record<string, boolean | undefined> | undefined;\n id?: string | undefined;\n style?: string | Partial<CSSStyleDeclaration> | undefined;\n [key: `data-${string}`]: string | number | boolean | undefined;\n [key: `aria-${string}`]: string | number | boolean | undefined;\n // Allow any other HTML attribute or DOM event handler (onClick, onInput, …).\n [key: string]: unknown;\n};\n\nconst VOID_TAGS = new Set([\n \"area\",\n \"base\",\n \"br\",\n \"col\",\n \"embed\",\n \"hr\",\n \"img\",\n \"input\",\n \"link\",\n \"meta\",\n \"source\",\n \"track\",\n \"wbr\",\n]);\n\nconst RAW = Symbol(\"klods.raw\");\n\ntype RawHtml = { [RAW]: true; html: string };\n\n/** Mark a string as already-escaped HTML; pass it as a child to inject as-is. */\nexport function raw(html: string): RawHtml {\n return { [RAW]: true, html };\n}\n\nfunction isRaw(value: unknown): value is RawHtml {\n return typeof value === \"object\" && value !== null && (value as { [RAW]?: unknown })[RAW] === true;\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction escapeAttr(str: string): string {\n return str.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\n/** Normalise a `class` value (string | array | record) into a clean string. */\nexport function classNames(input: KlodsAttrs[\"class\"]): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return input.trim();\n if (Array.isArray(input)) return input.filter(Boolean).join(\" \").trim();\n return Object.entries(input)\n .filter(([, v]) => Boolean(v))\n .map(([k]) => k)\n .join(\" \")\n .trim();\n}\n\n/** Merge two class values (used internally to combine builder defaults + user-supplied). */\nexport function mergeClasses(...inputs: Array<KlodsAttrs[\"class\"]>): string {\n return inputs\n .map((c) => classNames(c))\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction styleToString(style: string | Partial<CSSStyleDeclaration>): string {\n if (typeof style === \"string\") return style;\n return Object.entries(style)\n .filter(([, v]) => v !== undefined && v !== null && v !== \"\")\n .map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}:${String(v)}`)\n .join(\";\");\n}\n\nfunction flattenChildren(children: KlodsChild | KlodsChild[]): Array<Exclude<KlodsChild, KlodsChild[]>> {\n const out: Array<Exclude<KlodsChild, KlodsChild[]>> = [];\n const stack: KlodsChild[] = Array.isArray(children) ? [...children] : [children];\n while (stack.length) {\n const next = stack.shift();\n if (Array.isArray(next)) stack.unshift(...next);\n else if (next !== null && next !== undefined && next !== false && next !== true) out.push(next);\n }\n return out;\n}\n\nexport class KlodsNode {\n readonly tag: string;\n readonly attrs: KlodsAttrs;\n readonly children: KlodsChild[];\n\n constructor(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []) {\n this.tag = tag;\n this.attrs = attrs;\n this.children = Array.isArray(children) ? children : [children];\n }\n\n /** Render to a real DOM element. If `target` is given, append to it. */\n render(target?: Element | null): HTMLElement {\n const el = document.createElement(this.tag);\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) el.setAttribute(\"class\", cls);\n continue;\n }\n if (name === \"style\") {\n el.setAttribute(\"style\", styleToString(value as string | Partial<CSSStyleDeclaration>));\n continue;\n }\n if (name.startsWith(\"on\") && typeof value === \"function\") {\n el.addEventListener(name.slice(2).toLowerCase(), value as EventListener);\n continue;\n }\n if (value === true) {\n el.setAttribute(name, \"\");\n continue;\n }\n el.setAttribute(name, String(value));\n }\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) el.appendChild(child.render());\n else if (typeof child === \"object\" && child !== null && \"nodeType\" in child) {\n el.appendChild(child as Node);\n } else if (isRaw(child)) {\n const tpl = document.createElement(\"template\");\n tpl.innerHTML = child.html;\n el.appendChild(tpl.content);\n } else {\n el.appendChild(document.createTextNode(String(child)));\n }\n }\n if (target) target.appendChild(el);\n return el;\n }\n\n /** Render to a string of HTML. */\n toString(): string {\n const parts: string[] = [`<${this.tag}`];\n for (const [name, value] of Object.entries(this.attrs)) {\n if (value === undefined || value === null || value === false) continue;\n if (name === \"children\") continue;\n if (name.startsWith(\"on\") && typeof value === \"function\") continue;\n if (name === \"class\") {\n const cls = classNames(value as KlodsAttrs[\"class\"]);\n if (cls) parts.push(` class=\"${escapeAttr(cls)}\"`);\n continue;\n }\n if (name === \"style\") {\n parts.push(` style=\"${escapeAttr(styleToString(value as string | Partial<CSSStyleDeclaration>))}\"`);\n continue;\n }\n if (value === true) {\n parts.push(` ${name}`);\n continue;\n }\n parts.push(` ${name}=\"${escapeAttr(String(value))}\"`);\n }\n if (VOID_TAGS.has(this.tag)) {\n parts.push(\" />\");\n return parts.join(\"\");\n }\n parts.push(\">\");\n for (const child of flattenChildren(this.children)) {\n if (child instanceof KlodsNode) parts.push(child.toString());\n else if (isRaw(child)) parts.push(child.html);\n else if (typeof child === \"object\" && child !== null && \"outerHTML\" in child) {\n parts.push((child as Element).outerHTML);\n } else {\n parts.push(escapeHtml(String(child)));\n }\n }\n parts.push(`</${this.tag}>`);\n return parts.join(\"\");\n }\n}\n\n/**\n * Generic element builder. Most consumers use the named builders (page, header, …)\n * rather than calling `el` directly, but it's exported as an escape hatch.\n */\nexport function el(tag: string, attrs: KlodsAttrs = {}, children: KlodsChild | KlodsChild[] = []): KlodsNode {\n return new KlodsNode(tag, attrs, children);\n}\n\n/**\n * Factory that produces a typed builder for a tag with a base class. Modifier\n * props are converted into `--modifier` BEM classes and stripped from the output\n * attributes; everything else passes through untouched, so consumers can attach\n * arbitrary `id`, `data-*`, `aria-*`, event handlers, `style`, etc.\n */\nexport function builder<P extends Record<string, unknown> = Record<never, never>>(options: {\n tag: string;\n base: string;\n /** Map of prop name → class (or function returning a class) when the prop is truthy. */\n modifiers?: { [K in keyof P]?: string | ((value: P[K]) => string | undefined) };\n}): (props?: (P & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]) => KlodsNode {\n const { tag, base, modifiers = {} } = options;\n const modifierMap = modifiers as Record<string, string | ((value: unknown) => string | undefined) | undefined>;\n return (props, children) => {\n const userProps = (props ?? {}) as P & KlodsAttrs;\n const modClasses: string[] = [];\n const passthrough: KlodsAttrs = {};\n for (const [key, value] of Object.entries(userProps)) {\n const m = modifierMap[key];\n if (m !== undefined) {\n if (typeof m === \"function\") {\n const c = m(value);\n if (c) modClasses.push(c);\n } else if (value) {\n modClasses.push(m);\n }\n } else {\n passthrough[key] = value;\n }\n }\n const finalClass = mergeClasses(base, ...modClasses, userProps.class);\n const resolvedChildren =\n children !== undefined ? children : ((userProps.children as KlodsChild | KlodsChild[] | undefined) ?? []);\n delete passthrough.children;\n return new KlodsNode(tag, { ...passthrough, class: finalClass || undefined }, resolvedChildren);\n };\n}\n","// First wave of components: nav, card, button, badge, alert, prose helpers.\n// All match the BEM classes shipped by klods-css.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, el, KlodsNode } from \"./core.js\";\n\n// ── Nav ──────────────────────────────────────────────────────────────────\nexport const nav = builder({ tag: \"nav\", base: \"klods-nav\" });\nexport const navList = builder({ tag: \"ul\", base: \"klods-nav__list\" });\n\nexport type NavLinkProps = {\n href?: string;\n active?: boolean;\n};\nconst navLinkBuilder = builder<NavLinkProps>({\n tag: \"a\",\n base: \"klods-nav__link\",\n modifiers: { active: \"klods-nav__link--active\" },\n});\nexport function navLink(props?: (NavLinkProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Wrap each link in <li> so it's idiomatic inside <ul class=\"klods-nav__list\">.\n return el(\"li\", {}, [navLinkBuilder(props ?? null, children)]);\n}\n\n// ── Card ─────────────────────────────────────────────────────────────────\nexport type CardProps = {\n elevated?: boolean;\n};\nexport const card = builder<CardProps>({\n tag: \"div\",\n base: \"klods-card\",\n modifiers: { elevated: \"klods-card--elevated\" },\n});\nexport const cardTitle = builder({ tag: \"h3\", base: \"klods-card__title\" });\nexport const cardBody = builder({ tag: \"div\", base: \"klods-card__body\" });\nexport const cardFooter = builder({ tag: \"div\", base: \"klods-card__footer\" });\n\n// ── Button ───────────────────────────────────────────────────────────────\nexport type ButtonProps = {\n variant?: \"default\" | \"primary\" | \"danger\" | \"ghost\";\n type?: \"button\" | \"submit\" | \"reset\";\n};\nconst buttonBase = builder<ButtonProps>({\n tag: \"button\",\n base: \"klods-button\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-button--${v}` : undefined),\n },\n});\nexport function button(props?: (ButtonProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // Default `type=\"button\"` so it never accidentally submits a form.\n const merged = { type: \"button\", ...(props ?? {}) } as ButtonProps & KlodsAttrs;\n return buttonBase(merged, children);\n}\n\n// ── Badge ────────────────────────────────────────────────────────────────\nexport type BadgeProps = {\n variant?: \"default\" | \"accent\" | \"success\" | \"danger\";\n};\nexport const badge = builder<BadgeProps>({\n tag: \"span\",\n base: \"klods-badge\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-badge--${v}` : undefined),\n },\n});\n\n// ── Alert ────────────────────────────────────────────────────────────────\nexport type AlertProps = {\n variant?: \"default\" | \"info\" | \"success\" | \"warning\" | \"danger\";\n};\nconst alertBase = builder<AlertProps>({\n tag: \"div\",\n base: \"klods-alert\",\n modifiers: {\n variant: (v) => (v && v !== \"default\" ? `klods-alert--${v}` : undefined),\n },\n});\nexport function alert(props?: (AlertProps & KlodsAttrs) | null, children?: KlodsChild | KlodsChild[]): KlodsNode {\n // role=alert by default for assistive tech, overridable.\n const merged = { role: \"alert\", ...(props ?? {}) } as AlertProps & KlodsAttrs;\n return alertBase(merged, children);\n}\n\n// ── Prose helpers ────────────────────────────────────────────────────────\nexport const prose = builder({ tag: \"div\", base: \"klods-prose\" });\nexport const muted = builder({ tag: \"span\", base: \"klods-muted\" });\nexport const lead = builder({ tag: \"p\", base: \"klods-lead\" });\n\n// ── Table ────────────────────────────────────────────────────────────────\nexport type TableProps = {\n striped?: boolean;\n dense?: boolean;\n};\nexport const table = builder<TableProps>({\n tag: \"table\",\n base: \"klods-table\",\n modifiers: {\n striped: \"klods-table--striped\",\n dense: \"klods-table--dense\",\n },\n});\nexport const thead = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"thead\", attrs ?? {}, children);\nexport const tbody = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tbody\", attrs ?? {}, children);\nexport const tr = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"tr\", attrs ?? {}, children);\nexport const th = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"th\", attrs ?? {}, children);\nexport const td = (attrs?: KlodsAttrs | null, children?: KlodsChild | KlodsChild[]): KlodsNode =>\n el(\"td\", attrs ?? {}, children);\n\n// ── Code ─────────────────────────────────────────────────────────────────\nexport function codeBlock(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"pre\", attrs ?? {}, el(\"code\", {}, content));\n}\nexport function inlineCode(attrs?: KlodsAttrs | null, content?: KlodsChild | KlodsChild[]): KlodsNode {\n return el(\"code\", attrs ?? {}, content);\n}\n\n// ── Box ──────────────────────────────────────────────────────────────────\nexport const box = builder({ tag: \"div\", base: \"klods-box\" });\n","// Layout builders — the four corners and the \"I-always-forget\" utilities.\n// Each builder is a thin wrapper around `builder()` from core.ts.\n\nimport type { KlodsAttrs, KlodsChild } from \"./core.js\";\nimport { builder, KlodsNode } from \"./core.js\";\n\n// ── Page ─────────────────────────────────────────────────────────────────\nexport type PageProps = {\n /** Render with a sidebar column. */\n sidebar?: boolean;\n /** Place the sidebar on the right (only meaningful with `sidebar: true`). */\n sidebarRight?: boolean;\n /** Keep the header pinned to the top of the viewport while the page scrolls. */\n stickyHeader?: boolean;\n};\n\nexport const page = builder<PageProps>({\n tag: \"div\",\n base: \"klods-page\",\n modifiers: {\n sidebar: \"klods-page--with-sidebar\",\n sidebarRight: \"klods-page--sidebar-right\",\n stickyHeader: \"klods-page--sticky-header\",\n },\n});\n\n// ── Page slots ───────────────────────────────────────────────────────────\nexport const header = builder({ tag: \"header\", base: \"klods-header\" });\nexport const sidebar = builder({ tag: \"aside\", base: \"klods-sidebar\" });\n\nexport type ContentProps = {\n /** Cap content width to --klods-content-max and centre it. */\n narrow?: boolean;\n};\nexport const content = builder<ContentProps>({\n tag: \"main\",\n base: \"klods-content\",\n modifiers: { narrow: \"klods-content--narrow\" },\n});\n\nexport const footer = builder({ tag: \"footer\", base: \"klods-footer\" });\n\n// ── Layout utilities ─────────────────────────────────────────────────────\ntype GapProp = { gap?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 };\n\nconst gapModifier = (prefix: string) => (v: number | undefined) =>\n v === undefined ? undefined : `${prefix}--gap-${v}`;\n\nexport const stack = builder<GapProp>({\n tag: \"div\",\n base: \"klods-stack\",\n modifiers: { gap: gapModifier(\"klods-stack\") },\n});\n\nexport const cluster = builder<GapProp>({\n tag: \"div\",\n base: \"klods-cluster\",\n modifiers: { gap: gapModifier(\"klods-cluster\") },\n});\n\nexport const row = builder<GapProp>({\n tag: \"div\",\n base: \"klods-row\",\n modifiers: { gap: gapModifier(\"klods-row\") },\n});\n\nexport type GridProps = GapProp & {\n cols?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Auto-fit responsive columns; pair with `--klods-grid-min` if you want a custom minimum. */\n fit?: boolean;\n};\nexport const grid = builder<GridProps>({\n tag: \"div\",\n base: \"klods-grid\",\n modifiers: {\n gap: gapModifier(\"klods-grid\"),\n cols: (v) => (v === undefined ? undefined : `klods-grid--cols-${v}`),\n fit: \"klods-grid--fit\",\n },\n});\n\nexport const center = builder({ tag: \"div\", base: \"klods-center\" });\nexport const spread = builder({ tag: \"div\", base: \"klods-spread\" });\n\n// ── Convenience: empty fragment-ish wrapper for quick text + nodes ──────\nexport function text(value: string | number): KlodsNode {\n // Wrap loose text in a span so it composes anywhere a KlodsNode is expected.\n return new KlodsNode(\"span\", {}, [String(value)]);\n}\n\n// Re-export attribute / child types so consumers can extend a builder neatly.\nexport type { KlodsAttrs, KlodsChild };\n","// Utility builders — thin wrappers over the most-reached-for klods-css utility classes.\n\nimport { builder } from \"./core.js\";\n\n// ── Push ─────────────────────────────────────────────────────────────────\n// Renders a <span class=\"klods-push\">.\n// Pushes siblings to the end of a flex/grid row by consuming all remaining\n// inline space (margin-inline-start: auto).\nexport const push = builder({ tag: \"span\", base: \"klods-push\" });\n\n// ── Fill ─────────────────────────────────────────────────────────────────\n// Renders a <div class=\"klods-fill\">.\n// Grows to fill available flex/grid space (flex: 1 1 auto).\n// Useful as a wrapper when you need one slot to absorb leftover room —\n// e.g. equal-width side groups in a header to centre a middle item.\nexport const fill = builder({ tag: \"div\", base: \"klods-fill\" });\n"],"mappings":"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","names":["src_exports","__export","KlodsNode","alert","badge","box","builder","button","card","cardBody","cardFooter","cardTitle","center","classNames","cluster","codeBlock","content","el","fill","footer","grid","header","inlineCode","lead","mergeClasses","muted","nav","navLink","navList","page","prose","push","raw","row","sidebar","spread","stack","table","tbody","td","text","th","thead","tr","VOID_TAGS","RAW","raw","html","isRaw","value","escapeHtml","str","escapeAttr","classNames","input","v","k","mergeClasses","inputs","c","styleToString","style","m","flattenChildren","children","out","stack","next","KlodsNode","_KlodsNode","tag","attrs","__publicField","target","el","name","cls","child","tpl","parts","builder","options","base","modifiers","modifierMap","props","userProps","modClasses","passthrough","key","finalClass","resolvedChildren","nav","builder","navList","navLinkBuilder","navLink","props","children","el","card","cardTitle","cardBody","cardFooter","buttonBase","v","button","merged","badge","alertBase","alert","prose","muted","lead","table","thead","attrs","tbody","tr","th","td","codeBlock","content","inlineCode","box","page","builder","header","sidebar","content","footer","gapModifier","prefix","v","stack","cluster","row","grid","center","spread","text","value","KlodsNode","push","builder","fill"]}
|