@davidsouther/jiffies 2026.24.0 → 2026.24.2

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.
Files changed (226) hide show
  1. package/lib/esm/assert.d.ts +26 -0
  2. package/lib/esm/assert.js +38 -0
  3. package/lib/esm/awaitable.js +1 -0
  4. package/lib/esm/case.d.ts +1 -0
  5. package/lib/esm/case.js +5 -0
  6. package/lib/esm/components/accordion.d.ts +5 -0
  7. package/lib/esm/components/accordion.js +9 -0
  8. package/lib/esm/components/alert.d.ts +7 -0
  9. package/lib/esm/components/alert.js +31 -0
  10. package/lib/esm/components/button_bar.d.ts +8 -0
  11. package/lib/esm/components/button_bar.js +25 -0
  12. package/lib/esm/components/card.d.ts +8 -0
  13. package/lib/esm/components/card.js +31 -0
  14. package/lib/esm/components/children.d.ts +2 -0
  15. package/{src/components/children.ts → lib/esm/components/children.js} +2 -6
  16. package/lib/esm/components/form.d.ts +5 -0
  17. package/lib/esm/components/form.js +13 -0
  18. package/{src/components/index.ts → lib/esm/components/index.d.ts} +2 -15
  19. package/lib/esm/components/index.js +10 -0
  20. package/lib/esm/components/inline_edit.d.ts +12 -0
  21. package/lib/esm/components/inline_edit.js +48 -0
  22. package/lib/esm/components/link.d.ts +5 -0
  23. package/lib/esm/components/link.js +11 -0
  24. package/lib/esm/components/logger.d.ts +6 -0
  25. package/lib/esm/components/logger.js +22 -0
  26. package/lib/esm/components/modal.d.ts +2 -0
  27. package/{src/components/modal.ts → lib/esm/components/modal.js} +3 -8
  28. package/lib/esm/components/nav.d.ts +11 -0
  29. package/lib/esm/components/nav.js +27 -0
  30. package/lib/esm/components/property.d.ts +9 -0
  31. package/lib/esm/components/property.js +16 -0
  32. package/lib/esm/components/select.d.ts +10 -0
  33. package/lib/esm/components/select.js +3 -0
  34. package/lib/esm/components/tabs.d.ts +20 -0
  35. package/lib/esm/components/tabs.js +45 -0
  36. package/lib/esm/components/virtual_scroll.d.ts +42 -0
  37. package/lib/esm/components/virtual_scroll.js +94 -0
  38. package/lib/esm/debounce.d.ts +1 -0
  39. package/lib/esm/debounce.js +11 -0
  40. package/lib/esm/diff.d.ts +15 -0
  41. package/lib/esm/diff.js +50 -0
  42. package/lib/esm/display.d.ts +5 -0
  43. package/lib/esm/display.js +11 -0
  44. package/lib/esm/dom/css/border.d.ts +11 -0
  45. package/lib/esm/dom/css/border.js +27 -0
  46. package/lib/esm/dom/css/constants.d.ts +31 -0
  47. package/lib/esm/dom/css/constants.js +28 -0
  48. package/lib/esm/dom/css/core.d.ts +5 -0
  49. package/lib/esm/dom/css/core.js +24 -0
  50. package/lib/esm/dom/css/fstyle.d.ts +5 -0
  51. package/lib/esm/dom/css/fstyle.js +32 -0
  52. package/lib/esm/dom/css/sizing.d.ts +5 -0
  53. package/lib/esm/dom/css/sizing.js +10 -0
  54. package/lib/esm/dom/dom.d.ts +36 -0
  55. package/lib/esm/dom/dom.js +217 -0
  56. package/lib/esm/dom/fc.d.ts +10 -0
  57. package/lib/esm/dom/fc.js +32 -0
  58. package/lib/esm/dom/form/form.app.d.ts +1 -0
  59. package/lib/esm/dom/form/form.app.js +19 -0
  60. package/lib/esm/dom/form/form.d.ts +27 -0
  61. package/lib/esm/dom/form/form.js +65 -0
  62. package/lib/esm/dom/html.d.ts +112 -0
  63. package/{src/dom/html.ts → lib/esm/dom/html.js} +2 -14
  64. package/lib/esm/dom/hydrate.d.ts +39 -0
  65. package/lib/esm/dom/hydrate.js +187 -0
  66. package/lib/esm/dom/index.js +2 -0
  67. package/lib/esm/dom/navigation/index.d.ts +76 -0
  68. package/lib/esm/dom/navigation/index.js +292 -0
  69. package/lib/esm/dom/observable.d.ts +2 -0
  70. package/lib/esm/dom/observable.js +6 -0
  71. package/lib/esm/dom/provide.d.ts +3 -0
  72. package/lib/esm/dom/provide.js +7 -0
  73. package/lib/esm/dom/render.d.ts +8 -0
  74. package/lib/esm/dom/render.js +28 -0
  75. package/lib/esm/dom/router/link.d.ts +6 -0
  76. package/lib/esm/dom/router/link.js +3 -0
  77. package/lib/esm/dom/router/router.d.ts +13 -0
  78. package/lib/esm/dom/router/router.js +52 -0
  79. package/lib/esm/dom/svg.d.ts +64 -0
  80. package/{src/dom/svg.ts → lib/esm/dom/svg.js} +2 -19
  81. package/lib/esm/dom/types/css.d.ts +6590 -0
  82. package/lib/esm/dom/types/css.js +1 -0
  83. package/lib/esm/dom/types/dom.js +1 -0
  84. package/lib/esm/dom/types/html.d.ts +614 -0
  85. package/lib/esm/dom/types/html.js +1 -0
  86. package/lib/esm/dom/xml.d.ts +1 -0
  87. package/lib/esm/dom/xml.js +4 -0
  88. package/lib/esm/equal.d.ts +11 -0
  89. package/lib/esm/equal.js +43 -0
  90. package/lib/esm/fs.d.ts +72 -0
  91. package/lib/esm/fs.js +227 -0
  92. package/lib/esm/fs_node.d.ts +15 -0
  93. package/lib/esm/fs_node.js +45 -0
  94. package/lib/esm/generator.d.ts +1 -0
  95. package/lib/esm/generator.js +10 -0
  96. package/lib/esm/lock.d.ts +1 -0
  97. package/lib/esm/lock.js +23 -0
  98. package/lib/esm/log.d.ts +69 -0
  99. package/lib/esm/log.js +211 -0
  100. package/lib/esm/observable/event.d.ts +35 -0
  101. package/lib/esm/observable/event.js +46 -0
  102. package/lib/esm/observable/observable.d.ts +134 -0
  103. package/lib/esm/observable/observable.js +349 -0
  104. package/lib/esm/range.d.ts +1 -0
  105. package/lib/esm/range.js +7 -0
  106. package/lib/esm/result.d.ts +31 -0
  107. package/lib/esm/result.js +66 -0
  108. package/lib/esm/safe.d.ts +1 -0
  109. package/lib/esm/safe.js +10 -0
  110. package/lib/esm/server/http/apps.d.ts +5 -0
  111. package/lib/esm/server/http/apps.js +23 -0
  112. package/lib/esm/server/http/css.d.ts +5 -0
  113. package/lib/esm/server/http/css.js +43 -0
  114. package/lib/esm/server/http/index.d.ts +16 -0
  115. package/lib/esm/server/http/index.js +78 -0
  116. package/lib/esm/server/http/response.d.ts +4 -0
  117. package/lib/esm/server/http/response.js +43 -0
  118. package/lib/esm/server/http/sitemap.d.ts +2 -0
  119. package/lib/esm/server/http/sitemap.js +22 -0
  120. package/lib/esm/server/http/static.d.ts +2 -0
  121. package/lib/esm/server/http/static.js +22 -0
  122. package/lib/esm/server/http/typescript.d.ts +5 -0
  123. package/lib/esm/server/http/typescript.js +40 -0
  124. package/lib/esm/server/live-reload.d.ts +46 -0
  125. package/lib/esm/server/live-reload.js +161 -0
  126. package/lib/esm/server/main.d.ts +2 -0
  127. package/{src/server/main.ts → lib/esm/server/main.js} +8 -15
  128. package/lib/esm/server/ws/frame.d.ts +2 -0
  129. package/lib/esm/server/ws/frame.js +35 -0
  130. package/lib/esm/server/ws/handshake.d.ts +4 -0
  131. package/lib/esm/server/ws/handshake.js +32 -0
  132. package/lib/esm/server/ws/index.d.ts +14 -0
  133. package/lib/esm/server/ws/index.js +68 -0
  134. package/lib/esm/ssg/bundle.d.ts +14 -0
  135. package/lib/esm/ssg/bundle.js +73 -0
  136. package/lib/esm/ssg/copy-public.d.ts +6 -0
  137. package/lib/esm/ssg/copy-public.js +34 -0
  138. package/lib/esm/ssg/discover.d.ts +15 -0
  139. package/lib/esm/ssg/discover.js +117 -0
  140. package/lib/esm/ssg/main.d.ts +2 -0
  141. package/lib/esm/ssg/main.js +122 -0
  142. package/lib/esm/ssg/rewrite.d.ts +9 -0
  143. package/{src/ssg/rewrite.ts → lib/esm/ssg/rewrite.js} +6 -9
  144. package/lib/esm/ssg/ssg.d.ts +26 -0
  145. package/lib/esm/ssg/ssg.js +84 -0
  146. package/lib/esm/transpile.d.mts +3 -0
  147. package/lib/esm/transpile.mjs +12 -0
  148. package/package.json +11 -7
  149. package/src/404.html +0 -14
  150. package/src/assert.ts +0 -56
  151. package/src/case.ts +0 -5
  152. package/src/components/_notes +0 -33
  153. package/src/components/accordion.ts +0 -25
  154. package/src/components/alert.ts +0 -47
  155. package/src/components/button_bar.ts +0 -42
  156. package/src/components/card.ts +0 -54
  157. package/src/components/form.ts +0 -25
  158. package/src/components/inline_edit.ts +0 -78
  159. package/src/components/link.ts +0 -22
  160. package/src/components/logger.ts +0 -35
  161. package/src/components/nav.ts +0 -42
  162. package/src/components/property.ts +0 -32
  163. package/src/components/select.ts +0 -22
  164. package/src/components/tabs.ts +0 -82
  165. package/src/components/virtual_scroll.ts +0 -199
  166. package/src/debounce.ts +0 -14
  167. package/src/diff.ts +0 -82
  168. package/src/display.ts +0 -18
  169. package/src/dom/README.md +0 -107
  170. package/src/dom/SKILL.md +0 -201
  171. package/src/dom/css/border.ts +0 -47
  172. package/src/dom/css/constants.ts +0 -34
  173. package/src/dom/css/core.ts +0 -28
  174. package/src/dom/css/fstyle.ts +0 -42
  175. package/src/dom/css/sizing.ts +0 -11
  176. package/src/dom/dom.ts +0 -327
  177. package/src/dom/fc.ts +0 -81
  178. package/src/dom/form/form.app.ts +0 -44
  179. package/src/dom/form/form.ts +0 -151
  180. package/src/dom/form/index.html +0 -15
  181. package/src/dom/hydrate.ts +0 -206
  182. package/src/dom/navigation/index.ts +0 -349
  183. package/src/dom/observable.ts +0 -11
  184. package/src/dom/provide.ts +0 -11
  185. package/src/dom/render.ts +0 -41
  186. package/src/dom/router/link.ts +0 -14
  187. package/src/dom/router/router.ts +0 -72
  188. package/src/dom/types/css.ts +0 -10088
  189. package/src/dom/types/html.ts +0 -629
  190. package/src/dom/xml.ts +0 -11
  191. package/src/equal.ts +0 -66
  192. package/src/favicon.ico +0 -0
  193. package/src/fs.ts +0 -300
  194. package/src/fs_node.ts +0 -57
  195. package/src/generator.ts +0 -12
  196. package/src/hooks/_notes +0 -6
  197. package/src/lock.ts +0 -23
  198. package/src/log.ts +0 -307
  199. package/src/observable/_notes +0 -26
  200. package/src/observable/event.ts +0 -93
  201. package/src/observable/observable.ts +0 -484
  202. package/src/range.ts +0 -7
  203. package/src/result.ts +0 -107
  204. package/src/safe.ts +0 -12
  205. package/src/server/http/apps.ts +0 -26
  206. package/src/server/http/css.ts +0 -49
  207. package/src/server/http/index.ts +0 -127
  208. package/src/server/http/response.ts +0 -60
  209. package/src/server/http/sitemap.ts +0 -24
  210. package/src/server/http/static.ts +0 -28
  211. package/src/server/http/typescript.ts +0 -46
  212. package/src/server/live-reload.ts +0 -208
  213. package/src/server/ws/frame.ts +0 -36
  214. package/src/server/ws/handshake.ts +0 -42
  215. package/src/server/ws/index.ts +0 -100
  216. package/src/ssg/bundle.ts +0 -85
  217. package/src/ssg/copy-public.ts +0 -44
  218. package/src/ssg/discover.ts +0 -143
  219. package/src/ssg/main.ts +0 -168
  220. package/src/ssg/ssg.ts +0 -134
  221. package/src/transpile.mjs +0 -16
  222. package/src/zip/spec.txt +0 -3260
  223. package/tsconfig.json +0 -34
  224. /package/{src/awaitable.ts → lib/esm/awaitable.d.ts} +0 -0
  225. /package/{src/dom/index.ts → lib/esm/dom/index.d.ts} +0 -0
  226. /package/{src/dom/types/dom.ts → lib/esm/dom/types/dom.d.ts} +0 -0
