@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.
- package/README.md +18 -0
- package/dist/components/Button.js +8 -7
- package/dist/components/Callout.js +1 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +21 -4
- package/dist/components/Command.js +2 -2
- package/dist/components/Empty.js +22 -1
- package/dist/components/Field.d.ts +22 -19
- package/dist/components/Field.js +11 -9
- package/dist/components/Input.d.ts +10 -0
- package/dist/components/Input.js +26 -5
- package/dist/components/Kbd.js +11 -1
- package/dist/components/Menu.js +3 -3
- package/dist/components/Modal.js +2 -2
- package/dist/components/Pagination.js +6 -1
- package/dist/components/Progress.js +8 -1
- package/dist/components/Rows.d.ts +1 -1
- package/dist/components/Rows.js +1 -1
- package/dist/components/Select.d.ts +3 -1
- package/dist/components/Select.js +28 -6
- package/dist/components/Shell.d.ts +35 -0
- package/dist/components/Shell.js +36 -0
- package/dist/components/SizeGrid.js +14 -2
- package/dist/components/Stat.js +11 -2
- package/dist/components/Table.js +7 -1
- package/dist/components/Textarea.js +14 -4
- package/dist/components/ThemeSwitch.d.ts +19 -0
- package/dist/components/ThemeSwitch.js +65 -0
- package/dist/components/Toast.js +1 -1
- package/dist/components/Tooltip.js +16 -2
- package/dist/components/Tour.js +1 -1
- package/dist/components/fieldWiring.d.ts +44 -0
- package/dist/components/fieldWiring.js +30 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +8 -0
- package/dist/styles/index.css +1110 -355
- package/dist/tf.css +1110 -355
- package/dist/themes/choice.d.ts +71 -0
- package/dist/themes/choice.js +81 -0
- package/dist/valet.css +1110 -355
- 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
|
+
}
|