asterui 0.12.55 → 0.12.56
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/dist/components/ThemeController.js +102 -88
- package/dist/components/ThemeController.js.map +1 -1
- package/dist/components/ThemeProvider.js +17 -15
- package/dist/components/ThemeProvider.js.map +1 -1
- package/dist/hooks/useTheme.js +8 -6
- package/dist/hooks/useTheme.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,84 +1,94 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { useConfig as
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { jsx as r, jsxs as p } from "react/jsx-runtime";
|
|
2
|
+
import { useState as b, useEffect as f, useId as $ } from "react";
|
|
3
|
+
import { useConfig as S } from "./ConfigProvider.js";
|
|
4
|
+
import { useTheme as v } from "../hooks/useTheme.js";
|
|
5
|
+
const C = "swap", D = "swap-rotate", M = "swap-off", Z = "swap-on", N = "dropdown", B = "dropdown-end", y = "dropdown-content", A = "btn", E = "btn-sm", I = "btn-block", O = "btn-ghost", z = "toggle", j = "toggle-xs", F = "toggle-sm", H = "toggle-md", L = "toggle-lg", V = "toggle-xl";
|
|
6
|
+
function i() {
|
|
6
7
|
return document.documentElement.getAttribute("data-theme");
|
|
7
8
|
}
|
|
8
|
-
function
|
|
9
|
-
document.documentElement.setAttribute("data-theme",
|
|
9
|
+
function x(n) {
|
|
10
|
+
document.documentElement.setAttribute("data-theme", n);
|
|
10
11
|
}
|
|
11
|
-
function
|
|
12
|
-
lightTheme:
|
|
13
|
-
darkTheme:
|
|
14
|
-
onChange:
|
|
15
|
-
className:
|
|
12
|
+
function X({
|
|
13
|
+
lightTheme: n = "light",
|
|
14
|
+
darkTheme: o = "dark",
|
|
15
|
+
onChange: u,
|
|
16
|
+
className: m = ""
|
|
16
17
|
}) {
|
|
17
|
-
const [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const { setTheme: g, isDark: t } = v(), h = g ?? x, [c, d] = b(() => t !== void 0 ? t : i() === o);
|
|
19
|
+
f(() => {
|
|
20
|
+
if (t !== void 0) {
|
|
21
|
+
d(t);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const s = new MutationObserver(() => {
|
|
25
|
+
const e = i();
|
|
26
|
+
d(e === o);
|
|
22
27
|
});
|
|
23
|
-
return
|
|
24
|
-
}, [
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
+
return s.observe(document.documentElement, { attributes: !0, attributeFilter: ["data-theme"] }), () => s.disconnect();
|
|
29
|
+
}, [o, t]);
|
|
30
|
+
const l = (s) => {
|
|
31
|
+
const a = s.target.checked ? o : n;
|
|
32
|
+
h(a), u?.(a);
|
|
28
33
|
};
|
|
29
|
-
return /* @__PURE__ */
|
|
30
|
-
/* @__PURE__ */
|
|
34
|
+
return /* @__PURE__ */ p("label", { className: `${C} ${D} ${m}`, children: [
|
|
35
|
+
/* @__PURE__ */ r(
|
|
31
36
|
"input",
|
|
32
37
|
{
|
|
33
38
|
type: "checkbox",
|
|
34
|
-
checked:
|
|
35
|
-
onChange:
|
|
39
|
+
checked: c,
|
|
40
|
+
onChange: l
|
|
36
41
|
}
|
|
37
42
|
),
|
|
38
|
-
/* @__PURE__ */
|
|
43
|
+
/* @__PURE__ */ r(
|
|
39
44
|
"svg",
|
|
40
45
|
{
|
|
41
|
-
className: `${
|
|
46
|
+
className: `${M} h-8 w-8 fill-current`,
|
|
42
47
|
xmlns: "http://www.w3.org/2000/svg",
|
|
43
48
|
viewBox: "0 0 24 24",
|
|
44
|
-
children: /* @__PURE__ */
|
|
49
|
+
children: /* @__PURE__ */ r("path", { d: "M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" })
|
|
45
50
|
}
|
|
46
51
|
),
|
|
47
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ r(
|
|
48
53
|
"svg",
|
|
49
54
|
{
|
|
50
|
-
className: `${
|
|
55
|
+
className: `${Z} h-8 w-8 fill-current`,
|
|
51
56
|
xmlns: "http://www.w3.org/2000/svg",
|
|
52
57
|
viewBox: "0 0 24 24",
|
|
53
|
-
children: /* @__PURE__ */
|
|
58
|
+
children: /* @__PURE__ */ r("path", { d: "M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" })
|
|
54
59
|
}
|
|
55
60
|
)
|
|
56
61
|
] });
|
|
57
62
|
}
|
|
58
|
-
function
|
|
59
|
-
themes:
|
|
60
|
-
defaultTheme:
|
|
61
|
-
onChange:
|
|
62
|
-
className:
|
|
63
|
+
function G({
|
|
64
|
+
themes: n,
|
|
65
|
+
defaultTheme: o,
|
|
66
|
+
onChange: u,
|
|
67
|
+
className: m = ""
|
|
63
68
|
}) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
const { setTheme: g, resolvedTheme: t } = v(), h = g ?? x, c = $(), [d, l] = b(() => {
|
|
70
|
+
if (t && n.includes(t)) return t;
|
|
71
|
+
const e = i();
|
|
72
|
+
return e && n.includes(e) ? e : o || n[0] || "light";
|
|
67
73
|
});
|
|
68
|
-
|
|
74
|
+
f(() => {
|
|
75
|
+
if (t && n.includes(t)) {
|
|
76
|
+
l(t);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
69
79
|
const e = new MutationObserver(() => {
|
|
70
|
-
const
|
|
71
|
-
|
|
80
|
+
const a = i();
|
|
81
|
+
a && n.includes(a) && l(a);
|
|
72
82
|
});
|
|
73
83
|
return e.observe(document.documentElement, { attributes: !0, attributeFilter: ["data-theme"] }), () => e.disconnect();
|
|
74
|
-
}, [t]);
|
|
75
|
-
const
|
|
76
|
-
|
|
84
|
+
}, [n, t]);
|
|
85
|
+
const s = (e) => {
|
|
86
|
+
l(e), h(e), u?.(e);
|
|
77
87
|
};
|
|
78
|
-
return /* @__PURE__ */
|
|
79
|
-
/* @__PURE__ */
|
|
88
|
+
return /* @__PURE__ */ p("div", { className: `${N} ${B} ${m}`, children: [
|
|
89
|
+
/* @__PURE__ */ p("div", { tabIndex: 0, role: "button", className: A, children: [
|
|
80
90
|
"Theme",
|
|
81
|
-
/* @__PURE__ */
|
|
91
|
+
/* @__PURE__ */ r(
|
|
82
92
|
"svg",
|
|
83
93
|
{
|
|
84
94
|
width: "12px",
|
|
@@ -86,77 +96,81 @@ function H({
|
|
|
86
96
|
className: "inline-block h-2 w-2 fill-current opacity-60",
|
|
87
97
|
xmlns: "http://www.w3.org/2000/svg",
|
|
88
98
|
viewBox: "0 0 2048 2048",
|
|
89
|
-
children: /* @__PURE__ */
|
|
99
|
+
children: /* @__PURE__ */ r("path", { d: "M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z" })
|
|
90
100
|
}
|
|
91
101
|
)
|
|
92
102
|
] }),
|
|
93
|
-
/* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ r(
|
|
94
104
|
"ul",
|
|
95
105
|
{
|
|
96
106
|
tabIndex: 0,
|
|
97
|
-
className: `${
|
|
98
|
-
children:
|
|
107
|
+
className: `${y} bg-base-300 rounded-box z-[1] w-52 p-2 shadow-2xl max-h-96 overflow-y-auto`,
|
|
108
|
+
children: n.map((e) => /* @__PURE__ */ r("li", { children: /* @__PURE__ */ r(
|
|
99
109
|
"input",
|
|
100
110
|
{
|
|
101
111
|
type: "radio",
|
|
102
|
-
name:
|
|
103
|
-
className: `${
|
|
112
|
+
name: c,
|
|
113
|
+
className: `${A} ${E} ${I} ${O} justify-start`,
|
|
104
114
|
"aria-label": e,
|
|
105
115
|
value: e,
|
|
106
|
-
checked:
|
|
107
|
-
onChange: () =>
|
|
116
|
+
checked: d === e,
|
|
117
|
+
onChange: () => s(e)
|
|
108
118
|
}
|
|
109
119
|
) }, e))
|
|
110
120
|
}
|
|
111
121
|
)
|
|
112
122
|
] });
|
|
113
123
|
}
|
|
114
|
-
const
|
|
115
|
-
xs:
|
|
116
|
-
sm:
|
|
117
|
-
md:
|
|
118
|
-
lg:
|
|
119
|
-
xl:
|
|
124
|
+
const R = {
|
|
125
|
+
xs: j,
|
|
126
|
+
sm: F,
|
|
127
|
+
md: H,
|
|
128
|
+
lg: L,
|
|
129
|
+
xl: V
|
|
120
130
|
};
|
|
121
|
-
function
|
|
122
|
-
lightTheme:
|
|
123
|
-
darkTheme:
|
|
124
|
-
onChange:
|
|
125
|
-
size:
|
|
126
|
-
className:
|
|
131
|
+
function q({
|
|
132
|
+
lightTheme: n = "light",
|
|
133
|
+
darkTheme: o = "dark",
|
|
134
|
+
onChange: u,
|
|
135
|
+
size: m,
|
|
136
|
+
className: g = ""
|
|
127
137
|
}) {
|
|
128
|
-
const { componentSize:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
const { componentSize: t } = S(), { setTheme: h, isDark: c } = v(), d = h ?? x, l = m ?? t ?? "md", [s, e] = b(() => c !== void 0 ? c : i() === o);
|
|
139
|
+
f(() => {
|
|
140
|
+
if (c !== void 0) {
|
|
141
|
+
e(c);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const w = new MutationObserver(() => {
|
|
145
|
+
const T = i();
|
|
146
|
+
e(T === o);
|
|
133
147
|
});
|
|
134
|
-
return
|
|
135
|
-
}, [
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
|
|
148
|
+
return w.observe(document.documentElement, { attributes: !0, attributeFilter: ["data-theme"] }), () => w.disconnect();
|
|
149
|
+
}, [o, c]);
|
|
150
|
+
const a = (w) => {
|
|
151
|
+
const k = w.target.checked ? o : n;
|
|
152
|
+
d(k), u?.(k);
|
|
139
153
|
};
|
|
140
|
-
return /* @__PURE__ */
|
|
154
|
+
return /* @__PURE__ */ r(
|
|
141
155
|
"input",
|
|
142
156
|
{
|
|
143
157
|
type: "checkbox",
|
|
144
|
-
className: `${
|
|
145
|
-
checked:
|
|
146
|
-
onChange:
|
|
158
|
+
className: `${z} ${R[l]} ${g}`,
|
|
159
|
+
checked: s,
|
|
160
|
+
onChange: a,
|
|
147
161
|
"aria-label": "Toggle theme"
|
|
148
162
|
}
|
|
149
163
|
);
|
|
150
164
|
}
|
|
151
|
-
const
|
|
165
|
+
const U = Object.assign(
|
|
152
166
|
{},
|
|
153
167
|
{
|
|
154
|
-
Swap:
|
|
155
|
-
Dropdown:
|
|
156
|
-
Toggle:
|
|
168
|
+
Swap: X,
|
|
169
|
+
Dropdown: G,
|
|
170
|
+
Toggle: q
|
|
157
171
|
}
|
|
158
172
|
);
|
|
159
173
|
export {
|
|
160
|
-
|
|
174
|
+
U as ThemeController
|
|
161
175
|
};
|
|
162
176
|
//# sourceMappingURL=ThemeController.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeController.js","sources":["../../src/components/ThemeController.tsx"],"sourcesContent":["import React, { useState, useEffect, useId } from 'react'\nimport { useConfig } from './ConfigProvider'\n\n// DaisyUI classes\nconst dSwap = 'swap'\nconst dSwapRotate = 'swap-rotate'\nconst dSwapOff = 'swap-off'\nconst dSwapOn = 'swap-on'\nconst dDropdown = 'dropdown'\nconst dDropdownEnd = 'dropdown-end'\nconst dDropdownContent = 'dropdown-content'\nconst dBtn = 'btn'\nconst dBtnSm = 'btn-sm'\nconst dBtnBlock = 'btn-block'\nconst dBtnGhost = 'btn-ghost'\nconst dToggle = 'toggle'\nconst dToggleXs = 'toggle-xs'\nconst dToggleSm = 'toggle-sm'\nconst dToggleMd = 'toggle-md'\nconst dToggleLg = 'toggle-lg'\nconst dToggleXl = 'toggle-xl'\n\nexport interface ThemeControllerSwapProps {\n lightTheme?: string\n darkTheme?: string\n onChange?: (theme: string) => void\n className?: string\n}\n\nexport interface ThemeControllerDropdownProps {\n themes: string[]\n defaultTheme?: string\n onChange?: (theme: string) => void\n className?: string\n}\n\nexport interface ThemeControllerToggleProps {\n lightTheme?: string\n darkTheme?: string\n onChange?: (theme: string) => void\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n className?: string\n}\n\n// Get current theme from document\nfunction getCurrentTheme(): string | null {\n return document.documentElement.getAttribute('data-theme')\n}\n\n// Set theme on document\nfunction setTheme(theme: string) {\n document.documentElement.setAttribute('data-theme', theme)\n}\n\nfunction ThemeControllerSwap({\n lightTheme = 'light',\n darkTheme = 'dark',\n onChange,\n className = '',\n}: ThemeControllerSwapProps) {\n const [isDark, setIsDark] = useState(() => {\n const current = getCurrentTheme()\n return current === darkTheme\n })\n\n // Sync with external theme changes\n useEffect(() => {\n const observer = new MutationObserver(() => {\n const current = getCurrentTheme()\n setIsDark(current === darkTheme)\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [darkTheme])\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const checked = e.target.checked\n const theme = checked ? darkTheme : lightTheme\n setTheme(theme)\n onChange?.(theme)\n }\n\n return (\n <label className={`${dSwap} ${dSwapRotate} ${className}`}>\n <input\n type=\"checkbox\"\n checked={isDark}\n onChange={handleChange}\n />\n {/* sun icon */}\n <svg\n className={`${dSwapOff} h-8 w-8 fill-current`}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z\" />\n </svg>\n {/* moon icon */}\n <svg\n className={`${dSwapOn} h-8 w-8 fill-current`}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z\" />\n </svg>\n </label>\n )\n}\n\nfunction ThemeControllerDropdown({\n themes,\n defaultTheme,\n onChange,\n className = '',\n}: ThemeControllerDropdownProps) {\n const radioName = useId()\n const [selectedTheme, setSelectedTheme] = useState(() => {\n const current = getCurrentTheme()\n if (current && themes.includes(current)) return current\n return defaultTheme || themes[0] || 'light'\n })\n\n // Sync with external theme changes\n useEffect(() => {\n const observer = new MutationObserver(() => {\n const current = getCurrentTheme()\n if (current && themes.includes(current)) {\n setSelectedTheme(current)\n }\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [themes])\n\n const handleChange = (theme: string) => {\n setSelectedTheme(theme)\n setTheme(theme)\n onChange?.(theme)\n }\n\n return (\n <div className={`${dDropdown} ${dDropdownEnd} ${className}`}>\n <div tabIndex={0} role=\"button\" className={dBtn}>\n Theme\n <svg\n width=\"12px\"\n height=\"12px\"\n className=\"inline-block h-2 w-2 fill-current opacity-60\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 2048 2048\"\n >\n <path d=\"M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z\"></path>\n </svg>\n </div>\n <ul\n tabIndex={0}\n className={`${dDropdownContent} bg-base-300 rounded-box z-[1] w-52 p-2 shadow-2xl max-h-96 overflow-y-auto`}\n >\n {themes.map((theme) => (\n <li key={theme}>\n <input\n type=\"radio\"\n name={radioName}\n className={`${dBtn} ${dBtnSm} ${dBtnBlock} ${dBtnGhost} justify-start`}\n aria-label={theme}\n value={theme}\n checked={selectedTheme === theme}\n onChange={() => handleChange(theme)}\n />\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nconst sizeClasses: Record<string, string> = {\n xs: dToggleXs,\n sm: dToggleSm,\n md: dToggleMd,\n lg: dToggleLg,\n xl: dToggleXl,\n}\n\nfunction ThemeControllerToggle({\n lightTheme = 'light',\n darkTheme = 'dark',\n onChange,\n size,\n className = '',\n}: ThemeControllerToggleProps) {\n const { componentSize } = useConfig()\n const effectiveSize = size ?? componentSize ?? 'md'\n\n const [isDark, setIsDark] = useState(() => {\n const current = getCurrentTheme()\n return current === darkTheme\n })\n\n // Sync with external theme changes\n useEffect(() => {\n const observer = new MutationObserver(() => {\n const current = getCurrentTheme()\n setIsDark(current === darkTheme)\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [darkTheme])\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const checked = e.target.checked\n const theme = checked ? darkTheme : lightTheme\n setTheme(theme)\n onChange?.(theme)\n }\n\n return (\n <input\n type=\"checkbox\"\n className={`${dToggle} ${sizeClasses[effectiveSize]} ${className}`}\n checked={isDark}\n onChange={handleChange}\n aria-label=\"Toggle theme\"\n />\n )\n}\n\nexport const ThemeController = Object.assign(\n {},\n {\n Swap: ThemeControllerSwap,\n Dropdown: ThemeControllerDropdown,\n Toggle: ThemeControllerToggle,\n }\n)\n"],"names":["dSwap","dSwapRotate","dSwapOff","dSwapOn","dDropdown","dDropdownEnd","dDropdownContent","dBtn","dBtnSm","dBtnBlock","dBtnGhost","dToggle","dToggleXs","dToggleSm","dToggleMd","dToggleLg","dToggleXl","getCurrentTheme","setTheme","theme","ThemeControllerSwap","lightTheme","darkTheme","onChange","className","isDark","setIsDark","useState","useEffect","observer","current","handleChange","e","jsxs","jsx","ThemeControllerDropdown","themes","defaultTheme","radioName","useId","selectedTheme","setSelectedTheme","sizeClasses","ThemeControllerToggle","size","componentSize","useConfig","effectiveSize","ThemeController"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThemeController.js","sources":["../../src/components/ThemeController.tsx"],"sourcesContent":["import React, { useState, useEffect, useId } from 'react'\nimport { useConfig } from './ConfigProvider'\nimport { useTheme } from '../hooks/useTheme'\n\n// DaisyUI classes\nconst dSwap = 'swap'\nconst dSwapRotate = 'swap-rotate'\nconst dSwapOff = 'swap-off'\nconst dSwapOn = 'swap-on'\nconst dDropdown = 'dropdown'\nconst dDropdownEnd = 'dropdown-end'\nconst dDropdownContent = 'dropdown-content'\nconst dBtn = 'btn'\nconst dBtnSm = 'btn-sm'\nconst dBtnBlock = 'btn-block'\nconst dBtnGhost = 'btn-ghost'\nconst dToggle = 'toggle'\nconst dToggleXs = 'toggle-xs'\nconst dToggleSm = 'toggle-sm'\nconst dToggleMd = 'toggle-md'\nconst dToggleLg = 'toggle-lg'\nconst dToggleXl = 'toggle-xl'\n\nexport interface ThemeControllerSwapProps {\n lightTheme?: string\n darkTheme?: string\n onChange?: (theme: string) => void\n className?: string\n}\n\nexport interface ThemeControllerDropdownProps {\n themes: string[]\n defaultTheme?: string\n onChange?: (theme: string) => void\n className?: string\n}\n\nexport interface ThemeControllerToggleProps {\n lightTheme?: string\n darkTheme?: string\n onChange?: (theme: string) => void\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n className?: string\n}\n\n// Get current theme from document\nfunction getCurrentTheme(): string | null {\n return document.documentElement.getAttribute('data-theme')\n}\n\n// Set theme directly on document (fallback when no ThemeProvider)\nfunction setThemeDirectly(theme: string) {\n document.documentElement.setAttribute('data-theme', theme)\n}\n\nfunction ThemeControllerSwap({\n lightTheme = 'light',\n darkTheme = 'dark',\n onChange,\n className = '',\n}: ThemeControllerSwapProps) {\n const { setTheme: contextSetTheme, isDark: contextIsDark } = useTheme()\n const setTheme = contextSetTheme ?? setThemeDirectly\n\n const [isDark, setIsDark] = useState(() => {\n if (contextIsDark !== undefined) return contextIsDark\n const current = getCurrentTheme()\n return current === darkTheme\n })\n\n // Sync with context or external theme changes\n useEffect(() => {\n if (contextIsDark !== undefined) {\n setIsDark(contextIsDark)\n return\n }\n const observer = new MutationObserver(() => {\n const current = getCurrentTheme()\n setIsDark(current === darkTheme)\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [darkTheme, contextIsDark])\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const checked = e.target.checked\n const theme = checked ? darkTheme : lightTheme\n setTheme(theme)\n onChange?.(theme)\n }\n\n return (\n <label className={`${dSwap} ${dSwapRotate} ${className}`}>\n <input\n type=\"checkbox\"\n checked={isDark}\n onChange={handleChange}\n />\n {/* sun icon */}\n <svg\n className={`${dSwapOff} h-8 w-8 fill-current`}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z\" />\n </svg>\n {/* moon icon */}\n <svg\n className={`${dSwapOn} h-8 w-8 fill-current`}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z\" />\n </svg>\n </label>\n )\n}\n\nfunction ThemeControllerDropdown({\n themes,\n defaultTheme,\n onChange,\n className = '',\n}: ThemeControllerDropdownProps) {\n const { setTheme: contextSetTheme, resolvedTheme } = useTheme()\n const setTheme = contextSetTheme ?? setThemeDirectly\n const radioName = useId()\n\n const [selectedTheme, setSelectedTheme] = useState(() => {\n if (resolvedTheme && themes.includes(resolvedTheme)) return resolvedTheme\n const current = getCurrentTheme()\n if (current && themes.includes(current)) return current\n return defaultTheme || themes[0] || 'light'\n })\n\n // Sync with context or external theme changes\n useEffect(() => {\n if (resolvedTheme && themes.includes(resolvedTheme)) {\n setSelectedTheme(resolvedTheme)\n return\n }\n const observer = new MutationObserver(() => {\n const current = getCurrentTheme()\n if (current && themes.includes(current)) {\n setSelectedTheme(current)\n }\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [themes, resolvedTheme])\n\n const handleChange = (theme: string) => {\n setSelectedTheme(theme)\n setTheme(theme)\n onChange?.(theme)\n }\n\n return (\n <div className={`${dDropdown} ${dDropdownEnd} ${className}`}>\n <div tabIndex={0} role=\"button\" className={dBtn}>\n Theme\n <svg\n width=\"12px\"\n height=\"12px\"\n className=\"inline-block h-2 w-2 fill-current opacity-60\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 2048 2048\"\n >\n <path d=\"M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z\"></path>\n </svg>\n </div>\n <ul\n tabIndex={0}\n className={`${dDropdownContent} bg-base-300 rounded-box z-[1] w-52 p-2 shadow-2xl max-h-96 overflow-y-auto`}\n >\n {themes.map((theme) => (\n <li key={theme}>\n <input\n type=\"radio\"\n name={radioName}\n className={`${dBtn} ${dBtnSm} ${dBtnBlock} ${dBtnGhost} justify-start`}\n aria-label={theme}\n value={theme}\n checked={selectedTheme === theme}\n onChange={() => handleChange(theme)}\n />\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\nconst sizeClasses: Record<string, string> = {\n xs: dToggleXs,\n sm: dToggleSm,\n md: dToggleMd,\n lg: dToggleLg,\n xl: dToggleXl,\n}\n\nfunction ThemeControllerToggle({\n lightTheme = 'light',\n darkTheme = 'dark',\n onChange,\n size,\n className = '',\n}: ThemeControllerToggleProps) {\n const { componentSize } = useConfig()\n const { setTheme: contextSetTheme, isDark: contextIsDark } = useTheme()\n const setTheme = contextSetTheme ?? setThemeDirectly\n const effectiveSize = size ?? componentSize ?? 'md'\n\n const [isDark, setIsDark] = useState(() => {\n if (contextIsDark !== undefined) return contextIsDark\n const current = getCurrentTheme()\n return current === darkTheme\n })\n\n // Sync with context or external theme changes\n useEffect(() => {\n if (contextIsDark !== undefined) {\n setIsDark(contextIsDark)\n return\n }\n const observer = new MutationObserver(() => {\n const current = getCurrentTheme()\n setIsDark(current === darkTheme)\n })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] })\n return () => observer.disconnect()\n }, [darkTheme, contextIsDark])\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const checked = e.target.checked\n const theme = checked ? darkTheme : lightTheme\n setTheme(theme)\n onChange?.(theme)\n }\n\n return (\n <input\n type=\"checkbox\"\n className={`${dToggle} ${sizeClasses[effectiveSize]} ${className}`}\n checked={isDark}\n onChange={handleChange}\n aria-label=\"Toggle theme\"\n />\n )\n}\n\nexport const ThemeController = Object.assign(\n {},\n {\n Swap: ThemeControllerSwap,\n Dropdown: ThemeControllerDropdown,\n Toggle: ThemeControllerToggle,\n }\n)\n"],"names":["dSwap","dSwapRotate","dSwapOff","dSwapOn","dDropdown","dDropdownEnd","dDropdownContent","dBtn","dBtnSm","dBtnBlock","dBtnGhost","dToggle","dToggleXs","dToggleSm","dToggleMd","dToggleLg","dToggleXl","getCurrentTheme","setThemeDirectly","theme","ThemeControllerSwap","lightTheme","darkTheme","onChange","className","contextSetTheme","contextIsDark","useTheme","setTheme","isDark","setIsDark","useState","useEffect","observer","current","handleChange","e","jsxs","jsx","ThemeControllerDropdown","themes","defaultTheme","resolvedTheme","radioName","useId","selectedTheme","setSelectedTheme","sizeClasses","ThemeControllerToggle","size","componentSize","useConfig","effectiveSize","ThemeController"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as E } from "react/jsx-runtime";
|
|
2
|
-
import { useState as
|
|
3
|
-
const
|
|
2
|
+
import { useState as d, useMemo as m, useCallback as k, useEffect as l, createContext as P, useContext as v } from "react";
|
|
3
|
+
const A = /* @__PURE__ */ new Set([
|
|
4
4
|
"dark",
|
|
5
5
|
"synthwave",
|
|
6
6
|
"halloween",
|
|
@@ -14,7 +14,7 @@ const L = /* @__PURE__ */ new Set([
|
|
|
14
14
|
"dim",
|
|
15
15
|
"sunset"
|
|
16
16
|
]), h = P(void 0);
|
|
17
|
-
function
|
|
17
|
+
function L(t) {
|
|
18
18
|
if (typeof document > "u") return "#000000";
|
|
19
19
|
const e = document.createElement("canvas");
|
|
20
20
|
e.width = e.height = 1;
|
|
@@ -40,7 +40,7 @@ function b() {
|
|
|
40
40
|
};
|
|
41
41
|
const t = getComputedStyle(document.documentElement), e = (n, f) => {
|
|
42
42
|
const o = t.getPropertyValue(n).trim();
|
|
43
|
-
return o ?
|
|
43
|
+
return o ? L(o) : f;
|
|
44
44
|
};
|
|
45
45
|
return {
|
|
46
46
|
background: e("--color-base-100", "#ffffff"),
|
|
@@ -55,7 +55,7 @@ function b() {
|
|
|
55
55
|
error: e("--color-error", "#f87272")
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
function
|
|
58
|
+
function M() {
|
|
59
59
|
return typeof window > "u" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
60
60
|
}
|
|
61
61
|
function H(t) {
|
|
@@ -73,7 +73,7 @@ function I(t, e) {
|
|
|
73
73
|
} catch {
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
function
|
|
76
|
+
function F({
|
|
77
77
|
children: t,
|
|
78
78
|
defaultTheme: e = "system",
|
|
79
79
|
storageKey: n = "asterui-theme",
|
|
@@ -81,12 +81,14 @@ function j({
|
|
|
81
81
|
darkTheme: o = "dark",
|
|
82
82
|
isDarkTheme: i
|
|
83
83
|
}) {
|
|
84
|
-
const [u, w] =
|
|
84
|
+
const [u, w] = d(() => H(n) || e), [a, C] = d(M), c = m(() => u === "system" ? a === "dark" ? o : f : u, [u, a, f, o]), g = m(() => i ? i(c) : A.has(c), [c, i]), [y, S] = d(b), p = k((r) => {
|
|
85
85
|
w(r), I(n, r);
|
|
86
86
|
}, [n]);
|
|
87
87
|
l(() => {
|
|
88
88
|
typeof document > "u" || (document.documentElement.setAttribute("data-theme", c), requestAnimationFrame(() => {
|
|
89
|
-
|
|
89
|
+
requestAnimationFrame(() => {
|
|
90
|
+
S(b());
|
|
91
|
+
});
|
|
90
92
|
}));
|
|
91
93
|
}, [c]), l(() => {
|
|
92
94
|
if (typeof window > "u") return;
|
|
@@ -107,22 +109,22 @@ function j({
|
|
|
107
109
|
isDark: g,
|
|
108
110
|
setTheme: p,
|
|
109
111
|
colors: y,
|
|
110
|
-
systemTheme:
|
|
111
|
-
}), [u, c, g, p, y,
|
|
112
|
+
systemTheme: a
|
|
113
|
+
}), [u, c, g, p, y, a]);
|
|
112
114
|
return /* @__PURE__ */ E(h.Provider, { value: x, children: t });
|
|
113
115
|
}
|
|
114
|
-
function
|
|
116
|
+
function R() {
|
|
115
117
|
const t = v(h);
|
|
116
118
|
if (!t)
|
|
117
119
|
throw new Error("useThemeContext must be used within a ThemeProvider");
|
|
118
120
|
return t;
|
|
119
121
|
}
|
|
120
|
-
function
|
|
122
|
+
function j() {
|
|
121
123
|
return v(h) !== void 0;
|
|
122
124
|
}
|
|
123
125
|
export {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
F as ThemeProvider,
|
|
127
|
+
j as useHasThemeProvider,
|
|
128
|
+
R as useThemeContext
|
|
127
129
|
};
|
|
128
130
|
//# sourceMappingURL=ThemeProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.js","sources":["../../src/components/ThemeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface ThemeProviderProps {\n children: React.ReactNode\n /** Default theme. Use \"system\" to follow browser preference. */\n defaultTheme?: string\n /** localStorage key for persisting theme. Set to false to disable persistence. */\n storageKey?: string | false\n /** Light theme to use when system preference is light */\n lightTheme?: string\n /** Dark theme to use when system preference is dark */\n darkTheme?: string\n /** Custom function to determine if a theme is dark */\n isDarkTheme?: (theme: string) => boolean\n}\n\nexport interface ThemeColors {\n background: string\n foreground: string\n primary: string\n primaryContent: string\n secondary: string\n accent: string\n info: string\n success: string\n warning: string\n error: string\n}\n\nexport interface ThemeContextValue {\n /** The theme setting (what user selected: \"system\", \"light\", \"dark\", etc.) */\n theme: string\n /** The actual applied theme after resolving \"system\" */\n resolvedTheme: string\n /** Whether the resolved theme is dark */\n isDark: boolean\n /** Set the theme */\n setTheme: (theme: string) => void\n /** Computed theme colors as hex values (for canvas/non-CSS contexts) */\n colors: ThemeColors\n /** The system preference (\"light\" or \"dark\") */\n systemTheme: 'light' | 'dark'\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined)\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(key: string | false): string | null {\n if (!key || typeof window === 'undefined') return null\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction storeTheme(key: string | false, theme: string): void {\n if (!key || typeof window === 'undefined') return\n try {\n localStorage.setItem(key, theme)\n } catch {\n // Ignore storage errors\n }\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'asterui-theme',\n lightTheme = 'light',\n darkTheme = 'dark',\n isDarkTheme,\n}: ThemeProviderProps) {\n // Initialize theme from storage or default\n const [theme, setThemeState] = useState<string>(() => {\n const stored = getStoredTheme(storageKey)\n return stored || defaultTheme\n })\n\n // Track system preference\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(getSystemTheme)\n\n // Resolve the actual theme\n const resolvedTheme = useMemo(() => {\n if (theme === 'system') {\n return systemTheme === 'dark' ? darkTheme : lightTheme\n }\n return theme\n }, [theme, systemTheme, lightTheme, darkTheme])\n\n // Determine if dark\n const isDark = useMemo(() => {\n if (isDarkTheme) return isDarkTheme(resolvedTheme)\n return DARK_THEMES.has(resolvedTheme)\n }, [resolvedTheme, isDarkTheme])\n\n // Track colors (updated after theme applies)\n const [colors, setColors] = useState<ThemeColors>(getThemeColors)\n\n // Set theme function\n const setTheme = useCallback((newTheme: string) => {\n setThemeState(newTheme)\n storeTheme(storageKey, newTheme)\n }, [storageKey])\n\n // Apply theme to document\n useEffect(() => {\n if (typeof document === 'undefined') return\n document.documentElement.setAttribute('data-theme', resolvedTheme)\n\n // Update colors after a frame to ensure CSS has applied\n requestAnimationFrame(() => {\n setColors(getThemeColors())\n })\n }, [resolvedTheme])\n\n // Listen for system preference changes\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = (e: MediaQueryListEvent) => {\n setSystemTheme(e.matches ? 'dark' : 'light')\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [])\n\n // Listen for storage changes (cross-tab sync)\n useEffect(() => {\n if (!storageKey || typeof window === 'undefined') return\n\n const handleStorage = (e: StorageEvent) => {\n if (e.key === storageKey && e.newValue) {\n setThemeState(e.newValue)\n }\n }\n\n window.addEventListener('storage', handleStorage)\n return () => window.removeEventListener('storage', handleStorage)\n }, [storageKey])\n\n const value = useMemo<ThemeContextValue>(() => ({\n theme,\n resolvedTheme,\n isDark,\n setTheme,\n colors,\n systemTheme,\n }), [theme, resolvedTheme, isDark, setTheme, colors, systemTheme])\n\n return (\n <ThemeContext.Provider value={value}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\n/**\n * Hook to access theme context.\n * Must be used within a ThemeProvider.\n */\nexport function useThemeContext(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useThemeContext must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Check if ThemeProvider is present in the tree.\n */\nexport function useHasThemeProvider(): boolean {\n return useContext(ThemeContext) !== undefined\n}\n"],"names":["DARK_THEMES","ThemeContext","createContext","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getStoredTheme","key","storeTheme","theme","ThemeProvider","children","defaultTheme","storageKey","lightTheme","darkTheme","isDarkTheme","setThemeState","useState","systemTheme","setSystemTheme","resolvedTheme","useMemo","isDark","colors","setColors","setTheme","useCallback","newTheme","useEffect","mediaQuery","handleChange","e","handleStorage","jsx","useThemeContext","context","useContext","useHasThemeProvider"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThemeProvider.js","sources":["../../src/components/ThemeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface ThemeProviderProps {\n children: React.ReactNode\n /** Default theme. Use \"system\" to follow browser preference. */\n defaultTheme?: string\n /** localStorage key for persisting theme. Set to false to disable persistence. */\n storageKey?: string | false\n /** Light theme to use when system preference is light */\n lightTheme?: string\n /** Dark theme to use when system preference is dark */\n darkTheme?: string\n /** Custom function to determine if a theme is dark */\n isDarkTheme?: (theme: string) => boolean\n}\n\nexport interface ThemeColors {\n background: string\n foreground: string\n primary: string\n primaryContent: string\n secondary: string\n accent: string\n info: string\n success: string\n warning: string\n error: string\n}\n\nexport interface ThemeContextValue {\n /** The theme setting (what user selected: \"system\", \"light\", \"dark\", etc.) */\n theme: string\n /** The actual applied theme after resolving \"system\" */\n resolvedTheme: string\n /** Whether the resolved theme is dark */\n isDark: boolean\n /** Set the theme */\n setTheme: (theme: string) => void\n /** Computed theme colors as hex values (for canvas/non-CSS contexts) */\n colors: ThemeColors\n /** The system preference (\"light\" or \"dark\") */\n systemTheme: 'light' | 'dark'\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined)\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(key: string | false): string | null {\n if (!key || typeof window === 'undefined') return null\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction storeTheme(key: string | false, theme: string): void {\n if (!key || typeof window === 'undefined') return\n try {\n localStorage.setItem(key, theme)\n } catch {\n // Ignore storage errors\n }\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'asterui-theme',\n lightTheme = 'light',\n darkTheme = 'dark',\n isDarkTheme,\n}: ThemeProviderProps) {\n // Initialize theme from storage or default\n const [theme, setThemeState] = useState<string>(() => {\n const stored = getStoredTheme(storageKey)\n return stored || defaultTheme\n })\n\n // Track system preference\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(getSystemTheme)\n\n // Resolve the actual theme\n const resolvedTheme = useMemo(() => {\n if (theme === 'system') {\n return systemTheme === 'dark' ? darkTheme : lightTheme\n }\n return theme\n }, [theme, systemTheme, lightTheme, darkTheme])\n\n // Determine if dark\n const isDark = useMemo(() => {\n if (isDarkTheme) return isDarkTheme(resolvedTheme)\n return DARK_THEMES.has(resolvedTheme)\n }, [resolvedTheme, isDarkTheme])\n\n // Track colors (updated after theme applies)\n const [colors, setColors] = useState<ThemeColors>(getThemeColors)\n\n // Set theme function\n const setTheme = useCallback((newTheme: string) => {\n setThemeState(newTheme)\n storeTheme(storageKey, newTheme)\n }, [storageKey])\n\n // Apply theme to document\n useEffect(() => {\n if (typeof document === 'undefined') return\n document.documentElement.setAttribute('data-theme', resolvedTheme)\n\n // Double RAF ensures CSS has fully recalculated after attribute change\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setColors(getThemeColors())\n })\n })\n }, [resolvedTheme])\n\n // Listen for system preference changes\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = (e: MediaQueryListEvent) => {\n setSystemTheme(e.matches ? 'dark' : 'light')\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [])\n\n // Listen for storage changes (cross-tab sync)\n useEffect(() => {\n if (!storageKey || typeof window === 'undefined') return\n\n const handleStorage = (e: StorageEvent) => {\n if (e.key === storageKey && e.newValue) {\n setThemeState(e.newValue)\n }\n }\n\n window.addEventListener('storage', handleStorage)\n return () => window.removeEventListener('storage', handleStorage)\n }, [storageKey])\n\n const value = useMemo<ThemeContextValue>(() => ({\n theme,\n resolvedTheme,\n isDark,\n setTheme,\n colors,\n systemTheme,\n }), [theme, resolvedTheme, isDark, setTheme, colors, systemTheme])\n\n return (\n <ThemeContext.Provider value={value}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\n/**\n * Hook to access theme context.\n * Must be used within a ThemeProvider.\n */\nexport function useThemeContext(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useThemeContext must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Check if ThemeProvider is present in the tree.\n */\nexport function useHasThemeProvider(): boolean {\n return useContext(ThemeContext) !== undefined\n}\n"],"names":["DARK_THEMES","ThemeContext","createContext","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getStoredTheme","key","storeTheme","theme","ThemeProvider","children","defaultTheme","storageKey","lightTheme","darkTheme","isDarkTheme","setThemeState","useState","systemTheme","setSystemTheme","resolvedTheme","useMemo","isDark","colors","setColors","setTheme","useCallback","newTheme","useEffect","mediaQuery","handleChange","e","handleStorage","jsx","useThemeContext","context","useContext","useHasThemeProvider"],"mappings":";;AAGA,MAAMA,wBAAkB,IAAI;AAAA,EAC1B;AAAA,EAAQ;AAAA,EAAa;AAAA,EAAa;AAAA,EAAU;AAAA,EAAS;AAAA,EAAU;AAAA,EAC/D;AAAA,EAAY;AAAA,EAAS;AAAA,EAAU;AAAA,EAAO;AACxC,CAAC,GA4CKC,IAAeC,EAA6C,MAAS;AAG3E,SAASC,EAAWC,GAAuB;AACzC,MAAI,OAAO,WAAa,IAAa,QAAO;AAC5C,QAAMC,IAAS,SAAS,cAAc,QAAQ;AAC9C,EAAAA,EAAO,QAAQA,EAAO,SAAS;AAC/B,QAAMC,IAAMD,EAAO,WAAW,IAAI;AAClC,MAAI,CAACC,EAAK,QAAO;AACjB,EAAAA,EAAI,YAAYF,GAChBE,EAAI,SAAS,GAAG,GAAG,GAAG,CAAC;AACvB,QAAM,CAACC,GAAGC,GAAGC,CAAC,IAAIH,EAAI,aAAa,GAAG,GAAG,GAAG,CAAC,EAAE;AAC/C,SAAO,MAAM,KAAK,OAAOC,KAAK,OAAOC,KAAK,KAAKC,GAAG,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACzE;AAEA,SAASC,IAA8B;AACrC,MAAI,OAAO,WAAa;AACtB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,MACT,OAAO;AAAA,IAAA;AAIX,QAAMC,IAAQ,iBAAiB,SAAS,eAAe,GACjDC,IAAW,CAACC,GAAiBC,MAA6B;AAC9D,UAAMC,IAAQJ,EAAM,iBAAiBE,CAAO,EAAE,KAAA;AAC9C,WAAOE,IAAQZ,EAAWY,CAAK,IAAID;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,YAAYF,EAAS,oBAAoB,SAAS;AAAA,IAClD,YAAYA,EAAS,wBAAwB,SAAS;AAAA,IACtD,SAASA,EAAS,mBAAmB,SAAS;AAAA,IAC9C,gBAAgBA,EAAS,2BAA2B,SAAS;AAAA,IAC7D,WAAWA,EAAS,qBAAqB,SAAS;AAAA,IAClD,QAAQA,EAAS,kBAAkB,SAAS;AAAA,IAC5C,MAAMA,EAAS,gBAAgB,SAAS;AAAA,IACxC,SAASA,EAAS,mBAAmB,SAAS;AAAA,IAC9C,SAASA,EAAS,mBAAmB,SAAS;AAAA,IAC9C,OAAOA,EAAS,iBAAiB,SAAS;AAAA,EAAA;AAE9C;AAEA,SAASI,IAAmC;AAC1C,SAAI,OAAO,SAAW,MAAoB,UACnC,OAAO,WAAW,8BAA8B,EAAE,UAAU,SAAS;AAC9E;AAEA,SAASC,EAAeC,GAAoC;AAC1D,MAAI,CAACA,KAAO,OAAO,SAAW,IAAa,QAAO;AAClD,MAAI;AACF,WAAO,aAAa,QAAQA,CAAG;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAASC,EAAWD,GAAqBE,GAAqB;AAC5D,MAAI,GAACF,KAAO,OAAO,SAAW;AAC9B,QAAI;AACF,mBAAa,QAAQA,GAAKE,CAAK;AAAA,IACjC,QAAQ;AAAA,IAER;AACF;AAEO,SAASC,EAAc;AAAA,EAC5B,UAAAC;AAAA,EACA,cAAAC,IAAe;AAAA,EACf,YAAAC,IAAa;AAAA,EACb,YAAAC,IAAa;AAAA,EACb,WAAAC,IAAY;AAAA,EACZ,aAAAC;AACF,GAAuB;AAErB,QAAM,CAACP,GAAOQ,CAAa,IAAIC,EAAiB,MAC/BZ,EAAeO,CAAU,KACvBD,CAClB,GAGK,CAACO,GAAaC,CAAc,IAAIF,EAA2Bb,CAAc,GAGzEgB,IAAgBC,EAAQ,MACxBb,MAAU,WACLU,MAAgB,SAASJ,IAAYD,IAEvCL,GACN,CAACA,GAAOU,GAAaL,GAAYC,CAAS,CAAC,GAGxCQ,IAASD,EAAQ,MACjBN,IAAoBA,EAAYK,CAAa,IAC1ChC,EAAY,IAAIgC,CAAa,GACnC,CAACA,GAAeL,CAAW,CAAC,GAGzB,CAACQ,GAAQC,CAAS,IAAIP,EAAsBnB,CAAc,GAG1D2B,IAAWC,EAAY,CAACC,MAAqB;AACjD,IAAAX,EAAcW,CAAQ,GACtBpB,EAAWK,GAAYe,CAAQ;AAAA,EACjC,GAAG,CAACf,CAAU,CAAC;AAGf,EAAAgB,EAAU,MAAM;AACd,IAAI,OAAO,WAAa,QACxB,SAAS,gBAAgB,aAAa,cAAcR,CAAa,GAGjE,sBAAsB,MAAM;AAC1B,4BAAsB,MAAM;AAC1B,QAAAI,EAAU1B,GAAgB;AAAA,MAC5B,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAACsB,CAAa,CAAC,GAGlBQ,EAAU,MAAM;AACd,QAAI,OAAO,SAAW,IAAa;AAEnC,UAAMC,IAAa,OAAO,WAAW,8BAA8B,GAC7DC,IAAe,CAACC,MAA2B;AAC/C,MAAAZ,EAAeY,EAAE,UAAU,SAAS,OAAO;AAAA,IAC7C;AAEA,WAAAF,EAAW,iBAAiB,UAAUC,CAAY,GAC3C,MAAMD,EAAW,oBAAoB,UAAUC,CAAY;AAAA,EACpE,GAAG,CAAA,CAAE,GAGLF,EAAU,MAAM;AACd,QAAI,CAAChB,KAAc,OAAO,SAAW,IAAa;AAElD,UAAMoB,IAAgB,CAACD,MAAoB;AACzC,MAAIA,EAAE,QAAQnB,KAAcmB,EAAE,YAC5Bf,EAAce,EAAE,QAAQ;AAAA,IAE5B;AAEA,kBAAO,iBAAiB,WAAWC,CAAa,GACzC,MAAM,OAAO,oBAAoB,WAAWA,CAAa;AAAA,EAClE,GAAG,CAACpB,CAAU,CAAC;AAEf,QAAMT,IAAQkB,EAA2B,OAAO;AAAA,IAC9C,OAAAb;AAAA,IACA,eAAAY;AAAA,IACA,QAAAE;AAAA,IACA,UAAAG;AAAA,IACA,QAAAF;AAAA,IACA,aAAAL;AAAA,EAAA,IACE,CAACV,GAAOY,GAAeE,GAAQG,GAAUF,GAAQL,CAAW,CAAC;AAEjE,SACE,gBAAAe,EAAC5C,EAAa,UAAb,EAAsB,OAAAc,GACpB,UAAAO,EAAA,CACH;AAEJ;AAMO,SAASwB,IAAqC;AACnD,QAAMC,IAAUC,EAAW/C,CAAY;AACvC,MAAI,CAAC8C;AACH,UAAM,IAAI,MAAM,qDAAqD;AAEvE,SAAOA;AACT;AAKO,SAASE,IAA+B;AAC7C,SAAOD,EAAW/C,CAAY,MAAM;AACtC;"}
|
package/dist/hooks/useTheme.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState as i, useEffect as
|
|
1
|
+
import { useState as i, useEffect as m, useMemo as u } from "react";
|
|
2
2
|
import { useHasThemeProvider as d, useThemeContext as l } from "../components/ThemeProvider.js";
|
|
3
3
|
const h = /* @__PURE__ */ new Set([
|
|
4
4
|
"dark",
|
|
@@ -80,14 +80,16 @@ function T() {
|
|
|
80
80
|
isDark: !1,
|
|
81
81
|
colors: a()
|
|
82
82
|
}));
|
|
83
|
-
return
|
|
83
|
+
return m(() => {
|
|
84
84
|
const t = () => {
|
|
85
85
|
const s = b(), f = y();
|
|
86
86
|
let c = !1;
|
|
87
87
|
s ? c = h.has(s) : c = f === "dark", requestAnimationFrame(() => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
requestAnimationFrame(() => {
|
|
89
|
+
e({
|
|
90
|
+
isDark: c,
|
|
91
|
+
colors: a()
|
|
92
|
+
});
|
|
91
93
|
});
|
|
92
94
|
});
|
|
93
95
|
};
|
|
@@ -101,7 +103,7 @@ function T() {
|
|
|
101
103
|
return o.addEventListener("change", t), () => {
|
|
102
104
|
n.disconnect(), o.removeEventListener("change", t);
|
|
103
105
|
};
|
|
104
|
-
}, []),
|
|
106
|
+
}, []), u(() => ({
|
|
105
107
|
theme: void 0,
|
|
106
108
|
resolvedTheme: void 0,
|
|
107
109
|
isDark: r.isDark,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTheme.js","sources":["../../src/hooks/useTheme.ts"],"sourcesContent":["import { useEffect, useState, useMemo } from 'react'\nimport { useHasThemeProvider, useThemeContext, type ThemeColors } from '../components/ThemeProvider'\n\nexport type { ThemeColors }\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface UseThemeReturn {\n /** The theme setting (what user selected). Only available with ThemeProvider. */\n theme: string | undefined\n /** The actual applied theme. Only available with ThemeProvider. */\n resolvedTheme: string | undefined\n /** Whether dark mode is active */\n isDark: boolean\n /** Set the theme. Only available with ThemeProvider. */\n setTheme: ((theme: string) => void) | undefined\n /** Computed theme colors as hex values */\n colors: ThemeColors\n /** The system preference. Only available with ThemeProvider. */\n systemTheme: 'light' | 'dark' | undefined\n}\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getCurrentTheme(): string | null {\n if (typeof document === 'undefined') return null\n return document.documentElement.getAttribute('data-theme')\n}\n\n/**\n * Hook to detect current theme and get computed colors.\n *\n * When used within a ThemeProvider, returns full theme control including\n * setTheme, theme selection, and resolved theme.\n *\n * When used standalone (without ThemeProvider), provides read-only access\n * to isDark and colors based on the current data-theme attribute and\n * system preference.\n *\n * @example\n * // With ThemeProvider (full control)\n * const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()\n * setTheme('dark')\n * setTheme('system')\n *\n * @example\n * // Without ThemeProvider (read-only)\n * const { isDark, colors } = useTheme()\n * // colors.primary, colors.foreground, etc.\n */\nexport function useTheme(): UseThemeReturn {\n const hasProvider = useHasThemeProvider()\n\n // If we have a provider, use its context\n if (hasProvider) {\n // This is safe because hasProvider is stable after initial render\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const context = useThemeContext()\n return {\n theme: context.theme,\n resolvedTheme: context.resolvedTheme,\n isDark: context.isDark,\n setTheme: context.setTheme,\n colors: context.colors,\n systemTheme: context.systemTheme,\n }\n }\n\n // Standalone mode - no provider\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useThemeStandalone()\n}\n\n/**\n * Standalone theme detection (no ThemeProvider)\n */\nfunction useThemeStandalone(): UseThemeReturn {\n const [state, setState] = useState<{ isDark: boolean; colors: ThemeColors }>(() => ({\n isDark: false,\n colors: getThemeColors(),\n }))\n\n useEffect(() => {\n const updateTheme = () => {\n const currentTheme = getCurrentTheme()\n const systemTheme = getSystemTheme()\n\n // Determine if dark based on data-theme or system preference\n let isDark = false\n if (currentTheme) {\n isDark = DARK_THEMES.has(currentTheme)\n } else {\n isDark = systemTheme === 'dark'\n }\n\n //
|
|
1
|
+
{"version":3,"file":"useTheme.js","sources":["../../src/hooks/useTheme.ts"],"sourcesContent":["import { useEffect, useState, useMemo } from 'react'\nimport { useHasThemeProvider, useThemeContext, type ThemeColors } from '../components/ThemeProvider'\n\nexport type { ThemeColors }\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface UseThemeReturn {\n /** The theme setting (what user selected). Only available with ThemeProvider. */\n theme: string | undefined\n /** The actual applied theme. Only available with ThemeProvider. */\n resolvedTheme: string | undefined\n /** Whether dark mode is active */\n isDark: boolean\n /** Set the theme. Only available with ThemeProvider. */\n setTheme: ((theme: string) => void) | undefined\n /** Computed theme colors as hex values */\n colors: ThemeColors\n /** The system preference. Only available with ThemeProvider. */\n systemTheme: 'light' | 'dark' | undefined\n}\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getCurrentTheme(): string | null {\n if (typeof document === 'undefined') return null\n return document.documentElement.getAttribute('data-theme')\n}\n\n/**\n * Hook to detect current theme and get computed colors.\n *\n * When used within a ThemeProvider, returns full theme control including\n * setTheme, theme selection, and resolved theme.\n *\n * When used standalone (without ThemeProvider), provides read-only access\n * to isDark and colors based on the current data-theme attribute and\n * system preference.\n *\n * @example\n * // With ThemeProvider (full control)\n * const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()\n * setTheme('dark')\n * setTheme('system')\n *\n * @example\n * // Without ThemeProvider (read-only)\n * const { isDark, colors } = useTheme()\n * // colors.primary, colors.foreground, etc.\n */\nexport function useTheme(): UseThemeReturn {\n const hasProvider = useHasThemeProvider()\n\n // If we have a provider, use its context\n if (hasProvider) {\n // This is safe because hasProvider is stable after initial render\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const context = useThemeContext()\n return {\n theme: context.theme,\n resolvedTheme: context.resolvedTheme,\n isDark: context.isDark,\n setTheme: context.setTheme,\n colors: context.colors,\n systemTheme: context.systemTheme,\n }\n }\n\n // Standalone mode - no provider\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useThemeStandalone()\n}\n\n/**\n * Standalone theme detection (no ThemeProvider)\n */\nfunction useThemeStandalone(): UseThemeReturn {\n const [state, setState] = useState<{ isDark: boolean; colors: ThemeColors }>(() => ({\n isDark: false,\n colors: getThemeColors(),\n }))\n\n useEffect(() => {\n const updateTheme = () => {\n const currentTheme = getCurrentTheme()\n const systemTheme = getSystemTheme()\n\n // Determine if dark based on data-theme or system preference\n let isDark = false\n if (currentTheme) {\n isDark = DARK_THEMES.has(currentTheme)\n } else {\n isDark = systemTheme === 'dark'\n }\n\n // Double RAF ensures CSS has fully recalculated\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setState({\n isDark,\n colors: getThemeColors(),\n })\n })\n })\n }\n\n updateTheme()\n\n // Watch for theme changes via attribute mutation\n const observer = new MutationObserver(updateTheme)\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class']\n })\n\n // Watch for system preference changes\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n mediaQuery.addEventListener('change', updateTheme)\n\n return () => {\n observer.disconnect()\n mediaQuery.removeEventListener('change', updateTheme)\n }\n }, [])\n\n return useMemo(() => ({\n theme: undefined,\n resolvedTheme: undefined,\n isDark: state.isDark,\n setTheme: undefined,\n colors: state.colors,\n systemTheme: undefined,\n }), [state.isDark, state.colors])\n}\n\nexport default useTheme\n"],"names":["DARK_THEMES","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getCurrentTheme","useTheme","useHasThemeProvider","context","useThemeContext","useThemeStandalone","state","setState","useState","useEffect","updateTheme","currentTheme","systemTheme","isDark","observer","mediaQuery","useMemo"],"mappings":"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"}
|