package/src/404.html DELETED
@@ -1,14 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en-US">
3
- <head>
4
- <title>Not Found</title>
5
- <style>
6
- * {
7
- font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
8
- }
9
- </style>
10
- </head>
11
- <body>
12
- <h1>Not Found</h1>
13
- </body>
14
- </html>
package/src/assert.ts DELETED
@@ -1,56 +0,0 @@
1
- type AssertMessage = string | (() => string);
2
-
3
- export class AssertionError extends Error {
4
- constructor(message = "Assertion failed") {
5
- super(message);
6
- }
7
- }
8
-
9
- /**
10
- * Throw an error when a condition is not met.
11
- */
12
- export function assert<_T extends true>(
13
- condition: boolean,
14
- message?: AssertMessage,
15
- ): undefined | never {
16
- if (!condition) {
17
- throw new AssertionError(message instanceof Function ? message() : message);
18
- }
19
- }
20
-
21
- /**
22
- * Given a value, return it if it is not null nor undefined. Otherwise throw an
23
- * error.
24
- *
25
- * @template T
26
- * @returns {NonNullable<T>}
27
- */
28
- export function assertExists<T>(
29
- t: T,
30
- message: AssertMessage = "Assertion failed: value does not exist",
31
- ): NonNullable<T> {
32
- assert(t != null, message);
33
- return t as NonNullable<T>;
34
- }
35
-
36
- /**
37
- * @param {*} n
38
- * @returns string
39
- */
40
- export function assertString(
41
- n: unknown,
42
- message: AssertMessage = () => `Assertion failed: ${n} is not a string`,
43
- ): string {
44
- assert(typeof n === "string", message);
45
- return n as string;
46
- }
47
-
48
- /**
49
- * Compile time assertion that no value will used at this point in control flow.
50
- */
51
- export function checkExhaustive(
52
- value: never,
53
- message: AssertMessage = `Unexpected value ${value}`,
54
- ): never {
55
- throw new Error(message instanceof Function ? message() : message);
56
- }
package/src/case.ts DELETED
@@ -1,5 +0,0 @@
1
- export function dashCase(identifier: string) {
2
- return identifier
3
- .replace(/([a-z])([A-Z])/g, (_, a, b) => `${a}-${b.toLowerCase()}`)
4
- .replace(/ ([A-Z])/g, (_, b) => `-${b.toLowerCase()}`);
5
- }
@@ -1,33 +0,0 @@
1
- Nav
2
- ButtonBar
3
-
4
- Form
5
- FormGroup
6
- Input
7
- Select
8
- CheckGroup
9
- RadioGroup
10
- Textarea
11
- Switch
12
- Button
13
-
14
- Datatable
15
- Column
16
- Header
17
- Row
18
- Cell
19
- Sort
20
-
21
- Virtual Scroll
22
- Logs Viewer
23
-
24
- Chart
25
- Axis
26
- Legend
27
- Bar
28
- Scatter
29
- Line
30
-
31
- Graph
32
- Node
33
- Segment
@@ -1,25 +0,0 @@
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
-
5
- import type { Attrs, DenormChildren } from "../dom/dom.ts";
6
- import { details, summary } from "../dom/html.ts";
7
- import { toChildren } from "./children.ts";
8
-
9
- // Accordion props: the summary slot plus any DOM attrs (class, lang, ...) to apply
10
- // to the outermost <details>.
11
- export type AccordionProps = {
12
- summary: DenormChildren | DenormChildren[];
13
- } & Attrs<HTMLDetailsElement>;
14
-
15
- // Invariant: <summary> is always the first child; remaining children form the body.
16
- export function Accordion(
17
- { summary: summaryContent, ...attrs }: AccordionProps,
18
- ...bodyChildren: DenormChildren[]
19
- ): HTMLDetailsElement {
20
- return details(
21
- attrs,
22
- summary(...toChildren(summaryContent)),
23
- ...bodyChildren,
24
- );
25
- }
@@ -1,47 +0,0 @@
1
- import type { Attrs, DenormChildren } from "../dom/dom.ts";
2
- import { aside, small } from "../dom/html.ts";
3
-
4
- export type AlertVariant = "warning" | "error" | "info" | "success" | "neutral";
5
-
6
- // Alert/Chip props: the variant plus any DOM attrs (class, lang, style, ...) to
7
- // apply to the outermost element. The variant is consumed; the rest fall through.
8
- export type AlertProps = { variant: AlertVariant } & Attrs<HTMLElement>;
9
-
10
- // The variant vocabulary is this module's single source of truth; this map both
11
- // drives Alert's role derivation and stays exhaustive (a new variant without a
12
- // role entry is a type error).
13
- const ALERT_ROLE: Record<AlertVariant, "alert" | "status"> = {
14
- warning: "alert",
15
- error: "alert",
16
- info: "status",
17
- success: "status",
18
- neutral: "status",
19
- };
20
-
21
- // Alert emits aside[role][data-variant] for banner-level messaging.
22
- // Why: jiffies-css styles alerts by role + data-variant, not by class. role and
23
- // data-variant are not in the typed attrs surface (role is constrained, data-* is
24
- // not a property), so they are set with setAttribute.
25
- // Invariant: warning|error => role="alert"; info|success|neutral => role="status";
26
- // data-variant always equals the variant; emits no class of its own, but
27
- // forwards a caller-supplied class (and other attrs) to the <aside>.
28
- export function Alert(
29
- { variant, ...attrs }: AlertProps,
30
- ...children: DenormChildren[]
31
- ): HTMLElement {
32
- const el = aside(attrs, ...children);
33
- el.setAttribute("role", ALERT_ROLE[variant]);
34
- el.setAttribute("data-variant", variant);
35
- return el;
36
- }
37
-
38
- // Chip emits small[data-variant] for inline status pills. Same variant vocabulary
39
- // as Alert, no role. Not exercised by the feature test.
40
- export function Chip(
41
- { variant, ...attrs }: AlertProps,
42
- ...children: DenormChildren[]
43
- ): HTMLElement {
44
- const el = small(attrs, ...children);
45
- el.setAttribute("data-variant", variant);
46
- return el;
47
- }
@@ -1,42 +0,0 @@
1
- import { display } from "../display.ts";
2
- import { FC } from "../dom/fc.ts";
3
- import { fieldset, input, label } from "../dom/html.ts";
4
-
5
- let buttonBarId = 1;
6
- const nextId = () => buttonBarId++;
7
-
8
- const ButtonBar = FC<{
9
- // T extends Display
10
- // @ts-expect-error TODO(TFC)
11
- value: T;
12
- // @ts-expect-error TODO(TFC)
13
- values: T[];
14
- // @ts-expect-error TODO(TFC)
15
- events: { onSelect: (current: T) => void };
16
- }>("button-bar", (_el, { value, values, events }) => {
17
- const name = `button-bar-${nextId()}`;
18
- return fieldset(
19
- { class: "input-group" },
20
- ...values.flatMap((option) => {
21
- const opt = `${option}`.replace(/\s+/g, "_").toLowerCase();
22
- const id = `${name}-${opt}`;
23
- return [
24
- label(
25
- { role: "button", htmlFor: id },
26
- input({
27
- type: "radio",
28
- id,
29
- name,
30
- value: option,
31
- checked: option === value,
32
- events: {
33
- change: () => events.onSelect(option),
34
- },
35
- }),
36
- display(option),
37
- ),
38
- ];
39
- }),
40
- );
41
- });
42
- export default ButtonBar;
@@ -1,54 +0,0 @@
1
- import type { Attrs, DenormChildren } from "../dom/dom.ts";
2
- import { article, footer, header, main, section } from "../dom/html.ts";
3
- import { toChildren } from "./children.ts";
4
-
5
- export interface CardParts {
6
- header?: DenormChildren | DenormChildren[];
7
- footer?: DenormChildren | DenormChildren[];
8
- }
9
-
10
- // Card/Panel props: the structured header/footer slots plus any DOM attrs
11
- // (class, lang, style, ...) to apply to the outermost wrapper element.
12
- export type CardProps = CardParts & Attrs<HTMLElement>;
13
-
14
- // Build the shared header? / main / footer? sequence. `root` is the wrapper
15
- // element builder (article for Card, section for Panel); the only difference
16
- // between the two components is which wrapper they use.
17
- function cardLike(
18
- root: typeof article,
19
- { header: headerPart, footer: footerPart, ...attrs }: CardProps,
20
- children: DenormChildren[],
21
- ): HTMLElement {
22
- const sections: DenormChildren[] = [];
23
- if (headerPart !== undefined) {
24
- sections.push(header(...toChildren(headerPart)));
25
- }
26
- sections.push(main(...children));
27
- if (footerPart !== undefined) {
28
- sections.push(footer(...toChildren(footerPart)));
29
- }
30
- return root(attrs, ...sections);
31
- }
32
-
33
- // Card emits the jiffies-css elevated-card structure: article > header? / main / footer?.
34
- // Why: jiffies-css targets `article > main` for card body padding, so body content
35
- // must always be wrapped in <main>, never placed as a bare article child.
36
- // Invariants: <main> is always emitted (even with no parts); <header> only when
37
- // parts.header is set; <footer> only when parts.footer is set; child order is
38
- // always header, main, footer; emits no class of its own, but forwards
39
- // caller-supplied attrs (class, lang, ...) to the wrapper element.
40
- export function Card(
41
- parts: CardProps,
42
- ...children: DenormChildren[]
43
- ): HTMLElement {
44
- return cardLike(article, parts, children);
45
- }
46
-
47
- // Panel is the flat variant: section > header? / main / footer?. Same contract as
48
- // Card with `section` in place of `article`. Not exercised by the feature test.
49
- export function Panel(
50
- parts: CardProps,
51
- ...children: DenormChildren[]
52
- ): HTMLElement {
53
- return cardLike(section, parts, children);
54
- }
@@ -1,25 +0,0 @@
1
- import type { Attrs, DenormChildren } from "../dom/dom.ts";
2
- import { fieldset, legend } from "../dom/html.ts";
3
-
4
- // FormGroup props: the legend label plus any DOM attrs (class, lang, ...) to apply
5
- // to the outermost <fieldset>.
6
- export type FormGroupProps = {
7
- legend: DenormChildren;
8
- } & Attrs<HTMLFieldSetElement>;
9
-
10
- // FormGroup emits fieldset[role=group] > legend + children — the jiffies-css
11
- // grouped-controls pattern. This is the structural form group; the richer form
12
- // controls (Input, Select, Radios, ...) live in src/dom/form/form.ts.
13
- // Why: jiffies-css targets fieldset[role=group] to lay grouped controls out as a
14
- // row; role is set with setAttribute since "group" is outside the typed role surface.
15
-
16
- // Invariant: <legend> is the first child; role="group"; emits no class of its own,
17
- // but forwards caller-supplied attrs (class, lang, ...) to the <fieldset>.
18
- export function FormGroup(
19
- { legend: legendLabel, ...attrs }: FormGroupProps,
20
- ...children: DenormChildren[]
21
- ): HTMLFieldSetElement {
22
- const group = fieldset(attrs, legend(legendLabel), ...children);
23
- group.setAttribute("role", "group");
24
- return group;
25
- }
@@ -1,78 +0,0 @@
1
- import { width } from "../dom/css/sizing.ts";
2
- import { FC, State } from "../dom/fc.ts";
3
- import { input, span } from "../dom/html.ts";
4
-
5
- const Mode = { VIEW: 0, EDIT: 1 };
6
-
7
- export interface InlineEditState {
8
- mode: number;
9
- value: string;
10
- }
11
-
12
- export const InlineEdit = FC<
13
- {
14
- mode?: number;
15
- value: string;
16
- events: {
17
- change: (value: string) => void;
18
- };
19
- },
20
- InlineEditState
21
- >("inline-edit", (el, { mode = Mode.VIEW, value, events }) => {
22
- el[State] ??= { mode, value };
23
- const state = el[State];
24
-
25
- const render = () => {
26
- switch (state.mode) {
27
- case Mode.EDIT:
28
- return edit();
29
- case Mode.VIEW:
30
- return view();
31
- default:
32
- return span();
33
- }
34
- };
35
-
36
- const view = () =>
37
- span(
38
- {
39
- style: { cursor: "text", ...width("full", "inline") },
40
- events: {
41
- click: () => {
42
- state.mode = Mode.EDIT;
43
- el.update(render());
44
- },
45
- },
46
- },
47
- state.value ?? "",
48
- );
49
-
50
- const edit = () => {
51
- const edit = span(
52
- { style: { display: "block", position: "relative" } },
53
- input({
54
- style: {
55
- zIndex: "10",
56
- position: "absolute",
57
- left: "0",
58
- marginTop: "-0.375rem",
59
- },
60
- events: {
61
- blur: ({ target }) =>
62
- events.change((target as HTMLInputElement)?.value ?? ""),
63
- },
64
- type: "text",
65
- value: state.value ?? "",
66
- }),
67
- "\u00a0", // Hack to get the span to take up space
68
- );
69
- setTimeout(() => {
70
- edit.dispatchEvent(new Event("focus"));
71
- });
72
- return edit;
73
- };
74
-
75
- return render();
76
- });
77
-
78
- export default InlineEdit;
@@ -1,22 +0,0 @@
1
- import type { Attrs } from "../dom/dom.ts";
2
- import { link } from "../dom/html.ts";
3
-
4
- const JIFFIES_CSS_CDN =
5
- "https://unpkg.com/@davidsouther/jiffies-css/dist/index.css";
6
-
7
- // jiffiesCssLink props: an optional href override plus any DOM attrs (lang,
8
- // media, ...) to apply to the <link>.
9
- export type JiffiesCssLinkProps = { href?: string } & Attrs<HTMLLinkElement>;
10
-
11
- // jiffiesCssLink builds the <link> a page puts in <head> to load jiffies-css.
12
- // Why: callers must never hand-write the CDN URL or accidentally point at Pico;
13
- // this is the single sanctioned source of the stylesheet href.
14
- // Invariant: returns a <link rel="stylesheet"> whose href contains "jiffies-css"
15
- // and never "pico". Default href is the unpkg CDN; callers bundling locally pass
16
- // their own href.
17
- export function jiffiesCssLink({
18
- href = JIFFIES_CSS_CDN,
19
- ...attrs
20
- }: JiffiesCssLinkProps = {}): HTMLLinkElement {
21
- return link({ ...attrs, rel: "stylesheet", href });
22
- }
@@ -1,35 +0,0 @@
1
- import { type Display, display } from "../display.ts";
2
- import { code, div, li, pre, span, ul } from "../dom/html.ts";
3
- import { LEVEL, type Logger } from "../log.ts";
4
-
5
- export interface HTMLLogger extends Logger {
6
- root: Element;
7
- }
8
-
9
- export function isHTMLLogger(logger: Logger): logger is HTMLLogger {
10
- return (logger as HTMLLogger).root !== undefined;
11
- }
12
-
13
- export function makeHTMLLogger(name: string): HTMLLogger {
14
- const log = ul();
15
- const root = div(div(span(name)), log);
16
- const logger: Partial<HTMLLogger> = { level: LEVEL.INFO, root };
17
-
18
- function append(message: string): void {
19
- log.appendChild(li(pre(code(message))));
20
- }
21
-
22
- const logAt =
23
- (level: number) =>
24
- (message: Display): void =>
25
- level >= (logger.level ?? LEVEL.ERROR)
26
- ? append(display(message))
27
- : undefined;
28
-
29
- logger.debug = logAt(LEVEL.VERBOSE);
30
- logger.info = logAt(LEVEL.INFO);
31
- logger.warn = logAt(LEVEL.WARN);
32
- logger.error = logAt(LEVEL.ERROR);
33
-
34
- return logger as HTMLLogger;
35
- }
@@ -1,42 +0,0 @@
1
- import type { Attrs } from "../dom/dom.ts";
2
- import { a, li, nav, ol, span } from "../dom/html.ts";
3
-
4
- export interface NavItem {
5
- label: string;
6
- href?: string;
7
- current?: boolean;
8
- }
9
-
10
- // Nav/Breadcrumb props: the item list plus any DOM attrs (class, lang, ...) to
11
- // apply to the outermost element.
12
- export type NavProps = { items: NavItem[] } & Attrs<HTMLElement>;
13
-
14
- // One <li><a> per item. aria-current is set with setAttribute (aria-* is not in
15
- // the typed attrs surface); the anchor carries href only when the item supplies one.
16
- function navItem(item: NavItem): HTMLElement {
17
- const anchor = a(item.href ? { href: item.href } : {}, item.label);
18
- if (item.current) {
19
- anchor.setAttribute("aria-current", "page");
20
- }
21
- return li(anchor);
22
- }
23
-
24
- function navList(items: NavItem[]): HTMLElement {
25
- return ol(...items.map(navItem));
26
- }
27
-
28
- // Nav emits nav > ol > li > a, one <li> per item.
29
- // Why: jiffies-css targets the nav > ol > li > a chain; a bare ul > li > a is unstyled.
30
- // Invariants: every item is an <a> inside an <li> inside the single <ol>; an item
31
- // with current:true gets aria-current="page" on its <a>; emits no class of its own,
32
- // but forwards caller-supplied attrs (class, lang, ...) to the <nav>.
33
- export function Nav({ items, ...attrs }: NavProps): HTMLElement {
34
- return nav(attrs, navList(items));
35
- }
36
-
37
- // Breadcrumb wraps the same nav > ol > li chain in a <span> (span > nav > ol > li),
38
- // the jiffies-css breadcrumb selector. Same file, same pattern. Not exercised by
39
- // the feature test.
40
- export function Breadcrumb({ items, ...attrs }: NavProps): HTMLElement {
41
- return span(attrs, nav(navList(items)));
42
- }
@@ -1,32 +0,0 @@
1
- import type { Attrs, DenormChildren } from "../dom/dom.ts";
2
- import { dd, dl, dt } from "../dom/html.ts";
3
- import { toChildren } from "./children.ts";
4
-
5
- export interface PropertyEntry {
6
- label: string;
7
- value: DenormChildren | DenormChildren[];
8
- }
9
-
10
- // PropertySheet props: the entry list plus any DOM attrs (class, lang, ...) to
11
- // apply to the outermost <dl>.
12
- export type PropertySheetProps = {
13
- entries: PropertyEntry[];
14
- } & Attrs<HTMLDListElement>;
15
-
16
- // PropertySheet emits dl > (dt + dd)* — the jiffies-css property-sheet pattern,
17
- // one dt/dd pair per entry.
18
- // Why: jiffies-css targets dl > dt + dd for aligned label/value rows; a table or
19
- // div grid is unstyled.
20
- // Invariant: exactly one <dt> (the label) and one <dd> (the value) per entry, in
21
- // entry order; emits no class of its own, but forwards caller-supplied attrs
22
- // (class, lang, ...) to the <dl>.
23
- export function PropertySheet({
24
- entries,
25
- ...attrs
26
- }: PropertySheetProps): HTMLDListElement {
27
- const rows: DenormChildren[] = [];
28
- for (const entry of entries) {
29
- rows.push(dt(entry.label), dd(...toChildren(entry.value)));
30
- }
31
- return dl(attrs, ...rows);
32
- }
@@ -1,22 +0,0 @@
1
- import type { EventHandler } from "../dom/dom.ts";
2
- import { FC } from "../dom/fc.ts";
3
- import { option, select } from "../dom/html.ts";
4
-
5
- export const Select = FC<{
6
- name: string;
7
- value: string;
8
- events: {
9
- change: EventHandler;
10
- };
11
- disabled: boolean;
12
- options: [string, string][];
13
- }>(
14
- "jiffies-select",
15
- (_el, { name, events: { change }, disabled, value, options }) =>
16
- select(
17
- { name, events: { change }, disabled },
18
- ...options.map(([v, name]) =>
19
- option({ value: v, selected: value === v }, `${name}`),
20
- ),
21
- ),
22
- );
@@ -1,82 +0,0 @@
1
- import type { Attrs, DenormChildren } from "../dom/dom.ts";
2
- import { button, div, input, label } from "../dom/html.ts";
3
-
4
- // Shared tablist container: div[role=tablist] holding the supplied tab controls.
5
- // Both tab variants emit the same container; this is the one place the role string
6
- // lives (matching the cardLike/navList helpers elsewhere in the module).
7
- function tablist(
8
- attrs: Attrs<HTMLDivElement>,
9
- ...children: DenormChildren[]
10
- ): HTMLElement {
11
- const list = div(attrs, ...children);
12
- list.setAttribute("role", "tablist");
13
- return list;
14
- }
15
-
16
- export interface TabItem {
17
- label: string;
18
- selected?: boolean;
19
- onSelect?: (e: Event) => void; // JS variant only
20
- }
21
-
22
- export interface StaticTabItem {
23
- id: string;
24
- label: string;
25
- selected?: boolean;
26
- }
27
-
28
- // TabList props: the tab list plus any DOM attrs (class, lang, ...) to apply to
29
- // the outermost div[role=tablist].
30
- export type TabListProps = { tabs: TabItem[] } & Attrs<HTMLDivElement>;
31
-
32
- // StaticTabList props: the shared radio-group name and tab list, plus any DOM
33
- // attrs to apply to the outermost div[role=tablist].
34
- export type StaticTabListProps = {
35
- name: string;
36
- tabs: StaticTabItem[];
37
- } & Attrs<HTMLDivElement>;
38
-
39
- // TabList emits the JS-driven tab strip: div[role=tablist] > button[role=tab].
40
- // Why: jiffies-css targets [role=tablist] > button[role=tab][aria-selected]; the
41
- // caller owns which tab is active and re-renders via .update() on the element.
42
- // Invariant: role="tablist" on the container; every tab is a button[role=tab];
43
- // selected:true sets aria-selected="true"; onSelect is wired as a click handler.
44
- export function TabList({ tabs, ...attrs }: TabListProps): HTMLElement {
45
- const buttons = tabs.map((tab) => {
46
- const btn = button(
47
- tab.onSelect
48
- ? { type: "button", events: { click: tab.onSelect } }
49
- : { type: "button" },
50
- tab.label,
51
- );
52
- btn.setAttribute("role", "tab");
53
- if (tab.selected) {
54
- btn.setAttribute("aria-selected", "true");
55
- }
56
- return btn;
57
- });
58
- return tablist(attrs, ...buttons);
59
- }
60
-
61
- // StaticTabList emits the CSS-only tab strip: div[role=tablist] >
62
- // (input[type=radio][name][id] + label[role=tab][for])*. The shared name groups
63
- // the radios; :checked drives the active panel with zero JavaScript.
64
- // Invariant: role="tablist" on the container; one radio + one label[role=tab] per
65
- // tab; id/for pair come from StaticTabItem.id; selected:true sets defaultChecked.
66
- export function StaticTabList({
67
- name,
68
- tabs,
69
- ...attrs
70
- }: StaticTabListProps): HTMLElement {
71
- const children = tabs.flatMap((tab) => {
72
- const radio = input({ type: "radio", name, id: tab.id });
73
- if (tab.selected) {
74
- radio.defaultChecked = true;
75
- }
76
- const lbl = label(tab.label);
77
- lbl.setAttribute("for", tab.id);
78
- lbl.setAttribute("role", "tab");
79
- return [radio, lbl];
80
- });
81
- return tablist(attrs, ...children);
82
- }