@wtfalch/design 0.7.0 → 0.9.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 (41) hide show
  1. package/README.md +18 -0
  2. package/dist/components/Button.js +8 -7
  3. package/dist/components/Callout.js +1 -1
  4. package/dist/components/Card.d.ts +1 -1
  5. package/dist/components/Card.js +21 -4
  6. package/dist/components/Command.js +2 -2
  7. package/dist/components/Empty.js +22 -1
  8. package/dist/components/Field.d.ts +22 -19
  9. package/dist/components/Field.js +11 -9
  10. package/dist/components/Input.d.ts +10 -0
  11. package/dist/components/Input.js +26 -5
  12. package/dist/components/Kbd.js +11 -1
  13. package/dist/components/Menu.js +3 -3
  14. package/dist/components/Modal.js +2 -2
  15. package/dist/components/Pagination.js +6 -1
  16. package/dist/components/Progress.js +8 -1
  17. package/dist/components/Rows.d.ts +1 -1
  18. package/dist/components/Rows.js +1 -1
  19. package/dist/components/Select.d.ts +3 -1
  20. package/dist/components/Select.js +28 -6
  21. package/dist/components/Shell.d.ts +35 -0
  22. package/dist/components/Shell.js +36 -0
  23. package/dist/components/SizeGrid.js +14 -2
  24. package/dist/components/Stat.js +11 -2
  25. package/dist/components/Table.js +7 -1
  26. package/dist/components/Textarea.js +14 -4
  27. package/dist/components/ThemeSwitch.d.ts +19 -0
  28. package/dist/components/ThemeSwitch.js +65 -0
  29. package/dist/components/Toast.js +1 -1
  30. package/dist/components/Tooltip.js +16 -2
  31. package/dist/components/Tour.js +1 -1
  32. package/dist/components/fieldWiring.d.ts +44 -0
  33. package/dist/components/fieldWiring.js +30 -0
  34. package/dist/index.d.ts +10 -1
  35. package/dist/index.js +8 -0
  36. package/dist/styles/index.css +1110 -355
  37. package/dist/tf.css +1110 -355
  38. package/dist/themes/choice.d.ts +71 -0
  39. package/dist/themes/choice.js +81 -0
  40. package/dist/valet.css +1110 -355
  41. package/package.json +3 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * A person's theme, remembered, and applied before the page is drawn.
