@spatika/react 1.4.0 → 1.5.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/AGENTS.md +6 -3
- package/README.md +1 -1
- package/dist/composites/EventCalendar.js +3 -3
- package/dist/composites/GlassCard.d.ts +1 -1
- package/dist/composites/RichTextEditor.d.ts +1 -4
- package/dist/composites/RichTextEditor.d.ts.map +1 -1
- package/dist/composites/RichTextEditor.js +12 -19
- package/dist/composites/SurfaceCard.d.ts +1 -1
- package/dist/composites/Tag.d.ts +2 -2
- package/dist/index.d.ts +8 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -11
- package/dist/lib/breakpoints.d.ts +17 -0
- package/dist/lib/breakpoints.d.ts.map +1 -0
- package/dist/lib/breakpoints.js +23 -0
- package/dist/lib/create-theme.d.ts +162 -0
- package/dist/lib/create-theme.d.ts.map +1 -0
- package/dist/lib/create-theme.js +138 -0
- package/dist/lib/scheduler.js +2 -2
- package/dist/lib/themes.d.ts +17 -7
- package/dist/lib/themes.d.ts.map +1 -1
- package/dist/lib/themes.js +22 -8
- package/dist/lib/use-media-query.d.ts +9 -0
- package/dist/lib/use-media-query.d.ts.map +1 -1
- package/dist/lib/use-media-query.js +28 -1
- package/dist/primitives/Alert.d.ts +1 -1
- package/dist/primitives/Badge.d.ts +2 -2
- package/dist/primitives/Button.d.ts +2 -2
- package/dist/primitives/Container.d.ts +2 -2
- package/dist/primitives/Fab.d.ts +4 -4
- package/dist/primitives/Grid.d.ts +2 -1
- package/dist/primitives/Grid.d.ts.map +1 -1
- package/dist/primitives/Grid.js +10 -2
- package/dist/primitives/IconButton.d.ts +2 -2
- package/dist/primitives/ToggleButton.d.ts +1 -1
- package/dist/theme/SpatikaThemeProvider.d.ts +4 -5
- package/dist/theme/SpatikaThemeProvider.d.ts.map +1 -1
- package/dist/theme/SpatikaThemeProvider.js +28 -13
- package/package.json +4 -2
- package/skills/spatika-ui/SKILL.md +25 -6
- package/src/composites/EventCalendar.test.tsx +293 -0
- package/src/composites/EventCalendar.tsx +3 -3
- package/src/composites/RichTextEditor.test.tsx +51 -0
- package/src/composites/RichTextEditor.tsx +20 -84
- package/src/composites/chart-ui.test.tsx +21 -0
- package/src/composites/scheduler.test.tsx +0 -258
- package/src/index.test.ts +6 -0
- package/src/index.ts +138 -72
- package/src/lib/breakpoints.test.ts +29 -0
- package/src/lib/breakpoints.ts +28 -0
- package/src/lib/create-theme.test.ts +79 -0
- package/src/lib/create-theme.ts +249 -0
- package/src/lib/scheduler.test.ts +10 -0
- package/src/lib/scheduler.ts +2 -2
- package/src/lib/themes.test.ts +17 -0
- package/src/lib/themes.ts +43 -12
- package/src/lib/use-media-query.test.tsx +80 -0
- package/src/lib/use-media-query.ts +35 -1
- package/src/primitives/Grid.test.tsx +55 -0
- package/src/primitives/Grid.tsx +13 -3
- package/src/primitives/mui-more.test.tsx +17 -0
- package/src/theme/SpatikaThemeProvider.test.tsx +38 -0
- package/src/theme/SpatikaThemeProvider.tsx +43 -15
- package/src/composites/ChartDataGrid.test.tsx +0 -57
- package/src/composites/ChartDataGrid.tsx +0 -196
- package/src/composites/MapChart.test.tsx +0 -39
- package/src/composites/MapChart.tsx +0 -130
- package/src/composites/ScatterWebGL.test.tsx +0 -25
- package/src/composites/ScatterWebGL.tsx +0 -79
- package/src/composites/cartesian-charts.test.tsx +0 -136
- package/src/composites/cartesian-charts.tsx +0 -1355
- package/src/composites/chart-composition.test.tsx +0 -68
- package/src/composites/chart-composition.tsx +0 -1039
- package/src/composites/chart-interaction.ts +0 -136
- package/src/composites/chart-ui.tsx +0 -336
- package/src/composites/charts-3d.test.tsx +0 -25
- package/src/composites/charts-3d.tsx +0 -318
- package/src/composites/charts.test.tsx +0 -254
- package/src/composites/flow-charts.test.tsx +0 -98
- package/src/composites/flow-charts.tsx +0 -641
- package/src/composites/radial-charts.test.tsx +0 -89
- package/src/composites/radial-charts.tsx +0 -643
- package/src/lib/chart-export.test.ts +0 -20
- package/src/lib/chart-export.ts +0 -99
- package/src/lib/charts-platform.test.ts +0 -93
- package/src/lib/charts.test.ts +0 -132
- package/src/lib/charts.ts +0 -1021
- package/src/lib/geo.test.ts +0 -42
- package/src/lib/geo.ts +0 -138
- package/src/lib/iso.test.ts +0 -22
- package/src/lib/iso.ts +0 -102
- package/src/lib/webgl-scatter.test.ts +0 -36
- package/src/lib/webgl-scatter.ts +0 -230
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { isDarkTheme, isThemeId } from "./themes";
|
|
2
|
+
export const PALETTE_CSS_VARS = {
|
|
3
|
+
background: "--background",
|
|
4
|
+
foreground: "--foreground",
|
|
5
|
+
card: "--card",
|
|
6
|
+
cardForeground: "--card-foreground",
|
|
7
|
+
popover: "--popover",
|
|
8
|
+
popoverForeground: "--popover-foreground",
|
|
9
|
+
primary: "--primary",
|
|
10
|
+
primaryForeground: "--primary-foreground",
|
|
11
|
+
secondary: "--secondary",
|
|
12
|
+
secondaryForeground: "--secondary-foreground",
|
|
13
|
+
muted: "--muted",
|
|
14
|
+
mutedForeground: "--muted-foreground",
|
|
15
|
+
accent: "--accent",
|
|
16
|
+
accentForeground: "--accent-foreground",
|
|
17
|
+
destructive: "--destructive",
|
|
18
|
+
destructiveForeground: "--destructive-foreground",
|
|
19
|
+
border: "--border",
|
|
20
|
+
input: "--input",
|
|
21
|
+
inputBackground: "--input-background",
|
|
22
|
+
switchBackground: "--switch-background",
|
|
23
|
+
ring: "--ring",
|
|
24
|
+
sidebar: "--sidebar",
|
|
25
|
+
sidebarForeground: "--sidebar-foreground",
|
|
26
|
+
sidebarPrimary: "--sidebar-primary",
|
|
27
|
+
sidebarPrimaryForeground: "--sidebar-primary-foreground",
|
|
28
|
+
sidebarAccent: "--sidebar-accent",
|
|
29
|
+
sidebarAccentForeground: "--sidebar-accent-foreground",
|
|
30
|
+
sidebarBorder: "--sidebar-border",
|
|
31
|
+
sidebarRing: "--sidebar-ring",
|
|
32
|
+
accentInfo: "--accent-info",
|
|
33
|
+
accentInfoBg: "--accent-info-bg",
|
|
34
|
+
accentHighlight: "--accent-highlight",
|
|
35
|
+
accentHighlightBg: "--accent-highlight-bg",
|
|
36
|
+
accentWarm: "--accent-warm",
|
|
37
|
+
accentWarmBg: "--accent-warm-bg",
|
|
38
|
+
accentDangerBg: "--accent-danger-bg",
|
|
39
|
+
chart1: "--chart-1",
|
|
40
|
+
chart2: "--chart-2",
|
|
41
|
+
chart3: "--chart-3",
|
|
42
|
+
chart4: "--chart-4",
|
|
43
|
+
chart5: "--chart-5",
|
|
44
|
+
};
|
|
45
|
+
export const GLASS_CSS_VARS = {
|
|
46
|
+
bg: "--glass-bg",
|
|
47
|
+
panelBg: "--glass-panel-bg",
|
|
48
|
+
mutedBg: "--glass-muted-bg",
|
|
49
|
+
headerBg: "--glass-header-bg",
|
|
50
|
+
border: "--glass-border",
|
|
51
|
+
blur: "--glass-blur",
|
|
52
|
+
headerBlur: "--glass-header-blur",
|
|
53
|
+
menuBlur: "--glass-menu-blur",
|
|
54
|
+
saturate: "--glass-saturate",
|
|
55
|
+
shadow: "--glass-shadow",
|
|
56
|
+
highlight: "--glass-highlight",
|
|
57
|
+
overlayBg: "--modal-overlay-bg",
|
|
58
|
+
overlayBlur: "--modal-overlay-blur",
|
|
59
|
+
};
|
|
60
|
+
export const SHAPE_CSS_VARS = {
|
|
61
|
+
radius: "--radius",
|
|
62
|
+
radiusCard: "--radius-card",
|
|
63
|
+
radiusControl: "--radius-control",
|
|
64
|
+
radiusPill: "--radius-pill",
|
|
65
|
+
};
|
|
66
|
+
const CUSTOM_THEME_STYLE_ID = "spk-custom-themes";
|
|
67
|
+
function assignMappedVars(vars, source, map) {
|
|
68
|
+
if (!source)
|
|
69
|
+
return;
|
|
70
|
+
for (const [key, cssVar] of Object.entries(map)) {
|
|
71
|
+
const value = source[key];
|
|
72
|
+
if (typeof value === "string" && value.length > 0)
|
|
73
|
+
vars[cssVar] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Build a custom theme that overlays CSS variables on a built-in Spatika theme. */
|
|
77
|
+
export function createTheme(options) {
|
|
78
|
+
const id = options.id.trim();
|
|
79
|
+
if (!id) {
|
|
80
|
+
throw new Error("createTheme: `id` is required.");
|
|
81
|
+
}
|
|
82
|
+
if (isThemeId(id)) {
|
|
83
|
+
throw new Error(`createTheme: "${id}" is a built-in theme. Choose a different id.`);
|
|
84
|
+
}
|
|
85
|
+
const base = options.extends ?? "mukta";
|
|
86
|
+
const colorScheme = options.colorScheme ?? (isDarkTheme(base) ? "dark" : "light");
|
|
87
|
+
const vars = {};
|
|
88
|
+
assignMappedVars(vars, options.palette, PALETTE_CSS_VARS);
|
|
89
|
+
assignMappedVars(vars, options.glass, GLASS_CSS_VARS);
|
|
90
|
+
assignMappedVars(vars, options.shape, SHAPE_CSS_VARS);
|
|
91
|
+
if (options.typography?.fontFamily) {
|
|
92
|
+
vars["--font-sans"] = options.typography.fontFamily;
|
|
93
|
+
}
|
|
94
|
+
if (options.vars) {
|
|
95
|
+
for (const [key, value] of Object.entries(options.vars)) {
|
|
96
|
+
if (value)
|
|
97
|
+
vars[key] = value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
id,
|
|
102
|
+
label: options.label?.trim() || id,
|
|
103
|
+
extends: base,
|
|
104
|
+
colorScheme,
|
|
105
|
+
vars,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** Serialize a custom theme as an attribute selector rule. */
|
|
109
|
+
export function themeToCss(theme) {
|
|
110
|
+
const entries = Object.entries(theme.vars);
|
|
111
|
+
if (!entries.length) {
|
|
112
|
+
return `[data-spk-theme="${theme.id}"] {\n}\n`;
|
|
113
|
+
}
|
|
114
|
+
const decls = entries.map(([key, value]) => ` ${key}: ${value};`).join("\n");
|
|
115
|
+
const font = theme.vars["--font-sans"]
|
|
116
|
+
? `\n font-family: var(--font-sans);`
|
|
117
|
+
: "";
|
|
118
|
+
return `[data-spk-theme="${theme.id}"] {\n${decls}${font}\n}\n`;
|
|
119
|
+
}
|
|
120
|
+
/** Inject (or replace) a single stylesheet for every registered custom theme. */
|
|
121
|
+
export function injectCustomThemeStyles(themes) {
|
|
122
|
+
if (typeof document === "undefined")
|
|
123
|
+
return;
|
|
124
|
+
let el = document.getElementById(CUSTOM_THEME_STYLE_ID);
|
|
125
|
+
if (!themes.length) {
|
|
126
|
+
el?.remove();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!el) {
|
|
130
|
+
el = document.createElement("style");
|
|
131
|
+
el.id = CUSTOM_THEME_STYLE_ID;
|
|
132
|
+
document.head.appendChild(el);
|
|
133
|
+
}
|
|
134
|
+
el.textContent = themes.map(themeToCss).join("\n");
|
|
135
|
+
}
|
|
136
|
+
export function findCustomTheme(theme, customThemes) {
|
|
137
|
+
return customThemes?.find((item) => item.id === theme);
|
|
138
|
+
}
|
package/dist/lib/scheduler.js
CHANGED
|
@@ -504,8 +504,8 @@ export function eventColorStyle(tone, variant) {
|
|
|
504
504
|
return { backgroundColor: color };
|
|
505
505
|
if (variant === "bar") {
|
|
506
506
|
return {
|
|
507
|
-
backgroundColor:
|
|
508
|
-
color: "
|
|
507
|
+
backgroundColor: color,
|
|
508
|
+
color: "#ffffff",
|
|
509
509
|
};
|
|
510
510
|
}
|
|
511
511
|
if (variant === "wash") {
|
package/dist/lib/themes.d.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
/** Supported theme identifiers applied as classes on a root element. */
|
|
1
|
+
/** Supported built-in theme identifiers applied as classes on a root element. */
|
|
2
2
|
export declare const THEME_IDS: readonly ["mukta", "neelam", "usha", "sandhya"];
|
|
3
|
-
export type
|
|
4
|
-
|
|
3
|
+
export type BuiltinThemeId = (typeof THEME_IDS)[number];
|
|
4
|
+
/**
|
|
5
|
+
* Built-in ids plus custom ids from `createTheme`.
|
|
6
|
+
* Custom strings keep autocomplete for Mukta / Neelam / Usha / Sandhya.
|
|
7
|
+
*/
|
|
8
|
+
export type ThemeId = BuiltinThemeId | (string & {});
|
|
9
|
+
export declare const DEFAULT_THEME: BuiltinThemeId;
|
|
5
10
|
/** Default `localStorage` key used by the docs site and `SpatikaThemeProvider`. */
|
|
6
11
|
export declare const SPATIKA_THEME_STORAGE_KEY = "spk-theme";
|
|
7
|
-
export declare const THEME_LABELS: Record<
|
|
8
|
-
export declare function isThemeId(value: unknown): value is
|
|
12
|
+
export declare const THEME_LABELS: Record<BuiltinThemeId, string>;
|
|
13
|
+
export declare function isThemeId(value: unknown): value is BuiltinThemeId;
|
|
9
14
|
export declare function isDarkTheme(theme: ThemeId): boolean;
|
|
15
|
+
export type ApplyThemeCustom = {
|
|
16
|
+
id: string;
|
|
17
|
+
extends: BuiltinThemeId;
|
|
18
|
+
colorScheme: "light" | "dark";
|
|
19
|
+
};
|
|
10
20
|
/** Read a persisted theme. Returns `null` when missing or invalid. */
|
|
11
|
-
export declare function readStoredTheme(storageKey?: string | false): ThemeId | null;
|
|
21
|
+
export declare function readStoredTheme(storageKey?: string | false, allowed?: readonly string[]): ThemeId | null;
|
|
12
22
|
/** Persist a theme. No-op when `storageKey` is unset. */
|
|
13
23
|
export declare function writeStoredTheme(storageKey: string | false | undefined, theme: ThemeId): void;
|
|
14
24
|
/** Apply a theme class to an element (defaults to `<html>`). */
|
|
15
|
-
export declare function applyTheme(theme: ThemeId, target?: HTMLElement | null): void;
|
|
25
|
+
export declare function applyTheme(theme: ThemeId, target?: HTMLElement | null, custom?: ApplyThemeCustom | null): void;
|
|
16
26
|
//# sourceMappingURL=themes.d.ts.map
|
package/dist/lib/themes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../src/lib/themes.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../src/lib/themes.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,eAAO,MAAM,SAAS,iDAAkD,CAAC;AAEzE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAErD,eAAO,MAAM,aAAa,EAAE,cAAwB,CAAC;AAErD,mFAAmF;AACnF,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAKvD,CAAC;AAIF,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAEjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEnD;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,OAAO,GAAG,MAAM,CAAC;CAC/B,CAAC;AAEF,sEAAsE;AACtE,wBAAgB,eAAe,CAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,EAC3B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,GAAG,IAAI,CAWhB;AAED,yDAAyD;AACzD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAO7F;AAOD,gEAAgE;AAChE,wBAAgB,UAAU,CACxB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,EAC3B,MAAM,CAAC,EAAE,gBAAgB,GAAG,IAAI,QAcjC"}
|
package/dist/lib/themes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Supported theme identifiers applied as classes on a root element. */
|
|
1
|
+
/** Supported built-in theme identifiers applied as classes on a root element. */
|
|
2
2
|
export const THEME_IDS = ["mukta", "neelam", "usha", "sandhya"];
|
|
3
3
|
export const DEFAULT_THEME = "mukta";
|
|
4
4
|
/** Default `localStorage` key used by the docs site and `SpatikaThemeProvider`. */
|
|
@@ -17,12 +17,18 @@ export function isDarkTheme(theme) {
|
|
|
17
17
|
return theme === "neelam" || theme === "sandhya";
|
|
18
18
|
}
|
|
19
19
|
/** Read a persisted theme. Returns `null` when missing or invalid. */
|
|
20
|
-
export function readStoredTheme(storageKey) {
|
|
20
|
+
export function readStoredTheme(storageKey, allowed) {
|
|
21
21
|
if (!storageKey || typeof window === "undefined")
|
|
22
22
|
return null;
|
|
23
23
|
try {
|
|
24
24
|
const value = window.localStorage.getItem(storageKey);
|
|
25
|
-
|
|
25
|
+
if (!value)
|
|
26
|
+
return null;
|
|
27
|
+
if (isThemeId(value))
|
|
28
|
+
return value;
|
|
29
|
+
if (allowed?.includes(value))
|
|
30
|
+
return value;
|
|
31
|
+
return null;
|
|
26
32
|
}
|
|
27
33
|
catch {
|
|
28
34
|
return null;
|
|
@@ -39,15 +45,23 @@ export function writeStoredTheme(storageKey, theme) {
|
|
|
39
45
|
// Private mode / quota — keep the in-memory theme.
|
|
40
46
|
}
|
|
41
47
|
}
|
|
48
|
+
function resolveBase(theme, custom) {
|
|
49
|
+
if (custom)
|
|
50
|
+
return custom.extends;
|
|
51
|
+
return isThemeId(theme) ? theme : DEFAULT_THEME;
|
|
52
|
+
}
|
|
42
53
|
/** Apply a theme class to an element (defaults to `<html>`). */
|
|
43
|
-
export function applyTheme(theme, target) {
|
|
54
|
+
export function applyTheme(theme, target, custom) {
|
|
44
55
|
if (typeof document === "undefined")
|
|
45
56
|
return;
|
|
46
57
|
const root = target ?? document.documentElement;
|
|
58
|
+
const base = resolveBase(theme, custom);
|
|
59
|
+
const id = custom?.id ?? theme;
|
|
47
60
|
root.classList.remove(...THEME_CLASS_LIST);
|
|
48
|
-
if (
|
|
49
|
-
root.classList.add(
|
|
61
|
+
if (base !== DEFAULT_THEME) {
|
|
62
|
+
root.classList.add(base);
|
|
50
63
|
}
|
|
51
|
-
root.setAttribute("data-spk-theme",
|
|
52
|
-
|
|
64
|
+
root.setAttribute("data-spk-theme", id);
|
|
65
|
+
const dark = custom ? custom.colorScheme === "dark" : isDarkTheme(base);
|
|
66
|
+
root.style.colorScheme = dark ? "dark" : "light";
|
|
53
67
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import { BREAKPOINT_KEYS, BREAKPOINTS, breakpointDown, breakpointUp, type Breakpoint } from "./breakpoints";
|
|
1
2
|
/** Subscribe to a CSS media query. Defaults to `false` until mounted. */
|
|
2
3
|
export declare function useMediaQuery(query: string): boolean;
|
|
3
4
|
/** True when the viewport is below the `md` breakpoint (768px). */
|
|
4
5
|
export declare function useIsMobile(): boolean;
|
|
6
|
+
/** True from this breakpoint up (`xs` is always true). */
|
|
7
|
+
export declare function useBreakpointUp(key: Breakpoint): boolean;
|
|
8
|
+
/** True strictly below this breakpoint. */
|
|
9
|
+
export declare function useBreakpointDown(key: Exclude<Breakpoint, "xs">): boolean;
|
|
10
|
+
/** Largest matching breakpoint, starting at `xs`. */
|
|
11
|
+
export declare function useBreakpoint(): Breakpoint;
|
|
12
|
+
export { BREAKPOINTS, BREAKPOINT_KEYS, breakpointDown, breakpointUp };
|
|
13
|
+
export type { Breakpoint };
|
|
5
14
|
//# sourceMappingURL=use-media-query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-media-query.d.ts","sourceRoot":"","sources":["../../src/lib/use-media-query.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-media-query.d.ts","sourceRoot":"","sources":["../../src/lib/use-media-query.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,WAAW,EACX,cAAc,EACd,YAAY,EACZ,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AAEvB,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAapD;AAED,mEAAmE;AACnE,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAGxD;AAED,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,OAAO,CAEzE;AAED,qDAAqD;AACrD,wBAAgB,aAAa,IAAI,UAAU,CAU1C;AAED,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACtE,YAAY,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
|
+
import { BREAKPOINT_KEYS, BREAKPOINTS, breakpointDown, breakpointUp, } from "./breakpoints";
|
|
2
3
|
/** Subscribe to a CSS media query. Defaults to `false` until mounted. */
|
|
3
4
|
export function useMediaQuery(query) {
|
|
4
5
|
const [matches, setMatches] = useState(false);
|
|
@@ -15,5 +16,31 @@ export function useMediaQuery(query) {
|
|
|
15
16
|
}
|
|
16
17
|
/** True when the viewport is below the `md` breakpoint (768px). */
|
|
17
18
|
export function useIsMobile() {
|
|
18
|
-
return useMediaQuery(
|
|
19
|
+
return useMediaQuery(breakpointDown("md"));
|
|
19
20
|
}
|
|
21
|
+
/** True from this breakpoint up (`xs` is always true). */
|
|
22
|
+
export function useBreakpointUp(key) {
|
|
23
|
+
const matches = useMediaQuery(breakpointUp(key));
|
|
24
|
+
return key === "xs" || matches;
|
|
25
|
+
}
|
|
26
|
+
/** True strictly below this breakpoint. */
|
|
27
|
+
export function useBreakpointDown(key) {
|
|
28
|
+
return useMediaQuery(breakpointDown(key));
|
|
29
|
+
}
|
|
30
|
+
/** Largest matching breakpoint, starting at `xs`. */
|
|
31
|
+
export function useBreakpoint() {
|
|
32
|
+
const sm = useMediaQuery(breakpointUp("sm"));
|
|
33
|
+
const md = useMediaQuery(breakpointUp("md"));
|
|
34
|
+
const lg = useMediaQuery(breakpointUp("lg"));
|
|
35
|
+
const xl = useMediaQuery(breakpointUp("xl"));
|
|
36
|
+
if (xl)
|
|
37
|
+
return "xl";
|
|
38
|
+
if (lg)
|
|
39
|
+
return "lg";
|
|
40
|
+
if (md)
|
|
41
|
+
return "md";
|
|
42
|
+
if (sm)
|
|
43
|
+
return "sm";
|
|
44
|
+
return "xs";
|
|
45
|
+
}
|
|
46
|
+
export { BREAKPOINTS, BREAKPOINT_KEYS, breakpointDown, breakpointUp };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const alertVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "
|
|
4
|
+
variant?: "default" | "highlight" | "info" | "warm" | "danger" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
declare function Alert({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>): React.JSX.Element;
|
|
7
7
|
declare function AlertTitle({ className, ...props }: React.ComponentProps<"h5">): React.JSX.Element;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "
|
|
4
|
+
variant?: "default" | "secondary" | "destructive" | "highlight" | "outline" | "info" | "warm" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
declare const Badge: React.ForwardRefExoticComponent<Omit<React.ClassAttributes<HTMLSpanElement> & React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
|
|
7
|
-
variant?: "default" | "
|
|
7
|
+
variant?: "default" | "secondary" | "destructive" | "highlight" | "outline" | "info" | "warm" | null | undefined;
|
|
8
8
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
9
9
|
asChild?: boolean;
|
|
10
10
|
}, "ref"> & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "default" | "
|
|
4
|
+
variant?: "default" | "secondary" | "destructive" | "link" | "outline" | "ghost" | "glass" | "gradient" | null | undefined;
|
|
5
5
|
size?: "default" | "icon" | "touch" | "sm" | "lg" | "xl" | "icon-touch" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
declare const Button: React.ForwardRefExoticComponent<Omit<React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<(props?: ({
|
|
8
|
-
variant?: "default" | "
|
|
8
|
+
variant?: "default" | "secondary" | "destructive" | "link" | "outline" | "ghost" | "glass" | "gradient" | null | undefined;
|
|
9
9
|
size?: "default" | "icon" | "touch" | "sm" | "lg" | "xl" | "icon-touch" | null | undefined;
|
|
10
10
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
11
11
|
asChild?: boolean;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const containerVariants: (props?: ({
|
|
4
|
-
maxWidth?: "sm" | "
|
|
4
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "full" | null | undefined;
|
|
5
5
|
disableGutters?: boolean | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export type ContainerProps = React.ComponentPropsWithoutRef<"div"> & VariantProps<typeof containerVariants>;
|
|
8
8
|
declare const Container: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & VariantProps<(props?: ({
|
|
9
|
-
maxWidth?: "sm" | "
|
|
9
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "full" | null | undefined;
|
|
10
10
|
disableGutters?: boolean | null | undefined;
|
|
11
11
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
|
|
12
12
|
export { Container, containerVariants };
|
package/dist/primitives/Fab.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import * as React from "react";
|
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const fabVariants: (props?: ({
|
|
4
4
|
variant?: "circular" | "extended" | null | undefined;
|
|
5
|
-
color?: "default" | "
|
|
6
|
-
size?: "sm" | "
|
|
5
|
+
color?: "default" | "primary" | "glass" | null | undefined;
|
|
6
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
8
|
export type FabProps = Omit<React.ComponentPropsWithoutRef<"button">, "color"> & VariantProps<typeof fabVariants> & {
|
|
9
9
|
asChild?: boolean;
|
|
@@ -13,8 +13,8 @@ export type FabProps = Omit<React.ComponentPropsWithoutRef<"button">, "color"> &
|
|
|
13
13
|
*/
|
|
14
14
|
declare const Fab: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "color"> & VariantProps<(props?: ({
|
|
15
15
|
variant?: "circular" | "extended" | null | undefined;
|
|
16
|
-
color?: "default" | "
|
|
17
|
-
size?: "sm" | "
|
|
16
|
+
color?: "default" | "primary" | "glass" | null | undefined;
|
|
17
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
18
18
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
19
19
|
asChild?: boolean;
|
|
20
20
|
} & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -19,12 +19,13 @@ export type GridProps = React.ComponentPropsWithoutRef<"div"> & {
|
|
|
19
19
|
sm?: number | "auto";
|
|
20
20
|
md?: number | "auto";
|
|
21
21
|
lg?: number | "auto";
|
|
22
|
+
xl?: number | "auto";
|
|
22
23
|
size?: number | "grow";
|
|
23
24
|
};
|
|
24
25
|
/**
|
|
25
26
|
* 12-column CSS grid. Use `container` on the parent and breakpoint spans on items.
|
|
26
27
|
*/
|
|
27
|
-
declare function Grid({ container, item: _item, spacing, columns: _columns, xs, sm, md, lg, size, className, ...props }: GridProps): React.JSX.Element;
|
|
28
|
+
declare function Grid({ container, item: _item, spacing, columns: _columns, xs, sm, md, lg, xl, size, className, ...props }: GridProps): React.JSX.Element;
|
|
28
29
|
declare namespace Grid {
|
|
29
30
|
var displayName: string;
|
|
30
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Grid.d.ts","sourceRoot":"","sources":["../../src/primitives/Grid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GAAG;IAC7D,SAAS,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,QAAA,MAAM,GAAG;gBAPK,KAAK,CAAC,WAAW;cACnB,OAAO;wCAWlB,CAAC;AA6BF,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GAAG;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,EAAE,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB,CAAC;
|
|
1
|
+
{"version":3,"file":"Grid.d.ts","sourceRoot":"","sources":["../../src/primitives/Grid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GAAG;IAC7D,SAAS,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,QAAA,MAAM,GAAG;gBAPK,KAAK,CAAC,WAAW;cACnB,OAAO;wCAWlB,CAAC;AA6BF,MAAM,MAAM,SAAS,GAAG,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,GAAG;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,EAAE,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB,CAAC;AAcF;;GAEG;AACH,iBAAS,IAAI,CAAC,EACZ,SAAS,EACT,IAAI,EAAE,KAAK,EACX,OAAW,EACX,OAAO,EAAE,QAAa,EACtB,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,EACF,IAAI,EACJ,SAAS,EACT,GAAG,KAAK,EACT,EAAE,SAAS,qBAoBX;kBAjCQ,IAAI;;;AAoCb,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC"}
|
package/dist/primitives/Grid.js
CHANGED
|
@@ -39,12 +39,20 @@ function spanFor(value) {
|
|
|
39
39
|
return undefined;
|
|
40
40
|
return spanClass[value] ?? "col-span-12";
|
|
41
41
|
}
|
|
42
|
+
function prefixedSpan(prefix, value) {
|
|
43
|
+
if (typeof value !== "number")
|
|
44
|
+
return undefined;
|
|
45
|
+
const base = spanClass[value];
|
|
46
|
+
if (!base)
|
|
47
|
+
return undefined;
|
|
48
|
+
return base.replace("col-span", `${prefix}:col-span`);
|
|
49
|
+
}
|
|
42
50
|
/**
|
|
43
51
|
* 12-column CSS grid. Use `container` on the parent and breakpoint spans on items.
|
|
44
52
|
*/
|
|
45
|
-
function Grid({ container, item: _item, spacing = 2, columns: _columns = 12, xs, sm, md, lg, size, className, ...props }) {
|
|
53
|
+
function Grid({ container, item: _item, spacing = 2, columns: _columns = 12, xs, sm, md, lg, xl, size, className, ...props }) {
|
|
46
54
|
const span = spanFor(typeof size === "number" ? size : xs);
|
|
47
|
-
return (_jsx("div", { "data-slot": "grid", "data-container": container ? "" : undefined, "data-item": _item || !container ? "" : undefined, className: cn(container && "grid grid-cols-12", container && (gapClass[spacing] ?? "gap-2"), !container && (span ?? (size === "grow" ? "col-span-12" : "col-span-12")),
|
|
55
|
+
return (_jsx("div", { "data-slot": "grid", "data-container": container ? "" : undefined, "data-item": _item || !container ? "" : undefined, className: cn(container && "grid grid-cols-12", container && (gapClass[spacing] ?? "gap-2"), !container && (span ?? (size === "grow" ? "col-span-12" : "col-span-12")), prefixedSpan("sm", sm), prefixedSpan("md", md), prefixedSpan("lg", lg), prefixedSpan("xl", xl), className), ...props }));
|
|
48
56
|
}
|
|
49
57
|
Grid.displayName = "Grid";
|
|
50
58
|
export { Box, Grid };
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const iconButtonVariants: (props?: ({
|
|
4
4
|
variant?: "filled" | "outline" | "ghost" | "glass" | null | undefined;
|
|
5
|
-
size?: "touch" | "sm" | "
|
|
5
|
+
size?: "touch" | "sm" | "md" | "lg" | null | undefined;
|
|
6
6
|
shape?: "circular" | "rounded" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
8
|
export type IconButtonProps = React.ComponentPropsWithoutRef<"button"> & VariantProps<typeof iconButtonVariants> & {
|
|
@@ -13,7 +13,7 @@ export type IconButtonProps = React.ComponentPropsWithoutRef<"button"> & Variant
|
|
|
13
13
|
*/
|
|
14
14
|
declare const IconButton: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
|
|
15
15
|
variant?: "filled" | "outline" | "ghost" | "glass" | null | undefined;
|
|
16
|
-
size?: "touch" | "sm" | "
|
|
16
|
+
size?: "touch" | "sm" | "md" | "lg" | null | undefined;
|
|
17
17
|
shape?: "circular" | "rounded" | null | undefined;
|
|
18
18
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
19
19
|
asChild?: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
declare const toggleButtonVariants: (props?: ({
|
|
4
|
-
size?: "sm" | "
|
|
4
|
+
size?: "sm" | "md" | "lg" | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
export type ToggleButtonGroupProps = Omit<React.ComponentPropsWithoutRef<"div">, "onChange" | "value" | "defaultValue"> & {
|
|
7
7
|
exclusive?: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import { type SpatikaTheme } from "../lib/create-theme";
|
|
2
3
|
import { type ThemeId } from "../lib/themes";
|
|
3
4
|
export type SpatikaThemeContextValue = {
|
|
4
5
|
theme: ThemeId;
|
|
@@ -25,12 +26,10 @@ export type SpatikaThemeProviderProps = {
|
|
|
25
26
|
/** Optional element to theme instead of (or in addition to) document */
|
|
26
27
|
target?: HTMLElement | null;
|
|
27
28
|
className?: string;
|
|
29
|
+
/** Custom themes from `createTheme`. Switch them with `setTheme(id)`. */
|
|
30
|
+
customThemes?: readonly SpatikaTheme[];
|
|
28
31
|
};
|
|
29
|
-
|
|
30
|
-
* MUI-style theme provider for Spatika.
|
|
31
|
-
* Wraps the app (or a subtree) and applies theme classes / data attributes.
|
|
32
|
-
*/
|
|
33
|
-
export declare function SpatikaThemeProvider({ children, theme: themeProp, defaultTheme, storageKey, onThemeChange, syncDocument, target, className, }: SpatikaThemeProviderProps): import("react").JSX.Element;
|
|
32
|
+
export declare function SpatikaThemeProvider({ children, theme: themeProp, defaultTheme, storageKey, onThemeChange, syncDocument, target, className, customThemes, }: SpatikaThemeProviderProps): import("react").JSX.Element;
|
|
34
33
|
/** Access current theme and setter from `SpatikaThemeProvider`. */
|
|
35
34
|
export declare function useSpatikaTheme(): SpatikaThemeContextValue;
|
|
36
35
|
//# sourceMappingURL=SpatikaThemeProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpatikaThemeProvider.d.ts","sourceRoot":"","sources":["../../src/theme/SpatikaThemeProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SpatikaThemeProvider.d.ts","sourceRoot":"","sources":["../../src/theme/SpatikaThemeProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EASL,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B,CAAC;AAIF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,uBAAuB;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACxC,CAAC;AAgBF,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,KAAK,EAAE,SAAS,EAChB,YAA4B,EAC5B,UAAkB,EAClB,aAAa,EACb,YAAmB,EACnB,MAAM,EACN,SAAS,EACT,YAAkC,GACnC,EAAE,yBAAyB,+BA+C3B;AAED,mEAAmE;AACnE,wBAAgB,eAAe,IAAI,wBAAwB,CAM1D"}
|
|
@@ -1,32 +1,47 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState, } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
import { findCustomTheme, injectCustomThemeStyles, } from "../lib/create-theme";
|
|
5
|
+
import { applyTheme, DEFAULT_THEME, isDarkTheme, isThemeId, readStoredTheme, THEME_IDS, writeStoredTheme, } from "../lib/themes";
|
|
4
6
|
const SpatikaThemeContext = createContext(null);
|
|
7
|
+
function baseThemeClass(theme, custom) {
|
|
8
|
+
const base = custom?.extends ?? (isThemeId(theme) ? theme : DEFAULT_THEME);
|
|
9
|
+
return base === DEFAULT_THEME ? undefined : base;
|
|
10
|
+
}
|
|
5
11
|
/**
|
|
6
|
-
*
|
|
12
|
+
* Theme provider for Spatika.
|
|
7
13
|
* Wraps the app (or a subtree) and applies theme classes / data attributes.
|
|
8
14
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
const EMPTY_CUSTOM_THEMES = [];
|
|
16
|
+
export function SpatikaThemeProvider({ children, theme: themeProp, defaultTheme = DEFAULT_THEME, storageKey = false, onThemeChange, syncDocument = true, target, className, customThemes = EMPTY_CUSTOM_THEMES, }) {
|
|
17
|
+
const customIds = useMemo(() => customThemes.map((item) => item.id), [customThemes]);
|
|
18
|
+
const [uncontrolled, setUncontrolled] = useState(() => readStoredTheme(storageKey, customIds) ?? defaultTheme);
|
|
11
19
|
const theme = themeProp ?? uncontrolled;
|
|
20
|
+
const custom = findCustomTheme(theme, customThemes);
|
|
12
21
|
const setTheme = useCallback((next) => {
|
|
13
22
|
if (themeProp === undefined)
|
|
14
23
|
setUncontrolled(next);
|
|
15
24
|
onThemeChange?.(next);
|
|
16
25
|
}, [onThemeChange, themeProp]);
|
|
17
26
|
useLayoutEffect(() => {
|
|
27
|
+
injectCustomThemeStyles(customThemes);
|
|
18
28
|
if (syncDocument)
|
|
19
|
-
applyTheme(theme);
|
|
29
|
+
applyTheme(theme, undefined, custom);
|
|
20
30
|
if (target)
|
|
21
|
-
applyTheme(theme, target);
|
|
31
|
+
applyTheme(theme, target, custom);
|
|
22
32
|
writeStoredTheme(storageKey, theme);
|
|
23
|
-
}, [theme, syncDocument, target, storageKey]);
|
|
24
|
-
const value = useMemo(() =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
}, [theme, syncDocument, target, storageKey, custom, customThemes]);
|
|
34
|
+
const value = useMemo(() => {
|
|
35
|
+
const extras = customThemes.map((item) => item.id).filter((id) => !isThemeId(id));
|
|
36
|
+
return {
|
|
37
|
+
theme,
|
|
38
|
+
setTheme,
|
|
39
|
+
themes: extras.length ? [...THEME_IDS, ...extras] : THEME_IDS,
|
|
40
|
+
};
|
|
41
|
+
}, [theme, setTheme, customThemes]);
|
|
42
|
+
const dark = custom ? custom.colorScheme === "dark" : isDarkTheme(theme);
|
|
43
|
+
const themeClass = baseThemeClass(theme, custom);
|
|
44
|
+
return (_jsx(SpatikaThemeContext.Provider, { value: value, children: _jsx("div", { className: cn(className, themeClass), "data-spk-theme": theme, "data-slot": "spatika-theme-provider", style: { colorScheme: dark ? "dark" : "light" }, children: children }) }));
|
|
30
45
|
}
|
|
31
46
|
/** Access current theme and setter from `SpatikaThemeProvider`. */
|
|
32
47
|
export function useSpatikaTheme() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spatika/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Spatika UI — glass React primitives and composites for production apps",
|
|
6
6
|
"homepage": "https://spatika.sweyam.com",
|
|
@@ -59,7 +59,9 @@
|
|
|
59
59
|
"react-dom": "^18.2.0 || ^19.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@spatika/
|
|
62
|
+
"@spatika/charts": "1.5.0",
|
|
63
|
+
"@spatika/editor": "1.5.0",
|
|
64
|
+
"@spatika/tokens": "1.5.0",
|
|
63
65
|
"class-variance-authority": "^0.7.1",
|
|
64
66
|
"clsx": "^2.1.1",
|
|
65
67
|
"cmdk": "^1.1.1",
|