@shadow-js/ui 0.0.0-stage → 1.0.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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +598 -2
- package/dist/cli.mjs +2293 -0
- package/dist/dev/cli/schema.d.ts +73 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/locales/ar.d.ts +141 -0
- package/dist/dev/locales/ar.js +128 -0
- package/dist/dev/locales/ar.js.map +1 -0
- package/dist/dev/locales/en.d.ts +141 -0
- package/dist/dev/locales/en.js +128 -0
- package/dist/dev/locales/en.js.map +1 -0
- package/dist/dev/registry/utils/utils.d.ts +29 -0
- package/dist/dev/registry/utils/utils.js +27 -0
- package/dist/dev/registry/utils/utils.js.map +1 -0
- package/dist/dev/theme/theme-logic.d.ts +187 -0
- package/dist/dev/theme/theme-logic.js +184 -0
- package/dist/dev/theme/theme-logic.js.map +1 -0
- package/dist/dev/theme/theme-provider.d.ts +90 -0
- package/dist/dev/theme/theme-provider.js +150 -0
- package/dist/dev/theme/theme-provider.js.map +1 -0
- package/dist/prod/index.js +1 -0
- package/dist/prod/locales/ar.js +2 -0
- package/dist/prod/locales/ar.js.map +1 -0
- package/dist/prod/locales/en.js +2 -0
- package/dist/prod/locales/en.js.map +1 -0
- package/dist/prod/registry/utils/utils.js +2 -0
- package/dist/prod/registry/utils/utils.js.map +1 -0
- package/dist/prod/theme/theme-logic.js +2 -0
- package/dist/prod/theme/theme-logic.js.map +1 -0
- package/dist/prod/theme/theme-provider.js +2 -0
- package/dist/prod/theme/theme-provider.js.map +1 -0
- package/package.json +102 -4
- package/registry/accordion/accordion.tsx +210 -0
- package/registry/accordion/item.json +4 -0
- package/registry/alert/alert.tsx +200 -0
- package/registry/alert/item.json +4 -0
- package/registry/alert-dialog/alert-dialog.tsx +530 -0
- package/registry/alert-dialog/item.json +4 -0
- package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
- package/registry/aspect-ratio/item.json +4 -0
- package/registry/attachment/attachment.tsx +459 -0
- package/registry/attachment/item.json +4 -0
- package/registry/avatar/avatar.tsx +385 -0
- package/registry/avatar/item.json +4 -0
- package/registry/badge/badge.tsx +108 -0
- package/registry/badge/item.json +4 -0
- package/registry/breadcrumb/breadcrumb.tsx +354 -0
- package/registry/breadcrumb/item.json +4 -0
- package/registry/bubble/bubble.tsx +260 -0
- package/registry/bubble/item.json +4 -0
- package/registry/button/button.tsx +125 -0
- package/registry/button/item.json +4 -0
- package/registry/button-group/button-group.tsx +189 -0
- package/registry/button-group/item.json +4 -0
- package/registry/calendar/calendar.tsx +348 -0
- package/registry/calendar/item.json +4 -0
- package/registry/card/card.tsx +311 -0
- package/registry/card/item.json +4 -0
- package/registry/carousel/carousel.tsx +851 -0
- package/registry/carousel/item.json +4 -0
- package/registry/chart/chart.tsx +1956 -0
- package/registry/chart/item.json +4 -0
- package/registry/chart-scale/chart-scale.ts +670 -0
- package/registry/chart-scale/item.json +5 -0
- package/registry/checkbox/checkbox.tsx +79 -0
- package/registry/checkbox/item.json +4 -0
- package/registry/code-block/code-block.tsx +470 -0
- package/registry/code-block/item.json +4 -0
- package/registry/collapsible/collapsible.tsx +117 -0
- package/registry/collapsible/item.json +4 -0
- package/registry/combobox/combobox.tsx +977 -0
- package/registry/combobox/item.json +4 -0
- package/registry/command/command.tsx +757 -0
- package/registry/command/item.json +4 -0
- package/registry/context-menu/context-menu.tsx +697 -0
- package/registry/context-menu/item.json +4 -0
- package/registry/data-table/data-table.tsx +437 -0
- package/registry/data-table/item.json +4 -0
- package/registry/date-picker/date-picker.tsx +349 -0
- package/registry/date-picker/item.json +4 -0
- package/registry/dialog/dialog.tsx +412 -0
- package/registry/dialog/item.json +4 -0
- package/registry/direction/direction.tsx +88 -0
- package/registry/direction/item.json +4 -0
- package/registry/drawer/drawer.tsx +787 -0
- package/registry/drawer/item.json +4 -0
- package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
- package/registry/dropdown-menu/item.json +4 -0
- package/registry/empty/empty.tsx +240 -0
- package/registry/empty/item.json +4 -0
- package/registry/field/field.tsx +624 -0
- package/registry/field/item.json +4 -0
- package/registry/form/form.tsx +1112 -0
- package/registry/form/item.json +4 -0
- package/registry/hover-card/hover-card.tsx +144 -0
- package/registry/hover-card/item.json +4 -0
- package/registry/icons/icons.tsx +1691 -0
- package/registry/icons/item.json +4 -0
- package/registry/input/input.tsx +43 -0
- package/registry/input/item.json +4 -0
- package/registry/input-group/input-group.tsx +315 -0
- package/registry/input-group/item.json +4 -0
- package/registry/input-otp/input-otp.tsx +329 -0
- package/registry/input-otp/item.json +4 -0
- package/registry/item/item.json +4 -0
- package/registry/item/item.tsx +439 -0
- package/registry/kbd/item.json +4 -0
- package/registry/kbd/kbd.tsx +65 -0
- package/registry/label/item.json +4 -0
- package/registry/label/label.tsx +60 -0
- package/registry/marker/item.json +4 -0
- package/registry/marker/marker.tsx +175 -0
- package/registry/menubar/item.json +4 -0
- package/registry/menubar/menubar.tsx +694 -0
- package/registry/message/item.json +4 -0
- package/registry/message/message.tsx +280 -0
- package/registry/message-scroller/item.json +4 -0
- package/registry/message-scroller/message-scroller.tsx +262 -0
- package/registry/native-select/item.json +4 -0
- package/registry/native-select/native-select.tsx +113 -0
- package/registry/navigation-menu/item.json +4 -0
- package/registry/navigation-menu/navigation-menu.tsx +362 -0
- package/registry/pagination/item.json +4 -0
- package/registry/pagination/pagination.tsx +460 -0
- package/registry/password-input/item.json +5 -0
- package/registry/password-input/password-input.tsx +494 -0
- package/registry/popover/item.json +4 -0
- package/registry/popover/popover.tsx +284 -0
- package/registry/progress/item.json +4 -0
- package/registry/progress/progress.tsx +190 -0
- package/registry/questionnaire/item.json +4 -0
- package/registry/questionnaire/questionnaire.tsx +569 -0
- package/registry/radio-group/item.json +4 -0
- package/registry/radio-group/radio-group.tsx +101 -0
- package/registry/resizable/item.json +4 -0
- package/registry/resizable/resizable.tsx +455 -0
- package/registry/scroll-area/item.json +4 -0
- package/registry/scroll-area/scroll-area.tsx +149 -0
- package/registry/select/item.json +4 -0
- package/registry/select/select.tsx +571 -0
- package/registry/separator/item.json +4 -0
- package/registry/separator/separator.tsx +52 -0
- package/registry/sheet/item.json +4 -0
- package/registry/sheet/sheet.tsx +476 -0
- package/registry/sidebar/item.json +4 -0
- package/registry/sidebar/sidebar.tsx +1870 -0
- package/registry/skeleton/item.json +4 -0
- package/registry/skeleton/skeleton.tsx +47 -0
- package/registry/slider/item.json +4 -0
- package/registry/slider/slider.tsx +149 -0
- package/registry/spinner/item.json +4 -0
- package/registry/spinner/spinner.tsx +51 -0
- package/registry/switch/item.json +4 -0
- package/registry/switch/switch.tsx +93 -0
- package/registry/table/item.json +4 -0
- package/registry/table/table.tsx +373 -0
- package/registry/tabs/item.json +4 -0
- package/registry/tabs/tabs.tsx +209 -0
- package/registry/textarea/item.json +4 -0
- package/registry/textarea/textarea.tsx +51 -0
- package/registry/toast/item.json +4 -0
- package/registry/toast/toast.tsx +949 -0
- package/registry/toggle/item.json +4 -0
- package/registry/toggle/toggle.tsx +110 -0
- package/registry/toggle-group/item.json +4 -0
- package/registry/toggle-group/toggle-group.tsx +189 -0
- package/registry/tooltip/item.json +4 -0
- package/registry/tooltip/tooltip.tsx +169 -0
- package/registry/trace/item.json +4 -0
- package/registry/trace/trace.tsx +356 -0
- package/registry/ui-strings/item.json +5 -0
- package/registry/ui-strings/ui-strings.tsx +454 -0
- package/registry/use-highlighted-code/item.json +5 -0
- package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
- package/registry/use-mobile/item.json +5 -0
- package/registry/use-mobile/use-mobile.ts +46 -0
- package/registry/utils/item.json +5 -0
- package/registry/utils/utils.ts +38 -0
- package/registry.json +3274 -0
- package/styles/animations.css +321 -0
- package/styles/theme-core.css +55 -0
- package/styles/theme.css +12 -0
- package/styles/themes/gray.css +69 -0
- package/styles/themes/neutral.css +69 -0
- package/styles/themes/slate.css +69 -0
- package/styles/themes/stone.css +69 -0
- package/styles/themes/zinc.css +69 -0
- package/styles/typeset.css +251 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { applyTheme, isTheme, readStoredTheme, resolveTheme, writeStoredTheme } from "./theme-logic.js";
|
|
2
|
+
import { batch, createContext, onCleanup, onMount, untrack, useContext, useEffect, useMemo, useSignal } from "@shadow-js/core";
|
|
3
|
+
//#region src/theme/theme-provider.ts
|
|
4
|
+
const MISSING = {
|
|
5
|
+
theme: () => "system",
|
|
6
|
+
resolvedTheme: () => void 0,
|
|
7
|
+
systemTheme: () => void 0,
|
|
8
|
+
setTheme: () => void 0
|
|
9
|
+
};
|
|
10
|
+
const ThemeContext = createContext(MISSING, { name: "ShadowUITheme" });
|
|
11
|
+
const DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
12
|
+
/** Disables transitions until the next frame; returns nothing, cleans itself up. */
|
|
13
|
+
function suppressTransitions() {
|
|
14
|
+
const style = document.createElement("style");
|
|
15
|
+
style.textContent = "*,*::before,*::after{transition:none!important;animation-duration:0s!important}";
|
|
16
|
+
document.head.append(style);
|
|
17
|
+
window.getComputedStyle(document.body).getPropertyValue("opacity");
|
|
18
|
+
window.setTimeout(() => style.remove(), 1);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The page's `localStorage`, or `undefined` when the browser blocks it: reading the property
|
|
22
|
+
* itself throws a `DOMException` in some sandboxed and locked-down contexts.
|
|
23
|
+
*/
|
|
24
|
+
function localStorageOrUndefined() {
|
|
25
|
+
try {
|
|
26
|
+
return window.localStorage;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof DOMException) return void 0;
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Provides the color theme to the app: `"light"`, `"dark"` or `"system"`. The choice is persisted in
|
|
34
|
+
* `localStorage`, applied to `<html>` as a `light` or `dark` class (or a `data-*` attribute), and kept
|
|
35
|
+
* in sync across tabs and with the operating system. The provider renders no element of its own, and
|
|
36
|
+
* nothing touches the DOM until it has mounted, so it is safe to render on the server. Pair it with
|
|
37
|
+
* {@link themeScript} in the document head so the first paint already has the right theme.
|
|
38
|
+
*
|
|
39
|
+
* @param props The {@link ThemeProviderProps}: theme options (`storageKey`, `defaultTheme`,
|
|
40
|
+
* `attribute`, `enableSystem`), `disableTransitionOnChange` and `children`.
|
|
41
|
+
* @returns The children inside the theme context.
|
|
42
|
+
* @example
|
|
43
|
+
* import { ThemeProvider } from "@shadow-js/ui";
|
|
44
|
+
*
|
|
45
|
+
* function App() {
|
|
46
|
+
* return <p>Hello</p>;
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* export const root = (
|
|
50
|
+
* <ThemeProvider defaultTheme="system">
|
|
51
|
+
* <App />
|
|
52
|
+
* </ThemeProvider>
|
|
53
|
+
* );
|
|
54
|
+
* @see {@link useTheme}
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
*/
|
|
57
|
+
function ThemeProvider(props) {
|
|
58
|
+
const storageKey = () => props.storageKey ?? "shadow-ui-theme";
|
|
59
|
+
const defaultTheme = () => props.defaultTheme ?? "system";
|
|
60
|
+
const attribute = () => props.attribute ?? "class";
|
|
61
|
+
const enableSystem = () => props.enableSystem ?? true;
|
|
62
|
+
const [theme, setThemeState] = useSignal(untrack(defaultTheme));
|
|
63
|
+
const [systemDark, setSystemDark] = useSignal(void 0);
|
|
64
|
+
const resolved = useMemo(() => {
|
|
65
|
+
const dark = systemDark();
|
|
66
|
+
if (dark === void 0) return void 0;
|
|
67
|
+
return resolveTheme(theme(), dark, enableSystem());
|
|
68
|
+
});
|
|
69
|
+
const system = useMemo(() => {
|
|
70
|
+
const dark = systemDark();
|
|
71
|
+
return dark === void 0 ? void 0 : dark ? "dark" : "light";
|
|
72
|
+
});
|
|
73
|
+
onMount(() => {
|
|
74
|
+
const query = window.matchMedia(DARK_QUERY);
|
|
75
|
+
batch(() => {
|
|
76
|
+
const storage = localStorageOrUndefined();
|
|
77
|
+
const stored = storage === void 0 ? void 0 : readStoredTheme(storage, storageKey());
|
|
78
|
+
if (stored !== void 0) setThemeState(stored);
|
|
79
|
+
setSystemDark(query.matches);
|
|
80
|
+
});
|
|
81
|
+
const onSystemChange = (event) => {
|
|
82
|
+
setSystemDark(event.matches);
|
|
83
|
+
};
|
|
84
|
+
const onStorage = (event) => {
|
|
85
|
+
if (event.key !== storageKey()) return;
|
|
86
|
+
setThemeState(isTheme(event.newValue) ? event.newValue : defaultTheme());
|
|
87
|
+
};
|
|
88
|
+
query.addEventListener("change", onSystemChange);
|
|
89
|
+
window.addEventListener("storage", onStorage);
|
|
90
|
+
onCleanup(() => {
|
|
91
|
+
query.removeEventListener("change", onSystemChange);
|
|
92
|
+
window.removeEventListener("storage", onStorage);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const value = resolved();
|
|
97
|
+
if (value === void 0) return;
|
|
98
|
+
const attributeName = attribute();
|
|
99
|
+
const current = document.documentElement;
|
|
100
|
+
if (props.disableTransitionOnChange === true) suppressTransitions();
|
|
101
|
+
applyTheme(current, value, attributeName);
|
|
102
|
+
});
|
|
103
|
+
const value = {
|
|
104
|
+
theme,
|
|
105
|
+
resolvedTheme: resolved,
|
|
106
|
+
systemTheme: system,
|
|
107
|
+
setTheme(next) {
|
|
108
|
+
const chosen = typeof next === "function" ? next(untrack(theme)) : next;
|
|
109
|
+
if (!isTheme(chosen)) throw new TypeError(`setTheme: expected "light", "dark" or "system", received ${String(chosen)}`);
|
|
110
|
+
setThemeState(chosen);
|
|
111
|
+
const storage = localStorageOrUndefined();
|
|
112
|
+
if (storage !== void 0) writeStoredTheme(storage, untrack(storageKey), chosen);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
return ThemeContext.Provider({
|
|
116
|
+
value,
|
|
117
|
+
get children() {
|
|
118
|
+
return props.children;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Reads the theme and its setter. Call it inside a component rendered under {@link ThemeProvider}.
|
|
124
|
+
*
|
|
125
|
+
* @returns The {@link ThemeContextValue}: accessors for the chosen, the resolved and the system
|
|
126
|
+
* theme, and `setTheme`.
|
|
127
|
+
* @throws Error when no `ThemeProvider` is above the caller.
|
|
128
|
+
* @example
|
|
129
|
+
* import { useTheme } from "@shadow-js/ui";
|
|
130
|
+
*
|
|
131
|
+
* export function ThemeToggle() {
|
|
132
|
+
* const { resolvedTheme, setTheme } = useTheme();
|
|
133
|
+
* return (
|
|
134
|
+
* <button onClick={() => setTheme(resolvedTheme() === "dark" ? "light" : "dark")}>
|
|
135
|
+
* Toggle theme
|
|
136
|
+
* </button>
|
|
137
|
+
* );
|
|
138
|
+
* }
|
|
139
|
+
* @see {@link ThemeProvider}
|
|
140
|
+
* @since 1.0.0
|
|
141
|
+
*/
|
|
142
|
+
function useTheme() {
|
|
143
|
+
const value = useContext(ThemeContext);
|
|
144
|
+
if (value === MISSING) throw new Error("useTheme() must be used within <ThemeProvider>");
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { ThemeProvider, useTheme };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=theme-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-provider.js","names":[],"sources":["../../../src/theme/theme-provider.ts"],"sourcesContent":["import {\n type Accessor,\n batch,\n createContext,\n onCleanup,\n onMount,\n untrack,\n useContext,\n useEffect,\n useMemo,\n useSignal,\n} from \"@shadow-js/core\";\nimport type { JsxChild } from \"@shadow-js/web/jsx-runtime\";\nimport {\n applyTheme,\n DEFAULT_STORAGE_KEY,\n isTheme,\n type ResolvedTheme,\n readStoredTheme,\n resolveTheme,\n type Theme,\n type ThemeOptions,\n writeStoredTheme,\n} from \"./theme-logic\";\n\n/**\n * What {@link useTheme} returns. Every value is an accessor: call it to read.\n * @since 1.0.0\n */\nexport interface ThemeContextValue {\n /** The user's choice: `\"light\"`, `\"dark\"` or `\"system\"`. Call it to read the value. */\n readonly theme: Accessor<Theme>;\n /**\n * The theme that is shown: `\"light\"` or `\"dark\"`. `undefined` on the server and until the provider\n * has mounted, so markup that depends on it hydrates without a mismatch.\n */\n readonly resolvedTheme: Accessor<ResolvedTheme | undefined>;\n /**\n * The operating system preference: `\"light\"` or `\"dark\"`. `undefined` on the server and until the\n * provider has mounted.\n */\n readonly systemTheme: Accessor<ResolvedTheme | undefined>;\n /**\n * Chooses a theme, or derives one from the previous choice, and stores it under the provider's\n * `storageKey`. Throws a `TypeError` for a value that is not `\"light\"`, `\"dark\"` or `\"system\"`.\n */\n readonly setTheme: (theme: Theme | ((previous: Theme) => Theme)) => void;\n}\n\n/**\n * Props of {@link ThemeProvider}: the {@link ThemeOptions} plus a transition switch and the children.\n * @since 1.0.0\n */\nexport interface ThemeProviderProps extends ThemeOptions {\n /**\n * Stops CSS transitions for one frame while the theme changes, so colors flip at once instead\n * of fading piecemeal.\n * @default false\n */\n readonly disableTransitionOnChange?: boolean | undefined;\n /** The app to render inside the theme context. */\n readonly children?: JsxChild | undefined;\n}\n\nconst MISSING: ThemeContextValue = {\n theme: () => \"system\",\n resolvedTheme: () => undefined,\n systemTheme: () => undefined,\n setTheme: () => undefined,\n};\n\nconst ThemeContext = createContext<ThemeContextValue>(MISSING, { name: \"ShadowUITheme\" });\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\n/** Disables transitions until the next frame; returns nothing, cleans itself up. */\nfunction suppressTransitions(): void {\n const style = document.createElement(\"style\");\n style.textContent =\n \"*,*::before,*::after{transition:none!important;animation-duration:0s!important}\";\n document.head.append(style);\n // Reading a computed style forces the browser to apply the change with transitions off.\n window.getComputedStyle(document.body).getPropertyValue(\"opacity\");\n window.setTimeout(() => style.remove(), 1);\n}\n\n/**\n * The page's `localStorage`, or `undefined` when the browser blocks it: reading the property\n * itself throws a `DOMException` in some sandboxed and locked-down contexts.\n */\nfunction localStorageOrUndefined(): Storage | undefined {\n try {\n return window.localStorage;\n } catch (error) {\n if (error instanceof DOMException) return undefined;\n throw error;\n }\n}\n\n/**\n * Provides the color theme to the app: `\"light\"`, `\"dark\"` or `\"system\"`. The choice is persisted in\n * `localStorage`, applied to `<html>` as a `light` or `dark` class (or a `data-*` attribute), and kept\n * in sync across tabs and with the operating system. The provider renders no element of its own, and\n * nothing touches the DOM until it has mounted, so it is safe to render on the server. Pair it with\n * {@link themeScript} in the document head so the first paint already has the right theme.\n *\n * @param props The {@link ThemeProviderProps}: theme options (`storageKey`, `defaultTheme`,\n * `attribute`, `enableSystem`), `disableTransitionOnChange` and `children`.\n * @returns The children inside the theme context.\n * @example\n * import { ThemeProvider } from \"@shadow-js/ui\";\n *\n * function App() {\n * return <p>Hello</p>;\n * }\n *\n * export const root = (\n * <ThemeProvider defaultTheme=\"system\">\n * <App />\n * </ThemeProvider>\n * );\n * @see {@link useTheme}\n * @since 1.0.0\n */\nexport function ThemeProvider(props: ThemeProviderProps): JsxChild {\n const storageKey = (): string => props.storageKey ?? DEFAULT_STORAGE_KEY;\n const defaultTheme = (): Theme => props.defaultTheme ?? \"system\";\n const attribute = (): \"class\" | `data-${string}` => props.attribute ?? \"class\";\n const enableSystem = (): boolean => props.enableSystem ?? true;\n\n const [theme, setThemeState] = useSignal<Theme>(untrack(defaultTheme));\n const [systemDark, setSystemDark] = useSignal<boolean | undefined>(undefined);\n const resolved = useMemo((): ResolvedTheme | undefined => {\n const dark = systemDark();\n if (dark === undefined) return undefined;\n return resolveTheme(theme(), dark, enableSystem());\n });\n const system = useMemo((): ResolvedTheme | undefined => {\n const dark = systemDark();\n return dark === undefined ? undefined : dark ? \"dark\" : \"light\";\n });\n\n onMount(() => {\n const query = window.matchMedia(DARK_QUERY);\n batch(() => {\n const storage = localStorageOrUndefined();\n const stored = storage === undefined ? undefined : readStoredTheme(storage, storageKey());\n if (stored !== undefined) setThemeState(stored);\n setSystemDark(query.matches);\n });\n const onSystemChange = (event: MediaQueryListEvent): void => {\n setSystemDark(event.matches);\n };\n const onStorage = (event: StorageEvent): void => {\n if (event.key !== storageKey()) return;\n setThemeState(isTheme(event.newValue) ? event.newValue : defaultTheme());\n };\n query.addEventListener(\"change\", onSystemChange);\n window.addEventListener(\"storage\", onStorage);\n onCleanup(() => {\n query.removeEventListener(\"change\", onSystemChange);\n window.removeEventListener(\"storage\", onStorage);\n });\n });\n\n useEffect(() => {\n const value = resolved();\n if (value === undefined) return;\n const attributeName = attribute();\n const current = document.documentElement;\n if (props.disableTransitionOnChange === true) suppressTransitions();\n applyTheme(current, value, attributeName);\n });\n\n const value: ThemeContextValue = {\n theme,\n resolvedTheme: resolved,\n systemTheme: system,\n setTheme(next) {\n const chosen = typeof next === \"function\" ? next(untrack(theme)) : next;\n if (!isTheme(chosen)) {\n throw new TypeError(\n `setTheme: expected \"light\", \"dark\" or \"system\", received ${String(chosen)}`,\n );\n }\n setThemeState(chosen);\n const storage = localStorageOrUndefined();\n if (storage !== undefined) writeStoredTheme(storage, untrack(storageKey), chosen);\n },\n };\n\n return ThemeContext.Provider({\n value,\n get children() {\n return props.children;\n },\n }) as JsxChild;\n}\n\n/**\n * Reads the theme and its setter. Call it inside a component rendered under {@link ThemeProvider}.\n *\n * @returns The {@link ThemeContextValue}: accessors for the chosen, the resolved and the system\n * theme, and `setTheme`.\n * @throws Error when no `ThemeProvider` is above the caller.\n * @example\n * import { useTheme } from \"@shadow-js/ui\";\n *\n * export function ThemeToggle() {\n * const { resolvedTheme, setTheme } = useTheme();\n * return (\n * <button onClick={() => setTheme(resolvedTheme() === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle theme\n * </button>\n * );\n * }\n * @see {@link ThemeProvider}\n * @since 1.0.0\n */\nexport function useTheme(): ThemeContextValue {\n const value = useContext(ThemeContext);\n if (value === MISSING) throw new Error(\"useTheme() must be used within <ThemeProvider>\");\n return value;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cn as e}from"./registry/utils/utils.js";import{DEFAULT_STORAGE_KEY as t,applyTheme as n,isTheme as r,readStoredTheme as i,resolveTheme as a,themeScript as o,themeScriptTag as s,writeStoredTheme as c}from"./theme/theme-logic.js";import{ThemeProvider as l,useTheme as u}from"./theme/theme-provider.js";import{cva as d}from"class-variance-authority";export{t as DEFAULT_STORAGE_KEY,l as ThemeProvider,n as applyTheme,e as cn,d as cva,r as isTheme,i as readStoredTheme,a as resolveTheme,o as themeScript,s as themeScriptTag,u as useTheme,c as writeStoredTheme};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={breadcrumb:{label:`مسار التنقل`,more:`المزيد`},calendar:{month:`الشهر`,nextMonth:`الشهر التالي`,previousMonth:`الشهر السابق`,year:`السنة`},carousel:{goToSlide:`الانتقال إلى الشريحة {position}`,next:`الشريحة التالية`,previous:`الشريحة السابقة`,slide:`{position} من {total}`},chart:{announce:`{label}: {values}`,captionTruncated:`{series}. عرض {shown, number} من أصل {total, plural, zero {لا توجد نقاط بيانات} one {نقطة بيانات واحدة} two {نقطتي بيانات} few {# نقاط بيانات} many {# نقطة بيانات} other {# نقطة بيانات}}.`,label:{area:`مخطط مساحي لـ {series}`,bar:`مخطط أعمدة لـ {series}`,donut:`مخطط حلقي لـ {series}`,line:`مخطط خطي لـ {series}`,pie:`مخطط دائري لـ {series}`,radial:`مخطط شعاعي لـ {series}`},roledescription:`مخطط`,separator:`، `,share:`{name}: {value} ({percent})`,value:`{name}: {value}`},codeBlock:{copied:`تم النسخ`,copy:`نسخ الشيفرة`},combobox:{clear:`مسح`,toggle:`تبديل الاقتراحات`},command:{description:`ابحث عن أمر لتنفيذه...`,title:`لوحة الأوامر`},dataTable:{empty:`لا توجد نتائج.`,next:`التالي`,noPages:`لا توجد صفحات`,page:`الصفحة {page} من {pages}`,previous:`السابق`,view:`عرض`},datePicker:{open:`فتح التقويم`},dialog:{close:`إغلاق`},drawer:{handle:`اسحب لتغيير الحجم`},form:{errorSummary:{item:`{label}: {message}`,title:`{count, plural, zero {لا توجد مشكلات في النموذج} one {هناك مشكلة واحدة في النموذج} two {هناك مشكلتان في النموذج} few {هناك # مشكلات في النموذج} many {هناك # مشكلة في النموذج} other {هناك # مشكلة في النموذج}}`}},inputOtp:{character:`الحرف {position} من {total}`,digit:`الرقم {position} من {total}`},messageScroller:{label:`الرسائل`,toEnd:`الانتقال إلى أحدث رسالة`,toStart:`الانتقال إلى البداية`},pagination:{goNext:`الانتقال إلى الصفحة التالية`,goPrevious:`الانتقال إلى الصفحة السابقة`,goToPage:`الانتقال إلى الصفحة {page}`,label:`ترقيم الصفحات`,next:`التالي`,previous:`السابق`},password:{capsLock:`مفتاح Caps Lock مفعّل`,level:{0:`ضعيفة جداً`,1:`ضعيفة`,2:`مقبولة`,3:`جيدة`,4:`قوية`},show:`إظهار كلمة المرور`,strength:`قوة كلمة المرور`},questionnaire:{next:`التالي`,optional:`أجب عن هذا السؤال، أو تخطَّه، للمتابعة.`,previous:`السابق`,progress:`السؤال {current} من {total}`,progressLabel:`تقدم الاستبيان`,required:`أجب عن هذا السؤال للمتابعة.`,skip:`تخطَّ`,submit:`إرسال`},resizable:{handle:`تغيير حجم اللوحات`},sheet:{close:`إغلاق`},sidebar:{mobileDescription:`يعرض الشريط الجانبي للجوال.`,title:`الشريط الجانبي`,toggle:`تبديل الشريط الجانبي`},spinner:{loading:`جارٍ التحميل`},toast:{action:`إجراء`,close:`إغلاق الإشعار`,notifications:`الإشعارات`}};export{e as default};
|
|
2
|
+
//# sourceMappingURL=ar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ar.js","names":["ar"],"sources":["../../../src/locales/ar.ts"],"sourcesContent":["/**\n * The Arabic messages of the UI kit, as a catalog of messages: every label, description and live-region text the components show, keyed\n * by component. Pass it (or a copy with your own words) to the strings provider of your project, or into a `createI18n` catalog.\n *\n * @example\n * import ar from \"@shadow-js/ui/locales/ar\";\n *\n * console.log(typeof ar.toast.close);\n * @since 1.0.0\n */\nconst ar = {\n breadcrumb: {\n label: \"مسار التنقل\",\n more: \"المزيد\",\n },\n calendar: {\n month: \"الشهر\",\n nextMonth: \"الشهر التالي\",\n previousMonth: \"الشهر السابق\",\n year: \"السنة\",\n },\n carousel: {\n goToSlide: \"الانتقال إلى الشريحة {position}\",\n next: \"الشريحة التالية\",\n previous: \"الشريحة السابقة\",\n slide: \"{position} من {total}\",\n },\n chart: {\n announce: \"{label}: {values}\",\n captionTruncated:\n \"{series}. عرض {shown, number} من أصل {total, plural, zero {لا توجد نقاط بيانات} one {نقطة بيانات واحدة} two {نقطتي بيانات} few {# نقاط بيانات} many {# نقطة بيانات} other {# نقطة بيانات}}.\",\n label: {\n area: \"مخطط مساحي لـ {series}\",\n bar: \"مخطط أعمدة لـ {series}\",\n donut: \"مخطط حلقي لـ {series}\",\n line: \"مخطط خطي لـ {series}\",\n pie: \"مخطط دائري لـ {series}\",\n radial: \"مخطط شعاعي لـ {series}\",\n },\n roledescription: \"مخطط\",\n separator: \"، \",\n share: \"{name}: {value} ({percent})\",\n value: \"{name}: {value}\",\n },\n codeBlock: {\n copied: \"تم النسخ\",\n copy: \"نسخ الشيفرة\",\n },\n combobox: {\n clear: \"مسح\",\n toggle: \"تبديل الاقتراحات\",\n },\n command: {\n description: \"ابحث عن أمر لتنفيذه...\",\n title: \"لوحة الأوامر\",\n },\n dataTable: {\n empty: \"لا توجد نتائج.\",\n next: \"التالي\",\n noPages: \"لا توجد صفحات\",\n page: \"الصفحة {page} من {pages}\",\n previous: \"السابق\",\n view: \"عرض\",\n },\n datePicker: {\n open: \"فتح التقويم\",\n },\n dialog: {\n close: \"إغلاق\",\n },\n drawer: {\n handle: \"اسحب لتغيير الحجم\",\n },\n form: {\n errorSummary: {\n item: \"{label}: {message}\",\n title:\n \"{count, plural, zero {لا توجد مشكلات في النموذج} one {هناك مشكلة واحدة في النموذج} two {هناك مشكلتان في النموذج} few {هناك # مشكلات في النموذج} many {هناك # مشكلة في النموذج} other {هناك # مشكلة في النموذج}}\",\n },\n },\n inputOtp: {\n character: \"الحرف {position} من {total}\",\n digit: \"الرقم {position} من {total}\",\n },\n messageScroller: {\n label: \"الرسائل\",\n toEnd: \"الانتقال إلى أحدث رسالة\",\n toStart: \"الانتقال إلى البداية\",\n },\n pagination: {\n goNext: \"الانتقال إلى الصفحة التالية\",\n goPrevious: \"الانتقال إلى الصفحة السابقة\",\n goToPage: \"الانتقال إلى الصفحة {page}\",\n label: \"ترقيم الصفحات\",\n next: \"التالي\",\n previous: \"السابق\",\n },\n password: {\n capsLock: \"مفتاح Caps Lock مفعّل\",\n level: {\n \"0\": \"ضعيفة جداً\",\n \"1\": \"ضعيفة\",\n \"2\": \"مقبولة\",\n \"3\": \"جيدة\",\n \"4\": \"قوية\",\n },\n show: \"إظهار كلمة المرور\",\n strength: \"قوة كلمة المرور\",\n },\n questionnaire: {\n next: \"التالي\",\n optional: \"أجب عن هذا السؤال، أو تخطَّه، للمتابعة.\",\n previous: \"السابق\",\n progress: \"السؤال {current} من {total}\",\n progressLabel: \"تقدم الاستبيان\",\n required: \"أجب عن هذا السؤال للمتابعة.\",\n skip: \"تخطَّ\",\n submit: \"إرسال\",\n },\n resizable: {\n handle: \"تغيير حجم اللوحات\",\n },\n sheet: {\n close: \"إغلاق\",\n },\n sidebar: {\n mobileDescription: \"يعرض الشريط الجانبي للجوال.\",\n title: \"الشريط الجانبي\",\n toggle: \"تبديل الشريط الجانبي\",\n },\n spinner: {\n loading: \"جارٍ التحميل\",\n },\n toast: {\n action: \"إجراء\",\n close: \"إغلاق الإشعار\",\n notifications: \"الإشعارات\",\n },\n} as const;\n\nexport default ar;\n"],"mappings":"AAUA,MAAMA,EAAK,CACT,WAAY,CACV,MAAO,cACP,KAAM,QACR,EACA,SAAU,CACR,MAAO,QACP,UAAW,eACX,cAAe,eACf,KAAM,OACR,EACA,SAAU,CACR,UAAW,kCACX,KAAM,kBACN,SAAU,kBACV,MAAO,uBACT,EACA,MAAO,CACL,SAAU,oBACV,iBACE,8LACF,MAAO,CACL,KAAM,yBACN,IAAK,yBACL,MAAO,wBACP,KAAM,uBACN,IAAK,yBACL,OAAQ,wBACV,EACA,gBAAiB,OACjB,UAAW,KACX,MAAO,8BACP,MAAO,iBACT,EACA,UAAW,CACT,OAAQ,WACR,KAAM,aACR,EACA,SAAU,CACR,MAAO,MACP,OAAQ,kBACV,EACA,QAAS,CACP,YAAa,yBACb,MAAO,cACT,EACA,UAAW,CACT,MAAO,iBACP,KAAM,SACN,QAAS,gBACT,KAAM,2BACN,SAAU,SACV,KAAM,KACR,EACA,WAAY,CACV,KAAM,aACR,EACA,OAAQ,CACN,MAAO,OACT,EACA,OAAQ,CACN,OAAQ,mBACV,EACA,KAAM,CACJ,aAAc,CACZ,KAAM,qBACN,MACE,iNACJ,CACF,EACA,SAAU,CACR,UAAW,8BACX,MAAO,6BACT,EACA,gBAAiB,CACf,MAAO,UACP,MAAO,0BACP,QAAS,sBACX,EACA,WAAY,CACV,OAAQ,8BACR,WAAY,8BACZ,SAAU,6BACV,MAAO,gBACP,KAAM,SACN,SAAU,QACZ,EACA,SAAU,CACR,SAAU,wBACV,MAAO,CACL,EAAK,aACL,EAAK,QACL,EAAK,SACL,EAAK,OACL,EAAK,MACP,EACA,KAAM,oBACN,SAAU,iBACZ,EACA,cAAe,CACb,KAAM,SACN,SAAU,0CACV,SAAU,SACV,SAAU,8BACV,cAAe,iBACf,SAAU,8BACV,KAAM,QACN,OAAQ,OACV,EACA,UAAW,CACT,OAAQ,mBACV,EACA,MAAO,CACL,MAAO,OACT,EACA,QAAS,CACP,kBAAmB,8BACnB,MAAO,iBACP,OAAQ,sBACV,EACA,QAAS,CACP,QAAS,cACX,EACA,MAAO,CACL,OAAQ,QACR,MAAO,gBACP,cAAe,WACjB,CACF,E"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={breadcrumb:{label:`breadcrumb`,more:`More`},calendar:{month:`Month`,nextMonth:`Next month`,previousMonth:`Previous month`,year:`Year`},carousel:{goToSlide:`Go to slide {position}`,next:`Next slide`,previous:`Previous slide`,slide:`{position} of {total}`},chart:{announce:`{label}: {values}`,captionTruncated:`{series}. Showing {shown, number} of {total, plural, one {# data point} other {# data points}}.`,label:{area:`Area chart of {series}`,bar:`Bar chart of {series}`,donut:`Donut chart of {series}`,line:`Line chart of {series}`,pie:`Pie chart of {series}`,radial:`Radial chart of {series}`},roledescription:`chart`,separator:`, `,share:`{name}: {value} ({percent})`,value:`{name}: {value}`},codeBlock:{copied:`Copied`,copy:`Copy code`},combobox:{clear:`Clear`,toggle:`Toggle suggestions`},command:{description:`Search for a command to run...`,title:`Command Palette`},dataTable:{empty:`No results.`,next:`Next`,noPages:`No pages`,page:`Page {page} of {pages}`,previous:`Previous`,view:`View`},datePicker:{open:`Open calendar`},dialog:{close:`Close`},drawer:{handle:`Drag to resize`},form:{errorSummary:{item:`{label}: {message}`,title:`{count, plural, one {There is # problem with your submission} other {There are # problems with your submission}}`}},inputOtp:{character:`Character {position} of {total}`,digit:`Digit {position} of {total}`},messageScroller:{label:`Messages`,toEnd:`Scroll to latest`,toStart:`Scroll to start`},pagination:{goNext:`Go to next page`,goPrevious:`Go to previous page`,goToPage:`Go to page {page}`,label:`pagination`,next:`Next`,previous:`Previous`},password:{capsLock:`Caps Lock is on`,level:{0:`Very weak`,1:`Weak`,2:`Fair`,3:`Good`,4:`Strong`},show:`Show password`,strength:`Password strength`},questionnaire:{next:`Next`,optional:`Answer this question, or skip it, to continue.`,previous:`Back`,progress:`Question {current} of {total}`,progressLabel:`Questionnaire progress`,required:`Answer this question to continue.`,skip:`Skip`,submit:`Submit`},resizable:{handle:`Resize panels`},sheet:{close:`Close`},sidebar:{mobileDescription:`Displays the mobile sidebar.`,title:`Sidebar`,toggle:`Toggle Sidebar`},spinner:{loading:`Loading`},toast:{action:`Action`,close:`Close toast`,notifications:`Notifications`}};export{e as default};
|
|
2
|
+
//# sourceMappingURL=en.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"en.js","names":["en"],"sources":["../../../src/locales/en.ts"],"sourcesContent":["/**\n * The English messages of the UI kit, as a catalog of messages: every label, description and live-region text the components show, keyed\n * by component. Pass it (or a copy with your own words) to the strings provider of your project, or into a `createI18n` catalog.\n *\n * @example\n * import en from \"@shadow-js/ui/locales/en\";\n *\n * console.log(typeof en.toast.close);\n * @since 1.0.0\n */\nconst en = {\n breadcrumb: {\n label: \"breadcrumb\",\n more: \"More\",\n },\n calendar: {\n month: \"Month\",\n nextMonth: \"Next month\",\n previousMonth: \"Previous month\",\n year: \"Year\",\n },\n carousel: {\n goToSlide: \"Go to slide {position}\",\n next: \"Next slide\",\n previous: \"Previous slide\",\n slide: \"{position} of {total}\",\n },\n chart: {\n announce: \"{label}: {values}\",\n captionTruncated:\n \"{series}. Showing {shown, number} of {total, plural, one {# data point} other {# data points}}.\",\n label: {\n area: \"Area chart of {series}\",\n bar: \"Bar chart of {series}\",\n donut: \"Donut chart of {series}\",\n line: \"Line chart of {series}\",\n pie: \"Pie chart of {series}\",\n radial: \"Radial chart of {series}\",\n },\n roledescription: \"chart\",\n separator: \", \",\n share: \"{name}: {value} ({percent})\",\n value: \"{name}: {value}\",\n },\n codeBlock: {\n copied: \"Copied\",\n copy: \"Copy code\",\n },\n combobox: {\n clear: \"Clear\",\n toggle: \"Toggle suggestions\",\n },\n command: {\n description: \"Search for a command to run...\",\n title: \"Command Palette\",\n },\n dataTable: {\n empty: \"No results.\",\n next: \"Next\",\n noPages: \"No pages\",\n page: \"Page {page} of {pages}\",\n previous: \"Previous\",\n view: \"View\",\n },\n datePicker: {\n open: \"Open calendar\",\n },\n dialog: {\n close: \"Close\",\n },\n drawer: {\n handle: \"Drag to resize\",\n },\n form: {\n errorSummary: {\n item: \"{label}: {message}\",\n title:\n \"{count, plural, one {There is # problem with your submission} other {There are # problems with your submission}}\",\n },\n },\n inputOtp: {\n character: \"Character {position} of {total}\",\n digit: \"Digit {position} of {total}\",\n },\n messageScroller: {\n label: \"Messages\",\n toEnd: \"Scroll to latest\",\n toStart: \"Scroll to start\",\n },\n pagination: {\n goNext: \"Go to next page\",\n goPrevious: \"Go to previous page\",\n goToPage: \"Go to page {page}\",\n label: \"pagination\",\n next: \"Next\",\n previous: \"Previous\",\n },\n password: {\n capsLock: \"Caps Lock is on\",\n level: {\n \"0\": \"Very weak\",\n \"1\": \"Weak\",\n \"2\": \"Fair\",\n \"3\": \"Good\",\n \"4\": \"Strong\",\n },\n show: \"Show password\",\n strength: \"Password strength\",\n },\n questionnaire: {\n next: \"Next\",\n optional: \"Answer this question, or skip it, to continue.\",\n previous: \"Back\",\n progress: \"Question {current} of {total}\",\n progressLabel: \"Questionnaire progress\",\n required: \"Answer this question to continue.\",\n skip: \"Skip\",\n submit: \"Submit\",\n },\n resizable: {\n handle: \"Resize panels\",\n },\n sheet: {\n close: \"Close\",\n },\n sidebar: {\n mobileDescription: \"Displays the mobile sidebar.\",\n title: \"Sidebar\",\n toggle: \"Toggle Sidebar\",\n },\n spinner: {\n loading: \"Loading\",\n },\n toast: {\n action: \"Action\",\n close: \"Close toast\",\n notifications: \"Notifications\",\n },\n} as const;\n\nexport default en;\n"],"mappings":"AAUA,MAAMA,EAAK,CACT,WAAY,CACV,MAAO,aACP,KAAM,MACR,EACA,SAAU,CACR,MAAO,QACP,UAAW,aACX,cAAe,iBACf,KAAM,MACR,EACA,SAAU,CACR,UAAW,yBACX,KAAM,aACN,SAAU,iBACV,MAAO,uBACT,EACA,MAAO,CACL,SAAU,oBACV,iBACE,kGACF,MAAO,CACL,KAAM,yBACN,IAAK,wBACL,MAAO,0BACP,KAAM,yBACN,IAAK,wBACL,OAAQ,0BACV,EACA,gBAAiB,QACjB,UAAW,KACX,MAAO,8BACP,MAAO,iBACT,EACA,UAAW,CACT,OAAQ,SACR,KAAM,WACR,EACA,SAAU,CACR,MAAO,QACP,OAAQ,oBACV,EACA,QAAS,CACP,YAAa,iCACb,MAAO,iBACT,EACA,UAAW,CACT,MAAO,cACP,KAAM,OACN,QAAS,WACT,KAAM,yBACN,SAAU,WACV,KAAM,MACR,EACA,WAAY,CACV,KAAM,eACR,EACA,OAAQ,CACN,MAAO,OACT,EACA,OAAQ,CACN,OAAQ,gBACV,EACA,KAAM,CACJ,aAAc,CACZ,KAAM,qBACN,MACE,kHACJ,CACF,EACA,SAAU,CACR,UAAW,kCACX,MAAO,6BACT,EACA,gBAAiB,CACf,MAAO,WACP,MAAO,mBACP,QAAS,iBACX,EACA,WAAY,CACV,OAAQ,kBACR,WAAY,sBACZ,SAAU,oBACV,MAAO,aACP,KAAM,OACN,SAAU,UACZ,EACA,SAAU,CACR,SAAU,kBACV,MAAO,CACL,EAAK,YACL,EAAK,OACL,EAAK,OACL,EAAK,OACL,EAAK,QACP,EACA,KAAM,gBACN,SAAU,mBACZ,EACA,cAAe,CACb,KAAM,OACN,SAAU,iDACV,SAAU,OACV,SAAU,gCACV,cAAe,yBACf,SAAU,oCACV,KAAM,OACN,OAAQ,QACV,EACA,UAAW,CACT,OAAQ,eACV,EACA,MAAO,CACL,MAAO,OACT,EACA,QAAS,CACP,kBAAmB,+BACnB,MAAO,UACP,OAAQ,gBACV,EACA,QAAS,CACP,QAAS,SACX,EACA,MAAO,CACL,OAAQ,SACR,MAAO,cACP,cAAe,eACjB,CACF,E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":["cn","inputs","twMerge","clsx"],"sources":["../../../../registry/utils/utils.ts"],"sourcesContent":["import { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Anything a ShadowJS `class` attribute accepts: strings, numbers, nested (readonly) arrays and\n * `{ name: condition }` objects. Falsy values are skipped.\n * @since 1.0.0\n */\nexport type ClassValue =\n | string\n | number\n | bigint\n | boolean\n | null\n | undefined\n | readonly ClassValue[]\n | { readonly [name: string]: unknown };\n\n/**\n * Joins class names (strings, arrays, `{ name: condition }` objects) and resolves Tailwind\n * conflicts, so the last utility wins: `cn(\"px-2\", props.class)` lets the caller override `px-2`.\n *\n * @param inputs - Any mix of class values; falsy values are skipped.\n * @returns One class string with conflicting Tailwind utilities removed.\n * @example\n * ```ts\n * import { cn } from \"@/lib/utils\";\n *\n * const active = true;\n * export const classes = cn(\"px-2 py-1\", active && \"bg-primary\", \"px-4\");\n * // \"py-1 bg-primary px-4\"\n * ```\n * @since 1.0.0\n * @see {@link ClassValue}\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(...(inputs as Parameters<typeof clsx>)));\n}\n"],"mappings":"A,qEAmCA,SAAgBA,EAAG,GAAGC,EAA8B,CAClD,OAAOC,EAAQC,EAAK,GAAIF,CAAkC,CAAC,CAC7D,C"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`shadow-ui-theme`;function t(e){return e===`light`||e===`dark`||e===`system`}function n(e,t,n=!0){return e===`system`?n&&t?`dark`:`light`:e}function r(e,t,n=`class`){n===`class`?(e.classList.remove(`light`,`dark`),e.classList.add(t)):e.setAttribute(n,t),e.style.colorScheme=t}function i(e){return e instanceof DOMException&&(e.name===`SecurityError`||e.name===`QuotaExceededError`||e.name===`NotAllowedError`)}function a(e,n){try{let r=e.getItem(n);return t(r)?r:void 0}catch(e){if(i(e))return;throw e}}function o(e,t,n){try{return e.setItem(t,n),!0}catch(e){if(i(e))return!1;throw e}}function s(e={}){let t=e.storageKey??`shadow-ui-theme`,n=e.defaultTheme??`system`,r=e.attribute??`class`,i=e.enableSystem??!0,a=e=>JSON.stringify(e).replace(/</g,`\\u003c`);return[`(function(){`,`var k=${a(t)},f=${a(n)},a=${a(r)},s=${a(i)};`,`var r=document.documentElement;`,`function p(t){if(t==='system')return s&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';return t}`,`function w(m){if(a==='class'){r.classList.remove('light','dark');r.classList.add(m)}else{r.setAttribute(a,m)}r.style.colorScheme=m}`,`try{var t=localStorage.getItem(k);if(t!=='light'&&t!=='dark'&&t!=='system')t=f;w(p(t))}`,`catch(e){w(p(f))}`,`})();`].join(``)}function c(e={}){return`<script${e.nonce===void 0?``:` nonce="${e.nonce.replace(/[^A-Za-z0-9+/=_-]/g,``)}"`}>${s(e)}<\/script>`}export{e as DEFAULT_STORAGE_KEY,r as applyTheme,t as isTheme,a as readStoredTheme,n as resolveTheme,s as themeScript,c as themeScriptTag,o as writeStoredTheme};
|
|
2
|
+
//# sourceMappingURL=theme-logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-logic.js","names":["DEFAULT_STORAGE_KEY","isTheme","value","resolveTheme","theme","enableSystem","systemPrefersDark","applyTheme","attribute","root","resolved","isStorageUnavailable","error","readStoredTheme","storage","key","writeStoredTheme","themeScript","options","fallback","system","literal","themeScriptTag"],"sources":["../../../src/theme/theme-logic.ts"],"sourcesContent":["/**\n * Pure theme logic, shared by the provider and the no-flash script so both agree on what\n * \"system\", \"light\" and \"dark\" mean. Nothing here touches `window` unless it is handed an object.\n * @packageDocumentation\n */\n\n/**\n * The theme a user picks: `\"light\"`, `\"dark\"`, or `\"system\"` to follow the operating system.\n * @since 1.0.0\n */\nexport type Theme = \"light\" | \"dark\" | \"system\";\n\n/**\n * The theme that is actually shown: `\"light\"` or `\"dark\"`.\n * @since 1.0.0\n */\nexport type ResolvedTheme = \"light\" | \"dark\";\n\n/**\n * Where the resolved theme is written on the root element. `\"class\"` (the default) sets a `light`\n * or `dark` class, which the generated stylesheet matches for its dark styles (the `dark` variant of\n * Tailwind is `.dark`). A `data-*` name such as `\"data-theme\"` sets that attribute to `\"light\"` or\n * `\"dark\"` instead; the stylesheet then has to match that attribute as well.\n * @since 1.0.0\n */\nexport type ThemeAttribute = \"class\" | `data-${string}`;\n\n/**\n * Options shared by `ThemeProvider` and `themeScript`. Pass the same values to both, or the page can\n * show one theme before the provider switches to another.\n * @since 1.0.0\n */\nexport interface ThemeOptions {\n /**\n * `localStorage` key that persists the choice. The stored value is `\"light\"`, `\"dark\"` or\n * `\"system\"`; a missing or invalid value falls back to `defaultTheme`.\n * @default \"shadow-ui-theme\"\n */\n readonly storageKey?: string | undefined;\n /**\n * Theme used before the user chooses one, and when the stored value is missing or invalid.\n * @default \"system\"\n */\n readonly defaultTheme?: Theme | undefined;\n /**\n * Where the resolved theme is written: `\"class\"` for a `light` or `dark` class on `<html>`, or a\n * `data-*` attribute name such as `\"data-theme\"` (see {@link ThemeAttribute}).\n * @default \"class\"\n */\n readonly attribute?: ThemeAttribute | undefined;\n /**\n * Follow `prefers-color-scheme` while the theme is `\"system\"`. When `false`, `\"system\"` shows light.\n * @default true\n */\n readonly enableSystem?: boolean | undefined;\n}\n\n/**\n * The `localStorage` key that holds the theme choice when `storageKey` is not given.\n * @example\n * import { DEFAULT_STORAGE_KEY } from \"@shadow-js/ui\";\n *\n * window.localStorage.getItem(DEFAULT_STORAGE_KEY);\n * @since 1.0.0\n */\nexport const DEFAULT_STORAGE_KEY = \"shadow-ui-theme\";\n\n/**\n * Narrows an unknown value to a {@link Theme}. Use it on anything read from storage or from an event\n * before trusting it.\n *\n * @param value Anything, such as a value read from storage.\n * @returns `true` for `\"light\"`, `\"dark\"` and `\"system\"`, and `false` for everything else.\n * @example\n * import { DEFAULT_STORAGE_KEY, isTheme } from \"@shadow-js/ui\";\n *\n * isTheme(window.localStorage.getItem(DEFAULT_STORAGE_KEY)); // false when nothing is stored\n * @since 1.0.0\n */\nexport function isTheme(value: unknown): value is Theme {\n return value === \"light\" || value === \"dark\" || value === \"system\";\n}\n\n/**\n * Turns a theme choice into the theme to show. Only `\"system\"` depends on the operating system.\n *\n * @param theme The user's choice.\n * @param systemPrefersDark Whether the operating system prefers a dark scheme.\n * @param enableSystem When `false`, `\"system\"` resolves to light. Default `true`.\n * @returns `\"light\"` or `\"dark\"`.\n * @example\n * import { resolveTheme } from \"@shadow-js/ui\";\n *\n * resolveTheme(\"system\", true); // \"dark\"\n * @since 1.0.0\n */\nexport function resolveTheme(\n theme: Theme,\n systemPrefersDark: boolean,\n enableSystem = true,\n): ResolvedTheme {\n if (theme === \"system\") return enableSystem && systemPrefersDark ? \"dark\" : \"light\";\n return theme;\n}\n\n/**\n * Writes the resolved theme to the root element: sets the `light` or `dark` class (removing the\n * other) or the `data-*` attribute, and sets `color-scheme` so form controls and scrollbars follow.\n *\n * @param root The element to write to, usually `document.documentElement`.\n * @param resolved The theme to show.\n * @param attribute Where to write it: `\"class\"` (the default) or a `data-*` attribute name.\n * @example\n * import { applyTheme } from \"@shadow-js/ui\";\n *\n * applyTheme(document.documentElement, \"dark\");\n * @since 1.0.0\n */\nexport function applyTheme(\n root: HTMLElement,\n resolved: ResolvedTheme,\n attribute: ThemeAttribute = \"class\",\n): void {\n if (attribute === \"class\") {\n root.classList.remove(\"light\", \"dark\");\n root.classList.add(resolved);\n } else {\n root.setAttribute(attribute, resolved);\n }\n root.style.colorScheme = resolved;\n}\n\n/** The storage error names a locked-down browser raises; anything else is a bug and propagates. */\nfunction isStorageUnavailable(error: unknown): boolean {\n return (\n error instanceof DOMException &&\n (error.name === \"SecurityError\" ||\n error.name === \"QuotaExceededError\" ||\n error.name === \"NotAllowedError\")\n );\n}\n\n/**\n * Reads the stored theme. A missing or invalid value gives `undefined`. A storage the browser\n * blocks (private windows, disabled site data) or has run out of room for also gives `undefined`:\n * those raise a `SecurityError`, `NotAllowedError` or `QuotaExceededError` `DOMException`.\n *\n * @param storage The storage to read, usually `window.localStorage`.\n * @param key The storage key, such as {@link DEFAULT_STORAGE_KEY}.\n * @returns The stored theme, or `undefined` when it is missing, invalid or unavailable.\n * @throws The original error when it is not one of the storage errors above, because that is a bug.\n * @example\n * import { DEFAULT_STORAGE_KEY, readStoredTheme } from \"@shadow-js/ui\";\n *\n * readStoredTheme(window.localStorage, DEFAULT_STORAGE_KEY);\n * @since 1.0.0\n */\nexport function readStoredTheme(storage: Storage, key: string): Theme | undefined {\n try {\n const value = storage.getItem(key);\n return isTheme(value) ? value : undefined;\n } catch (error) {\n if (isStorageUnavailable(error)) return undefined;\n throw error;\n }\n}\n\n/**\n * Stores the theme under the key. A storage that is blocked or full does not throw: the function\n * returns `false` instead.\n *\n * @param storage The storage to write, usually `window.localStorage`.\n * @param key The storage key, such as {@link DEFAULT_STORAGE_KEY}.\n * @param theme The theme to store.\n * @returns `true` when the value was written, `false` when the storage refused it.\n * @throws The original error when it is not one of the storage errors listed in {@link readStoredTheme}.\n * @example\n * import { DEFAULT_STORAGE_KEY, writeStoredTheme } from \"@shadow-js/ui\";\n *\n * writeStoredTheme(window.localStorage, DEFAULT_STORAGE_KEY, \"dark\");\n * @since 1.0.0\n */\nexport function writeStoredTheme(storage: Storage, key: string, theme: Theme): boolean {\n try {\n storage.setItem(key, theme);\n return true;\n } catch (error) {\n if (isStorageUnavailable(error)) return false;\n throw error;\n }\n}\n\n/**\n * The inline script that applies the stored theme before the first paint, so a dark-mode user does\n * not see a flash of the light theme. Put the returned text in a `<script>` in the document\n * `<head>`: a `<script>` in `index.html`, or the `document.head` option of Dusk's `createHandler`.\n * An inline script in the head runs before the body is rendered. Pass the same options to\n * `ThemeProvider`.\n *\n * The script is plain ES5 with no dependencies. It reads the stored theme, falls back to\n * `defaultTheme` when the value is missing or invalid or when storage is blocked, and writes the same\n * class or attribute and `color-scheme` as {@link applyTheme}. The function returns text only, so you\n * place the tag yourself. {@link themeScriptTag} returns the same script in a tag with a CSP `nonce`.\n *\n * @param options Must match the `ThemeProvider` options.\n * @returns JavaScript source text, without `<script>` tags.\n * @example\n * import { themeScript } from \"@shadow-js/ui\";\n *\n * const html = `<script>${themeScript({ defaultTheme: \"dark\" })}</script>`;\n * @see {@link themeScriptTag}\n * @since 1.0.0\n */\nexport function themeScript(options: ThemeOptions = {}): string {\n const key = options.storageKey ?? DEFAULT_STORAGE_KEY;\n const fallback = options.defaultTheme ?? \"system\";\n const attribute = options.attribute ?? \"class\";\n const system = options.enableSystem ?? true;\n // `<` is escaped so a stored value can never close the surrounding <script> element.\n const literal = (value: unknown): string => JSON.stringify(value).replace(/</g, \"\\\\u003c\");\n return [\n \"(function(){\",\n `var k=${literal(key)},f=${literal(fallback)},a=${literal(attribute)},s=${literal(system)};`,\n \"var r=document.documentElement;\",\n \"function p(t){if(t==='system')return s&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';return t}\",\n \"function w(m){if(a==='class'){r.classList.remove('light','dark');r.classList.add(m)}else{r.setAttribute(a,m)}r.style.colorScheme=m}\",\n \"try{var t=localStorage.getItem(k);if(t!=='light'&&t!=='dark'&&t!=='system')t=f;w(p(t))}\",\n \"catch(e){w(p(f))}\",\n \"})();\",\n ].join(\"\");\n}\n\n/**\n * {@link themeScript} wrapped in a `<script>` element, as an HTML string, for a server that builds the\n * document. Pass the page's CSP nonce so the inline script is allowed by a policy that uses nonces. In\n * Dusk, the nonce of each response is `event.nonce`, available in the `document.head` function.\n * Only base64 characters (`A-Z`, `a-z`, `0-9`, `+`, `/`, `=`, `_`, `-`) are kept from the nonce.\n *\n * @param options The theme options plus the CSP `nonce` of the page. Without a nonce the tag has no\n * `nonce` attribute.\n * @returns A `<script>` element as HTML text.\n * @example\n * import { createHandler, type DuskRuntimeManifest } from \"@shadow-js/dusk/server\";\n * import { pages } from \"@shadow-js/dusk/features/pages\";\n * import { themeScriptTag } from \"@shadow-js/ui\";\n *\n * declare const manifest: DuskRuntimeManifest;\n *\n * export const handler = createHandler({\n * features: [pages()],\n * manifest,\n * security: { csp: {} },\n * document: { head: (event) => themeScriptTag({ nonce: event.nonce }) },\n * });\n * @see {@link themeScript}\n * @since 1.0.0\n */\nexport function themeScriptTag(\n options: ThemeOptions & { readonly nonce?: string | undefined } = {},\n): string {\n const nonce =\n options.nonce === undefined\n ? \"\"\n : ` nonce=\"${options.nonce.replace(/[^A-Za-z0-9+/=_-]/g, \"\")}\"`;\n return `<script${nonce}>${themeScript(options)}</script>`;\n}\n"],"mappings":"AAiEA,MAAaA,EAAsB,kBAcnC,SAAgBC,EAAQ,EAAgC,CACtD,OAAOC,IAAU,SAAWA,IAAU,QAAUA,IAAU,QAC5D,CAeA,SAAgBC,EACd,EACA,EACA,EAAe,GACA,CAEf,OADIC,IAAU,SAAiBC,GAAgBC,EAAoB,OAAS,QACrEF,CACT,CAeA,SAAgBG,EACd,EACA,EACA,EAA4B,QACtB,CACFC,IAAc,SAChBC,EAAK,UAAU,OAAO,QAAS,MAAM,EACrCA,EAAK,UAAU,IAAIC,CAAQ,GAE3BD,EAAK,aAAaD,EAAWE,CAAQ,EAEvC,EAAK,MAAM,YAAcA,CAC3B,CAGA,SAASC,EAAqB,EAAyB,CACrD,OACEC,aAAiB,eAChBA,EAAM,OAAS,iBACdA,EAAM,OAAS,sBACfA,EAAM,OAAS,kBAErB,CAiBA,SAAgBC,EAAgB,EAAkB,EAAgC,CAChF,GAAI,CACF,IAAMX,EAAQY,EAAQ,QAAQC,CAAG,EACjC,OAAOd,EAAQC,CAAK,EAAIA,EAAQ,IAAA,EAClC,OAASU,EAAO,CACd,GAAID,EAAqBC,CAAK,EAAG,OACjC,MAAMA,CACR,CACF,CAiBA,SAAgBI,EAAiB,EAAkB,EAAa,EAAuB,CACrF,GAAI,CAEF,OADAF,EAAQ,QAAQC,EAAKX,CAAK,EACnB,EACT,OAASQ,EAAO,CACd,GAAID,EAAqBC,CAAK,EAAG,MAAO,GACxC,MAAMA,CACR,CACF,CAuBA,SAAgBK,EAAY,EAAwB,CAAC,EAAW,CAC9D,IAAMF,EAAMG,EAAQ,YAAA,kBACdC,EAAWD,EAAQ,cAAgB,SACnCV,EAAYU,EAAQ,WAAa,QACjCE,EAASF,EAAQ,cAAgB,GAEjCG,EAAW,GAA2B,KAAK,UAAUnB,CAAK,CAAC,CAAC,QAAQ,KAAM,SAAS,EACzF,MAAO,CACL,eACA,SAASmB,EAAQN,CAAG,EAAE,KAAKM,EAAQF,CAAQ,EAAE,KAAKE,EAAQb,CAAS,EAAE,KAAKa,EAAQD,CAAM,EAAE,GAC1F,kCACA,6HACA,sIACA,0FACA,oBACA,OACF,CAAC,CAAC,KAAK,EAAE,CACX,CA2BA,SAAgBE,EACd,EAAkE,CAAC,EAC3D,CAKR,MAAO,UAHLJ,EAAQ,QAAU,IAAA,GACd,GACA,WAAWA,EAAQ,MAAM,QAAQ,qBAAsB,EAAE,EAAE,GAC1C,GAAGD,EAAYC,CAAO,EAAE,WACjD,C"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{applyTheme as e,isTheme as t,readStoredTheme as n,resolveTheme as r,writeStoredTheme as i}from"./theme-logic.js";import{batch as a,createContext as o,onCleanup as s,onMount as c,untrack as l,useContext as u,useEffect as d,useMemo as f,useSignal as p}from"@shadow-js/core";const m={theme:()=>`system`,resolvedTheme:()=>void 0,systemTheme:()=>void 0,setTheme:()=>void 0},h=o(m,{name:`ShadowUITheme`});function g(){let e=document.createElement(`style`);e.textContent=`*,*::before,*::after{transition:none!important;animation-duration:0s!important}`,document.head.append(e),window.getComputedStyle(document.body).getPropertyValue(`opacity`),window.setTimeout(()=>e.remove(),1)}function _(){try{return window.localStorage}catch(e){if(e instanceof DOMException)return;throw e}}function v(o){let u=()=>o.storageKey??`shadow-ui-theme`,m=()=>o.defaultTheme??`system`,v=()=>o.attribute??`class`,y=()=>o.enableSystem??!0,[b,x]=p(l(m)),[S,C]=p(void 0),w=f(()=>{let e=S();if(e!==void 0)return r(b(),e,y())}),T=f(()=>{let e=S();return e===void 0?void 0:e?`dark`:`light`});c(()=>{let e=window.matchMedia(`(prefers-color-scheme: dark)`);a(()=>{let t=_(),r=t===void 0?void 0:n(t,u());r!==void 0&&x(r),C(e.matches)});let r=e=>{C(e.matches)},i=e=>{e.key===u()&&x(t(e.newValue)?e.newValue:m())};e.addEventListener(`change`,r),window.addEventListener(`storage`,i),s(()=>{e.removeEventListener(`change`,r),window.removeEventListener(`storage`,i)})}),d(()=>{let t=w();if(t===void 0)return;let n=v(),r=document.documentElement;o.disableTransitionOnChange===!0&&g(),e(r,t,n)});let E={theme:b,resolvedTheme:w,systemTheme:T,setTheme(e){let n=typeof e==`function`?e(l(b)):e;if(!t(n))throw TypeError(`setTheme: expected "light", "dark" or "system", received ${String(n)}`);x(n);let r=_();r!==void 0&&i(r,l(u),n)}};return h.Provider({value:E,get children(){return o.children}})}function y(){let e=u(h);if(e===m)throw Error(`useTheme() must be used within <ThemeProvider>`);return e}export{v as ThemeProvider,y as useTheme};
|
|
2
|
+
//# sourceMappingURL=theme-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-provider.js","names":["MISSING","ThemeContext","createContext","suppressTransitions","style","localStorageOrUndefined","error","ThemeProvider","storageKey","props","defaultTheme","attribute","enableSystem","theme","setThemeState","useSignal","untrack","systemDark","setSystemDark","resolved","useMemo","dark","resolveTheme","system","onMount","query","batch","storage","stored","readStoredTheme","onSystemChange","event","onStorage","isTheme","onCleanup","useEffect","value","attributeName","current","applyTheme","chosen","next","writeStoredTheme","useTheme","useContext"],"sources":["../../../src/theme/theme-provider.ts"],"sourcesContent":["import {\n type Accessor,\n batch,\n createContext,\n onCleanup,\n onMount,\n untrack,\n useContext,\n useEffect,\n useMemo,\n useSignal,\n} from \"@shadow-js/core\";\nimport type { JsxChild } from \"@shadow-js/web/jsx-runtime\";\nimport {\n applyTheme,\n DEFAULT_STORAGE_KEY,\n isTheme,\n type ResolvedTheme,\n readStoredTheme,\n resolveTheme,\n type Theme,\n type ThemeOptions,\n writeStoredTheme,\n} from \"./theme-logic\";\n\n/**\n * What {@link useTheme} returns. Every value is an accessor: call it to read.\n * @since 1.0.0\n */\nexport interface ThemeContextValue {\n /** The user's choice: `\"light\"`, `\"dark\"` or `\"system\"`. Call it to read the value. */\n readonly theme: Accessor<Theme>;\n /**\n * The theme that is shown: `\"light\"` or `\"dark\"`. `undefined` on the server and until the provider\n * has mounted, so markup that depends on it hydrates without a mismatch.\n */\n readonly resolvedTheme: Accessor<ResolvedTheme | undefined>;\n /**\n * The operating system preference: `\"light\"` or `\"dark\"`. `undefined` on the server and until the\n * provider has mounted.\n */\n readonly systemTheme: Accessor<ResolvedTheme | undefined>;\n /**\n * Chooses a theme, or derives one from the previous choice, and stores it under the provider's\n * `storageKey`. Throws a `TypeError` for a value that is not `\"light\"`, `\"dark\"` or `\"system\"`.\n */\n readonly setTheme: (theme: Theme | ((previous: Theme) => Theme)) => void;\n}\n\n/**\n * Props of {@link ThemeProvider}: the {@link ThemeOptions} plus a transition switch and the children.\n * @since 1.0.0\n */\nexport interface ThemeProviderProps extends ThemeOptions {\n /**\n * Stops CSS transitions for one frame while the theme changes, so colors flip at once instead\n * of fading piecemeal.\n * @default false\n */\n readonly disableTransitionOnChange?: boolean | undefined;\n /** The app to render inside the theme context. */\n readonly children?: JsxChild | undefined;\n}\n\nconst MISSING: ThemeContextValue = {\n theme: () => \"system\",\n resolvedTheme: () => undefined,\n systemTheme: () => undefined,\n setTheme: () => undefined,\n};\n\nconst ThemeContext = createContext<ThemeContextValue>(MISSING, { name: \"ShadowUITheme\" });\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\n/** Disables transitions until the next frame; returns nothing, cleans itself up. */\nfunction suppressTransitions(): void {\n const style = document.createElement(\"style\");\n style.textContent =\n \"*,*::before,*::after{transition:none!important;animation-duration:0s!important}\";\n document.head.append(style);\n // Reading a computed style forces the browser to apply the change with transitions off.\n window.getComputedStyle(document.body).getPropertyValue(\"opacity\");\n window.setTimeout(() => style.remove(), 1);\n}\n\n/**\n * The page's `localStorage`, or `undefined` when the browser blocks it: reading the property\n * itself throws a `DOMException` in some sandboxed and locked-down contexts.\n */\nfunction localStorageOrUndefined(): Storage | undefined {\n try {\n return window.localStorage;\n } catch (error) {\n if (error instanceof DOMException) return undefined;\n throw error;\n }\n}\n\n/**\n * Provides the color theme to the app: `\"light\"`, `\"dark\"` or `\"system\"`. The choice is persisted in\n * `localStorage`, applied to `<html>` as a `light` or `dark` class (or a `data-*` attribute), and kept\n * in sync across tabs and with the operating system. The provider renders no element of its own, and\n * nothing touches the DOM until it has mounted, so it is safe to render on the server. Pair it with\n * {@link themeScript} in the document head so the first paint already has the right theme.\n *\n * @param props The {@link ThemeProviderProps}: theme options (`storageKey`, `defaultTheme`,\n * `attribute`, `enableSystem`), `disableTransitionOnChange` and `children`.\n * @returns The children inside the theme context.\n * @example\n * import { ThemeProvider } from \"@shadow-js/ui\";\n *\n * function App() {\n * return <p>Hello</p>;\n * }\n *\n * export const root = (\n * <ThemeProvider defaultTheme=\"system\">\n * <App />\n * </ThemeProvider>\n * );\n * @see {@link useTheme}\n * @since 1.0.0\n */\nexport function ThemeProvider(props: ThemeProviderProps): JsxChild {\n const storageKey = (): string => props.storageKey ?? DEFAULT_STORAGE_KEY;\n const defaultTheme = (): Theme => props.defaultTheme ?? \"system\";\n const attribute = (): \"class\" | `data-${string}` => props.attribute ?? \"class\";\n const enableSystem = (): boolean => props.enableSystem ?? true;\n\n const [theme, setThemeState] = useSignal<Theme>(untrack(defaultTheme));\n const [systemDark, setSystemDark] = useSignal<boolean | undefined>(undefined);\n const resolved = useMemo((): ResolvedTheme | undefined => {\n const dark = systemDark();\n if (dark === undefined) return undefined;\n return resolveTheme(theme(), dark, enableSystem());\n });\n const system = useMemo((): ResolvedTheme | undefined => {\n const dark = systemDark();\n return dark === undefined ? undefined : dark ? \"dark\" : \"light\";\n });\n\n onMount(() => {\n const query = window.matchMedia(DARK_QUERY);\n batch(() => {\n const storage = localStorageOrUndefined();\n const stored = storage === undefined ? undefined : readStoredTheme(storage, storageKey());\n if (stored !== undefined) setThemeState(stored);\n setSystemDark(query.matches);\n });\n const onSystemChange = (event: MediaQueryListEvent): void => {\n setSystemDark(event.matches);\n };\n const onStorage = (event: StorageEvent): void => {\n if (event.key !== storageKey()) return;\n setThemeState(isTheme(event.newValue) ? event.newValue : defaultTheme());\n };\n query.addEventListener(\"change\", onSystemChange);\n window.addEventListener(\"storage\", onStorage);\n onCleanup(() => {\n query.removeEventListener(\"change\", onSystemChange);\n window.removeEventListener(\"storage\", onStorage);\n });\n });\n\n useEffect(() => {\n const value = resolved();\n if (value === undefined) return;\n const attributeName = attribute();\n const current = document.documentElement;\n if (props.disableTransitionOnChange === true) suppressTransitions();\n applyTheme(current, value, attributeName);\n });\n\n const value: ThemeContextValue = {\n theme,\n resolvedTheme: resolved,\n systemTheme: system,\n setTheme(next) {\n const chosen = typeof next === \"function\" ? next(untrack(theme)) : next;\n if (!isTheme(chosen)) {\n throw new TypeError(\n `setTheme: expected \"light\", \"dark\" or \"system\", received ${String(chosen)}`,\n );\n }\n setThemeState(chosen);\n const storage = localStorageOrUndefined();\n if (storage !== undefined) writeStoredTheme(storage, untrack(storageKey), chosen);\n },\n };\n\n return ThemeContext.Provider({\n value,\n get children() {\n return props.children;\n },\n }) as JsxChild;\n}\n\n/**\n * Reads the theme and its setter. Call it inside a component rendered under {@link ThemeProvider}.\n *\n * @returns The {@link ThemeContextValue}: accessors for the chosen, the resolved and the system\n * theme, and `setTheme`.\n * @throws Error when no `ThemeProvider` is above the caller.\n * @example\n * import { useTheme } from \"@shadow-js/ui\";\n *\n * export function ThemeToggle() {\n * const { resolvedTheme, setTheme } = useTheme();\n * return (\n * <button onClick={() => setTheme(resolvedTheme() === \"dark\" ? \"light\" : \"dark\")}>\n * Toggle theme\n * </button>\n * );\n * }\n * @see {@link ThemeProvider}\n * @since 1.0.0\n */\nexport function useTheme(): ThemeContextValue {\n const value = useContext(ThemeContext);\n if (value === MISSING) throw new Error(\"useTheme() must be used within <ThemeProvider>\");\n return value;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,104 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadow-js/ui",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "shadcn-style components you add to your project with a CLI, styled with Tailwind v4",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css",
|
|
9
|
+
"./dist/cli.mjs"
|
|
10
|
+
],
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22.12.0"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"shadow-ui": "./dist/cli.mjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"registry",
|
|
20
|
+
"registry.json",
|
|
21
|
+
"styles",
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"provenance": true
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/dev/index.d.ts",
|
|
32
|
+
"development": "./dist/dev/index.js",
|
|
33
|
+
"default": "./dist/prod/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./locales/en": {
|
|
36
|
+
"types": "./dist/dev/locales/en.d.ts",
|
|
37
|
+
"development": "./dist/dev/locales/en.js",
|
|
38
|
+
"default": "./dist/prod/locales/en.js"
|
|
39
|
+
},
|
|
40
|
+
"./locales/ar": {
|
|
41
|
+
"types": "./dist/dev/locales/ar.d.ts",
|
|
42
|
+
"development": "./dist/dev/locales/ar.js",
|
|
43
|
+
"default": "./dist/prod/locales/ar.js"
|
|
44
|
+
},
|
|
45
|
+
"./theme.css": "./styles/theme.css",
|
|
46
|
+
"./animations.css": "./styles/animations.css",
|
|
47
|
+
"./typeset.css": "./styles/typeset.css",
|
|
48
|
+
"./themes/*.css": "./styles/themes/*.css",
|
|
49
|
+
"./registry.json": "./registry.json",
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsdown --config-loader native",
|
|
54
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.registry.json && npm run typecheck:types",
|
|
55
|
+
"typecheck:types": "tsc -p tsconfig.test-types.json",
|
|
56
|
+
"lint": "biome check .",
|
|
57
|
+
"test": "vitest run --project unit --project dom --project server",
|
|
58
|
+
"test:gc": "vitest run --project gc --passWithNoTests",
|
|
59
|
+
"test:perf": "vitest run --project perf --passWithNoTests",
|
|
60
|
+
"check": "npm run lint && npm run typecheck && npm run build && npm test && npm run test:perf && npm run test:gc"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"@shadow-js/core": "^1.0.0",
|
|
64
|
+
"@shadow-js/primitives": "^1.0.0",
|
|
65
|
+
"@shadow-js/web": "^1.0.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@biomejs/biome": "2.5.15",
|
|
69
|
+
"@shadow-js/compiler": "^1.0.0",
|
|
70
|
+
"@shadow-js/core": "^1.0.0",
|
|
71
|
+
"@shadow-js/forms": "^1.0.0",
|
|
72
|
+
"@shadow-js/primitives": "^1.0.0",
|
|
73
|
+
"@shadow-js/testing": "^1.0.0",
|
|
74
|
+
"@shadow-js/web": "^1.0.0",
|
|
75
|
+
"@tailwindcss/node": "4.3.3",
|
|
76
|
+
"@types/node": "^24.0.0",
|
|
77
|
+
"fast-check": "^4.10.2",
|
|
78
|
+
"happy-dom": "^21.0.3",
|
|
79
|
+
"oxc-parser": "0.153.0",
|
|
80
|
+
"shiki": "4.5.0",
|
|
81
|
+
"tailwindcss": "4.3.3",
|
|
82
|
+
"tsdown": "0.23.0",
|
|
83
|
+
"typescript": "7.0.2",
|
|
84
|
+
"vitest": "5.0.3"
|
|
85
|
+
},
|
|
86
|
+
"dependencies": {
|
|
87
|
+
"@shadow-js/table": "1.0.0",
|
|
88
|
+
"class-variance-authority": "0.7.1",
|
|
89
|
+
"clsx": "2.1.1",
|
|
90
|
+
"tailwind-merge": "3.7.0"
|
|
91
|
+
},
|
|
92
|
+
"homepage": "https://ui.shadowjs.net",
|
|
93
|
+
"keywords": [
|
|
94
|
+
"shadowjs",
|
|
95
|
+
"ui"
|
|
96
|
+
],
|
|
97
|
+
"repository": {
|
|
98
|
+
"type": "git",
|
|
99
|
+
"url": "git+https://github.com/shadowjs-dev/ui.git"
|
|
100
|
+
},
|
|
101
|
+
"bugs": {
|
|
102
|
+
"url": "https://github.com/shadowjs-dev/ui/issues"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type AccordionContentProps,
|
|
4
|
+
type AccordionItemProps,
|
|
5
|
+
Accordion as AccordionPrimitive,
|
|
6
|
+
type AccordionProps,
|
|
7
|
+
type AccordionTriggerProps,
|
|
8
|
+
type PolymorphicTarget,
|
|
9
|
+
} from "@shadow-js/primitives";
|
|
10
|
+
import type { JSX } from "@shadow-js/web";
|
|
11
|
+
import { ChevronDownIcon } from "@/components/ui/icons";
|
|
12
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A vertically stacked set of headings that each reveal a section. With `type="single"` one item is
|
|
16
|
+
* open at a time, and `collapsible` lets the open item close. With `type="multiple"` any number of
|
|
17
|
+
* items can be open. Control it with `value` and `onValueChange`, or start uncontrolled with
|
|
18
|
+
* `defaultValue`.
|
|
19
|
+
*
|
|
20
|
+
* Keyboard and ARIA: Enter and Space on a trigger toggle its item. Up and Down move focus between
|
|
21
|
+
* the triggers and wrap around (Left and Right when `orientation="horizontal"`). Home and End focus
|
|
22
|
+
* the first and last enabled trigger. Each trigger is a `button` inside an `h3` heading, with
|
|
23
|
+
* `aria-expanded` and `aria-controls`. Each panel is a `role="region"` labeled by its trigger, so
|
|
24
|
+
* give every trigger visible text as its accessible name. The behavior comes from `Accordion` in
|
|
25
|
+
* `@shadow-js/primitives`.
|
|
26
|
+
*
|
|
27
|
+
* Compared with shadcn/ui: `class` replaces `className`, `as` replaces `asChild`, every element
|
|
28
|
+
* carries `data-slot`, and the props are typed from `@shadow-js/primitives`.
|
|
29
|
+
*
|
|
30
|
+
* @param props - The accordion props: `type`, `value` or `defaultValue`, and the `div` attributes.
|
|
31
|
+
* @returns The accordion root.
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* import {
|
|
35
|
+
* Accordion,
|
|
36
|
+
* AccordionContent,
|
|
37
|
+
* AccordionItem,
|
|
38
|
+
* AccordionTrigger,
|
|
39
|
+
* } from "@/components/ui/accordion";
|
|
40
|
+
*
|
|
41
|
+
* export function FaqAccordion() {
|
|
42
|
+
* return (
|
|
43
|
+
* <Accordion type="single" collapsible defaultValue="a">
|
|
44
|
+
* <AccordionItem value="a">
|
|
45
|
+
* <AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
46
|
+
* <AccordionContent>Yes. It follows the WAI-ARIA pattern.</AccordionContent>
|
|
47
|
+
* </AccordionItem>
|
|
48
|
+
* </Accordion>
|
|
49
|
+
* );
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
* @see {@link AccordionItem}
|
|
54
|
+
* @see {@link AccordionTrigger}
|
|
55
|
+
* @see {@link AccordionContent}
|
|
56
|
+
*/
|
|
57
|
+
function Accordion(props: AccordionProps): JSX.Element {
|
|
58
|
+
return <AccordionPrimitive data-slot="accordion" {...props} />;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* One section of an accordion: a trigger and its panel. The `value` identifies the item in the
|
|
63
|
+
* accordion's `value` and `defaultValue`. The `class` is merged with the item's border classes.
|
|
64
|
+
*
|
|
65
|
+
* @param props - The item props: `value` (required), `disabled`, and the `div` attributes.
|
|
66
|
+
* @returns The item element.
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* import {
|
|
70
|
+
* Accordion,
|
|
71
|
+
* AccordionContent,
|
|
72
|
+
* AccordionItem,
|
|
73
|
+
* AccordionTrigger,
|
|
74
|
+
* } from "@/components/ui/accordion";
|
|
75
|
+
*
|
|
76
|
+
* export function ShippingFaq() {
|
|
77
|
+
* return (
|
|
78
|
+
* <Accordion type="multiple">
|
|
79
|
+
* <AccordionItem value="shipping">
|
|
80
|
+
* <AccordionTrigger>Shipping</AccordionTrigger>
|
|
81
|
+
* <AccordionContent>Orders ship within two days.</AccordionContent>
|
|
82
|
+
* </AccordionItem>
|
|
83
|
+
* </Accordion>
|
|
84
|
+
* );
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
* @since 1.0.0
|
|
88
|
+
* @see {@link Accordion}
|
|
89
|
+
*/
|
|
90
|
+
function AccordionItem(props: AccordionItemProps): JSX.Element {
|
|
91
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
92
|
+
return (
|
|
93
|
+
<AccordionPrimitive.Item
|
|
94
|
+
data-slot="accordion-item"
|
|
95
|
+
class={cn("border-b last:border-b-0", local.class)}
|
|
96
|
+
{...rest}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The props as the implementation sees them. */
|
|
102
|
+
type Loose = {
|
|
103
|
+
readonly as?: PolymorphicTarget;
|
|
104
|
+
readonly class?: ClassValue;
|
|
105
|
+
readonly children?: JSX.Element;
|
|
106
|
+
readonly [prop: string]: unknown;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The heading button of an item, with a chevron that turns when the item opens. It sits inside an
|
|
111
|
+
* `h3` heading. Enter and Space toggle it; Up and Down (Left and Right when horizontal) and Home and
|
|
112
|
+
* End move focus between the triggers of the accordion.
|
|
113
|
+
*
|
|
114
|
+
* The trigger's text is its accessible name. Its `class` goes on the button, and `as` replaces the
|
|
115
|
+
* button element.
|
|
116
|
+
*
|
|
117
|
+
* @param props - The trigger props: the `button` attributes and `children` (the label).
|
|
118
|
+
* @returns The heading with the trigger button inside.
|
|
119
|
+
* @example
|
|
120
|
+
* ```tsx
|
|
121
|
+
* import {
|
|
122
|
+
* Accordion,
|
|
123
|
+
* AccordionContent,
|
|
124
|
+
* AccordionItem,
|
|
125
|
+
* AccordionTrigger,
|
|
126
|
+
* } from "@/components/ui/accordion";
|
|
127
|
+
*
|
|
128
|
+
* export function Question() {
|
|
129
|
+
* return (
|
|
130
|
+
* <Accordion type="single">
|
|
131
|
+
* <AccordionItem value="a">
|
|
132
|
+
* <AccordionTrigger class="text-base">Is it styled?</AccordionTrigger>
|
|
133
|
+
* <AccordionContent>Yes.</AccordionContent>
|
|
134
|
+
* </AccordionItem>
|
|
135
|
+
* </Accordion>
|
|
136
|
+
* );
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
* @since 1.0.0
|
|
140
|
+
* @see {@link AccordionItem}
|
|
141
|
+
* @see {@link AccordionContent}
|
|
142
|
+
*/
|
|
143
|
+
function AccordionTrigger<T extends PolymorphicTarget = "button">(
|
|
144
|
+
props: AccordionTriggerProps<T>,
|
|
145
|
+
): JSX.Element {
|
|
146
|
+
const [local, rest] = splitProps(props as unknown as Loose, ["class", "children"]);
|
|
147
|
+
return (
|
|
148
|
+
<AccordionPrimitive.Header class="flex">
|
|
149
|
+
<AccordionPrimitive.Trigger
|
|
150
|
+
data-slot="accordion-trigger"
|
|
151
|
+
class={cn(
|
|
152
|
+
"flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-start text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
153
|
+
local.class,
|
|
154
|
+
)}
|
|
155
|
+
{...rest}
|
|
156
|
+
>
|
|
157
|
+
{local.children}
|
|
158
|
+
<ChevronDownIcon class="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200 motion-reduce:transition-none" />
|
|
159
|
+
</AccordionPrimitive.Trigger>
|
|
160
|
+
</AccordionPrimitive.Header>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The panel of an item. It animates its height when it opens and closes. The `class` goes on the
|
|
166
|
+
* inner wrapper, so padding does not break the animation.
|
|
167
|
+
*
|
|
168
|
+
* Keyboard and ARIA: the panel is a `role="region"` labeled by its trigger (`aria-labelledby`). It
|
|
169
|
+
* has no keys of its own, and arrow keys pressed inside it do not move focus between triggers.
|
|
170
|
+
*
|
|
171
|
+
* @param props - The panel props: `children` and the `div` attributes. The `class` goes on the
|
|
172
|
+
* inner wrapper.
|
|
173
|
+
* @returns The panel element.
|
|
174
|
+
* @example
|
|
175
|
+
* ```tsx
|
|
176
|
+
* import {
|
|
177
|
+
* Accordion,
|
|
178
|
+
* AccordionContent,
|
|
179
|
+
* AccordionItem,
|
|
180
|
+
* AccordionTrigger,
|
|
181
|
+
* } from "@/components/ui/accordion";
|
|
182
|
+
*
|
|
183
|
+
* export function Answer() {
|
|
184
|
+
* return (
|
|
185
|
+
* <Accordion type="single" defaultValue="a">
|
|
186
|
+
* <AccordionItem value="a">
|
|
187
|
+
* <AccordionTrigger>Can I cancel?</AccordionTrigger>
|
|
188
|
+
* <AccordionContent class="text-muted-foreground">Yes, at any time.</AccordionContent>
|
|
189
|
+
* </AccordionItem>
|
|
190
|
+
* </Accordion>
|
|
191
|
+
* );
|
|
192
|
+
* }
|
|
193
|
+
* ```
|
|
194
|
+
* @since 1.0.0
|
|
195
|
+
* @see {@link AccordionTrigger}
|
|
196
|
+
*/
|
|
197
|
+
function AccordionContent(props: AccordionContentProps): JSX.Element {
|
|
198
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
199
|
+
return (
|
|
200
|
+
<AccordionPrimitive.Content
|
|
201
|
+
data-slot="accordion-content"
|
|
202
|
+
class="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
|
203
|
+
{...rest}
|
|
204
|
+
>
|
|
205
|
+
<div class={cn("pt-0 pb-4", local.class)}>{local.children}</div>
|
|
206
|
+
</AccordionPrimitive.Content>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|