@viniaraujo68/plinth 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +361 -2
- package/dist/anchoring.d.ts +61 -0
- package/dist/anchoring.js +151 -0
- package/dist/async-action.svelte.d.ts +37 -0
- package/dist/async-action.svelte.js +64 -0
- package/dist/attachments/index.d.ts +2 -0
- package/dist/attachments/index.js +2 -0
- package/dist/attachments/sortable.d.ts +6 -0
- package/dist/attachments/sortable.js +227 -0
- package/dist/attachments/tooltip.d.ts +37 -0
- package/dist/attachments/tooltip.js +166 -0
- package/dist/components/AsyncButton.svelte +49 -0
- package/dist/components/AsyncButton.svelte.d.ts +32 -0
- package/dist/components/Combobox.svelte +380 -0
- package/dist/components/Combobox.svelte.d.ts +74 -0
- package/dist/components/Copyable.svelte +116 -0
- package/dist/components/Copyable.svelte.d.ts +37 -0
- package/dist/components/DateRangePicker.svelte +266 -0
- package/dist/components/DateRangePicker.svelte.d.ts +66 -0
- package/dist/components/Dialog.svelte +102 -0
- package/dist/components/Dialog.svelte.d.ts +38 -0
- package/dist/components/DragHandle.svelte +26 -0
- package/dist/components/DragHandle.svelte.d.ts +7 -0
- package/dist/components/Dropdown.svelte +204 -0
- package/dist/components/Dropdown.svelte.d.ts +49 -0
- package/dist/components/ErrorDisplay.svelte +59 -0
- package/dist/components/ErrorDisplay.svelte.d.ts +24 -0
- package/dist/components/ErrorPage.svelte +119 -0
- package/dist/components/ErrorPage.svelte.d.ts +52 -0
- package/dist/components/JsonInput.svelte +106 -0
- package/dist/components/JsonInput.svelte.d.ts +40 -0
- package/dist/components/Listbox.svelte +257 -0
- package/dist/components/Listbox.svelte.d.ts +78 -0
- package/dist/components/LoadingButton.svelte +36 -0
- package/dist/components/LoadingButton.svelte.d.ts +22 -0
- package/dist/components/Logo.svelte +112 -0
- package/dist/components/Logo.svelte.d.ts +29 -0
- package/dist/components/Modal.svelte +187 -0
- package/dist/components/Modal.svelte.d.ts +62 -0
- package/dist/components/MultiSelect.svelte +256 -0
- package/dist/components/MultiSelect.svelte.d.ts +89 -0
- package/dist/components/OptionPanel.svelte +241 -0
- package/dist/components/OptionPanel.svelte.d.ts +51 -0
- package/dist/components/Paginator.svelte +205 -0
- package/dist/components/Paginator.svelte.d.ts +48 -0
- package/dist/components/RefreshButton.svelte +246 -0
- package/dist/components/RefreshButton.svelte.d.ts +46 -0
- package/dist/components/SegmentedControl.svelte +147 -0
- package/dist/components/SegmentedControl.svelte.d.ts +71 -0
- package/dist/components/Select.svelte +312 -0
- package/dist/components/Select.svelte.d.ts +68 -0
- package/dist/components/Skeleton.svelte +97 -0
- package/dist/components/Skeleton.svelte.d.ts +32 -0
- package/dist/components/Tooltip.svelte +60 -0
- package/dist/components/Tooltip.svelte.d.ts +58 -0
- package/dist/components/date-range.d.ts +101 -0
- package/dist/components/date-range.js +144 -0
- package/dist/components/index.d.ts +26 -0
- package/dist/components/index.js +38 -0
- package/dist/components/json-text.d.ts +22 -0
- package/dist/components/json-text.js +22 -0
- package/dist/components/pick.d.ts +18 -0
- package/dist/components/pick.js +18 -0
- package/dist/components/segmented.d.ts +7 -0
- package/dist/components/segmented.js +1 -0
- package/dist/components/select.d.ts +66 -0
- package/dist/components/select.js +90 -0
- package/dist/confirm/Confirmer.svelte +194 -0
- package/dist/confirm/Confirmer.svelte.d.ts +45 -0
- package/dist/confirm/challenge.d.ts +16 -0
- package/dist/confirm/challenge.js +16 -0
- package/dist/confirm/confirm-manager.svelte.d.ts +91 -0
- package/dist/confirm/confirm-manager.svelte.js +149 -0
- package/dist/confirm/index.d.ts +3 -0
- package/dist/confirm/index.js +3 -0
- package/dist/error.d.ts +25 -0
- package/dist/error.js +39 -0
- package/dist/formatters.d.ts +95 -0
- package/dist/formatters.js +146 -0
- package/dist/http.d.ts +68 -0
- package/dist/http.js +138 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +14 -0
- package/dist/routing.svelte.d.ts +169 -0
- package/dist/routing.svelte.js +208 -0
- package/dist/shell/AppShell.svelte +874 -0
- package/dist/shell/AppShell.svelte.d.ts +93 -0
- package/dist/shell/Breadcrumbs.svelte +78 -0
- package/dist/shell/Breadcrumbs.svelte.d.ts +25 -0
- package/dist/shell/crumbs.d.ts +23 -0
- package/dist/shell/crumbs.js +1 -0
- package/dist/shell/index.d.ts +3 -0
- package/dist/shell/index.js +6 -0
- package/dist/table/ColumnMenu.svelte +249 -0
- package/dist/table/ColumnMenu.svelte.d.ts +47 -0
- package/dist/table/DataTable.svelte +643 -0
- package/dist/table/DataTable.svelte.d.ts +153 -0
- package/dist/table/columns.d.ts +55 -0
- package/dist/table/columns.js +153 -0
- package/dist/table/index.d.ts +4 -0
- package/dist/table/index.js +8 -0
- package/dist/table/sort.d.ts +48 -0
- package/dist/table/sort.js +139 -0
- package/dist/table/types.d.ts +93 -0
- package/dist/table/types.js +1 -0
- package/dist/theme/ThemeController.svelte +54 -0
- package/dist/theme/ThemeController.svelte.d.ts +27 -0
- package/dist/theme/ThemeToggle.svelte +119 -0
- package/dist/theme/ThemeToggle.svelte.d.ts +52 -0
- package/dist/theme/css-color.d.ts +23 -0
- package/dist/theme/css-color.js +31 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +5 -0
- package/dist/theme/theme.css +410 -0
- package/dist/theme/theme.svelte.d.ts +54 -0
- package/dist/theme/theme.svelte.js +58 -0
- package/dist/toast/Toaster.svelte +257 -0
- package/dist/toast/Toaster.svelte.d.ts +35 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +2 -0
- package/dist/toast/toast-manager.svelte.d.ts +99 -0
- package/dist/toast/toast-manager.svelte.js +176 -0
- package/dist/user/ScopedComponent.svelte +47 -0
- package/dist/user/ScopedComponent.svelte.d.ts +29 -0
- package/dist/user/anonymous.d.ts +21 -0
- package/dist/user/anonymous.js +26 -0
- package/dist/user/context.d.ts +55 -0
- package/dist/user/context.js +13 -0
- package/dist/user/index.d.ts +3 -0
- package/dist/user/index.js +6 -0
- package/package.json +133 -4
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { DARK_THEME, getThemeContext, LIGHT_THEME, themeCookie } from "./theme.svelte.js";
|
|
3
|
+
|
|
4
|
+
const theme = getThemeContext();
|
|
5
|
+
|
|
6
|
+
// A consumer that stamps `data-theme` on <html> before hydration is seeding the FIRST PAINT
|
|
7
|
+
// from a cookie no checkbox has read yet. CSS already makes a checked controller outrank that
|
|
8
|
+
// seed, but "system" checks nothing, so a stale seed would go on painting a theme the user has
|
|
9
|
+
// just left. Releasing it once, on mount, hands the root over to this checkbox for good; the
|
|
10
|
+
// effect reads nothing reactive, so it runs exactly once.
|
|
11
|
+
$effect(() => {
|
|
12
|
+
const root = document.documentElement;
|
|
13
|
+
const seed = root.getAttribute("data-theme");
|
|
14
|
+
if (seed === LIGHT_THEME || seed === DARK_THEME) root.removeAttribute("data-theme");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
$effect(() => {
|
|
18
|
+
document.cookie = themeCookie(theme.preference);
|
|
19
|
+
});
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<!--
|
|
23
|
+
@component
|
|
24
|
+
The daisyUI theme-controller checkbox, plus persistence of whatever it is holding.
|
|
25
|
+
|
|
26
|
+
The checkbox encodes the PREFERENCE, not the resolved theme: unchecked means "system", which the
|
|
27
|
+
stylesheet answers with `color-scheme: light dark` so the OS decides at first paint with no JS
|
|
28
|
+
involved; checked pins the theme named by `value`. All three states are therefore fully
|
|
29
|
+
determined by the cookie, which is what makes a server-rendered first paint flash-free.
|
|
30
|
+
|
|
31
|
+
A `data-theme` stamped on <html> before hydration — the head-script trick a client-rendered app
|
|
32
|
+
uses to avoid a flash — is a seed for that first paint and nothing more. This checkbox outranks
|
|
33
|
+
it as soon as it is checked, and drops the seed altogether on mount, so from then on the
|
|
34
|
+
preference is the only thing painting the page. A `data-theme` on any element BELOW the root is
|
|
35
|
+
untouched by all of this and keeps re-theming its own subtree.
|
|
36
|
+
|
|
37
|
+
Mount it exactly once and unconditionally, near the app root — unmounting it drops the app back
|
|
38
|
+
to "system". Nothing else in an app should carry the `theme-controller` class; a picker elsewhere
|
|
39
|
+
writes `preference` on the theme context and lets this component follow.
|
|
40
|
+
|
|
41
|
+
```svelte
|
|
42
|
+
setThemeContext(new ThemeContext(readThemePreference()));
|
|
43
|
+
...
|
|
44
|
+
<ThemeController />
|
|
45
|
+
```
|
|
46
|
+
-->
|
|
47
|
+
|
|
48
|
+
<input
|
|
49
|
+
type="checkbox"
|
|
50
|
+
class="theme-controller"
|
|
51
|
+
value={theme.preference === "light" ? LIGHT_THEME : DARK_THEME}
|
|
52
|
+
hidden
|
|
53
|
+
checked={theme.preference !== "system"}
|
|
54
|
+
/>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daisyUI theme-controller checkbox, plus persistence of whatever it is holding.
|
|
3
|
+
*
|
|
4
|
+
* The checkbox encodes the PREFERENCE, not the resolved theme: unchecked means "system", which the
|
|
5
|
+
* stylesheet answers with `color-scheme: light dark` so the OS decides at first paint with no JS
|
|
6
|
+
* involved; checked pins the theme named by `value`. All three states are therefore fully
|
|
7
|
+
* determined by the cookie, which is what makes a server-rendered first paint flash-free.
|
|
8
|
+
*
|
|
9
|
+
* A `data-theme` stamped on <html> before hydration — the head-script trick a client-rendered app
|
|
10
|
+
* uses to avoid a flash — is a seed for that first paint and nothing more. This checkbox outranks
|
|
11
|
+
* it as soon as it is checked, and drops the seed altogether on mount, so from then on the
|
|
12
|
+
* preference is the only thing painting the page. A `data-theme` on any element BELOW the root is
|
|
13
|
+
* untouched by all of this and keeps re-theming its own subtree.
|
|
14
|
+
*
|
|
15
|
+
* Mount it exactly once and unconditionally, near the app root — unmounting it drops the app back
|
|
16
|
+
* to "system". Nothing else in an app should carry the `theme-controller` class; a picker elsewhere
|
|
17
|
+
* writes `preference` on the theme context and lets this component follow.
|
|
18
|
+
*
|
|
19
|
+
* ```svelte
|
|
20
|
+
* setThemeContext(new ThemeContext(readThemePreference()));
|
|
21
|
+
* ...
|
|
22
|
+
* <ThemeController />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare const ThemeController: import("svelte").Component<Record<string, never>, {}, "">;
|
|
26
|
+
type ThemeController = ReturnType<typeof ThemeController>;
|
|
27
|
+
export default ThemeController;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getThemeContext, type ThemePreference } from "./theme.svelte.js";
|
|
3
|
+
|
|
4
|
+
// Cycling through "system" rather than flipping between the two explicit themes is the point:
|
|
5
|
+
// "follow the OS" is a real choice a user can get back to, and a two-state toggle strands it.
|
|
6
|
+
const NEXT: Record<ThemePreference, ThemePreference> = {
|
|
7
|
+
system: "light",
|
|
8
|
+
light: "dark",
|
|
9
|
+
dark: "system",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const LABELS: Record<ThemePreference, string> = {
|
|
13
|
+
system: "System",
|
|
14
|
+
light: "Light",
|
|
15
|
+
dark: "Dark",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
/** Extra classes for the button, so a caller can size or place it. */
|
|
20
|
+
class?: string;
|
|
21
|
+
/** Hide the written preference and keep only the icon. The accessible name is unaffected. */
|
|
22
|
+
iconOnly?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Names one preference — the visible text, and the words the default accessible name is built
|
|
25
|
+
* from. A function of the preference, and not three strings, because it is the same wording in
|
|
26
|
+
* both places; overriding it localizes both at once.
|
|
27
|
+
*/
|
|
28
|
+
preferenceLabel?: (preference: ThemePreference) => string;
|
|
29
|
+
/**
|
|
30
|
+
* Accessible name of the button, given the preference on screen and the one a click moves to.
|
|
31
|
+
* Defaults to the English sentence around `preferenceLabel`, so a caller who only needs the
|
|
32
|
+
* three words translated can leave this alone; supply it to phrase the whole sentence.
|
|
33
|
+
*/
|
|
34
|
+
label?: (current: ThemePreference, next: ThemePreference) => string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
class: className = "",
|
|
39
|
+
iconOnly = false,
|
|
40
|
+
preferenceLabel = (preference: ThemePreference) => LABELS[preference],
|
|
41
|
+
label,
|
|
42
|
+
}: Props = $props();
|
|
43
|
+
|
|
44
|
+
const theme = getThemeContext();
|
|
45
|
+
|
|
46
|
+
const next = $derived(NEXT[theme.preference]);
|
|
47
|
+
|
|
48
|
+
const accessibleName = $derived(
|
|
49
|
+
label?.(theme.preference, next) ??
|
|
50
|
+
`Theme: ${preferenceLabel(theme.preference)}. Switch to ${preferenceLabel(next)}.`,
|
|
51
|
+
);
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<!--
|
|
55
|
+
@component
|
|
56
|
+
A visible control cycling the theme preference System → Light → Dark → System.
|
|
57
|
+
|
|
58
|
+
It only writes `preference` on the theme context; the actual repaint is done by the
|
|
59
|
+
`ThemeController` checkbox, which has to be mounted once near the app root. That split is what
|
|
60
|
+
lets an app place any number of pickers wherever it likes without ever duplicating the
|
|
61
|
+
`theme-controller` element, whose presence is what CSS keys on.
|
|
62
|
+
|
|
63
|
+
The wording is English by default and both halves of it are props, because the library ships no
|
|
64
|
+
translations. `preferenceLabel` names a single preference — the visible text and the words inside
|
|
65
|
+
the accessible name — and is usually the only one an app needs. `label` replaces the sentence those
|
|
66
|
+
words sit in, for a language the "Theme: X. Switch to Y." shape does not fit.
|
|
67
|
+
|
|
68
|
+
```svelte
|
|
69
|
+
<script lang="ts">
|
|
70
|
+
import { ThemeToggle, type ThemePreference } from "@viniaraujo68/plinth/theme";
|
|
71
|
+
|
|
72
|
+
const PREFERENCES: Record<ThemePreference, string> = {
|
|
73
|
+
system: "Sistema",
|
|
74
|
+
light: "Claro",
|
|
75
|
+
dark: "Escuro",
|
|
76
|
+
};
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<ThemeToggle
|
|
80
|
+
preferenceLabel={(preference) => PREFERENCES[preference]}
|
|
81
|
+
label={(current, next) => `Tema: ${PREFERENCES[current]}. Mudar para ${PREFERENCES[next]}.`}
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
-->
|
|
85
|
+
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
class="btn gap-2 btn-ghost btn-sm {className}"
|
|
89
|
+
aria-label={accessibleName}
|
|
90
|
+
data-preference={theme.preference}
|
|
91
|
+
onclick={() => (theme.preference = next)}
|
|
92
|
+
>
|
|
93
|
+
<!-- aria-hidden because the button already carries the whole meaning in its accessible name. -->
|
|
94
|
+
<svg
|
|
95
|
+
class="size-4"
|
|
96
|
+
viewBox="0 0 24 24"
|
|
97
|
+
fill="none"
|
|
98
|
+
stroke="currentColor"
|
|
99
|
+
stroke-width="1.75"
|
|
100
|
+
stroke-linecap="round"
|
|
101
|
+
stroke-linejoin="round"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
>
|
|
104
|
+
{#if theme.preference === "light"}
|
|
105
|
+
<circle cx="12" cy="12" r="4" />
|
|
106
|
+
<path
|
|
107
|
+
d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"
|
|
108
|
+
/>
|
|
109
|
+
{:else if theme.preference === "dark"}
|
|
110
|
+
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z" />
|
|
111
|
+
{:else}
|
|
112
|
+
<rect x="2" y="4" width="20" height="13" rx="2" />
|
|
113
|
+
<path d="M8 21h8M12 17v4" />
|
|
114
|
+
{/if}
|
|
115
|
+
</svg>
|
|
116
|
+
{#if !iconOnly}
|
|
117
|
+
<span aria-hidden="true">{preferenceLabel(theme.preference)}</span>
|
|
118
|
+
{/if}
|
|
119
|
+
</button>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ThemePreference } from "./theme.svelte.js";
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Extra classes for the button, so a caller can size or place it. */
|
|
4
|
+
class?: string;
|
|
5
|
+
/** Hide the written preference and keep only the icon. The accessible name is unaffected. */
|
|
6
|
+
iconOnly?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Names one preference — the visible text, and the words the default accessible name is built
|
|
9
|
+
* from. A function of the preference, and not three strings, because it is the same wording in
|
|
10
|
+
* both places; overriding it localizes both at once.
|
|
11
|
+
*/
|
|
12
|
+
preferenceLabel?: (preference: ThemePreference) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Accessible name of the button, given the preference on screen and the one a click moves to.
|
|
15
|
+
* Defaults to the English sentence around `preferenceLabel`, so a caller who only needs the
|
|
16
|
+
* three words translated can leave this alone; supply it to phrase the whole sentence.
|
|
17
|
+
*/
|
|
18
|
+
label?: (current: ThemePreference, next: ThemePreference) => string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A visible control cycling the theme preference System → Light → Dark → System.
|
|
22
|
+
*
|
|
23
|
+
* It only writes `preference` on the theme context; the actual repaint is done by the
|
|
24
|
+
* `ThemeController` checkbox, which has to be mounted once near the app root. That split is what
|
|
25
|
+
* lets an app place any number of pickers wherever it likes without ever duplicating the
|
|
26
|
+
* `theme-controller` element, whose presence is what CSS keys on.
|
|
27
|
+
*
|
|
28
|
+
* The wording is English by default and both halves of it are props, because the library ships no
|
|
29
|
+
* translations. `preferenceLabel` names a single preference — the visible text and the words inside
|
|
30
|
+
* the accessible name — and is usually the only one an app needs. `label` replaces the sentence those
|
|
31
|
+
* words sit in, for a language the "Theme: X. Switch to Y." shape does not fit.
|
|
32
|
+
*
|
|
33
|
+
* ```svelte
|
|
34
|
+
* <script lang="ts">
|
|
35
|
+
* import { ThemeToggle, type ThemePreference } from "@viniaraujo68/plinth/theme";
|
|
36
|
+
*
|
|
37
|
+
* const PREFERENCES: Record<ThemePreference, string> = {
|
|
38
|
+
* system: "Sistema",
|
|
39
|
+
* light: "Claro",
|
|
40
|
+
* dark: "Escuro",
|
|
41
|
+
* };
|
|
42
|
+
* </script>
|
|
43
|
+
*
|
|
44
|
+
* <ThemeToggle
|
|
45
|
+
* preferenceLabel={(preference) => PREFERENCES[preference]}
|
|
46
|
+
* label={(current, next) => `Tema: ${PREFERENCES[current]}. Mudar para ${PREFERENCES[next]}.`}
|
|
47
|
+
* />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare const ThemeToggle: import("svelte").Component<Props, {}, "">;
|
|
51
|
+
type ThemeToggle = ReturnType<typeof ThemeToggle>;
|
|
52
|
+
export default ThemeToggle;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves CSS colour expressions — `var(--color-primary)`, a `color-mix()` over theme tokens —
|
|
3
|
+
* into the concrete colour strings a canvas needs. A chart library draws on a `<canvas>`, which
|
|
4
|
+
* knows nothing of custom properties, so a chart that follows the theme has to ask the page what
|
|
5
|
+
* its tokens currently compute to, in the right place in the tree.
|
|
6
|
+
*/
|
|
7
|
+
export interface CssColorReader {
|
|
8
|
+
/**
|
|
9
|
+
* The computed colour of `expression` inside the host, as `rgb(...)` or whatever the browser
|
|
10
|
+
* serialises it to. `fallback` answers for an expression the browser rejects — a typo, or a
|
|
11
|
+
* colour function it does not support yet.
|
|
12
|
+
*/
|
|
13
|
+
read: (expression: string, fallback: string) => string;
|
|
14
|
+
/** Removes the probe element. Reads after this answer the fallback. */
|
|
15
|
+
dispose: () => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One hidden probe element, appended inside `host` so custom properties resolve against the host's
|
|
19
|
+
* own cascade — a theme scoped to a subtree, a palette declared on the chart's wrapper — rather
|
|
20
|
+
* than against the document root. The probe is reused for every read, since each one costs a style
|
|
21
|
+
* recalculation and a chart reads a dozen colours per render.
|
|
22
|
+
*/
|
|
23
|
+
export declare const createCssColorReader: (host: HTMLElement) => CssColorReader;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One hidden probe element, appended inside `host` so custom properties resolve against the host's
|
|
3
|
+
* own cascade — a theme scoped to a subtree, a palette declared on the chart's wrapper — rather
|
|
4
|
+
* than against the document root. The probe is reused for every read, since each one costs a style
|
|
5
|
+
* recalculation and a chart reads a dozen colours per render.
|
|
6
|
+
*/
|
|
7
|
+
export const createCssColorReader = (host) => {
|
|
8
|
+
const probe = document.createElement("span");
|
|
9
|
+
probe.style.position = "absolute";
|
|
10
|
+
probe.style.visibility = "hidden";
|
|
11
|
+
probe.style.pointerEvents = "none";
|
|
12
|
+
host.appendChild(probe);
|
|
13
|
+
let disposed = false;
|
|
14
|
+
return {
|
|
15
|
+
read(expression, fallback) {
|
|
16
|
+
if (disposed)
|
|
17
|
+
return fallback;
|
|
18
|
+
probe.style.color = "";
|
|
19
|
+
probe.style.color = expression;
|
|
20
|
+
// An expression the browser cannot parse leaves the declaration empty, and the computed
|
|
21
|
+
// colour would then be whatever the probe inherits — a colour, just not the one asked for.
|
|
22
|
+
if (probe.style.color === "")
|
|
23
|
+
return fallback;
|
|
24
|
+
return getComputedStyle(probe).color || fallback;
|
|
25
|
+
},
|
|
26
|
+
dispose() {
|
|
27
|
+
disposed = true;
|
|
28
|
+
probe.remove();
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { DARK_THEME, getThemeContext, LIGHT_THEME, parseThemePreference, readThemePreference, setThemeContext, THEME_COOKIE, ThemeContext, themeCookie, type ThemePreference, } from "./theme.svelte.js";
|
|
2
|
+
export { default as ThemeController } from "./ThemeController.svelte";
|
|
3
|
+
export { default as ThemeToggle } from "./ThemeToggle.svelte";
|
|
4
|
+
export { createCssColorReader, type CssColorReader } from "./css-color.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DARK_THEME, getThemeContext, LIGHT_THEME, parseThemePreference, readThemePreference, setThemeContext, THEME_COOKIE, ThemeContext, themeCookie, } from "./theme.svelte.js";
|
|
2
|
+
export { default as ThemeController } from "./ThemeController.svelte";
|
|
3
|
+
export { default as ThemeToggle } from "./ThemeToggle.svelte";
|
|
4
|
+
// Theme tokens resolved to concrete colours, for the one surface CSS cannot reach: a canvas.
|
|
5
|
+
export { createCssColorReader } from "./css-color.js";
|