@kala-ui/react-app 0.1.0-beta.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 +13 -0
- package/LICENSE +21 -0
- package/README.md +36 -0
- package/dist/components/app-shell/app-shell.d.ts +54 -0
- package/dist/components/app-shell/app-shell.js +91 -0
- package/dist/components/app-shell/index.d.ts +1 -0
- package/dist/components/app-shell/index.js +2 -0
- package/dist/components/charts/area-chart.d.ts +6 -0
- package/dist/components/charts/area-chart.js +172 -0
- package/dist/components/charts/bar-chart.d.ts +6 -0
- package/dist/components/charts/bar-chart.js +153 -0
- package/dist/components/charts/chart-skeleton.d.ts +36 -0
- package/dist/components/charts/chart-skeleton.js +13 -0
- package/dist/components/charts/chart.d.ts +25 -0
- package/dist/components/charts/chart.js +48 -0
- package/dist/components/charts/chart.types.d.ts +196 -0
- package/dist/components/charts/chart.types.js +6 -0
- package/dist/components/charts/donut-chart.d.ts +7 -0
- package/dist/components/charts/donut-chart.js +146 -0
- package/dist/components/charts/index.d.ts +14 -0
- package/dist/components/charts/index.js +15 -0
- package/dist/components/charts/line-chart.d.ts +6 -0
- package/dist/components/charts/line-chart.js +158 -0
- package/dist/components/charts/radial-bar-chart.d.ts +6 -0
- package/dist/components/charts/radial-bar-chart.js +115 -0
- package/dist/components/charts/theme-utils.d.ts +77 -0
- package/dist/components/charts/theme-utils.js +243 -0
- package/dist/components/charts/use-theme-aware-chart.d.ts +12 -0
- package/dist/components/charts/use-theme-aware-chart.js +68 -0
- package/dist/components/charts/utils.d.ts +56 -0
- package/dist/components/charts/utils.js +135 -0
- package/dist/components/data-table/column-filters.d.ts +14 -0
- package/dist/components/data-table/column-filters.js +51 -0
- package/dist/components/data-table/column-header-filter.d.ts +14 -0
- package/dist/components/data-table/column-header-filter.js +75 -0
- package/dist/components/data-table/data-table-body-rows.d.ts +23 -0
- package/dist/components/data-table/data-table-body-rows.js +43 -0
- package/dist/components/data-table/data-table-filter-chips.d.ts +14 -0
- package/dist/components/data-table/data-table-filter-chips.js +42 -0
- package/dist/components/data-table/data-table-header-row.d.ts +21 -0
- package/dist/components/data-table/data-table-header-row.js +35 -0
- package/dist/components/data-table/data-table-skeleton.d.ts +49 -0
- package/dist/components/data-table/data-table-skeleton.js +32 -0
- package/dist/components/data-table/data-table-toolbar.d.ts +18 -0
- package/dist/components/data-table/data-table-toolbar.js +25 -0
- package/dist/components/data-table/data-table.d.ts +23 -0
- package/dist/components/data-table/data-table.js +250 -0
- package/dist/components/data-table/data-table.types.d.ts +305 -0
- package/dist/components/data-table/data-table.types.js +6 -0
- package/dist/components/data-table/index.d.ts +6 -0
- package/dist/components/data-table/index.js +6 -0
- package/dist/components/data-table/pagination-nav.d.ts +15 -0
- package/dist/components/data-table/pagination-nav.js +23 -0
- package/dist/components/data-table/useTableState.d.ts +29 -0
- package/dist/components/data-table/useTableState.js +312 -0
- package/dist/components/dnd/dnd.d.ts +118 -0
- package/dist/components/dnd/dnd.js +128 -0
- package/dist/components/dnd/index.d.ts +1 -0
- package/dist/components/dnd/index.js +2 -0
- package/dist/components/footer/footer.d.ts +37 -0
- package/dist/components/footer/footer.js +8 -0
- package/dist/components/footer/index.d.ts +2 -0
- package/dist/components/footer/index.js +2 -0
- package/dist/components/header/header-skeleton.d.ts +54 -0
- package/dist/components/header/header-skeleton.js +23 -0
- package/dist/components/header/header.d.ts +40 -0
- package/dist/components/header/header.js +77 -0
- package/dist/components/header/header.types.d.ts +39 -0
- package/dist/components/header/header.types.js +2 -0
- package/dist/components/header/index.d.ts +4 -0
- package/dist/components/header/index.js +3 -0
- package/dist/components/metric-card/index.d.ts +2 -0
- package/dist/components/metric-card/index.js +3 -0
- package/dist/components/metric-card/metric-card-skeleton.d.ts +32 -0
- package/dist/components/metric-card/metric-card-skeleton.js +29 -0
- package/dist/components/metric-card/metric-card.d.ts +16 -0
- package/dist/components/metric-card/metric-card.js +55 -0
- package/dist/components/metric-card/metric-card.types.d.ts +14 -0
- package/dist/components/metric-card/metric-card.types.js +2 -0
- package/dist/components/nav-link/index.d.ts +1 -0
- package/dist/components/nav-link/index.js +2 -0
- package/dist/components/nav-link/nav-link.d.ts +26 -0
- package/dist/components/nav-link/nav-link.js +23 -0
- package/dist/components/navigation/index.d.ts +4 -0
- package/dist/components/navigation/index.js +3 -0
- package/dist/components/navigation/navigation-skeleton.d.ts +54 -0
- package/dist/components/navigation/navigation-skeleton.js +23 -0
- package/dist/components/navigation/navigation.d.ts +26 -0
- package/dist/components/navigation/navigation.js +44 -0
- package/dist/components/session-card/index.d.ts +2 -0
- package/dist/components/session-card/index.js +3 -0
- package/dist/components/session-card/session-card-skeleton.d.ts +31 -0
- package/dist/components/session-card/session-card-skeleton.js +28 -0
- package/dist/components/session-card/session-card.d.ts +27 -0
- package/dist/components/session-card/session-card.js +67 -0
- package/dist/components/sidebar/index.d.ts +3 -0
- package/dist/components/sidebar/index.js +3 -0
- package/dist/components/sidebar/sidebar-skeleton.d.ts +54 -0
- package/dist/components/sidebar/sidebar-skeleton.js +23 -0
- package/dist/components/sidebar/sidebar.d.ts +46 -0
- package/dist/components/sidebar/sidebar.js +69 -0
- package/dist/components/social-login-button/index.d.ts +1 -0
- package/dist/components/social-login-button/index.js +2 -0
- package/dist/components/social-login-button/social-login-button.d.ts +20 -0
- package/dist/components/social-login-button/social-login-button.js +57 -0
- package/dist/components/social-login-buttons/index.d.ts +1 -0
- package/dist/components/social-login-buttons/index.js +2 -0
- package/dist/components/social-login-buttons/social-login-buttons.d.ts +34 -0
- package/dist/components/social-login-buttons/social-login-buttons.js +18 -0
- package/dist/components/sparkline-chart/index.d.ts +2 -0
- package/dist/components/sparkline-chart/index.js +2 -0
- package/dist/components/sparkline-chart/sparkline-chart.d.ts +51 -0
- package/dist/components/sparkline-chart/sparkline-chart.js +134 -0
- package/dist/components/user-menu-dropdown/index.d.ts +1 -0
- package/dist/components/user-menu-dropdown/index.js +2 -0
- package/dist/components/user-menu-dropdown/user-menu-dropdown.d.ts +31 -0
- package/dist/components/user-menu-dropdown/user-menu-dropdown.js +27 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +19 -0
- package/dist/lib/active-path.d.ts +5 -0
- package/dist/lib/active-path.js +12 -0
- package/dist/styles/app.css +2125 -0
- package/package.json +164 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Radial Bar Chart Component
|
|
4
|
+
* Circular progress bars for showing percentages and completion rates
|
|
5
|
+
*/
|
|
6
|
+
"use client";
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
import { cn } from "@kala-ui/react/lib/utils";
|
|
9
|
+
import { useMemo } from "react";
|
|
10
|
+
import { Chart, getTooltipTheme } from "./chart.js";
|
|
11
|
+
import { useThemeAwareChart } from "./use-theme-aware-chart.js";
|
|
12
|
+
import { getDefaultChartOptions } from "./utils.js";
|
|
13
|
+
export function RadialBarChart({ series, labels, colors, height = 350, width = "100%", className, title, subtitle, animations = true, dataLabels = true, hollowSize = "50%", isLoading, emptyMessage, options: customOptions, }) {
|
|
14
|
+
const { colors: themeColors } = useThemeAwareChart();
|
|
15
|
+
const isEmpty = !isLoading && (series.length === 0 || labels.length === 0);
|
|
16
|
+
const chartOptions = useMemo(() => {
|
|
17
|
+
const baseOptions = getDefaultChartOptions();
|
|
18
|
+
// Use theme-aware colors if not provided
|
|
19
|
+
const chartColors = colors || themeColors.mixed;
|
|
20
|
+
const options = {
|
|
21
|
+
...baseOptions,
|
|
22
|
+
chart: {
|
|
23
|
+
...baseOptions.chart,
|
|
24
|
+
type: "radialBar",
|
|
25
|
+
animations: {
|
|
26
|
+
enabled: animations,
|
|
27
|
+
speed: 800,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
colors: chartColors,
|
|
31
|
+
plotOptions: {
|
|
32
|
+
radialBar: {
|
|
33
|
+
hollow: {
|
|
34
|
+
size: hollowSize,
|
|
35
|
+
},
|
|
36
|
+
track: {
|
|
37
|
+
background: "var(--muted)",
|
|
38
|
+
strokeWidth: "100%",
|
|
39
|
+
},
|
|
40
|
+
dataLabels: {
|
|
41
|
+
show: dataLabels,
|
|
42
|
+
name: {
|
|
43
|
+
fontSize: "14px",
|
|
44
|
+
color: "var(--muted-foreground)",
|
|
45
|
+
offsetY: -10,
|
|
46
|
+
},
|
|
47
|
+
value: {
|
|
48
|
+
fontSize: "24px",
|
|
49
|
+
fontWeight: 700,
|
|
50
|
+
color: "var(--foreground)",
|
|
51
|
+
offsetY: 5,
|
|
52
|
+
formatter: (value) => `${value}%`,
|
|
53
|
+
},
|
|
54
|
+
total: {
|
|
55
|
+
show: series.length > 1,
|
|
56
|
+
label: "Total",
|
|
57
|
+
fontSize: "14px",
|
|
58
|
+
color: "var(--muted-foreground)",
|
|
59
|
+
formatter: (w) => {
|
|
60
|
+
const total = w.globals.series.reduce((a, b) => a + b, 0) /
|
|
61
|
+
w.globals.series.length;
|
|
62
|
+
return `${Math.round(total)}%`;
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
labels,
|
|
69
|
+
legend: {
|
|
70
|
+
show: series.length > 1,
|
|
71
|
+
position: "bottom",
|
|
72
|
+
horizontalAlign: "center",
|
|
73
|
+
labels: {
|
|
74
|
+
colors: "var(--foreground)",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
tooltip: {
|
|
78
|
+
theme: getTooltipTheme(),
|
|
79
|
+
},
|
|
80
|
+
...(title && {
|
|
81
|
+
title: {
|
|
82
|
+
text: title,
|
|
83
|
+
style: {
|
|
84
|
+
fontSize: "16px",
|
|
85
|
+
fontWeight: "600",
|
|
86
|
+
color: "var(--foreground)",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
...(subtitle && {
|
|
91
|
+
subtitle: {
|
|
92
|
+
text: subtitle,
|
|
93
|
+
style: {
|
|
94
|
+
fontSize: "12px",
|
|
95
|
+
color: "var(--muted-foreground)",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
...customOptions,
|
|
100
|
+
};
|
|
101
|
+
return options;
|
|
102
|
+
}, [
|
|
103
|
+
colors,
|
|
104
|
+
themeColors,
|
|
105
|
+
series,
|
|
106
|
+
labels,
|
|
107
|
+
dataLabels,
|
|
108
|
+
hollowSize,
|
|
109
|
+
animations,
|
|
110
|
+
title,
|
|
111
|
+
subtitle,
|
|
112
|
+
customOptions,
|
|
113
|
+
]);
|
|
114
|
+
return (_jsx(Chart, { className: cn("w-full", className), options: chartOptions, series: series, type: "radialBar", height: height, width: width, isLoading: isLoading, isEmpty: isEmpty, emptyMessage: emptyMessage }));
|
|
115
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart Theme Utilities
|
|
3
|
+
*
|
|
4
|
+
* Charts resolve their colors from the live CSS custom properties (design
|
|
5
|
+
* tokens), so a host that overrides `--primary`, `--border`, … flows into
|
|
6
|
+
* every chart automatically — no JS palette to keep in sync. Tinted/shaded
|
|
7
|
+
* ramp stops are built with `color-mix`. When resolution is impossible
|
|
8
|
+
* (SSR, jsdom, unsupported syntax) each color falls back to a curated hex.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Chart color palette type
|
|
12
|
+
*/
|
|
13
|
+
export type ColorPalette = string[];
|
|
14
|
+
/**
|
|
15
|
+
* Color scheme configuration for different themes
|
|
16
|
+
*/
|
|
17
|
+
export interface ThemeColorScheme {
|
|
18
|
+
primary: ColorPalette;
|
|
19
|
+
success: ColorPalette;
|
|
20
|
+
warning: ColorPalette;
|
|
21
|
+
destructive: ColorPalette;
|
|
22
|
+
info: ColorPalette;
|
|
23
|
+
mixed: ColorPalette;
|
|
24
|
+
grid: string;
|
|
25
|
+
axisLabels: string;
|
|
26
|
+
tooltipBg: string;
|
|
27
|
+
tooltipText: string;
|
|
28
|
+
}
|
|
29
|
+
export type ChartThemeName = "light" | "dark" | "neutral" | "accent";
|
|
30
|
+
/**
|
|
31
|
+
* Get theme-aware chart colors, resolved from the active CSS tokens.
|
|
32
|
+
* @param theme - Optional theme override, otherwise detects from DOM.
|
|
33
|
+
* When an explicit theme differs from the DOM state there is no matching
|
|
34
|
+
* stylesheet to resolve against, so the curated fallbacks are returned.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getChartColors(theme?: ChartThemeName): ThemeColorScheme;
|
|
37
|
+
/**
|
|
38
|
+
* Get default chart options with theme-aware colors
|
|
39
|
+
* @param theme - Optional theme override
|
|
40
|
+
*/
|
|
41
|
+
export declare function getThemeAwareChartOptions(theme?: ChartThemeName): {
|
|
42
|
+
grid: {
|
|
43
|
+
borderColor: string;
|
|
44
|
+
strokeDashArray: number;
|
|
45
|
+
xaxis: {
|
|
46
|
+
lines: {
|
|
47
|
+
show: boolean;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
tooltip: {
|
|
52
|
+
theme: "dark" | "light";
|
|
53
|
+
};
|
|
54
|
+
legend: {
|
|
55
|
+
labels: {
|
|
56
|
+
colors: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Create a theme-aware chart instance
|
|
62
|
+
* Use this to get colors that will update when theme changes
|
|
63
|
+
*/
|
|
64
|
+
export declare function createThemeAwareChart(): {
|
|
65
|
+
/**
|
|
66
|
+
* Get current colors
|
|
67
|
+
*/
|
|
68
|
+
getColors: () => ThemeColorScheme;
|
|
69
|
+
/**
|
|
70
|
+
* Update colors based on current theme
|
|
71
|
+
*/
|
|
72
|
+
updateColors: () => boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Get current theme
|
|
75
|
+
*/
|
|
76
|
+
getTheme: () => ChartThemeName;
|
|
77
|
+
};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Chart Theme Utilities
|
|
4
|
+
*
|
|
5
|
+
* Charts resolve their colors from the live CSS custom properties (design
|
|
6
|
+
* tokens), so a host that overrides `--primary`, `--border`, … flows into
|
|
7
|
+
* every chart automatically — no JS palette to keep in sync. Tinted/shaded
|
|
8
|
+
* ramp stops are built with `color-mix`. When resolution is impossible
|
|
9
|
+
* (SSR, jsdom, unsupported syntax) each color falls back to a curated hex.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Curated fallback palettes, used whenever CSS resolution is unavailable.
|
|
13
|
+
* These mirror the default token values per theme.
|
|
14
|
+
*/
|
|
15
|
+
const FALLBACK = {
|
|
16
|
+
light: {
|
|
17
|
+
primary: ["#3b82f6", "#60a5fa", "#93c5fd", "#bfdbfe"],
|
|
18
|
+
success: ["#10b981", "#34d399", "#6ee7b7", "#a7f3d0"],
|
|
19
|
+
warning: ["#f59e0b", "#fbbf24", "#fcd34d", "#fde68a"],
|
|
20
|
+
destructive: ["#ef4444", "#f87171", "#fca5a5", "#fecaca"],
|
|
21
|
+
info: ["#0ea5e9", "#38bdf8", "#7dd3fc", "#bae6fd"],
|
|
22
|
+
mixed: ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#0ea5e9", "#6b7280"],
|
|
23
|
+
grid: "#e5e7eb",
|
|
24
|
+
axisLabels: "#6b7280",
|
|
25
|
+
tooltipBg: "#ffffff",
|
|
26
|
+
tooltipText: "#171717",
|
|
27
|
+
},
|
|
28
|
+
dark: {
|
|
29
|
+
primary: ["#60a5fa", "#3b82f6", "#2563eb", "#1d4ed8"],
|
|
30
|
+
success: ["#34d399", "#10b981", "#059669", "#047857"],
|
|
31
|
+
warning: ["#fbbf24", "#f59e0b", "#d97706", "#b45309"],
|
|
32
|
+
destructive: ["#f87171", "#ef4444", "#dc2626", "#b91c1c"],
|
|
33
|
+
info: ["#38bdf8", "#0ea5e9", "#0284c7", "#0369a1"],
|
|
34
|
+
mixed: ["#60a5fa", "#34d399", "#fbbf24", "#f87171", "#38bdf8", "#a1a1aa"],
|
|
35
|
+
grid: "#374151",
|
|
36
|
+
axisLabels: "#a1a1aa",
|
|
37
|
+
tooltipBg: "#1e293b",
|
|
38
|
+
tooltipText: "#fafafa",
|
|
39
|
+
},
|
|
40
|
+
neutral: {
|
|
41
|
+
primary: ["#171717", "#262626", "#404040", "#525252"],
|
|
42
|
+
success: ["#22c55e", "#16a34a", "#15803d", "#166534"],
|
|
43
|
+
warning: ["#f59e0b", "#d97706", "#b45309", "#92400e"],
|
|
44
|
+
destructive: ["#ef4444", "#dc2626", "#b91c1c", "#991b1b"],
|
|
45
|
+
info: ["#3b82f6", "#2563eb", "#1d4ed8", "#1e40af"],
|
|
46
|
+
mixed: ["#171717", "#22c55e", "#f59e0b", "#ef4444", "#3b82f6", "#737373"],
|
|
47
|
+
grid: "#e5e5e5",
|
|
48
|
+
axisLabels: "#737373",
|
|
49
|
+
tooltipBg: "#ffffff",
|
|
50
|
+
tooltipText: "#171717",
|
|
51
|
+
},
|
|
52
|
+
accent: {
|
|
53
|
+
primary: ["#3b82f6", "#60a5fa", "#93c5fd", "#bfdbfe"],
|
|
54
|
+
success: ["#10b981", "#34d399", "#6ee7b7", "#a7f3d0"],
|
|
55
|
+
warning: ["#f59e0b", "#fbbf24", "#fcd34d", "#fde68a"],
|
|
56
|
+
destructive: ["#ef4444", "#f87171", "#fca5a5", "#fecaca"],
|
|
57
|
+
info: ["#0ea5e9", "#38bdf8", "#7dd3fc", "#bae6fd"],
|
|
58
|
+
mixed: ["#3b82f6", "#10b981", "#f59e0b", "#ef4444", "#0ea5e9", "#6b7280"],
|
|
59
|
+
grid: "#e5e7eb",
|
|
60
|
+
axisLabels: "#737373",
|
|
61
|
+
tooltipBg: "#ffffff",
|
|
62
|
+
tooltipText: "#171717",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Get current theme from DOM
|
|
67
|
+
*/
|
|
68
|
+
function getCurrentTheme() {
|
|
69
|
+
// Chart components call this during render; on the server there is no DOM.
|
|
70
|
+
if (typeof document === "undefined")
|
|
71
|
+
return "light";
|
|
72
|
+
if (document.documentElement.classList.contains("dark")) {
|
|
73
|
+
return document.documentElement.classList.contains("accent")
|
|
74
|
+
? "accent"
|
|
75
|
+
: "dark";
|
|
76
|
+
}
|
|
77
|
+
if (document.documentElement.classList.contains("neutral")) {
|
|
78
|
+
return "neutral";
|
|
79
|
+
}
|
|
80
|
+
if (document.documentElement.classList.contains("accent")) {
|
|
81
|
+
return "accent";
|
|
82
|
+
}
|
|
83
|
+
return "light";
|
|
84
|
+
}
|
|
85
|
+
/*
|
|
86
|
+
* Resolution cache. Keyed by the requested CSS color; invalidated whenever
|
|
87
|
+
* the theme class list on <html> changes (the standard way themes switch).
|
|
88
|
+
*/
|
|
89
|
+
const resolutionCache = new Map();
|
|
90
|
+
let cacheKey = null;
|
|
91
|
+
/**
|
|
92
|
+
* Resolve any CSS color expression (var(), color-mix(), …) to a concrete
|
|
93
|
+
* color string via a detached probe element. Returns `fallback` when there
|
|
94
|
+
* is no DOM or the expression cannot be resolved (SSR, test environments).
|
|
95
|
+
*/
|
|
96
|
+
function resolveColor(cssColor, fallback) {
|
|
97
|
+
if (typeof document === "undefined")
|
|
98
|
+
return fallback;
|
|
99
|
+
const classKey = document.documentElement.className;
|
|
100
|
+
if (cacheKey !== classKey) {
|
|
101
|
+
resolutionCache.clear();
|
|
102
|
+
cacheKey = classKey;
|
|
103
|
+
}
|
|
104
|
+
const cached = resolutionCache.get(cssColor);
|
|
105
|
+
if (cached !== undefined)
|
|
106
|
+
return cached;
|
|
107
|
+
let resolved = "";
|
|
108
|
+
try {
|
|
109
|
+
const probe = document.createElement("div");
|
|
110
|
+
probe.style.display = "none";
|
|
111
|
+
probe.style.color = cssColor;
|
|
112
|
+
document.body.appendChild(probe);
|
|
113
|
+
resolved = getComputedStyle(probe).color;
|
|
114
|
+
document.body.removeChild(probe);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
resolved = "";
|
|
118
|
+
}
|
|
119
|
+
// Unresolved expressions come back empty or unresolved-with-var(); both
|
|
120
|
+
// mean the runtime can't compute the color — use the curated fallback.
|
|
121
|
+
if (!resolved || resolved.includes("var("))
|
|
122
|
+
resolved = fallback;
|
|
123
|
+
resolutionCache.set(cssColor, resolved);
|
|
124
|
+
return resolved;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Light-surface ramp: base color, then progressively lighter tints.
|
|
128
|
+
*/
|
|
129
|
+
function tintRamp(base, fallback) {
|
|
130
|
+
return [
|
|
131
|
+
resolveColor(base, fallback[0]),
|
|
132
|
+
resolveColor(`color-mix(in oklab, ${base}, white 30%)`, fallback[1]),
|
|
133
|
+
resolveColor(`color-mix(in oklab, ${base}, white 55%)`, fallback[2]),
|
|
134
|
+
resolveColor(`color-mix(in oklab, ${base}, white 75%)`, fallback[3]),
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Dark-surface ramp: lighter than base, base, then progressively darker shades.
|
|
139
|
+
*/
|
|
140
|
+
function shadeRamp(base, fallback) {
|
|
141
|
+
return [
|
|
142
|
+
resolveColor(`color-mix(in oklab, ${base}, white 25%)`, fallback[0]),
|
|
143
|
+
resolveColor(base, fallback[1]),
|
|
144
|
+
resolveColor(`color-mix(in oklab, ${base}, black 15%)`, fallback[2]),
|
|
145
|
+
resolveColor(`color-mix(in oklab, ${base}, black 35%)`, fallback[3]),
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Build the token-driven color scheme for a theme.
|
|
150
|
+
*/
|
|
151
|
+
function buildScheme(theme) {
|
|
152
|
+
const fallback = FALLBACK[theme];
|
|
153
|
+
const isDark = theme === "dark";
|
|
154
|
+
const ramp = isDark ? shadeRamp : tintRamp;
|
|
155
|
+
return {
|
|
156
|
+
primary: ramp("var(--primary)", fallback.primary),
|
|
157
|
+
success: ramp("var(--success)", fallback.success),
|
|
158
|
+
warning: ramp("var(--warning)", fallback.warning),
|
|
159
|
+
destructive: ramp("var(--destructive)", fallback.destructive),
|
|
160
|
+
info: ramp("var(--info)", fallback.info),
|
|
161
|
+
mixed: [
|
|
162
|
+
resolveColor("var(--primary)", fallback.mixed[0]),
|
|
163
|
+
resolveColor("var(--success)", fallback.mixed[1]),
|
|
164
|
+
resolveColor("var(--warning)", fallback.mixed[2]),
|
|
165
|
+
resolveColor("var(--destructive)", fallback.mixed[3]),
|
|
166
|
+
resolveColor("var(--info)", fallback.mixed[4]),
|
|
167
|
+
resolveColor("var(--muted-foreground)", fallback.mixed[5]),
|
|
168
|
+
],
|
|
169
|
+
grid: resolveColor("color-mix(in oklab, var(--border), transparent 45%)", fallback.grid),
|
|
170
|
+
axisLabels: resolveColor("var(--muted-foreground)", fallback.axisLabels),
|
|
171
|
+
tooltipBg: resolveColor("var(--popover)", fallback.tooltipBg),
|
|
172
|
+
tooltipText: resolveColor("var(--popover-foreground)", fallback.tooltipText),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get theme-aware chart colors, resolved from the active CSS tokens.
|
|
177
|
+
* @param theme - Optional theme override, otherwise detects from DOM.
|
|
178
|
+
* When an explicit theme differs from the DOM state there is no matching
|
|
179
|
+
* stylesheet to resolve against, so the curated fallbacks are returned.
|
|
180
|
+
*/
|
|
181
|
+
export function getChartColors(theme) {
|
|
182
|
+
const activeTheme = theme || getCurrentTheme();
|
|
183
|
+
if (theme && theme !== getCurrentTheme())
|
|
184
|
+
return FALLBACK[theme];
|
|
185
|
+
return buildScheme(activeTheme);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get default chart options with theme-aware colors
|
|
189
|
+
* @param theme - Optional theme override
|
|
190
|
+
*/
|
|
191
|
+
export function getThemeAwareChartOptions(theme) {
|
|
192
|
+
const colors = getChartColors(theme);
|
|
193
|
+
const tooltipTheme = getCurrentTheme() === "dark" ? "dark" : "light";
|
|
194
|
+
return {
|
|
195
|
+
grid: {
|
|
196
|
+
borderColor: colors.grid,
|
|
197
|
+
strokeDashArray: 3,
|
|
198
|
+
xaxis: {
|
|
199
|
+
lines: {
|
|
200
|
+
show: false,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
tooltip: {
|
|
205
|
+
theme: tooltipTheme,
|
|
206
|
+
},
|
|
207
|
+
legend: {
|
|
208
|
+
labels: {
|
|
209
|
+
colors: colors.axisLabels,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Create a theme-aware chart instance
|
|
216
|
+
* Use this to get colors that will update when theme changes
|
|
217
|
+
*/
|
|
218
|
+
export function createThemeAwareChart() {
|
|
219
|
+
let currentTheme = getCurrentTheme();
|
|
220
|
+
let colors = getChartColors(currentTheme);
|
|
221
|
+
return {
|
|
222
|
+
/**
|
|
223
|
+
* Get current colors
|
|
224
|
+
*/
|
|
225
|
+
getColors: () => colors,
|
|
226
|
+
/**
|
|
227
|
+
* Update colors based on current theme
|
|
228
|
+
*/
|
|
229
|
+
updateColors: () => {
|
|
230
|
+
const newTheme = getCurrentTheme();
|
|
231
|
+
if (newTheme !== currentTheme) {
|
|
232
|
+
currentTheme = newTheme;
|
|
233
|
+
colors = getChartColors(currentTheme);
|
|
234
|
+
return true; // Theme changed
|
|
235
|
+
}
|
|
236
|
+
return false; // Theme unchanged
|
|
237
|
+
},
|
|
238
|
+
/**
|
|
239
|
+
* Get current theme
|
|
240
|
+
*/
|
|
241
|
+
getTheme: () => currentTheme,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-Aware Chart Hook
|
|
3
|
+
* Custom hook to manage theme-aware charts with automatic re-renders on theme changes
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Hook to get theme-aware chart colors with automatic re-renders on theme changes
|
|
7
|
+
* This hook listens for theme changes and triggers component re-renders to update chart colors
|
|
8
|
+
*/
|
|
9
|
+
export declare function useThemeAwareChart(): {
|
|
10
|
+
colors: import("./theme-utils").ThemeColorScheme;
|
|
11
|
+
theme: "accent" | "dark" | "light" | "neutral";
|
|
12
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Theme-Aware Chart Hook
|
|
4
|
+
* Custom hook to manage theme-aware charts with automatic re-renders on theme changes
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useState } from "react";
|
|
7
|
+
import { getChartColors } from "./theme-utils.js";
|
|
8
|
+
/**
|
|
9
|
+
* Hook to get theme-aware chart colors with automatic re-renders on theme changes
|
|
10
|
+
* This hook listens for theme changes and triggers component re-renders to update chart colors
|
|
11
|
+
*/
|
|
12
|
+
export function useThemeAwareChart() {
|
|
13
|
+
const [theme, setTheme] = useState(() => {
|
|
14
|
+
// Detect initial theme from DOM
|
|
15
|
+
if (typeof document === "undefined")
|
|
16
|
+
return "light";
|
|
17
|
+
if (document.documentElement.classList.contains("dark")) {
|
|
18
|
+
return document.documentElement.classList.contains("accent")
|
|
19
|
+
? "accent"
|
|
20
|
+
: "dark";
|
|
21
|
+
}
|
|
22
|
+
if (document.documentElement.classList.contains("neutral")) {
|
|
23
|
+
return "neutral";
|
|
24
|
+
}
|
|
25
|
+
if (document.documentElement.classList.contains("accent")) {
|
|
26
|
+
return "accent";
|
|
27
|
+
}
|
|
28
|
+
return "light";
|
|
29
|
+
});
|
|
30
|
+
const colors = getChartColors(theme);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
// Listen for theme changes by observing DOM class changes
|
|
33
|
+
if (typeof document === "undefined")
|
|
34
|
+
return;
|
|
35
|
+
const observer = new MutationObserver((mutations) => {
|
|
36
|
+
mutations.forEach((mutation) => {
|
|
37
|
+
if (mutation.type === "attributes" &&
|
|
38
|
+
mutation.attributeName === "class") {
|
|
39
|
+
const element = document.documentElement;
|
|
40
|
+
const classList = element.classList;
|
|
41
|
+
let newTheme = "light";
|
|
42
|
+
if (classList.contains("dark")) {
|
|
43
|
+
newTheme = classList.contains("accent") ? "accent" : "dark";
|
|
44
|
+
}
|
|
45
|
+
else if (classList.contains("neutral")) {
|
|
46
|
+
newTheme = "neutral";
|
|
47
|
+
}
|
|
48
|
+
else if (classList.contains("accent")) {
|
|
49
|
+
newTheme = "accent";
|
|
50
|
+
}
|
|
51
|
+
setTheme(newTheme);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
// Observe the document element for class changes (theme changes)
|
|
56
|
+
observer.observe(document.documentElement, {
|
|
57
|
+
attributes: true,
|
|
58
|
+
attributeFilter: ["class"],
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
observer.disconnect();
|
|
62
|
+
};
|
|
63
|
+
}, []);
|
|
64
|
+
return {
|
|
65
|
+
colors,
|
|
66
|
+
theme,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart Utilities
|
|
3
|
+
* Helper functions and default configurations for charts
|
|
4
|
+
*/
|
|
5
|
+
import type { ApexOptions } from "apexcharts";
|
|
6
|
+
/**
|
|
7
|
+
* Get default chart options that match our design system
|
|
8
|
+
* @param theme - Optional theme override, otherwise detects from DOM
|
|
9
|
+
*/
|
|
10
|
+
export declare function getDefaultChartOptions(theme?: "light" | "dark" | "neutral" | "accent"): ApexOptions;
|
|
11
|
+
/**
|
|
12
|
+
* Default color palette matching design tokens
|
|
13
|
+
* @param theme - Optional theme override, otherwise detects from DOM
|
|
14
|
+
* @deprecated Use getChartColors() instead for theme-aware colors
|
|
15
|
+
*/
|
|
16
|
+
export declare function defaultColors(theme?: "light" | "dark" | "neutral" | "accent"): {
|
|
17
|
+
primary: import("./theme-utils").ColorPalette;
|
|
18
|
+
success: import("./theme-utils").ColorPalette;
|
|
19
|
+
warning: import("./theme-utils").ColorPalette;
|
|
20
|
+
destructive: import("./theme-utils").ColorPalette;
|
|
21
|
+
info: import("./theme-utils").ColorPalette;
|
|
22
|
+
mixed: import("./theme-utils").ColorPalette;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Format number with appropriate units
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatNumber(value: number): string;
|
|
28
|
+
/**
|
|
29
|
+
* Format currency values
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatCurrency(value: number, currency?: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Format percentage values
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatPercentage(value: number, decimals?: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Generate gradient colors for area charts
|
|
38
|
+
*/
|
|
39
|
+
export declare function createGradient(_color: string, opacity?: number): {
|
|
40
|
+
type: "gradient";
|
|
41
|
+
gradient: {
|
|
42
|
+
shadeIntensity: number;
|
|
43
|
+
opacityFrom: number;
|
|
44
|
+
opacityTo: number;
|
|
45
|
+
stops: number[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Get responsive breakpoints for charts
|
|
50
|
+
*/
|
|
51
|
+
export declare function getResponsiveOptions(): ApexResponsive[];
|
|
52
|
+
interface ApexResponsive {
|
|
53
|
+
breakpoint: number;
|
|
54
|
+
options: ApexOptions;
|
|
55
|
+
}
|
|
56
|
+
export {};
|