@hackcove/ui-react 0.0.0-stage → 0.8.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.
Files changed (172) hide show
  1. package/LICENSE +21 -0
  2. package/MIGRATING.md +116 -0
  3. package/README.md +400 -2
  4. package/dist/AppShell.d.ts +97 -0
  5. package/dist/AppShell.js +106 -0
  6. package/dist/Avatar.d.ts +49 -0
  7. package/dist/Avatar.js +65 -0
  8. package/dist/AvatarStack.d.ts +29 -0
  9. package/dist/AvatarStack.js +26 -0
  10. package/dist/Badge.d.ts +14 -0
  11. package/dist/Badge.js +11 -0
  12. package/dist/Breadcrumbs.d.ts +28 -0
  13. package/dist/Breadcrumbs.js +30 -0
  14. package/dist/BulletList.d.ts +4 -0
  15. package/dist/BulletList.js +5 -0
  16. package/dist/Button.d.ts +78 -0
  17. package/dist/Button.js +133 -0
  18. package/dist/ButtonGroup.d.ts +15 -0
  19. package/dist/ButtonGroup.js +10 -0
  20. package/dist/Callout.d.ts +24 -0
  21. package/dist/Callout.js +28 -0
  22. package/dist/Card.d.ts +95 -0
  23. package/dist/Card.js +134 -0
  24. package/dist/CardBadge.d.ts +7 -0
  25. package/dist/CardBadge.js +12 -0
  26. package/dist/CardParts.d.ts +25 -0
  27. package/dist/CardParts.js +25 -0
  28. package/dist/Checkbox.d.ts +29 -0
  29. package/dist/Checkbox.js +36 -0
  30. package/dist/Chevron.d.ts +5 -0
  31. package/dist/Chevron.js +6 -0
  32. package/dist/Cluster.d.ts +51 -0
  33. package/dist/Cluster.js +38 -0
  34. package/dist/Code.d.ts +4 -0
  35. package/dist/Code.js +5 -0
  36. package/dist/CodeBlock.d.ts +27 -0
  37. package/dist/CodeBlock.js +12 -0
  38. package/dist/CodeExample.d.ts +17 -0
  39. package/dist/CodeExample.js +13 -0
  40. package/dist/CodeText.d.ts +7 -0
  41. package/dist/CodeText.js +21 -0
  42. package/dist/ColourSchemeToggle.d.ts +34 -0
  43. package/dist/ColourSchemeToggle.js +50 -0
  44. package/dist/Container.d.ts +22 -0
  45. package/dist/Container.js +13 -0
  46. package/dist/CopyButton.d.ts +17 -0
  47. package/dist/CopyButton.js +13 -0
  48. package/dist/CopyLine.d.ts +9 -0
  49. package/dist/CopyLine.js +7 -0
  50. package/dist/DateCalendar.d.ts +10 -0
  51. package/dist/DateCalendar.js +45 -0
  52. package/dist/DateField.d.ts +87 -0
  53. package/dist/DateField.js +100 -0
  54. package/dist/DateFieldCalendar.d.ts +6 -0
  55. package/dist/DateFieldCalendar.js +35 -0
  56. package/dist/Dialog.d.ts +26 -0
  57. package/dist/Dialog.js +18 -0
  58. package/dist/Disclosure.d.ts +15 -0
  59. package/dist/Disclosure.js +12 -0
  60. package/dist/Divider.d.ts +4 -0
  61. package/dist/Divider.js +5 -0
  62. package/dist/Drawer.d.ts +29 -0
  63. package/dist/Drawer.js +22 -0
  64. package/dist/EmptyState.d.ts +18 -0
  65. package/dist/EmptyState.js +14 -0
  66. package/dist/Field.d.ts +21 -0
  67. package/dist/Field.js +19 -0
  68. package/dist/FieldDialog.d.ts +28 -0
  69. package/dist/FieldDialog.js +34 -0
  70. package/dist/FileDrop.d.ts +17 -0
  71. package/dist/FileDrop.js +12 -0
  72. package/dist/FilterGroup.d.ts +7 -0
  73. package/dist/FilterGroup.js +6 -0
  74. package/dist/FormRow.d.ts +42 -0
  75. package/dist/FormRow.js +33 -0
  76. package/dist/Icon.d.ts +20 -0
  77. package/dist/Icon.js +13 -0
  78. package/dist/IconButton.d.ts +57 -0
  79. package/dist/IconButton.js +51 -0
  80. package/dist/Input.d.ts +22 -0
  81. package/dist/Input.js +22 -0
  82. package/dist/InputShell.d.ts +16 -0
  83. package/dist/InputShell.js +26 -0
  84. package/dist/Kbd.d.ts +6 -0
  85. package/dist/Kbd.js +5 -0
  86. package/dist/KeyValueList.d.ts +23 -0
  87. package/dist/KeyValueList.js +19 -0
  88. package/dist/Lightbox.d.ts +28 -0
  89. package/dist/Lightbox.js +44 -0
  90. package/dist/ListRow.d.ts +109 -0
  91. package/dist/ListRow.js +92 -0
  92. package/dist/Markdown.d.ts +9 -0
  93. package/dist/Markdown.js +43 -0
  94. package/dist/MetaList.d.ts +23 -0
  95. package/dist/MetaList.js +14 -0
  96. package/dist/OutlineRegion.d.ts +43 -0
  97. package/dist/OutlineRegion.js +27 -0
  98. package/dist/PageFade.d.ts +26 -0
  99. package/dist/PageFade.js +39 -0
  100. package/dist/PageHeader.d.ts +24 -0
  101. package/dist/PageHeader.js +14 -0
  102. package/dist/PageLayouts.d.ts +130 -0
  103. package/dist/PageLayouts.js +86 -0
  104. package/dist/Pill.d.ts +53 -0
  105. package/dist/Pill.js +34 -0
  106. package/dist/ProgressBar.d.ts +32 -0
  107. package/dist/ProgressBar.js +32 -0
  108. package/dist/RadioGroup.d.ts +48 -0
  109. package/dist/RadioGroup.js +39 -0
  110. package/dist/SearchInput.d.ts +8 -0
  111. package/dist/SearchInput.js +40 -0
  112. package/dist/SegmentedControl.d.ts +20 -0
  113. package/dist/SegmentedControl.js +31 -0
  114. package/dist/Select.d.ts +5 -0
  115. package/dist/Select.js +9 -0
  116. package/dist/SplitBar.d.ts +27 -0
  117. package/dist/SplitBar.js +15 -0
  118. package/dist/Stack.d.ts +70 -0
  119. package/dist/Stack.js +47 -0
  120. package/dist/StackedBar.d.ts +56 -0
  121. package/dist/StackedBar.js +35 -0
  122. package/dist/StatTile.d.ts +21 -0
  123. package/dist/StatTile.js +22 -0
  124. package/dist/StatusPages.d.ts +38 -0
  125. package/dist/StatusPages.js +57 -0
  126. package/dist/Steps.d.ts +17 -0
  127. package/dist/Steps.js +14 -0
  128. package/dist/StickyBar.d.ts +12 -0
  129. package/dist/StickyBar.js +10 -0
  130. package/dist/Table.d.ts +142 -0
  131. package/dist/Table.js +138 -0
  132. package/dist/Tabs.d.ts +40 -0
  133. package/dist/Tabs.js +49 -0
  134. package/dist/TextLink.d.ts +51 -0
  135. package/dist/TextLink.js +37 -0
  136. package/dist/Textarea.d.ts +5 -0
  137. package/dist/Textarea.js +7 -0
  138. package/dist/Timeline.d.ts +27 -0
  139. package/dist/Timeline.js +29 -0
  140. package/dist/Video.d.ts +13 -0
  141. package/dist/Video.js +9 -0
  142. package/dist/colourScheme.d.ts +21 -0
  143. package/dist/colourScheme.js +53 -0
  144. package/dist/control.d.ts +31 -0
  145. package/dist/control.js +38 -0
  146. package/dist/cx.d.ts +10 -0
  147. package/dist/cx.js +12 -0
  148. package/dist/highlight.d.ts +16 -0
  149. package/dist/highlight.js +292 -0
  150. package/dist/index.d.ts +70 -0
  151. package/dist/index.js +66 -0
  152. package/dist/isoDate.d.ts +5 -0
  153. package/dist/isoDate.js +16 -0
  154. package/dist/lift.harness.d.ts +2 -0
  155. package/dist/lift.harness.js +3 -0
  156. package/dist/link.d.ts +50 -0
  157. package/dist/link.js +25 -0
  158. package/dist/modal.d.ts +11 -0
  159. package/dist/modal.js +41 -0
  160. package/dist/props.d.ts +14 -0
  161. package/dist/props.js +1 -0
  162. package/dist/shownDate.d.ts +45 -0
  163. package/dist/shownDate.js +88 -0
  164. package/dist/size.d.ts +6 -0
  165. package/dist/size.js +1 -0
  166. package/dist/switcher.d.ts +88 -0
  167. package/dist/switcher.js +71 -0
  168. package/dist/typography.d.ts +123 -0
  169. package/dist/typography.js +120 -0
  170. package/dist/ui.d.ts +10 -0
  171. package/dist/ui.js +27 -0
  172. package/package.json +91 -4