3
+ *
4
+ * **Three apps wrote this and two of them wrote it identically.**
5
+ * `src/theme/first-paint.ts` is byte-for-byte the same file in
6
+ * wtfalch-manage and app-template; valet's is the same script with the
7
+ * comment rewritten. Each also keeps a `themes.ts` listing ids and labels the
8
+ * package already knows -- a `Theme` carries the `name` a picker shows, and a
9
+ * `Product` carries its themes and which one it wears by default -- so the
10
+ * app-side list was a second copy of a fact, kept in step by hand.
11
+ *
12
+ * **The script is a string, and it has to be.** It runs before the bundle, in
13
+ * a blocking `<script>` in `<head>`, because a theme applied after hydration
14
+ * means a person who chose Paper sees Night for a frame. That is the whole
15
+ * reason this is not simply a `useEffect`.
16
+ *
17
+ * **It sets `data-theme` and nothing else.** The palettes ship as CSS from
18
+ * `productCss`, generated from the same objects the contrast test measures,
19
+ * so the attribute is the entire mechanism. `applyTheme` writes custom
20
+ * properties instead and is for the case with no stylesheet to lean on --
21
+ * theming a subtree, or a product whose CSS is not the one loaded.
22
+ *
23
+ * A copy of this shipped with a bug worth keeping: a stored id the app no
24
+ * longer offers has to fall back, or a browser that remembers `sepia` from a
25
+ * palette you dropped renders unthemed. The list is inlined into the script
26
+ * for that check.
27
+ */
28
+ import type { BrandName } from '../components/brandMarks.js';
29
+ import { type Product } from '../products/index.js';
30
+ /** Where the choice lives. One key across the estate, so a person who picked
31
+ * Night on one app is not asked again on the next one under the same
32
+ * origin. The README has used this name since 0.1.0. */
33
+ export declare const THEME_STORAGE_KEY = "theme";
34
+ export interface ThemeChoice {
35
+ id: string;
36
+ /** What the picker calls it -- the theme's own `name`. */
37
+ label: string;
38
+ /** The line under it, where a picker has room. */
39
+ note: string;
40
+ }
41
+ /**
42
+ * What this product offers, in the order it declared them.
43
+ *
44
+ * `only` narrows to a subset, for an app that ships a product's palette
45
+ * without all of its themes. An id the product does not have is dropped
46
+ * rather than thrown on: the list is presentation, and a picker missing a row
47
+ * is better than a page that will not render.
48
+ */
49
+ export declare function themeChoices(product: BrandName | Product, only?: readonly string[]): ThemeChoice[];
50
+ /**
51
+ * The blocking script, as a string to put in a `<script>` in `<head>`.
52
+ *
53
+ * `dangerouslySetInnerHTML={{ __html: themeChoiceScript('tf') }}` in a Next
54
+ * root layout, above everything. It reads the stored id, checks it against
55
+ * what this app offers, and writes `data-theme` on `<html>`.
56
+ *
57
+ * Wrapped in try/catch because `localStorage` throws outright in a browser
58
+ * set to block site data, and an exception here happens before anything is
59
+ * drawn -- so the page that fails to read a preference would otherwise fail
60
+ * to render at all.
61
+ */
62
+ export declare function themeChoiceScript(product: BrandName | Product, options?: {
63
+ only?: readonly string[];
64
+ storageKey?: string;
65
+ }): string;
66
+ /** What the script would have written, for code that needs the same answer
67
+ * after hydration. Reads the same key and applies the same fallback. */
68
+ export declare function storedTheme(product: BrandName | Product, options?: {
69
+ only?: readonly string[];
70
+ storageKey?: string;
71
+ }): string;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * A person's theme, remembered, and applied before the page is drawn.
3
+ *
4
+ * **Three apps wrote this and two of them wrote it identically.**
5
+ * `src/theme/first-paint.ts` is byte-for-byte the same file in
6
+ * wtfalch-manage and app-template; valet's is the same script with the
7
+ * comment rewritten. Each also keeps a `themes.ts` listing ids and labels the
8
+ * package already knows -- a `Theme` carries the `name` a picker shows, and a
9
+ * `Product` carries its themes and which one it wears by default -- so the
10
+ * app-side list was a second copy of a fact, kept in step by hand.
11
+ *
12
+ * **The script is a string, and it has to be.** It runs before the bundle, in
13
+ * a blocking `<script>` in `<head>`, because a theme applied after hydration
14
+ * means a person who chose Paper sees Night for a frame. That is the whole
15
+ * reason this is not simply a `useEffect`.
16
+ *
17
+ * **It sets `data-theme` and nothing else.** The palettes ship as CSS from
18
+ * `productCss`, generated from the same objects the contrast test measures,
19
+ * so the attribute is the entire mechanism. `applyTheme` writes custom
20
+ * properties instead and is for the case with no stylesheet to lean on --
21
+ * theming a subtree, or a product whose CSS is not the one loaded.
22
+ *
23
+ * A copy of this shipped with a bug worth keeping: a stored id the app no
24
+ * longer offers has to fall back, or a browser that remembers `sepia` from a
25
+ * palette you dropped renders unthemed. The list is inlined into the script
26
+ * for that check.
27
+ */
28
+ import { PRODUCTS } from '../products/index.js';
29
+ /** Where the choice lives. One key across the estate, so a person who picked
30
+ * Night on one app is not asked again on the next one under the same
31
+ * origin. The README has used this name since 0.1.0. */
32
+ export const THEME_STORAGE_KEY = 'theme';
33
+ function resolve(product) {
34
+ return typeof product === 'string' ? PRODUCTS[product] : product;
35
+ }
36
+ /**
37
+ * What this product offers, in the order it declared them.
38
+ *
39
+ * `only` narrows to a subset, for an app that ships a product's palette
40
+ * without all of its themes. An id the product does not have is dropped
41
+ * rather than thrown on: the list is presentation, and a picker missing a row
42
+ * is better than a page that will not render.
43
+ */
44
+ export function themeChoices(product, only) {
45
+ const p = resolve(product);
46
+ const ids = only ? only.filter((id) => id in p.themes) : Object.keys(p.themes);
47
+ return ids.map((id) => ({ id, label: p.themes[id].name, note: p.themes[id].note }));
48
+ }
49
+ /**
50
+ * The blocking script, as a string to put in a `<script>` in `<head>`.
51
+ *
52
+ * `dangerouslySetInnerHTML={{ __html: themeChoiceScript('tf') }}` in a Next
53
+ * root layout, above everything. It reads the stored id, checks it against
54
+ * what this app offers, and writes `data-theme` on `<html>`.
55
+ *
56
+ * Wrapped in try/catch because `localStorage` throws outright in a browser
57
+ * set to block site data, and an exception here happens before anything is
58
+ * drawn -- so the page that fails to read a preference would otherwise fail
59
+ * to render at all.
60
+ */
61
+ export function themeChoiceScript(product, options = {}) {
62
+ const p = resolve(product);
63
+ const ids = themeChoices(product, options.only).map((c) => c.id);
64
+ const key = JSON.stringify(options.storageKey ?? THEME_STORAGE_KEY);
65
+ const fallback = JSON.stringify(ids.includes(p.defaultTheme) ? p.defaultTheme : (ids[0] ?? p.defaultTheme));
66
+ return `(function(){try{var t=localStorage.getItem(${key});document.documentElement.dataset.theme=${JSON.stringify(ids)}.indexOf(t)>=0?t:${fallback}}catch(e){}})()`;
67
+ }
68
+ /** What the script would have written, for code that needs the same answer
69
+ * after hydration. Reads the same key and applies the same fallback. */
70
+ export function storedTheme(product, options = {}) {
71
+ const p = resolve(product);
72
+ const ids = themeChoices(product, options.only).map((c) => c.id);
73
+ const fallback = ids.includes(p.defaultTheme) ? p.defaultTheme : (ids[0] ?? p.defaultTheme);
74
+ try {
75
+ const stored = localStorage.getItem(options.storageKey ?? THEME_STORAGE_KEY);
76
+ return stored && ids.includes(stored) ? stored : fallback;
77
+ }
78
+ catch {
79
+ return fallback;
80
+ }
81
+ }