@davidsouther/jiffies 2026.24.0 → 2026.24.1
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/lib/esm/assert.d.ts +26 -0
- package/lib/esm/assert.js +38 -0
- package/lib/esm/awaitable.js +1 -0
- package/lib/esm/case.d.ts +1 -0
- package/lib/esm/case.js +5 -0
- package/lib/esm/components/accordion.d.ts +5 -0
- package/lib/esm/components/accordion.js +9 -0
- package/lib/esm/components/alert.d.ts +7 -0
- package/lib/esm/components/alert.js +31 -0
- package/lib/esm/components/button_bar.d.ts +8 -0
- package/lib/esm/components/button_bar.js +25 -0
- package/lib/esm/components/card.d.ts +8 -0
- package/lib/esm/components/card.js +31 -0
- package/lib/esm/components/children.d.ts +2 -0
- package/{src/components/children.ts → lib/esm/components/children.js} +2 -6
- package/lib/esm/components/form.d.ts +5 -0
- package/lib/esm/components/form.js +13 -0
- package/{src/components/index.ts → lib/esm/components/index.d.ts} +2 -15
- package/lib/esm/components/index.js +10 -0
- package/lib/esm/components/inline_edit.d.ts +12 -0
- package/lib/esm/components/inline_edit.js +48 -0
- package/lib/esm/components/link.d.ts +5 -0
- package/lib/esm/components/link.js +11 -0
- package/lib/esm/components/logger.d.ts +6 -0
- package/lib/esm/components/logger.js +22 -0
- package/lib/esm/components/modal.d.ts +2 -0
- package/{src/components/modal.ts → lib/esm/components/modal.js} +3 -8
- package/lib/esm/components/nav.d.ts +11 -0
- package/lib/esm/components/nav.js +27 -0
- package/lib/esm/components/property.d.ts +9 -0
- package/lib/esm/components/property.js +16 -0
- package/lib/esm/components/select.d.ts +10 -0
- package/lib/esm/components/select.js +3 -0
- package/lib/esm/components/tabs.d.ts +20 -0
- package/lib/esm/components/tabs.js +45 -0
- package/lib/esm/components/virtual_scroll.d.ts +42 -0
- package/lib/esm/components/virtual_scroll.js +94 -0
- package/lib/esm/debounce.d.ts +1 -0
- package/lib/esm/debounce.js +11 -0
- package/lib/esm/diff.d.ts +15 -0
- package/lib/esm/diff.js +50 -0
- package/lib/esm/display.d.ts +5 -0
- package/lib/esm/display.js +11 -0
- package/lib/esm/dom/css/border.d.ts +11 -0
- package/lib/esm/dom/css/border.js +27 -0
- package/lib/esm/dom/css/constants.d.ts +31 -0
- package/lib/esm/dom/css/constants.js +28 -0
- package/lib/esm/dom/css/core.d.ts +5 -0
- package/lib/esm/dom/css/core.js +24 -0
- package/lib/esm/dom/css/fstyle.d.ts +5 -0
- package/lib/esm/dom/css/fstyle.js +32 -0
- package/lib/esm/dom/css/sizing.d.ts +5 -0
- package/lib/esm/dom/css/sizing.js +10 -0
- package/lib/esm/dom/dom.d.ts +36 -0
- package/lib/esm/dom/dom.js +217 -0
- package/lib/esm/dom/fc.d.ts +10 -0
- package/lib/esm/dom/fc.js +32 -0
- package/lib/esm/dom/form/form.app.d.ts +1 -0
- package/lib/esm/dom/form/form.app.js +19 -0
- package/lib/esm/dom/form/form.d.ts +27 -0
- package/lib/esm/dom/form/form.js +65 -0
- package/lib/esm/dom/html.d.ts +112 -0
- package/{src/dom/html.ts → lib/esm/dom/html.js} +2 -14
- package/lib/esm/dom/hydrate.d.ts +39 -0
- package/lib/esm/dom/hydrate.js +187 -0
- package/lib/esm/dom/index.js +2 -0
- package/lib/esm/dom/navigation/index.d.ts +76 -0
- package/lib/esm/dom/navigation/index.js +292 -0
- package/lib/esm/dom/observable.d.ts +2 -0
- package/lib/esm/dom/observable.js +6 -0
- package/lib/esm/dom/provide.d.ts +3 -0
- package/lib/esm/dom/provide.js +7 -0
- package/lib/esm/dom/render.d.ts +8 -0
- package/lib/esm/dom/render.js +28 -0
- package/lib/esm/dom/router/link.d.ts +6 -0
- package/lib/esm/dom/router/link.js +3 -0
- package/lib/esm/dom/router/router.d.ts +13 -0
- package/lib/esm/dom/router/router.js +52 -0
- package/lib/esm/dom/svg.d.ts +64 -0
- package/{src/dom/svg.ts → lib/esm/dom/svg.js} +2 -19
- package/lib/esm/dom/types/css.d.ts +6590 -0
- package/lib/esm/dom/types/css.js +1 -0
- package/lib/esm/dom/types/dom.js +1 -0
- package/lib/esm/dom/types/html.d.ts +614 -0
- package/lib/esm/dom/types/html.js +1 -0
- package/lib/esm/dom/xml.d.ts +1 -0
- package/lib/esm/dom/xml.js +4 -0
- package/lib/esm/equal.d.ts +11 -0
- package/lib/esm/equal.js +43 -0
- package/lib/esm/fs.d.ts +72 -0
- package/lib/esm/fs.js +227 -0
- package/lib/esm/fs_node.d.ts +15 -0
- package/lib/esm/fs_node.js +45 -0
- package/lib/esm/generator.d.ts +1 -0
- package/lib/esm/generator.js +10 -0
- package/lib/esm/lock.d.ts +1 -0
- package/lib/esm/lock.js +23 -0
- package/lib/esm/log.d.ts +69 -0
- package/lib/esm/log.js +211 -0
- package/lib/esm/observable/event.d.ts +35 -0
- package/lib/esm/observable/event.js +46 -0
- package/lib/esm/observable/observable.d.ts +134 -0
- package/lib/esm/observable/observable.js +349 -0
- package/lib/esm/range.d.ts +1 -0
- package/lib/esm/range.js +7 -0
- package/lib/esm/result.d.ts +31 -0
- package/lib/esm/result.js +66 -0
- package/lib/esm/safe.d.ts +1 -0
- package/lib/esm/safe.js +10 -0
- package/lib/esm/server/http/apps.d.ts +5 -0
- package/lib/esm/server/http/apps.js +23 -0
- package/lib/esm/server/http/css.d.ts +5 -0
- package/lib/esm/server/http/css.js +43 -0
- package/lib/esm/server/http/index.d.ts +16 -0
- package/lib/esm/server/http/index.js +78 -0
- package/lib/esm/server/http/response.d.ts +4 -0
- package/lib/esm/server/http/response.js +43 -0
- package/lib/esm/server/http/sitemap.d.ts +2 -0
- package/lib/esm/server/http/sitemap.js +22 -0
- package/lib/esm/server/http/static.d.ts +2 -0
- package/lib/esm/server/http/static.js +22 -0
- package/lib/esm/server/http/typescript.d.ts +5 -0
- package/lib/esm/server/http/typescript.js +40 -0
- package/lib/esm/server/live-reload.d.ts +46 -0
- package/lib/esm/server/live-reload.js +161 -0
- package/lib/esm/server/main.d.ts +2 -0
- package/{src/server/main.ts → lib/esm/server/main.js} +8 -15
- package/lib/esm/server/ws/frame.d.ts +2 -0
- package/lib/esm/server/ws/frame.js +35 -0
- package/lib/esm/server/ws/handshake.d.ts +4 -0
- package/lib/esm/server/ws/handshake.js +32 -0
- package/lib/esm/server/ws/index.d.ts +14 -0
- package/lib/esm/server/ws/index.js +68 -0
- package/lib/esm/ssg/bundle.d.ts +14 -0
- package/lib/esm/ssg/bundle.js +73 -0
- package/lib/esm/ssg/copy-public.d.ts +6 -0
- package/lib/esm/ssg/copy-public.js +34 -0
- package/lib/esm/ssg/discover.d.ts +15 -0
- package/lib/esm/ssg/discover.js +117 -0
- package/lib/esm/ssg/main.d.ts +2 -0
- package/lib/esm/ssg/main.js +122 -0
- package/lib/esm/ssg/rewrite.d.ts +9 -0
- package/{src/ssg/rewrite.ts → lib/esm/ssg/rewrite.js} +6 -9
- package/lib/esm/ssg/ssg.d.ts +26 -0
- package/lib/esm/ssg/ssg.js +84 -0
- package/lib/esm/transpile.d.mts +3 -0
- package/lib/esm/transpile.mjs +12 -0
- package/package.json +11 -7
- package/src/404.html +0 -14
- package/src/assert.ts +0 -56
- package/src/case.ts +0 -5
- package/src/components/_notes +0 -33
- package/src/components/accordion.ts +0 -25
- package/src/components/alert.ts +0 -47
- package/src/components/button_bar.ts +0 -42
- package/src/components/card.ts +0 -54
- package/src/components/form.ts +0 -25
- package/src/components/inline_edit.ts +0 -78
- package/src/components/link.ts +0 -22
- package/src/components/logger.ts +0 -35
- package/src/components/nav.ts +0 -42
- package/src/components/property.ts +0 -32
- package/src/components/select.ts +0 -22
- package/src/components/tabs.ts +0 -82
- package/src/components/virtual_scroll.ts +0 -199
- package/src/debounce.ts +0 -14
- package/src/diff.ts +0 -82
- package/src/display.ts +0 -18
- package/src/dom/README.md +0 -107
- package/src/dom/SKILL.md +0 -201
- package/src/dom/css/border.ts +0 -47
- package/src/dom/css/constants.ts +0 -34
- package/src/dom/css/core.ts +0 -28
- package/src/dom/css/fstyle.ts +0 -42
- package/src/dom/css/sizing.ts +0 -11
- package/src/dom/dom.ts +0 -327
- package/src/dom/fc.ts +0 -81
- package/src/dom/form/form.app.ts +0 -44
- package/src/dom/form/form.ts +0 -151
- package/src/dom/form/index.html +0 -15
- package/src/dom/hydrate.ts +0 -206
- package/src/dom/navigation/index.ts +0 -349
- package/src/dom/observable.ts +0 -11
- package/src/dom/provide.ts +0 -11
- package/src/dom/render.ts +0 -41
- package/src/dom/router/link.ts +0 -14
- package/src/dom/router/router.ts +0 -72
- package/src/dom/types/css.ts +0 -10088
- package/src/dom/types/html.ts +0 -629
- package/src/dom/xml.ts +0 -11
- package/src/equal.ts +0 -66
- package/src/favicon.ico +0 -0
- package/src/fs.ts +0 -300
- package/src/fs_node.ts +0 -57
- package/src/generator.ts +0 -12
- package/src/hooks/_notes +0 -6
- package/src/lock.ts +0 -23
- package/src/log.ts +0 -307
- package/src/observable/_notes +0 -26
- package/src/observable/event.ts +0 -93
- package/src/observable/observable.ts +0 -484
- package/src/range.ts +0 -7
- package/src/result.ts +0 -107
- package/src/safe.ts +0 -12
- package/src/server/http/apps.ts +0 -26
- package/src/server/http/css.ts +0 -49
- package/src/server/http/index.ts +0 -127
- package/src/server/http/response.ts +0 -60
- package/src/server/http/sitemap.ts +0 -24
- package/src/server/http/static.ts +0 -28
- package/src/server/http/typescript.ts +0 -46
- package/src/server/live-reload.ts +0 -208
- package/src/server/ws/frame.ts +0 -36
- package/src/server/ws/handshake.ts +0 -42
- package/src/server/ws/index.ts +0 -100
- package/src/ssg/bundle.ts +0 -85
- package/src/ssg/copy-public.ts +0 -44
- package/src/ssg/discover.ts +0 -143
- package/src/ssg/main.ts +0 -168
- package/src/ssg/ssg.ts +0 -134
- package/src/transpile.mjs +0 -16
- package/src/zip/spec.txt +0 -3260
- package/tsconfig.json +0 -34
- /package/{src/awaitable.ts → lib/esm/awaitable.d.ts} +0 -0
- /package/{src/dom/index.ts → lib/esm/dom/index.d.ts} +0 -0
- /package/{src/dom/types/dom.ts → lib/esm/dom/types/dom.d.ts} +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type AssertMessage = string | (() => string);
|
|
2
|
+
export declare class AssertionError extends Error {
|
|
3
|
+
constructor(message?: string);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Throw an error when a condition is not met.
|
|
7
|
+
*/
|
|
8
|
+
export declare function assert<_T extends true>(condition: boolean, message?: AssertMessage): undefined | never;
|
|
9
|
+
/**
|
|
10
|
+
* Given a value, return it if it is not null nor undefined. Otherwise throw an
|
|
11
|
+
* error.
|
|
12
|
+
*
|
|
13
|
+
* @template T
|
|
14
|
+
* @returns {NonNullable<T>}
|
|
15
|
+
*/
|
|
16
|
+
export declare function assertExists<T>(t: T, message?: AssertMessage): NonNullable<T>;
|
|
17
|
+
/**
|
|
18
|
+
* @param {*} n
|
|
19
|
+
* @returns string
|
|
20
|
+
*/
|
|
21
|
+
export declare function assertString(n: unknown, message?: AssertMessage): string;
|
|
22
|
+
/**
|
|
23
|
+
* Compile time assertion that no value will used at this point in control flow.
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkExhaustive(value: never, message?: AssertMessage): never;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export class AssertionError extends Error {
|
|
2
|
+
constructor(message = "Assertion failed") {
|
|
3
|
+
super(message);
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Throw an error when a condition is not met.
|
|
8
|
+
*/
|
|
9
|
+
export function assert(condition, message) {
|
|
10
|
+
if (!condition) {
|
|
11
|
+
throw new AssertionError(message instanceof Function ? message() : message);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Given a value, return it if it is not null nor undefined. Otherwise throw an
|
|
16
|
+
* error.
|
|
17
|
+
*
|
|
18
|
+
* @template T
|
|
19
|
+
* @returns {NonNullable<T>}
|
|
20
|
+
*/
|
|
21
|
+
export function assertExists(t, message = "Assertion failed: value does not exist") {
|
|
22
|
+
assert(t != null, message);
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @param {*} n
|
|
27
|
+
* @returns string
|
|
28
|
+
*/
|
|
29
|
+
export function assertString(n, message = () => `Assertion failed: ${n} is not a string`) {
|
|
30
|
+
assert(typeof n === "string", message);
|
|
31
|
+
return n;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Compile time assertion that no value will used at this point in control flow.
|
|
35
|
+
*/
|
|
36
|
+
export function checkExhaustive(value, message = `Unexpected value ${value}`) {
|
|
37
|
+
throw new Error(message instanceof Function ? message() : message);
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function dashCase(identifier: string): string;
|
package/lib/esm/case.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Attrs, DenormChildren } from "../dom/dom.ts";
|
|
2
|
+
export type AccordionProps = {
|
|
3
|
+
summary: DenormChildren | DenormChildren[];
|
|
4
|
+
} & Attrs<HTMLDetailsElement>;
|
|
5
|
+
export declare function Accordion({ summary: summaryContent, ...attrs }: AccordionProps, ...bodyChildren: DenormChildren[]): HTMLDetailsElement;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Accordion emits the jiffies-css disclosure widget: details > summary + body.
|
|
2
|
+
// Why: jiffies-css targets details > summary; the summary must be the first child
|
|
3
|
+
// and the disclosed body follows it.
|
|
4
|
+
import { details, summary } from "../dom/html.js";
|
|
5
|
+
import { toChildren } from "./children.js";
|
|
6
|
+
// Invariant: <summary> is always the first child; remaining children form the body.
|
|
7
|
+
export function Accordion({ summary: summaryContent, ...attrs }, ...bodyChildren) {
|
|
8
|
+
return details(attrs, summary(...toChildren(summaryContent)), ...bodyChildren);
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Attrs, DenormChildren } from "../dom/dom.ts";
|
|
2
|
+
export type AlertVariant = "warning" | "error" | "info" | "success" | "neutral";
|
|
3
|
+
export type AlertProps = {
|
|
4
|
+
variant: AlertVariant;
|
|
5
|
+
} & Attrs<HTMLElement>;
|
|
6
|
+
export declare function Alert({ variant, ...attrs }: AlertProps, ...children: DenormChildren[]): HTMLElement;
|
|
7
|
+
export declare function Chip({ variant, ...attrs }: AlertProps, ...children: DenormChildren[]): HTMLElement;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { aside, small } from "../dom/html.js";
|
|
2
|
+
// The variant vocabulary is this module's single source of truth; this map both
|
|
3
|
+
// drives Alert's role derivation and stays exhaustive (a new variant without a
|
|
4
|
+
// role entry is a type error).
|
|
5
|
+
const ALERT_ROLE = {
|
|
6
|
+
warning: "alert",
|
|
7
|
+
error: "alert",
|
|
8
|
+
info: "status",
|
|
9
|
+
success: "status",
|
|
10
|
+
neutral: "status",
|
|
11
|
+
};
|
|
12
|
+
// Alert emits aside[role][data-variant] for banner-level messaging.
|
|
13
|
+
// Why: jiffies-css styles alerts by role + data-variant, not by class. role and
|
|
14
|
+
// data-variant are not in the typed attrs surface (role is constrained, data-* is
|
|
15
|
+
// not a property), so they are set with setAttribute.
|
|
16
|
+
// Invariant: warning|error => role="alert"; info|success|neutral => role="status";
|
|
17
|
+
// data-variant always equals the variant; emits no class of its own, but
|
|
18
|
+
// forwards a caller-supplied class (and other attrs) to the <aside>.
|
|
19
|
+
export function Alert({ variant, ...attrs }, ...children) {
|
|
20
|
+
const el = aside(attrs, ...children);
|
|
21
|
+
el.setAttribute("role", ALERT_ROLE[variant]);
|
|
22
|
+
el.setAttribute("data-variant", variant);
|
|
23
|
+
return el;
|
|
24
|
+
}
|
|
25
|
+
// Chip emits small[data-variant] for inline status pills. Same variant vocabulary
|
|
26
|
+
// as Alert, no role. Not exercised by the feature test.
|
|
27
|
+
export function Chip({ variant, ...attrs }, ...children) {
|
|
28
|
+
const el = small(attrs, ...children);
|
|
29
|
+
el.setAttribute("data-variant", variant);
|
|
30
|
+
return el;
|
|
31
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { display } from "../display.js";
|
|
2
|
+
import { FC } from "../dom/fc.js";
|
|
3
|
+
import { fieldset, input, label } from "../dom/html.js";
|
|
4
|
+
let buttonBarId = 1;
|
|
5
|
+
const nextId = () => buttonBarId++;
|
|
6
|
+
const ButtonBar = FC("button-bar", (_el, { value, values, events }) => {
|
|
7
|
+
const name = `button-bar-${nextId()}`;
|
|
8
|
+
return fieldset({ class: "input-group" }, ...values.flatMap((option) => {
|
|
9
|
+
const opt = `${option}`.replace(/\s+/g, "_").toLowerCase();
|
|
10
|
+
const id = `${name}-${opt}`;
|
|
11
|
+
return [
|
|
12
|
+
label({ role: "button", htmlFor: id }, input({
|
|
13
|
+
type: "radio",
|
|
14
|
+
id,
|
|
15
|
+
name,
|
|
16
|
+
value: option,
|
|
17
|
+
checked: option === value,
|
|
18
|
+
events: {
|
|
19
|
+
change: () => events.onSelect(option),
|
|
20
|
+
},
|
|
21
|
+
}), display(option)),
|
|
22
|
+
];
|
|
23
|
+
}));
|
|
24
|
+
});
|
|
25
|
+
export default ButtonBar;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Attrs, DenormChildren } from "../dom/dom.ts";
|
|
2
|
+
export interface CardParts {
|
|
3
|
+
header?: DenormChildren | DenormChildren[];
|
|
4
|
+
footer?: DenormChildren | DenormChildren[];
|
|
5
|
+
}
|
|
6
|
+
export type CardProps = CardParts & Attrs<HTMLElement>;
|
|
7
|
+
export declare function Card(parts: CardProps, ...children: DenormChildren[]): HTMLElement;
|
|
8
|
+
export declare function Panel(parts: CardProps, ...children: DenormChildren[]): HTMLElement;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { article, footer, header, main, section } from "../dom/html.js";
|
|
2
|
+
import { toChildren } from "./children.js";
|
|
3
|
+
// Build the shared header? / main / footer? sequence. `root` is the wrapper
|
|
4
|
+
// element builder (article for Card, section for Panel); the only difference
|
|
5
|
+
// between the two components is which wrapper they use.
|
|
6
|
+
function cardLike(root, { header: headerPart, footer: footerPart, ...attrs }, children) {
|
|
7
|
+
const sections = [];
|
|
8
|
+
if (headerPart !== undefined) {
|
|
9
|
+
sections.push(header(...toChildren(headerPart)));
|
|
10
|
+
}
|
|
11
|
+
sections.push(main(...children));
|
|
12
|
+
if (footerPart !== undefined) {
|
|
13
|
+
sections.push(footer(...toChildren(footerPart)));
|
|
14
|
+
}
|
|
15
|
+
return root(attrs, ...sections);
|
|
16
|
+
}
|
|
17
|
+
// Card emits the jiffies-css elevated-card structure: article > header? / main / footer?.
|
|
18
|
+
// Why: jiffies-css targets `article > main` for card body padding, so body content
|
|
19
|
+
// must always be wrapped in <main>, never placed as a bare article child.
|
|
20
|
+
// Invariants: <main> is always emitted (even with no parts); <header> only when
|
|
21
|
+
// parts.header is set; <footer> only when parts.footer is set; child order is
|
|
22
|
+
// always header, main, footer; emits no class of its own, but forwards
|
|
23
|
+
// caller-supplied attrs (class, lang, ...) to the wrapper element.
|
|
24
|
+
export function Card(parts, ...children) {
|
|
25
|
+
return cardLike(article, parts, children);
|
|
26
|
+
}
|
|
27
|
+
// Panel is the flat variant: section > header? / main / footer?. Same contract as
|
|
28
|
+
// Card with `section` in place of `article`. Not exercised by the feature test.
|
|
29
|
+
export function Panel(parts, ...children) {
|
|
30
|
+
return cardLike(section, parts, children);
|
|
31
|
+
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import type { DenormChildren } from "../dom/dom";
|
|
2
|
-
|
|
3
1
|
// Normalize a "one child or many" part-slot to a flat children array. Several
|
|
4
2
|
// components accept either a single child or an array in the same position
|
|
5
3
|
// (CardParts.header/footer, Accordion summary, PropertyEntry.value); this is the
|
|
6
4
|
// one place that distinction is collapsed.
|
|
7
|
-
export function toChildren(
|
|
8
|
-
|
|
9
|
-
): DenormChildren[] {
|
|
10
|
-
return Array.isArray(part) ? part : [part];
|
|
5
|
+
export function toChildren(part) {
|
|
6
|
+
return Array.isArray(part) ? part : [part];
|
|
11
7
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Attrs, DenormChildren } from "../dom/dom.ts";
|
|
2
|
+
export type FormGroupProps = {
|
|
3
|
+
legend: DenormChildren;
|
|
4
|
+
} & Attrs<HTMLFieldSetElement>;
|
|
5
|
+
export declare function FormGroup({ legend: legendLabel, ...attrs }: FormGroupProps, ...children: DenormChildren[]): HTMLFieldSetElement;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { fieldset, legend } from "../dom/html.js";
|
|
2
|
+
// FormGroup emits fieldset[role=group] > legend + children — the jiffies-css
|
|
3
|
+
// grouped-controls pattern. This is the structural form group; the richer form
|
|
4
|
+
// controls (Input, Select, Radios, ...) live in src/dom/form/form.ts.
|
|
5
|
+
// Why: jiffies-css targets fieldset[role=group] to lay grouped controls out as a
|
|
6
|
+
// row; role is set with setAttribute since "group" is outside the typed role surface.
|
|
7
|
+
// Invariant: <legend> is the first child; role="group"; emits no class of its own,
|
|
8
|
+
// but forwards caller-supplied attrs (class, lang, ...) to the <fieldset>.
|
|
9
|
+
export function FormGroup({ legend: legendLabel, ...attrs }, ...children) {
|
|
10
|
+
const group = fieldset(attrs, legend(legendLabel), ...children);
|
|
11
|
+
group.setAttribute("role", "group");
|
|
12
|
+
return group;
|
|
13
|
+
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Re-exports the public component surface. The feature test imports from here.
|
|
2
|
-
|
|
3
1
|
export { Accordion, type AccordionProps } from "./accordion.ts";
|
|
4
2
|
export { Alert, type AlertProps, type AlertVariant, Chip } from "./alert.ts";
|
|
5
3
|
export { Card, type CardParts, type CardProps, Panel } from "./card.ts";
|
|
@@ -7,16 +5,5 @@ export { FormGroup, type FormGroupProps } from "./form.ts";
|
|
|
7
5
|
export { type JiffiesCssLinkProps, jiffiesCssLink } from "./link.ts";
|
|
8
6
|
export { Modal } from "./modal.ts";
|
|
9
7
|
export { Breadcrumb, Nav, type NavItem, type NavProps } from "./nav.ts";
|
|
10
|
-
export {
|
|
11
|
-
|
|
12
|
-
PropertySheet,
|
|
13
|
-
type PropertySheetProps,
|
|
14
|
-
} from "./property.ts";
|
|
15
|
-
export {
|
|
16
|
-
type StaticTabItem,
|
|
17
|
-
StaticTabList,
|
|
18
|
-
type StaticTabListProps,
|
|
19
|
-
type TabItem,
|
|
20
|
-
TabList,
|
|
21
|
-
type TabListProps,
|
|
22
|
-
} from "./tabs.ts";
|
|
8
|
+
export { type PropertyEntry, PropertySheet, type PropertySheetProps, } from "./property.ts";
|
|
9
|
+
export { type StaticTabItem, StaticTabList, type StaticTabListProps, type TabItem, TabList, type TabListProps, } from "./tabs.ts";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Re-exports the public component surface. The feature test imports from here.
|
|
2
|
+
export { Accordion } from "./accordion.js";
|
|
3
|
+
export { Alert, Chip } from "./alert.js";
|
|
4
|
+
export { Card, Panel } from "./card.js";
|
|
5
|
+
export { FormGroup } from "./form.js";
|
|
6
|
+
export { jiffiesCssLink } from "./link.js";
|
|
7
|
+
export { Modal } from "./modal.js";
|
|
8
|
+
export { Breadcrumb, Nav } from "./nav.js";
|
|
9
|
+
export { PropertySheet, } from "./property.js";
|
|
10
|
+
export { StaticTabList, TabList, } from "./tabs.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface InlineEditState {
|
|
2
|
+
mode: number;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const InlineEdit: import("../dom/fc.ts").FCComponentCtor<{
|
|
6
|
+
mode?: number;
|
|
7
|
+
value: string;
|
|
8
|
+
events: {
|
|
9
|
+
change: (value: string) => void;
|
|
10
|
+
};
|
|
11
|
+
}, InlineEditState>;
|
|
12
|
+
export default InlineEdit;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { width } from "../dom/css/sizing.js";
|
|
2
|
+
import { FC, State } from "../dom/fc.js";
|
|
3
|
+
import { input, span } from "../dom/html.js";
|
|
4
|
+
const Mode = { VIEW: 0, EDIT: 1 };
|
|
5
|
+
export const InlineEdit = FC("inline-edit", (el, { mode = Mode.VIEW, value, events }) => {
|
|
6
|
+
el[State] ??= { mode, value };
|
|
7
|
+
const state = el[State];
|
|
8
|
+
const render = () => {
|
|
9
|
+
switch (state.mode) {
|
|
10
|
+
case Mode.EDIT:
|
|
11
|
+
return edit();
|
|
12
|
+
case Mode.VIEW:
|
|
13
|
+
return view();
|
|
14
|
+
default:
|
|
15
|
+
return span();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const view = () => span({
|
|
19
|
+
style: { cursor: "text", ...width("full", "inline") },
|
|
20
|
+
events: {
|
|
21
|
+
click: () => {
|
|
22
|
+
state.mode = Mode.EDIT;
|
|
23
|
+
el.update(render());
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}, state.value ?? "");
|
|
27
|
+
const edit = () => {
|
|
28
|
+
const edit = span({ style: { display: "block", position: "relative" } }, input({
|
|
29
|
+
style: {
|
|
30
|
+
zIndex: "10",
|
|
31
|
+
position: "absolute",
|
|
32
|
+
left: "0",
|
|
33
|
+
marginTop: "-0.375rem",
|
|
34
|
+
},
|
|
35
|
+
events: {
|
|
36
|
+
blur: ({ target }) => events.change(target?.value ?? ""),
|
|
37
|
+
},
|
|
38
|
+
type: "text",
|
|
39
|
+
value: state.value ?? "",
|
|
40
|
+
}), "\u00a0");
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
edit.dispatchEvent(new Event("focus"));
|
|
43
|
+
});
|
|
44
|
+
return edit;
|
|
45
|
+
};
|
|
46
|
+
return render();
|
|
47
|
+
});
|
|
48
|
+
export default InlineEdit;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { link } from "../dom/html.js";
|
|
2
|
+
const JIFFIES_CSS_CDN = "https://unpkg.com/@davidsouther/jiffies-css/dist/index.css";
|
|
3
|
+
// jiffiesCssLink builds the <link> a page puts in <head> to load jiffies-css.
|
|
4
|
+
// Why: callers must never hand-write the CDN URL or accidentally point at Pico;
|
|
5
|
+
// this is the single sanctioned source of the stylesheet href.
|
|
6
|
+
// Invariant: returns a <link rel="stylesheet"> whose href contains "jiffies-css"
|
|
7
|
+
// and never "pico". Default href is the unpkg CDN; callers bundling locally pass
|
|
8
|
+
// their own href.
|
|
9
|
+
export function jiffiesCssLink({ href = JIFFIES_CSS_CDN, ...attrs } = {}) {
|
|
10
|
+
return link({ ...attrs, rel: "stylesheet", href });
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { display } from "../display.js";
|
|
2
|
+
import { code, div, li, pre, span, ul } from "../dom/html.js";
|
|
3
|
+
import { LEVEL } from "../log.js";
|
|
4
|
+
export function isHTMLLogger(logger) {
|
|
5
|
+
return logger.root !== undefined;
|
|
6
|
+
}
|
|
7
|
+
export function makeHTMLLogger(name) {
|
|
8
|
+
const log = ul();
|
|
9
|
+
const root = div(div(span(name)), log);
|
|
10
|
+
const logger = { level: LEVEL.INFO, root };
|
|
11
|
+
function append(message) {
|
|
12
|
+
log.appendChild(li(pre(code(message))));
|
|
13
|
+
}
|
|
14
|
+
const logAt = (level) => (message) => level >= (logger.level ?? LEVEL.ERROR)
|
|
15
|
+
? append(display(message))
|
|
16
|
+
: undefined;
|
|
17
|
+
logger.debug = logAt(LEVEL.VERBOSE);
|
|
18
|
+
logger.info = logAt(LEVEL.INFO);
|
|
19
|
+
logger.warn = logAt(LEVEL.WARN);
|
|
20
|
+
logger.error = logAt(LEVEL.ERROR);
|
|
21
|
+
return logger;
|
|
22
|
+
}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { dialog } from "../dom/html.ts";
|
|
3
|
-
|
|
1
|
+
import { dialog } from "../dom/html.js";
|
|
4
2
|
// Modal emits a <dialog>. It has no domain props, so its leading argument is the
|
|
5
3
|
// denormalized attrs of any html builder: a plain object is attrs (class, lang,
|
|
6
4
|
// ...) applied to the <dialog>, anything else is the first child. The element
|
|
7
5
|
// already carries .update() (attached by up()), so callers toggle visibility
|
|
8
6
|
// later with modal.update({ open: true }) / modal.update({ open: false }).
|
|
9
7
|
// Invariant: returns a <dialog> whose children are the supplied content.
|
|
10
|
-
export function Modal(
|
|
11
|
-
|
|
12
|
-
...children: DenormChildren[]
|
|
13
|
-
): HTMLDialogElement {
|
|
14
|
-
return dialog(attrs, ...children);
|
|
8
|
+
export function Modal(attrs, ...children) {
|
|
9
|
+
return dialog(attrs, ...children);
|
|
15
10
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Attrs } from "../dom/dom.ts";
|
|
2
|
+
export interface NavItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
current?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export type NavProps = {
|
|
8
|
+
items: NavItem[];
|
|
9
|
+
} & Attrs<HTMLElement>;
|
|
10
|
+
export declare function Nav({ items, ...attrs }: NavProps): HTMLElement;
|
|
11
|
+
export declare function Breadcrumb({ items, ...attrs }: NavProps): HTMLElement;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { a, li, nav, ol, span } from "../dom/html.js";
|
|
2
|
+
// One <li><a> per item. aria-current is set with setAttribute (aria-* is not in
|
|
3
|
+
// the typed attrs surface); the anchor carries href only when the item supplies one.
|
|
4
|
+
function navItem(item) {
|
|
5
|
+
const anchor = a(item.href ? { href: item.href } : {}, item.label);
|
|
6
|
+
if (item.current) {
|
|
7
|
+
anchor.setAttribute("aria-current", "page");
|
|
8
|
+
}
|
|
9
|
+
return li(anchor);
|
|
10
|
+
}
|
|
11
|
+
function navList(items) {
|
|
12
|
+
return ol(...items.map(navItem));
|
|
13
|
+
}
|
|
14
|
+
// Nav emits nav > ol > li > a, one <li> per item.
|
|
15
|
+
// Why: jiffies-css targets the nav > ol > li > a chain; a bare ul > li > a is unstyled.
|
|
16
|
+
// Invariants: every item is an <a> inside an <li> inside the single <ol>; an item
|
|
17
|
+
// with current:true gets aria-current="page" on its <a>; emits no class of its own,
|
|
18
|
+
// but forwards caller-supplied attrs (class, lang, ...) to the <nav>.
|
|
19
|
+
export function Nav({ items, ...attrs }) {
|
|
20
|
+
return nav(attrs, navList(items));
|
|
21
|
+
}
|
|
22
|
+
// Breadcrumb wraps the same nav > ol > li chain in a <span> (span > nav > ol > li),
|
|
23
|
+
// the jiffies-css breadcrumb selector. Same file, same pattern. Not exercised by
|
|
24
|
+
// the feature test.
|
|
25
|
+
export function Breadcrumb({ items, ...attrs }) {
|
|
26
|
+
return span(attrs, nav(navList(items)));
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Attrs, DenormChildren } from "../dom/dom.ts";
|
|
2
|
+
export interface PropertyEntry {
|
|
3
|
+
label: string;
|
|
4
|
+
value: DenormChildren | DenormChildren[];
|
|
5
|
+
}
|
|
6
|
+
export type PropertySheetProps = {
|
|
7
|
+
entries: PropertyEntry[];
|
|
8
|
+
} & Attrs<HTMLDListElement>;
|
|
9
|
+
export declare function PropertySheet({ entries, ...attrs }: PropertySheetProps): HTMLDListElement;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { dd, dl, dt } from "../dom/html.js";
|
|
2
|
+
import { toChildren } from "./children.js";
|
|
3
|
+
// PropertySheet emits dl > (dt + dd)* — the jiffies-css property-sheet pattern,
|
|
4
|
+
// one dt/dd pair per entry.
|
|
5
|
+
// Why: jiffies-css targets dl > dt + dd for aligned label/value rows; a table or
|
|
6
|
+
// div grid is unstyled.
|
|
7
|
+
// Invariant: exactly one <dt> (the label) and one <dd> (the value) per entry, in
|
|
8
|
+
// entry order; emits no class of its own, but forwards caller-supplied attrs
|
|
9
|
+
// (class, lang, ...) to the <dl>.
|
|
10
|
+
export function PropertySheet({ entries, ...attrs }) {
|
|
11
|
+
const rows = [];
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
rows.push(dt(entry.label), dd(...toChildren(entry.value)));
|
|
14
|
+
}
|
|
15
|
+
return dl(attrs, ...rows);
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EventHandler } from "../dom/dom.ts";
|
|
2
|
+
export declare const Select: import("../dom/fc.ts").FCComponentCtor<{
|
|
3
|
+
name: string;
|
|
4
|
+
value: string;
|
|
5
|
+
events: {
|
|
6
|
+
change: EventHandler;
|
|
7
|
+
};
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
options: [string, string][];
|
|
10
|
+
}, object>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { FC } from "../dom/fc.js";
|
|
2
|
+
import { option, select } from "../dom/html.js";
|
|
3
|
+
export const Select = FC("jiffies-select", (_el, { name, events: { change }, disabled, value, options }) => select({ name, events: { change }, disabled }, ...options.map(([v, name]) => option({ value: v, selected: value === v }, `${name}`))));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Attrs } from "../dom/dom.ts";
|
|
2
|
+
export interface TabItem {
|
|
3
|
+
label: string;
|
|
4
|
+
selected?: boolean;
|
|
5
|
+
onSelect?: (e: Event) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface StaticTabItem {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
selected?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export type TabListProps = {
|
|
13
|
+
tabs: TabItem[];
|
|
14
|
+
} & Attrs<HTMLDivElement>;
|
|
15
|
+
export type StaticTabListProps = {
|
|
16
|
+
name: string;
|
|
17
|
+
tabs: StaticTabItem[];
|
|
18
|
+
} & Attrs<HTMLDivElement>;
|
|
19
|
+
export declare function TabList({ tabs, ...attrs }: TabListProps): HTMLElement;
|
|
20
|
+
export declare function StaticTabList({ name, tabs, ...attrs }: StaticTabListProps): HTMLElement;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { button, div, input, label } from "../dom/html.js";
|
|
2
|
+
// Shared tablist container: div[role=tablist] holding the supplied tab controls.
|
|
3
|
+
// Both tab variants emit the same container; this is the one place the role string
|
|
4
|
+
// lives (matching the cardLike/navList helpers elsewhere in the module).
|
|
5
|
+
function tablist(attrs, ...children) {
|
|
6
|
+
const list = div(attrs, ...children);
|
|
7
|
+
list.setAttribute("role", "tablist");
|
|
8
|
+
return list;
|
|
9
|
+
}
|
|
10
|
+
// TabList emits the JS-driven tab strip: div[role=tablist] > button[role=tab].
|
|
11
|
+
// Why: jiffies-css targets [role=tablist] > button[role=tab][aria-selected]; the
|
|
12
|
+
// caller owns which tab is active and re-renders via .update() on the element.
|
|
13
|
+
// Invariant: role="tablist" on the container; every tab is a button[role=tab];
|
|
14
|
+
// selected:true sets aria-selected="true"; onSelect is wired as a click handler.
|
|
15
|
+
export function TabList({ tabs, ...attrs }) {
|
|
16
|
+
const buttons = tabs.map((tab) => {
|
|
17
|
+
const btn = button(tab.onSelect
|
|
18
|
+
? { type: "button", events: { click: tab.onSelect } }
|
|
19
|
+
: { type: "button" }, tab.label);
|
|
20
|
+
btn.setAttribute("role", "tab");
|
|
21
|
+
if (tab.selected) {
|
|
22
|
+
btn.setAttribute("aria-selected", "true");
|
|
23
|
+
}
|
|
24
|
+
return btn;
|
|
25
|
+
});
|
|
26
|
+
return tablist(attrs, ...buttons);
|
|
27
|
+
}
|
|
28
|
+
// StaticTabList emits the CSS-only tab strip: div[role=tablist] >
|
|
29
|
+
// (input[type=radio][name][id] + label[role=tab][for])*. The shared name groups
|
|
30
|
+
// the radios; :checked drives the active panel with zero JavaScript.
|
|
31
|
+
// Invariant: role="tablist" on the container; one radio + one label[role=tab] per
|
|
32
|
+
// tab; id/for pair come from StaticTabItem.id; selected:true sets defaultChecked.
|
|
33
|
+
export function StaticTabList({ name, tabs, ...attrs }) {
|
|
34
|
+
const children = tabs.flatMap((tab) => {
|
|
35
|
+
const radio = input({ type: "radio", name, id: tab.id });
|
|
36
|
+
if (tab.selected) {
|
|
37
|
+
radio.defaultChecked = true;
|
|
38
|
+
}
|
|
39
|
+
const lbl = label(tab.label);
|
|
40
|
+
lbl.setAttribute("for", tab.id);
|
|
41
|
+
lbl.setAttribute("role", "tab");
|
|
42
|
+
return [radio, lbl];
|
|
43
|
+
});
|
|
44
|
+
return tablist(attrs, ...children);
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface VirtualScrollSettings {
|
|
2
|
+
minIndex: number;
|
|
3
|
+
maxIndex: number;
|
|
4
|
+
startIndex: number;
|
|
5
|
+
itemHeight: number;
|
|
6
|
+
count: number;
|
|
7
|
+
tolerance: number;
|
|
8
|
+
}
|
|
9
|
+
export type VirtualScrollDataAdapter<T> = (offset: number, limit: number) => Iterable<T>;
|
|
10
|
+
export declare function arrayAdapter<T>(data: T[]): VirtualScrollDataAdapter<T>;
|
|
11
|
+
export interface VirtualScrollProps<T, U extends HTMLElement> {
|
|
12
|
+
settings: Partial<VirtualScrollSettings>;
|
|
13
|
+
get: VirtualScrollDataAdapter<T>;
|
|
14
|
+
row: (t: T) => U;
|
|
15
|
+
}
|
|
16
|
+
export declare function fillVirtualScrollSettings(settings: Partial<VirtualScrollSettings>): VirtualScrollSettings;
|
|
17
|
+
export declare function initialState<T>(settings: VirtualScrollSettings): VirtualScrollState<T>;
|
|
18
|
+
export declare function getData<T>(minIndex: number, maxIndex: number, offset: number, limit: number, get: VirtualScrollDataAdapter<T>): T[];
|
|
19
|
+
export declare function doScroll<T>(scrollTop: number, state: VirtualScrollState<T>, get: VirtualScrollDataAdapter<T>): {
|
|
20
|
+
scrollTop: number;
|
|
21
|
+
topPaddingHeight: number;
|
|
22
|
+
bottomPaddingHeight: number;
|
|
23
|
+
data: T[];
|
|
24
|
+
};
|
|
25
|
+
interface VirtualScrollState<T, U extends HTMLElement = HTMLElement> {
|
|
26
|
+
settings: VirtualScrollSettings;
|
|
27
|
+
scrollTop: number;
|
|
28
|
+
bufferedItems: number;
|
|
29
|
+
totalHeight: number;
|
|
30
|
+
viewportHeight: number;
|
|
31
|
+
topPaddingHeight: number;
|
|
32
|
+
bottomPaddingHeight: number;
|
|
33
|
+
toleranceHeight: number;
|
|
34
|
+
data: T[];
|
|
35
|
+
rows: U[];
|
|
36
|
+
}
|
|
37
|
+
export interface VirtualScroll<T, U extends HTMLElement> {
|
|
38
|
+
state: VirtualScrollState<T>;
|
|
39
|
+
rows: U[];
|
|
40
|
+
}
|
|
41
|
+
export declare const VirtualScroll: import("../dom/fc.ts").FCComponentCtor<VirtualScrollProps<unknown, HTMLElement>, VirtualScrollState<unknown, HTMLElement>>;
|
|
42
|
+
export default VirtualScroll;
|