@@ -0,0 +1,31 @@
1
+ import type { Size } from "./size.js";
2
+ /** A control's size: the library's one `Size` scale. */
3
+ export type ControlSize = Size;
4
+ /** `full` fills the parent (the default); `fill` grows to the space left in a flex row; `auto` adds no width class, so the control is as wide as the browser makes it; the rest are fixed. */
5
+ export type ControlWidth = "full" | "fill" | "auto" | "xs" | "sm" | "md" | "lg";
6
+ /** The props `Input`, `Select` and `Textarea` share. They replace the native `size` and `width`. */
7
+ export type ControlProps = {
8
+ /**
9
+ * `sm` is compact, `text-xs` with less padding, for dense forms and table rows; `lg` is `text-base`
10
+ * with more padding, for a hero form or a touch screen.
11
+ * @default md
12
+ */
13
+ size?: ControlSize | undefined;
14
+ /** `full` (default) fills the parent; `fill` grows to fill the room left in a row beside other items (it can shrink to nothing, so buttons stay on the line); `xs`, `sm`, `md`, `lg` are fixed; `auto` adds no width class, so the control is as wide as the browser makes it. */
15
+ width?: ControlWidth | undefined;
16
+ };
17
+ /** The height and type of a control: 26px, 38px and 46px with the border. */
18
+ export declare const CONTROL_SIZE: Record<ControlSize, string>;
19
+ /** One width class per control, so a fixed width never fights `w-full`. `auto` has none. */
20
+ export declare const CONTROL_WIDTH: Record<ControlWidth, string | undefined>;
21
+ /** Which edge an `Input`'s text sits on: `start` (default), or `end` for a numeric field. */
22
+ export type ControlAlign = "start" | "end";
23
+ /** The text alignment class of an `Input` with `align="end"`; `start` is the browser default and has none. */
24
+ export declare const CONTROL_ALIGN: Record<ControlAlign, string | undefined>;
25
+ /** The horizontal padding of a control, and of an `Input`'s icon gutter. */
26
+ export declare const CONTROL_PAD: Record<ControlSize, {
27
+ x: string;
28
+ icon: string;
29
+ }>;
30
+ /** Shared look of text controls; `size` sets the height and type, `width` the one width class. */
31
+ export declare function controlClass(size?: ControlSize, width?: ControlWidth): string;
@@ -0,0 +1,38 @@
1
+ import { focusRing } from "./cx.js";
2
+ /** The height and type of a control: 26px, 38px and 46px with the border. */
3
+ export const CONTROL_SIZE = {
4
+ sm: "py-1 text-xs",
5
+ md: "py-2 text-sm",
6
+ lg: "py-2.5 text-base",
7
+ };
8
+ /** One width class per control, so a fixed width never fights `w-full`. `auto` has none. */
9
+ export const CONTROL_WIDTH = {
10
+ full: "w-full",
11
+ fill: "w-full min-w-0 flex-1",
12
+ auto: undefined,
13
+ xs: "w-20",
14
+ sm: "w-28",
15
+ md: "w-36",
16
+ lg: "w-48",
17
+ };
18
+ /** The text alignment class of an `Input` with `align="end"`; `start` is the browser default and has none. */
19
+ export const CONTROL_ALIGN = {
20
+ start: undefined,
21
+ end: "text-right",
22
+ };
23
+ /** The horizontal padding of a control, and of an `Input`'s icon gutter. */
24
+ export const CONTROL_PAD = {
25
+ sm: { x: "px-2", icon: "pr-2 pl-7" },
26
+ md: { x: "px-3", icon: "pr-3 pl-9" },
27
+ lg: { x: "px-4", icon: "pr-4 pl-11" },
28
+ };
29
+ /** Shared look of text controls; `size` sets the height and type, `width` the one width class. */
30
+ export function controlClass(size = "md", width = "full") {
31
+ return [
32
+ CONTROL_WIDTH[width],
33
+ CONTROL_SIZE[size],
34
+ `rounded-lg border border-slate-300 bg-white text-slate-900 placeholder:text-slate-400 transition hover:border-violet-400 disabled:opacity-50 ${focusRing}`,
35
+ ]
36
+ .filter(Boolean)
37
+ .join(" ");
38
+ }
package/dist/cx.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /** Join class names, dropping falsy parts. */
2
+ export declare function cx(...parts: Array<string | false | null | undefined>): string;
3
+ /** The keyboard focus ring every interactive control shares. */
4
+ export declare const focusRing = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600";
5
+ /**
6
+ * On a row or card that holds a stretched link (`link-stretch`): every interactive descendant sits
7
+ * above that link and is clickable on its own. `z-10` needs a position, so each gets `relative`,
8
+ * except one that is already `absolute` and the stretched link itself.
9
+ */
10
+ export declare const liftInteractive = "[&_:is(a,button,label,select,textarea,input,summary):not(.link-stretch):not(.absolute)]:relative [&_:is(a,button,label,select,textarea,input,summary):not(.link-stretch)]:z-10";
package/dist/cx.js ADDED
@@ -0,0 +1,12 @@
1
+ /** Join class names, dropping falsy parts. */
2
+ export function cx(...parts) {
3
+ return parts.filter(Boolean).join(" ");
4
+ }
5
+ /** The keyboard focus ring every interactive control shares. */
6
+ export const focusRing = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600";
7
+ /**
8
+ * On a row or card that holds a stretched link (`link-stretch`): every interactive descendant sits
9
+ * above that link and is clickable on its own. `z-10` needs a position, so each gets `relative`,
10
+ * except one that is already `absolute` and the stretched link itself.
11
+ */
12
+ export const liftInteractive = "[&_:is(a,button,label,select,textarea,input,summary):not(.link-stretch):not(.absolute)]:relative [&_:is(a,button,label,select,textarea,input,summary):not(.link-stretch)]:z-10";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A small tokenizer for the languages the docs show. It splits code into coloured runs and nothing
3
+ * else: no HTML, no client JS. `CodeText` maps a run to a palette utility, so a theme recolours
4
+ * highlighted code exactly as it recolours everything else.
5
+ *
6
+ * The grammars are deliberately shallow — enough for examples, not a compiler front end. `text`
7
+ * returns the code as one plain run, so nothing is coloured where no language is named.
8
+ */
9
+ export type CodeLanguage = "text" | "tsx" | "ts" | "json" | "bash" | "css";
10
+ export type TokenKind = "plain" | "comment" | "keyword" | "string" | "number" | "tag" | "attr" | "function";
11
+ export type Token = {
12
+ text: string;
13
+ kind: TokenKind;
14
+ };
15
+ /** The coloured runs of `code`. The text of every token joined back together is `code`. */
16
+ export declare function highlight(code: string, language: CodeLanguage): Token[];
@@ -0,0 +1,292 @@
1
+ /**
2
+ * A small tokenizer for the languages the docs show. It splits code into coloured runs and nothing
3
+ * else: no HTML, no client JS. `CodeText` maps a run to a palette utility, so a theme recolours
4
+ * highlighted code exactly as it recolours everything else.
5
+ *
6
+ * The grammars are deliberately shallow — enough for examples, not a compiler front end. `text`
7
+ * returns the code as one plain run, so nothing is coloured where no language is named.
8
+ */
9
+ /** The coloured runs of `code`. The text of every token joined back together is `code`. */
10
+ export function highlight(code, language) {
11
+ const tokens = [];
12
+ const emit = (kind, text) => {
13
+ if (text === "")
14
+ return;
15
+ const last = tokens.at(-1);
16
+ if (last !== undefined && last.kind === kind)
17
+ last.text += text;
18
+ else
19
+ tokens.push({ kind, text });
20
+ };
21
+ if (language === "text")
22
+ emit("plain", code);
23
+ else if (language === "bash")
24
+ scanBash(code, emit);
25
+ else if (language === "css")
26
+ scanCss(code, emit);
27
+ // JSON is JS with no code around it: keys are strings, `true`/`false`/`null` are keywords.
28
+ else
29
+ scanJs(code, emit);
30
+ return tokens;
31
+ }
32
+ const isLetter = (c) => c !== "" && /[A-Za-z_$]/.test(c);
33
+ /** The first rule that matches at `at`, if any. Every pattern consumes at least one character. */
34
+ function match(rules, code, at) {
35
+ for (const rule of rules) {
36
+ rule.pattern.lastIndex = at;
37
+ const found = rule.pattern.exec(code);
38
+ if (found === null)
39
+ continue;
40
+ const end = at + found[0].length;
41
+ const kind = typeof rule.kind === "function" ? rule.kind(code, at, end) : rule.kind;
42
+ return { kind, end };
43
+ }
44
+ return undefined;
45
+ }
46
+ /** The index past the quoted run starting at `at`, or `undefined` when there is none there. */
47
+ function quotedEnd(code, at) {
48
+ const quote = code.charAt(at);
49
+ if (quote !== '"' && quote !== "'" && quote !== "`")
50
+ return undefined;
51
+ let i = at + 1;
52
+ while (i < code.length) {
53
+ const c = code.charAt(i);
54
+ if (c === "\\") {
55
+ i += 2;
56
+ continue;
57
+ }
58
+ if (c === quote)
59
+ return i + 1;
60
+ if (c === "\n" && quote !== "`")
61
+ return i;
62
+ i += 1;
63
+ }
64
+ return code.length;
65
+ }
66
+ const STRING_RULES = [
67
+ { pattern: /"(?:\\.|[^"\\\n])*"?/y, kind: "string" },
68
+ { pattern: /'(?:\\.|[^'\\\n])*'?/y, kind: "string" },
69
+ { pattern: /`(?:\\.|[^`\\])*`?/y, kind: "string" },
70
+ ];
71
+ const JS_KEYWORDS = new Set(("as async await break case catch class const continue default delete do else export " +
72
+ "extends false finally for from function if import in instanceof interface let new " +
73
+ "null of return satisfies static super switch this throw true try type typeof " +
74
+ "undefined var void while yield").split(" "));
75
+ /** The first character past `from` that is not a space. */
76
+ function afterSpace(code, from) {
77
+ let i = from;
78
+ while (code.charAt(i) === " ")
79
+ i += 1;
80
+ return code.charAt(i);
81
+ }
82
+ const JS_RULES = [
83
+ { pattern: /\/\/[^\n]*/y, kind: "comment" },
84
+ { pattern: /\/\*[\s\S]*?(?:\*\/|$)/y, kind: "comment" },
85
+ ...STRING_RULES,
86
+ { pattern: /\d[\w.]*/y, kind: "number" },
87
+ {
88
+ pattern: /[A-Za-z_$][\w$]*/y,
89
+ kind: (code, start, end) => {
90
+ if (JS_KEYWORDS.has(code.slice(start, end)))
91
+ return "keyword";
92
+ return afterSpace(code, end) === "(" ? "function" : "plain";
93
+ },
94
+ },
95
+ ];
96
+ /** `\w`-style names in a tag, including the dashes of `aria-label` and `data-testid`. */
97
+ const TAG_NAME_RULE = { pattern: /[A-Za-z_$][\w$-]*/y, kind: "tag" };
98
+ const ATTR_RULE = { pattern: /[A-Za-z_$][\w$-]*/y, kind: "attr" };
99
+ function take(scan, token) {
100
+ scan.emit(token.kind, scan.code.slice(scan.at, token.end));
101
+ scan.at = token.end;
102
+ }
103
+ function takeChar(scan) {
104
+ scan.emit("plain", scan.code.charAt(scan.at));
105
+ scan.at += 1;
106
+ }
107
+ /** `<Name` or `</Name` opens an element here; in children text a fragment `<>` does too. */
108
+ function opensElement(code, at) {
109
+ const next = code.charAt(at + 1);
110
+ if (next === "/")
111
+ return isLetter(code.charAt(at + 2));
112
+ return next === ">" || isLetter(next);
113
+ }
114
+ function openElement(scan) {
115
+ const next = scan.code.charAt(scan.at + 1);
116
+ scan.closing = next === "/";
117
+ scan.openedFrom = scan.mode;
118
+ scan.emit("plain", scan.closing ? "</" : "<");
119
+ scan.at += scan.closing ? 2 : 1;
120
+ scan.mode = "tag";
121
+ const name = match([TAG_NAME_RULE], scan.code, scan.at);
122
+ if (name !== undefined)
123
+ take(scan, name);
124
+ }
125
+ function endTag(scan) {
126
+ const selfClosed = scan.code.charAt(scan.at - 1) === "/";
127
+ takeChar(scan);
128
+ if (scan.closing)
129
+ scan.mode = scan.elements.pop() ?? "children";
130
+ else if (selfClosed)
131
+ scan.mode = scan.openedFrom;
132
+ else {
133
+ scan.elements.push(scan.openedFrom);
134
+ scan.mode = "children";
135
+ }
136
+ }
137
+ function openBrace(scan, back) {
138
+ scan.expressions.push({ back, depth: 0 });
139
+ scan.mode = "js";
140
+ takeChar(scan);
141
+ }
142
+ /** In code, `{` only deepens the JSX expression it is already inside. */
143
+ function openNestedBrace(scan) {
144
+ const open = scan.expressions.at(-1);
145
+ if (open !== undefined)
146
+ open.depth += 1;
147
+ takeChar(scan);
148
+ }
149
+ function closeBrace(scan) {
150
+ const open = scan.expressions.at(-1);
151
+ if (open === undefined) {
152
+ takeChar(scan);
153
+ return;
154
+ }
155
+ if (open.depth === 0) {
156
+ scan.expressions.pop();
157
+ scan.mode = open.back;
158
+ }
159
+ else
160
+ open.depth -= 1;
161
+ takeChar(scan);
162
+ }
163
+ function codeStep(scan) {
164
+ const c = scan.code.charAt(scan.at);
165
+ if (c === "<" && opensElement(scan.code, scan.at))
166
+ return openElement(scan);
167
+ if (c === "{")
168
+ return openNestedBrace(scan);
169
+ if (c === "}")
170
+ return closeBrace(scan);
171
+ const token = match(JS_RULES, scan.code, scan.at);
172
+ if (token !== undefined)
173
+ return take(scan, token);
174
+ takeChar(scan);
175
+ }
176
+ function tagStep(scan) {
177
+ const c = scan.code.charAt(scan.at);
178
+ if (c === "{")
179
+ return openBrace(scan, "tag");
180
+ if (c === ">")
181
+ return endTag(scan);
182
+ const quoted = quotedEnd(scan.code, scan.at);
183
+ if (quoted !== undefined)
184
+ return take(scan, { kind: "string", end: quoted });
185
+ const token = match([ATTR_RULE], scan.code, scan.at);
186
+ if (token !== undefined)
187
+ return take(scan, token);
188
+ takeChar(scan);
189
+ }
190
+ function childrenStep(scan) {
191
+ const c = scan.code.charAt(scan.at);
192
+ if (c === "{")
193
+ return openBrace(scan, "children");
194
+ if (c === "<" && opensElement(scan.code, scan.at))
195
+ return openElement(scan);
196
+ takeChar(scan);
197
+ }
198
+ /**
199
+ * JS, TS and TSX. Three modes — code, inside a tag, and children text — keep an apostrophe in
200
+ * `<p>Don't</p>` as text rather than the start of a runaway string, and every open element records
201
+ * the mode to return to, so a `{…}` expression that holds JSX closes back into code.
202
+ */
203
+ function scanJs(code, emit) {
204
+ const scan = {
205
+ code,
206
+ emit,
207
+ at: 0,
208
+ mode: "js",
209
+ elements: [],
210
+ expressions: [],
211
+ closing: false,
212
+ openedFrom: "js",
213
+ };
214
+ while (scan.at < code.length) {
215
+ if (scan.mode === "js")
216
+ codeStep(scan);
217
+ else if (scan.mode === "tag")
218
+ tagStep(scan);
219
+ else
220
+ childrenStep(scan);
221
+ }
222
+ }
223
+ /** A `#` comment reaches its line end; it starts only at the start of a line or after a space. */
224
+ function bashCommentEnd(code, at) {
225
+ if (code.charAt(at) !== "#")
226
+ return undefined;
227
+ if (at > 0 && !/\s/.test(code.charAt(at - 1)))
228
+ return undefined;
229
+ const newline = code.indexOf("\n", at);
230
+ return newline === -1 ? code.length : newline;
231
+ }
232
+ /** A word of a shell command: a quoted string, a `-flag`, or a run of path characters. */
233
+ function bashWord(code, at, isCommand) {
234
+ const quoted = quotedEnd(code, at);
235
+ if (quoted !== undefined)
236
+ return { kind: "string", end: quoted };
237
+ const token = match([
238
+ { pattern: /-{1,2}[\w-]+/y, kind: "attr" },
239
+ { pattern: /[A-Za-z0-9_./@:~+-]+/y, kind: isCommand ? "function" : "plain" },
240
+ ], code, at);
241
+ return token;
242
+ }
243
+ function scanBash(code, emit) {
244
+ let at = 0;
245
+ /** The next word is the command: the line's first word, or one after `&&`, `|` or `;`. */
246
+ let isCommand = true;
247
+ while (at < code.length) {
248
+ const comment = bashCommentEnd(code, at);
249
+ if (comment !== undefined) {
250
+ emit("comment", code.slice(at, comment));
251
+ at = comment;
252
+ continue;
253
+ }
254
+ const word = bashWord(code, at, isCommand);
255
+ if (word !== undefined) {
256
+ emit(word.kind, code.slice(at, word.end));
257
+ at = word.end;
258
+ isCommand = false;
259
+ continue;
260
+ }
261
+ const c = code.charAt(at);
262
+ if (c === "\n" || c === "&" || c === "|" || c === ";")
263
+ isCommand = true;
264
+ emit("plain", c);
265
+ at += 1;
266
+ }
267
+ }
268
+ const CSS_RULES = [
269
+ { pattern: /\/\*[\s\S]*?(?:\*\/|$)/y, kind: "comment" },
270
+ ...STRING_RULES,
271
+ { pattern: /@[\w-]+/y, kind: "keyword" },
272
+ { pattern: /#[\w]+/y, kind: "number" },
273
+ { pattern: /\d[\w.]*/y, kind: "number" },
274
+ {
275
+ // A property name is a word before `:`; anything else is a selector or a value.
276
+ pattern: /-?[A-Za-z][\w-]*/y,
277
+ kind: (code, _start, end) => (afterSpace(code, end) === ":" ? "attr" : "plain"),
278
+ },
279
+ ];
280
+ function scanCss(code, emit) {
281
+ let at = 0;
282
+ while (at < code.length) {
283
+ const token = match(CSS_RULES, code, at);
284
+ if (token !== undefined) {
285
+ emit(token.kind, code.slice(at, token.end));
286
+ at = token.end;
287
+ continue;
288
+ }
289
+ emit("plain", code.charAt(at));
290
+ at += 1;
291
+ }
292
+ }
@@ -0,0 +1,70 @@
1
+ export { Card } from "./Card.js";
2
+ export { EpicHeader, groupByEpic } from "./ui.js";
3
+ export { Pill, type PillTone } from "./Pill.js";
4
+ export { Badge } from "./Badge.js";
5
+ export { StatTile, type StatTileTone } from "./StatTile.js";
6
+ export { ProgressBar, type BarSize, type BarTone } from "./ProgressBar.js";
7
+ export { SplitBar } from "./SplitBar.js";
8
+ export { Legend, StackedBar, type LegendItem, type StackedSegment, type VizTone, } from "./StackedBar.js";
9
+ export { Markdown } from "./Markdown.js";
10
+ export { AppShell, AppShellBrand, type AppShellBreakpoint, AppShellHideBelow, appShellRootClassFor, } from "./AppShell.js";
11
+ export { Container } from "./Container.js";
12
+ export { PageHeader, type PageHeaderProps } from "./PageHeader.js";
13
+ export { CardGrid, CenteredLayout, DetailLayout, FormLayout, FullBleedLayout, ListLayout, ProseLayout, SidebarLayout, } from "./PageLayouts.js";
14
+ export { StickyBar } from "./StickyBar.js";
15
+ export { ErrorPage, Forbidden, Maintenance, NotFound, Offline } from "./StatusPages.js";
16
+ export { Cluster, ClusterGrow, type ClusterAlign, type ClusterGap, type ClusterJustify, } from "./Cluster.js";
17
+ export { Section, Stack, type StackGap, type StackWidth } from "./Stack.js";
18
+ export { Button, type ButtonLook, type ButtonSize, type ButtonTone, type ButtonVariant, } from "./Button.js";
19
+ export { ButtonGroup } from "./ButtonGroup.js";
20
+ export { type LinkRender, type LinkRenderProps } from "./link.js";
21
+ export { Avatar, avatarColourClass, initialsOf, type AvatarShape, type AvatarSize, } from "./Avatar.js";
22
+ export { AvatarStack, type AvatarStackPerson } from "./AvatarStack.js";
23
+ export { Breadcrumbs, type Crumb } from "./Breadcrumbs.js";
24
+ export { BulletList } from "./BulletList.js";
25
+ export { Callout, type CalloutTone } from "./Callout.js";
26
+ export { ListRow, type ListRowProps } from "./ListRow.js";
27
+ export { OutlineRegion, RegionFrame, type RegionRect } from "./OutlineRegion.js";
28
+ export { type CellTone, type RowTone, Table, TableBody, TableCell, TableFoot, TableGroupRow, TableHead, TableHeaderCell, TableRow, } from "./Table.js";
29
+ export { EmptyState } from "./EmptyState.js";
30
+ export { Step, Steps } from "./Steps.js";
31
+ export { Timeline, TimelineItem } from "./Timeline.js";
32
+ export { Code } from "./Code.js";
33
+ export { CodeBlock } from "./CodeBlock.js";
34
+ export type { CodeLanguage } from "./highlight.js";
35
+ export { CodeExample } from "./CodeExample.js";
36
+ export { Disclosure } from "./Disclosure.js";
37
+ export { Divider } from "./Divider.js";
38
+ export { PageFade, startPageTransition } from "./PageFade.js";
39
+ export { Dialog, type DialogProps } from "./Dialog.js";
40
+ export { Drawer, type DrawerProps } from "./Drawer.js";
41
+ export { Lightbox, type LightboxProps } from "./Lightbox.js";
42
+ export { Icon } from "./Icon.js";
43
+ export { IconButton, type IconButtonTone, type IconButtonVariant } from "./IconButton.js";
44
+ export { ColourSchemeToggle } from "./ColourSchemeToggle.js";
45
+ export { applyColourScheme, schemeClass, type ColourSchemeClasses, colourSchemeScript, readColourScheme, storeColourScheme, type ColourScheme, } from "./colourScheme.js";
46
+ export { Tabs, type Tab, type TabsProps } from "./Tabs.js";
47
+ export { SegmentedControl, type Segment } from "./SegmentedControl.js";
48
+ export { FilterGroup } from "./FilterGroup.js";
49
+ export { Checkbox } from "./Checkbox.js";
50
+ export { Radio, RadioGroup } from "./RadioGroup.js";
51
+ export { FileDrop } from "./FileDrop.js";
52
+ export { CopyButton } from "./CopyButton.js";
53
+ export { CopyLine } from "./CopyLine.js";
54
+ export { Field } from "./Field.js";
55
+ export { FormGrid, FormRow, FormRowField, type FormRowSpan } from "./FormRow.js";
56
+ export { FieldDialog, type FieldDialogProps } from "./FieldDialog.js";
57
+ export { Input } from "./Input.js";
58
+ export type { ControlAlign, ControlSize, ControlWidth } from "./control.js";
59
+ export type { Size } from "./size.js";
60
+ export { MetaList, type MetaListSeparator } from "./MetaList.js";
61
+ export { Kbd } from "./Kbd.js";
62
+ export { KeyValue, KeyValueList } from "./KeyValueList.js";
63
+ export { Select } from "./Select.js";
64
+ export { Textarea } from "./Textarea.js";
65
+ export { Video } from "./Video.js";
66
+ export { TextLink, type TextLinkSize } from "./TextLink.js";
67
+ export { Caption, Display, Lead, Overline, PageHeading, SectionHeading, SubsectionHeading, type SubsectionHeadingSize, Text, type TextTone, type TypographyColor, type TypographyProps, } from "./typography.js";
68
+ export { DateField, type DateFieldProps } from "./DateField.js";
69
+ export { fromISODate, toISODate } from "./isoDate.js";
70
+ export type { CalendarDate } from "@internationalized/date";
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ export { Card } from "./Card.js";
2
+ export { EpicHeader, groupByEpic } from "./ui.js";
3
+ export { Pill } from "./Pill.js";
4
+ export { Badge } from "./Badge.js";
5
+ export { StatTile } from "./StatTile.js";
6
+ export { ProgressBar } from "./ProgressBar.js";
7
+ export { SplitBar } from "./SplitBar.js";
8
+ export { Legend, StackedBar, } from "./StackedBar.js";
9
+ export { Markdown } from "./Markdown.js";
10
+ export { AppShell, AppShellBrand, AppShellHideBelow, appShellRootClassFor, } from "./AppShell.js";
11
+ export { Container } from "./Container.js";
12
+ export { PageHeader } from "./PageHeader.js";
13
+ export { CardGrid, CenteredLayout, DetailLayout, FormLayout, FullBleedLayout, ListLayout, ProseLayout, SidebarLayout, } from "./PageLayouts.js";
14
+ export { StickyBar } from "./StickyBar.js";
15
+ export { ErrorPage, Forbidden, Maintenance, NotFound, Offline } from "./StatusPages.js";
16
+ export { Cluster, ClusterGrow, } from "./Cluster.js";
17
+ export { Section, Stack } from "./Stack.js";
18
+ export { Button, } from "./Button.js";
19
+ export { ButtonGroup } from "./ButtonGroup.js";
20
+ export {} from "./link.js";
21
+ export { Avatar, avatarColourClass, initialsOf, } from "./Avatar.js";
22
+ export { AvatarStack } from "./AvatarStack.js";
23
+ export { Breadcrumbs } from "./Breadcrumbs.js";
24
+ export { BulletList } from "./BulletList.js";
25
+ export { Callout } from "./Callout.js";
26
+ export { ListRow } from "./ListRow.js";
27
+ export { OutlineRegion, RegionFrame } from "./OutlineRegion.js";
28
+ export { Table, TableBody, TableCell, TableFoot, TableGroupRow, TableHead, TableHeaderCell, TableRow, } from "./Table.js";
29
+ export { EmptyState } from "./EmptyState.js";
30
+ export { Step, Steps } from "./Steps.js";
31
+ export { Timeline, TimelineItem } from "./Timeline.js";
32
+ export { Code } from "./Code.js";
33
+ export { CodeBlock } from "./CodeBlock.js";
34
+ export { CodeExample } from "./CodeExample.js";
35
+ export { Disclosure } from "./Disclosure.js";
36
+ export { Divider } from "./Divider.js";
37
+ export { PageFade, startPageTransition } from "./PageFade.js";
38
+ export { Dialog } from "./Dialog.js";
39
+ export { Drawer } from "./Drawer.js";
40
+ export { Lightbox } from "./Lightbox.js";
41
+ export { Icon } from "./Icon.js";
42
+ export { IconButton } from "./IconButton.js";
43
+ export { ColourSchemeToggle } from "./ColourSchemeToggle.js";
44
+ export { applyColourScheme, schemeClass, colourSchemeScript, readColourScheme, storeColourScheme, } from "./colourScheme.js";
45
+ export { Tabs } from "./Tabs.js";
46
+ export { SegmentedControl } from "./SegmentedControl.js";
47
+ export { FilterGroup } from "./FilterGroup.js";
48
+ export { Checkbox } from "./Checkbox.js";
49
+ export { Radio, RadioGroup } from "./RadioGroup.js";
50
+ export { FileDrop } from "./FileDrop.js";
51
+ export { CopyButton } from "./CopyButton.js";
52
+ export { CopyLine } from "./CopyLine.js";
53
+ export { Field } from "./Field.js";
54
+ export { FormGrid, FormRow, FormRowField } from "./FormRow.js";
55
+ export { FieldDialog } from "./FieldDialog.js";
56
+ export { Input } from "./Input.js";
57
+ export { MetaList } from "./MetaList.js";
58
+ export { Kbd } from "./Kbd.js";
59
+ export { KeyValue, KeyValueList } from "./KeyValueList.js";
60
+ export { Select } from "./Select.js";
61
+ export { Textarea } from "./Textarea.js";
62
+ export { Video } from "./Video.js";
63
+ export { TextLink } from "./TextLink.js";
64
+ export { Caption, Display, Lead, Overline, PageHeading, SectionHeading, SubsectionHeading, Text, } from "./typography.js";
65
+ export { DateField } from "./DateField.js";
66
+ export { fromISODate, toISODate } from "./isoDate.js";
@@ -0,0 +1,5 @@
1
+ import { CalendarDate } from "@internationalized/date";
2
+ /** `"1990-05-17"` for `CalendarDate(1990, 5, 17)`; `""` for no date, so it drops into a form field. */
3
+ export declare function toISODate(date: CalendarDate | null | undefined): string;
4
+ /** The `CalendarDate` of a `"YYYY-MM-DD"` string; `null` for an empty, malformed or impossible one. */
5
+ export declare function fromISODate(iso: string | null | undefined): CalendarDate | null;
@@ -0,0 +1,16 @@
1
+ import { CalendarDate } from "@internationalized/date";
2
+ const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})$/;
3
+ /** `"1990-05-17"` for `CalendarDate(1990, 5, 17)`; `""` for no date, so it drops into a form field. */
4
+ export function toISODate(date) {
5
+ return date ? date.toString() : "";
6
+ }
7
+ /** The `CalendarDate` of a `"YYYY-MM-DD"` string; `null` for an empty, malformed or impossible one. */
8
+ export function fromISODate(iso) {
9
+ const match = ISO_DATE.exec(iso ?? "");
10
+ if (!match)
11
+ return null;
12
+ const [year, month, day] = [Number(match[1]), Number(match[2]), Number(match[3])];
13
+ const date = new CalendarDate(year, month, day);
14
+ // CalendarDate constrains 1990-02-31 to 1990-02-28; a string that does not survive is impossible.
15
+ return date.toString() === iso ? date : null;
16
+ }
@@ -0,0 +1,2 @@
1
+ /** `liftInteractive` as it appears in rendered markup, where `&` is escaped. */
2
+ export declare const lifted: string;
@@ -0,0 +1,3 @@
1
+ import { liftInteractive } from "./cx.js";
2
+ /** `liftInteractive` as it appears in rendered markup, where `&` is escaped. */
3
+ export const lifted = liftInteractive.replaceAll("&", "&amp;");
package/dist/link.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { ComponentPropsWithoutRef, ReactElement, ReactNode } from "react";
2
+ type AriaCurrent = NonNullable<ComponentPropsWithoutRef<"a">["aria-current"]>;
3
+ /**
4
+ * What a `render` function receives to draw a link with the app's own router `<Link>`: the
5
+ * component's classes, its label as `children`, and (for a tab that is current, a link that is
6
+ * external or an option with a tooltip) `aria-current`, `target`, `rel` and `title`. Spread it on
7
+ * the router's link: `render: (props) => <Link to="/quotes" {...props} />`. `@hackcove/ui-react` has no
8
+ * router dependency.
9
+ *
10
+ * An optional field is `?: T`, never `?: T | undefined`, and a key that has no value is left out
11
+ * rather than set to `undefined`. A router's `<Link>` declares its props that way, so under
12
+ * `exactOptionalPropertyTypes` the spread compiles. Any other anchor prop the caller gave the
13
+ * component (`id`, `data-*`, `download`, a handler) arrives too, untyped here, and `<Link>`
14
+ * takes it as its own.
15
+ */
16
+ export type LinkRenderProps = {
17
+ className: string;
18
+ children: ReactNode;
19
+ "aria-current"?: AriaCurrent;
20
+ target?: string;
21
+ rel?: string;
22
+ title?: string;
23
+ };
24
+ /** Draws one link. The component hands it `LinkRenderProps`; it returns the app's own link element. */
25
+ export type LinkRender = (props: LinkRenderProps) => ReactNode;
26
+ /**
27
+ * A link drawn by `render` when there is one, otherwise a plain `<a href>`. `external` adds
28
+ * `target="_blank"` and `rel="noreferrer"` to either, unless the caller passes its own.
29
+ */
30
+ export declare function renderLink({ href, render, external, className, children, "aria-current": current, target, rel, title, ...rest }: {
31
+ className: string;
32
+ children: ReactNode;
33
+ "aria-current"?: AriaCurrent | undefined;
34
+ target?: string | undefined;
35
+ rel?: string | undefined;
36
+ title?: string | undefined;
37
+ href?: string | undefined;
38
+ render?: LinkRender | undefined;
39
+ external?: boolean | undefined;
40
+ } & Omit<ComponentPropsWithoutRef<"a">, "href" | "className" | "children" | "aria-current">): ReactElement;
41
+ /**
42
+ * The `data-*` attributes an item may carry onto its button or link, so a caller keeps its test
43
+ * ids on a tab. Only `data-*`: there is no `className` or `style`.
44
+ */
45
+ export type DataAttributes = {
46
+ [name: `data-${string}`]: string | number | boolean | undefined;
47
+ };
48
+ /** The `data-*` entries of `item`, ready to spread on the element it draws. */
49
+ export declare function dataAttributes(item: object): DataAttributes;
50
+ export {};
package/dist/link.js ADDED
@@ -0,0 +1,25 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * A link drawn by `render` when there is one, otherwise a plain `<a href>`. `external` adds
4
+ * `target="_blank"` and `rel="noreferrer"` to either, unless the caller passes its own.
5
+ */
6
+ export function renderLink({ href, render, external = false, className, children, "aria-current": current, target, rel, title, ...rest }) {
7
+ const opened = external ? { target: "_blank", rel: "noreferrer" } : {};
8
+ const all = {
9
+ ...rest,
10
+ className,
11
+ children,
12
+ ...(current === undefined ? {} : { "aria-current": current }),
13
+ ...opened,
14
+ ...(target === undefined ? {} : { target }),
15
+ ...(rel === undefined ? {} : { rel }),
16
+ ...(title === undefined ? {} : { title }),
17
+ };
18
+ if (render)
19
+ return _jsx(_Fragment, { children: render(all) });
20
+ return _jsx("a", { ...(href === undefined ? {} : { href }), ...all });
21
+ }
22
+ /** The `data-*` entries of `item`, ready to spread on the element it draws. */
23
+ export function dataAttributes(item) {
24
+ return Object.fromEntries(Object.entries(item).filter(([name]) => name.startsWith("data-")));
25
+ }