@kbach/react 0.6.51 → 1.0.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/README.md +90 -418
- package/dist/chunk-AFY36PRX.mjs +414 -0
- package/dist/chunk-G3RJJNSY.mjs +100 -0
- package/dist/chunk-VBPQAIZL.mjs +32 -0
- package/dist/index.d.mts +117 -0
- package/dist/index.d.ts +84 -295
- package/dist/index.js +618 -1101
- package/dist/index.mjs +143 -550
- package/dist/jsx-dev-runtime.d.mts +22 -0
- package/dist/jsx-dev-runtime.d.ts +6 -5
- package/dist/jsx-dev-runtime.js +453 -695
- package/dist/jsx-dev-runtime.mjs +3 -3
- package/dist/jsx-runtime.d.mts +22 -0
- package/dist/jsx-runtime.d.ts +11 -6
- package/dist/jsx-runtime.js +455 -706
- package/dist/jsx-runtime.mjs +4 -9
- package/dist/theme-BKAb78KP.d.mts +20 -0
- package/dist/theme-BKAb78KP.d.ts +20 -0
- package/dist/vite-plugin.d.mts +20 -147
- package/dist/vite-plugin.d.ts +20 -147
- package/dist/vite-plugin.js +1380 -3722
- package/dist/vite-plugin.mjs +994 -3713
- package/package.json +20 -46
- package/KBACH.md +0 -1044
- package/dist/chunk-KRRGLJLA.mjs +0 -4187
- package/dist/chunk-LIUJPOTS.mjs +0 -208
- package/dist/core/index.d.ts +0 -1346
- package/dist/core/index.js +0 -3712
- package/dist/native.d.ts +0 -314
- package/dist/native.js +0 -99
- package/dist/web-substitute-CfpYFnE7.d.ts +0 -22
- package/jsx-dev-runtime.js +0 -3
- package/jsx-runtime.js +0 -3
- package/kbach-react.md +0 -889
- package/scripts/postinstall.js +0 -28
- package/src/native/babel/index.js +0 -30
- package/src/native/babel-plugin/index.js +0 -605
- package/src/native/babel-plugin/index.test.ts +0 -86
- package/types.d.ts +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,589 +1,182 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
|
+
disableRuntimeCSS,
|
|
3
|
+
initKbach,
|
|
4
|
+
isKbachReady,
|
|
5
|
+
isRuntimeCSSDisabled,
|
|
6
|
+
kb
|
|
7
|
+
} from "./chunk-G3RJJNSY.mjs";
|
|
2
8
|
import {
|
|
3
9
|
BASE_RESET,
|
|
4
|
-
EMPTY_BREAKPOINTS,
|
|
5
|
-
InteractiveWrapper,
|
|
6
10
|
RESET_STYLE_ID,
|
|
7
|
-
buildConfig,
|
|
8
|
-
chain,
|
|
9
|
-
clearCache,
|
|
10
|
-
composeNativeStyle,
|
|
11
|
-
defaultColors,
|
|
12
11
|
defaultTheme,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
getActiveBreakpoints,
|
|
17
|
-
getConfig,
|
|
18
|
-
getEffectiveIsWeb,
|
|
19
|
-
getGlobalScreens,
|
|
20
|
-
getImpliedRNClasses,
|
|
21
|
-
getWebTag,
|
|
22
|
-
hasInteractiveBuckets,
|
|
23
|
-
hasResponsiveBuckets,
|
|
24
|
-
initConfig,
|
|
25
|
-
isModeAwareColor,
|
|
26
|
-
isNative,
|
|
27
|
-
isWeb,
|
|
28
|
-
kbachWarn,
|
|
29
|
-
normalizeClassString,
|
|
30
|
-
onConfigChange,
|
|
31
|
-
parseClass,
|
|
32
|
-
parseClasses,
|
|
33
|
-
parseHexRgb,
|
|
34
|
-
registerWebElement,
|
|
35
|
-
resetConfig,
|
|
36
|
-
resolve,
|
|
37
|
-
setGlobalDarkMode,
|
|
38
|
-
setGlobalWidth,
|
|
39
|
-
setResolveTarget,
|
|
40
|
-
splitClassTokens,
|
|
41
|
-
syncGlobalDarkMode,
|
|
42
|
-
syncGlobalScreens,
|
|
43
|
-
syncGlobalWidth,
|
|
44
|
-
transformToWebProps,
|
|
45
|
-
updateConfig,
|
|
46
|
-
useConditionalGlobalDarkMode,
|
|
47
|
-
useConditionalWidth,
|
|
48
|
-
useGlobalDarkMode,
|
|
49
|
-
useGlobalWidth,
|
|
50
|
-
useSyncExternalStore
|
|
51
|
-
} from "./chunk-KRRGLJLA.mjs";
|
|
52
|
-
|
|
53
|
-
// src/context.tsx
|
|
54
|
-
import { createContext, useContext } from "react";
|
|
55
|
-
var ThemeContext = globalThis.__kbachThemeContext ?? (globalThis.__kbachThemeContext = createContext(null));
|
|
56
|
-
function useTheme() {
|
|
57
|
-
const ctx = useContext(ThemeContext);
|
|
58
|
-
if (!ctx) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
"[Kbach] useTheme() must be called inside a <ThemeProvider>. Wrap your app root with <ThemeProvider>."
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
return ctx;
|
|
64
|
-
}
|
|
65
|
-
function useIsDark() {
|
|
66
|
-
return useTheme().isDark;
|
|
67
|
-
}
|
|
12
|
+
getTheme,
|
|
13
|
+
setTheme
|
|
14
|
+
} from "./chunk-AFY36PRX.mjs";
|
|
68
15
|
|
|
69
|
-
// src/
|
|
70
|
-
import {
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const rgb = parseHexRgb(color);
|
|
75
|
-
if (!rgb) return color;
|
|
76
|
-
const [r, g, b] = rgb;
|
|
77
|
-
return `rgba(${r},${g},${b},${a})`;
|
|
78
|
-
}
|
|
79
|
-
if (color.startsWith("rgb(")) return color.replace("rgb(", "rgba(").replace(")", `,${a})`);
|
|
80
|
-
if (color.startsWith("rgba(")) return color.replace(/,\s*[\d.]+\)$/, `,${a})`);
|
|
81
|
-
return color;
|
|
82
|
-
}
|
|
83
|
-
function pickSide(value, isDark) {
|
|
84
|
-
return isModeAwareColor(value) ? isDark ? value.dark : value.light : value;
|
|
85
|
-
}
|
|
86
|
-
function makeShadeProxy(shades, isDark) {
|
|
87
|
-
return new Proxy(shades, {
|
|
88
|
-
get(target, prop) {
|
|
89
|
-
const key = String(prop);
|
|
90
|
-
if (key === "then") return void 0;
|
|
91
|
-
if (key.includes("/")) {
|
|
92
|
-
const slash = key.indexOf("/");
|
|
93
|
-
const shade = key.slice(0, slash);
|
|
94
|
-
const op = Number(key.slice(slash + 1));
|
|
95
|
-
const color2 = target[shade];
|
|
96
|
-
return color2 !== void 0 ? applyOpacity(pickSide(color2, isDark), op) : void 0;
|
|
97
|
-
}
|
|
98
|
-
const color = target[key];
|
|
99
|
-
return color !== void 0 ? pickSide(color, isDark) : void 0;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
function wrapColors(rawColors, isDark = false) {
|
|
104
|
-
const cache = /* @__PURE__ */ new Map();
|
|
105
|
-
const alpha = (color, opacity) => opacity === void 0 ? color : applyOpacity(color, opacity);
|
|
106
|
-
return new Proxy({ alpha }, {
|
|
107
|
-
get(_, prop) {
|
|
108
|
-
const key = String(prop);
|
|
109
|
-
if (key === "then") return void 0;
|
|
110
|
-
if (key === "alpha") return alpha;
|
|
111
|
-
if (key.includes("/")) {
|
|
112
|
-
const slash = key.indexOf("/");
|
|
113
|
-
const name = key.slice(0, slash);
|
|
114
|
-
const op = Number(key.slice(slash + 1));
|
|
115
|
-
const entry2 = rawColors[name];
|
|
116
|
-
return entry2 !== void 0 && (typeof entry2 === "string" || isModeAwareColor(entry2)) ? applyOpacity(pickSide(entry2, isDark), op) : void 0;
|
|
117
|
-
}
|
|
118
|
-
const entry = rawColors[key];
|
|
119
|
-
if (entry === void 0) return void 0;
|
|
120
|
-
if (typeof entry === "string" || isModeAwareColor(entry)) return pickSide(entry, isDark);
|
|
121
|
-
const cacheKey = `${key}:${isDark}`;
|
|
122
|
-
if (!cache.has(cacheKey)) cache.set(cacheKey, makeShadeProxy(entry, isDark));
|
|
123
|
-
return cache.get(cacheKey);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
function useColors() {
|
|
128
|
-
const { config, isDark } = useTheme();
|
|
129
|
-
return useMemo(() => wrapColors(config.theme.colors, isDark), [config.theme.colors, isDark]);
|
|
16
|
+
// src/KbachReset.tsx
|
|
17
|
+
import { jsx } from "react/jsx-runtime";
|
|
18
|
+
function KbachReset() {
|
|
19
|
+
if (isRuntimeCSSDisabled()) return null;
|
|
20
|
+
return /* @__PURE__ */ jsx("style", { id: RESET_STYLE_ID, children: BASE_RESET });
|
|
130
21
|
}
|
|
131
22
|
|
|
132
|
-
// src/
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
23
|
+
// src/darkModeStore.ts
|
|
24
|
+
var STORAGE_KEY = "kbach-theme";
|
|
25
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
26
|
+
function systemPrefersDark() {
|
|
27
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
28
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
136
29
|
}
|
|
137
|
-
function
|
|
138
|
-
|
|
139
|
-
return useMemo2(() => wrapSpacing(config.theme.spacing), [config.theme.spacing]);
|
|
30
|
+
function storage() {
|
|
31
|
+
return typeof window === "undefined" ? null : window.localStorage;
|
|
140
32
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
useCallback,
|
|
145
|
-
useEffect,
|
|
146
|
-
useLayoutEffect,
|
|
147
|
-
useMemo as useMemo3,
|
|
148
|
-
useRef,
|
|
149
|
-
useState
|
|
150
|
-
} from "react";
|
|
151
|
-
import { jsx } from "react/jsx-runtime";
|
|
152
|
-
var useIsomorphicLayoutEffect = isWeb || isNative ? useLayoutEffect : useEffect;
|
|
153
|
-
var STORAGE_KEY = "kbach-theme";
|
|
154
|
-
var _mountedProviderCount = 0;
|
|
155
|
-
var _warnedMultipleProviders = false;
|
|
156
|
-
var _mountedConfigOverrideCount = 0;
|
|
157
|
-
var _warnedConfigOverride = false;
|
|
158
|
-
var _warnedMediaModeToggle = false;
|
|
159
|
-
function loadPersistedMode() {
|
|
33
|
+
function readPersistedMode() {
|
|
34
|
+
const ls = storage();
|
|
35
|
+
if (!ls) return null;
|
|
160
36
|
try {
|
|
161
|
-
|
|
162
|
-
return null;
|
|
37
|
+
const raw = ls.getItem(STORAGE_KEY);
|
|
38
|
+
return raw === "light" || raw === "dark" || raw === "system" ? raw : null;
|
|
163
39
|
} catch {
|
|
164
40
|
return null;
|
|
165
41
|
}
|
|
166
42
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
43
|
+
function resolveIsDark(m) {
|
|
44
|
+
return m === "system" ? systemPrefersDark() : m === "dark";
|
|
45
|
+
}
|
|
46
|
+
function applyToDom(dark) {
|
|
47
|
+
if (typeof document === "undefined") return;
|
|
48
|
+
const root = document.documentElement;
|
|
49
|
+
const strategy = getTheme().darkMode;
|
|
50
|
+
if (strategy === "class") {
|
|
51
|
+
root.classList.toggle("dark", dark);
|
|
52
|
+
root.classList.toggle("light", !dark);
|
|
53
|
+
} else if (strategy === "attribute") {
|
|
54
|
+
root.setAttribute("data-theme", dark ? "dark" : "light");
|
|
171
55
|
}
|
|
172
56
|
}
|
|
173
|
-
|
|
174
|
-
|
|
57
|
+
var persistedMode = readPersistedMode();
|
|
58
|
+
var hadPersistedMode = persistedMode !== null;
|
|
59
|
+
var mode = persistedMode ?? "system";
|
|
60
|
+
var isDark = resolveIsDark(mode);
|
|
61
|
+
applyToDom(isDark);
|
|
62
|
+
if (typeof window !== "undefined" && window.matchMedia) {
|
|
63
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
64
|
+
const onSystemChange = (event) => {
|
|
65
|
+
if (mode !== "system") return;
|
|
66
|
+
isDark = event.matches;
|
|
67
|
+
applyToDom(isDark);
|
|
68
|
+
notify();
|
|
175
69
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
70
|
+
if (mq.addEventListener) mq.addEventListener("change", onSystemChange);
|
|
71
|
+
else mq.addListener(onSystemChange);
|
|
72
|
+
}
|
|
73
|
+
function notify() {
|
|
74
|
+
for (const listener of listeners) listener();
|
|
75
|
+
}
|
|
76
|
+
function getGlobalDarkMode() {
|
|
77
|
+
return isDark;
|
|
78
|
+
}
|
|
79
|
+
function getGlobalThemeMode() {
|
|
80
|
+
return mode;
|
|
81
|
+
}
|
|
82
|
+
function setGlobalThemeMode(nextMode) {
|
|
83
|
+
mode = nextMode;
|
|
84
|
+
isDark = resolveIsDark(nextMode);
|
|
85
|
+
applyToDom(isDark);
|
|
86
|
+
const ls = storage();
|
|
87
|
+
if (ls) {
|
|
88
|
+
try {
|
|
89
|
+
ls.setItem(STORAGE_KEY, nextMode);
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
notify();
|
|
181
94
|
}
|
|
182
|
-
function
|
|
183
|
-
|
|
184
|
-
return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
|
|
95
|
+
function toggleGlobalDarkMode() {
|
|
96
|
+
setGlobalThemeMode(isDark ? "light" : "dark");
|
|
185
97
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
98
|
+
function subscribeGlobalDarkMode(listener) {
|
|
99
|
+
listeners.add(listener);
|
|
100
|
+
return () => listeners.delete(listener);
|
|
188
101
|
}
|
|
189
|
-
function
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
root.classList.toggle("dark", resolvedMode === "dark");
|
|
196
|
-
root.classList.toggle("light", resolvedMode === "light");
|
|
197
|
-
}
|
|
102
|
+
function seedDefaultMode(fallback) {
|
|
103
|
+
if (hadPersistedMode || mode === fallback) return;
|
|
104
|
+
mode = fallback;
|
|
105
|
+
isDark = resolveIsDark(fallback);
|
|
106
|
+
applyToDom(isDark);
|
|
107
|
+
notify();
|
|
198
108
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
109
|
+
|
|
110
|
+
// src/useGlobalDarkMode.ts
|
|
111
|
+
import { useSyncExternalStore } from "react";
|
|
112
|
+
var getServerSnapshot = () => false;
|
|
113
|
+
function useGlobalDarkMode() {
|
|
114
|
+
return useSyncExternalStore(subscribeGlobalDarkMode, getGlobalDarkMode, getServerSnapshot);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/ThemeProvider.tsx
|
|
118
|
+
import { useEffect, useMemo, useRef, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
119
|
+
|
|
120
|
+
// src/ThemeContext.tsx
|
|
121
|
+
import { createContext, useContext } from "react";
|
|
122
|
+
var ThemeContext = createContext(null);
|
|
123
|
+
function useTheme() {
|
|
124
|
+
const ctx = useContext(ThemeContext);
|
|
125
|
+
if (!ctx) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
'useTheme() must be called within a <ThemeProvider>. For dark-mode state without a provider, use useGlobalDarkMode() (read) or toggleGlobalDarkMode()/setGlobalThemeMode() (write) from "@kbach/react" instead.'
|
|
128
|
+
);
|
|
216
129
|
}
|
|
130
|
+
return ctx;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/ThemeProvider.tsx
|
|
134
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
135
|
+
var mountedProviderCount = 0;
|
|
136
|
+
var warnedMultipleProviders = false;
|
|
137
|
+
function ThemeProvider({ children, defaultMode = "system" }) {
|
|
217
138
|
useEffect(() => {
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"Multiple <ThemeProvider> instances are mounted at once. Dark mode and responsive width are shared through one global store, so whichever provider rendered most recently wins for every consumer \u2014 nested or per-section theming is not isolated between providers."
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
if (_mountedConfigOverrideCount > 1 && !_warnedConfigOverride) {
|
|
228
|
-
_warnedConfigOverride = true;
|
|
229
|
-
kbachWarn(
|
|
230
|
-
"Multiple <ThemeProvider config={...}> overrides are mounted at once. The resolved config is shared through the same global store as dark mode/width (see the multiple-providers warning), so per-tree config overrides are not actually isolated between providers \u2014 plain className/kb elements with no hover:/dark:/sm: modifier resolve against whichever override last committed, not necessarily their nearest ancestor <ThemeProvider>."
|
|
231
|
-
);
|
|
232
|
-
}
|
|
139
|
+
mountedProviderCount++;
|
|
140
|
+
if (mountedProviderCount > 1 && !warnedMultipleProviders) {
|
|
141
|
+
warnedMultipleProviders = true;
|
|
142
|
+
console.warn(
|
|
143
|
+
"[kbach] Multiple <ThemeProvider> instances are mounted at once. Dark-mode state is one global store (the same one useGlobalDarkMode()/toggleGlobalDarkMode() read and write), not scoped per-provider, so they will fight over the same state instead of each getting their own."
|
|
144
|
+
);
|
|
233
145
|
}
|
|
234
146
|
return () => {
|
|
235
|
-
|
|
236
|
-
if (configOverride) _mountedConfigOverrideCount--;
|
|
237
|
-
if (_prevConfigOverrideRef.current !== void 0 && _mountedProviderCount === 0) {
|
|
238
|
-
resetConfig();
|
|
239
|
-
}
|
|
147
|
+
mountedProviderCount--;
|
|
240
148
|
};
|
|
241
149
|
}, []);
|
|
242
|
-
const
|
|
243
|
-
const effectiveWidth = getEffectiveIsWeb() ? webWidth : windowWidthProp ?? 0;
|
|
244
|
-
const numericScreens = useMemo3(() => {
|
|
245
|
-
const out = {};
|
|
246
|
-
for (const [k, v] of Object.entries(resolvedConfig.theme.screens ?? {})) {
|
|
247
|
-
out[k] = typeof v === "number" ? v : parseInt(String(v), 10);
|
|
248
|
-
}
|
|
249
|
-
return out;
|
|
250
|
-
}, [resolvedConfig.theme.screens]);
|
|
251
|
-
const _prevWidthRef = useRef(-1);
|
|
252
|
-
if (_prevWidthRef.current !== effectiveWidth) {
|
|
253
|
-
_prevWidthRef.current = effectiveWidth;
|
|
254
|
-
syncGlobalWidth(effectiveWidth);
|
|
255
|
-
}
|
|
256
|
-
const _prevScreensRef = useRef(null);
|
|
257
|
-
if (_prevScreensRef.current !== numericScreens) {
|
|
258
|
-
_prevScreensRef.current = numericScreens;
|
|
259
|
-
syncGlobalScreens(numericScreens);
|
|
260
|
-
}
|
|
261
|
-
useEffect(() => {
|
|
262
|
-
setGlobalWidth(effectiveWidth);
|
|
263
|
-
}, [effectiveWidth]);
|
|
150
|
+
const seeded = useRef(false);
|
|
264
151
|
useEffect(() => {
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const update = () => {
|
|
269
|
-
cancelAnimationFrame(raf);
|
|
270
|
-
raf = requestAnimationFrame(() => {
|
|
271
|
-
const w = window.innerWidth;
|
|
272
|
-
setWebWidth(w);
|
|
273
|
-
setGlobalWidth(w);
|
|
274
|
-
});
|
|
275
|
-
};
|
|
276
|
-
window.addEventListener("resize", update);
|
|
277
|
-
return () => {
|
|
278
|
-
window.removeEventListener("resize", update);
|
|
279
|
-
cancelAnimationFrame(raf);
|
|
280
|
-
};
|
|
281
|
-
}, []);
|
|
282
|
-
const webScheme = useSyncExternalStore(
|
|
283
|
-
subscribeSystemScheme,
|
|
284
|
-
getSystemScheme,
|
|
285
|
-
getSystemSchemeServerSnapshot
|
|
286
|
-
);
|
|
287
|
-
const systemScheme = getEffectiveIsWeb() ? webScheme : colorScheme === "dark" ? "dark" : "light";
|
|
288
|
-
const [mode, _setMode] = useState(defaultMode);
|
|
289
|
-
useEffect(() => {
|
|
290
|
-
if (disablePersistence) return;
|
|
291
|
-
const persisted = loadPersistedMode();
|
|
292
|
-
if (persisted) _setMode(persisted);
|
|
152
|
+
if (seeded.current) return;
|
|
153
|
+
seeded.current = true;
|
|
154
|
+
seedDefaultMode(defaultMode);
|
|
293
155
|
}, []);
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
_setMode(e.newValue);
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
window.addEventListener("storage", onStorage);
|
|
303
|
-
return () => window.removeEventListener("storage", onStorage);
|
|
304
|
-
}, [disablePersistence]);
|
|
305
|
-
const setMode = useCallback((next) => {
|
|
306
|
-
if (process.env.NODE_ENV !== "production" && isWeb && resolvedConfig.darkMode === "media" && !_warnedMediaModeToggle) {
|
|
307
|
-
_warnedMediaModeToggle = true;
|
|
308
|
-
kbachWarn(
|
|
309
|
-
'setMode()/toggle() was called while darkMode is "media" \u2014 the resolved mode/isDark from useTheme()/useIsDark() will update, but the actual CSS stays driven by prefers-color-scheme and will not change to match. Use darkMode: "attribute" or "class" if the app needs manual toggling.'
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
_setMode(next);
|
|
313
|
-
if (!disablePersistence) persistMode(next);
|
|
314
|
-
}, [disablePersistence, resolvedConfig.darkMode]);
|
|
315
|
-
const toggle = useCallback(() => {
|
|
316
|
-
setMode(mode === "dark" || mode === "system" && systemScheme === "dark" ? "light" : "dark");
|
|
317
|
-
}, [mode, systemScheme, setMode]);
|
|
318
|
-
const resolvedMode = mode === "system" ? systemScheme : mode;
|
|
319
|
-
const isDark = resolvedMode === "dark";
|
|
320
|
-
syncGlobalDarkMode(isDark);
|
|
321
|
-
useIsomorphicLayoutEffect(() => {
|
|
322
|
-
applyWebTheme(resolvedMode, resolvedConfig.darkMode);
|
|
323
|
-
setGlobalDarkMode(isDark);
|
|
324
|
-
}, [isDark, resolvedMode, resolvedConfig.darkMode]);
|
|
325
|
-
useEffect(() => {
|
|
326
|
-
if (configOverride) return;
|
|
327
|
-
let mounted = true;
|
|
328
|
-
const unsub = onConfigChange((config) => {
|
|
329
|
-
if (mounted) setResolvedConfig(config);
|
|
330
|
-
});
|
|
331
|
-
return () => {
|
|
332
|
-
mounted = false;
|
|
333
|
-
unsub();
|
|
334
|
-
};
|
|
335
|
-
}, [configOverride]);
|
|
336
|
-
const contextValue = useMemo3(
|
|
337
|
-
() => ({ mode, resolvedMode, isDark, setMode, toggle, config: resolvedConfig }),
|
|
338
|
-
[mode, resolvedMode, isDark, setMode, toggle, resolvedConfig]
|
|
156
|
+
const isDark2 = useSyncExternalStore2(subscribeGlobalDarkMode, getGlobalDarkMode, () => false);
|
|
157
|
+
const mode2 = useSyncExternalStore2(subscribeGlobalDarkMode, getGlobalThemeMode, () => defaultMode);
|
|
158
|
+
const value = useMemo(
|
|
159
|
+
() => ({ mode: mode2, isDark: isDark2, setMode: setGlobalThemeMode, toggle: toggleGlobalDarkMode }),
|
|
160
|
+
[mode2, isDark2]
|
|
339
161
|
);
|
|
340
|
-
return /* @__PURE__ */
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// src/KbachReset.tsx
|
|
344
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
345
|
-
function KbachReset() {
|
|
346
|
-
if (isNative) return null;
|
|
347
|
-
return /* @__PURE__ */ jsx2("style", { id: RESET_STYLE_ID, children: BASE_RESET });
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// src/styled.tsx
|
|
351
|
-
import React2, {
|
|
352
|
-
forwardRef,
|
|
353
|
-
useState as useState2,
|
|
354
|
-
useCallback as useCallback2,
|
|
355
|
-
useMemo as useMemo4
|
|
356
|
-
} from "react";
|
|
357
|
-
function styled(Component, baseClasses = "") {
|
|
358
|
-
const Styled = forwardRef(
|
|
359
|
-
(props, ref) => {
|
|
360
|
-
const {
|
|
361
|
-
kb: extraClasses,
|
|
362
|
-
style: styleProp,
|
|
363
|
-
onPressIn,
|
|
364
|
-
onPressOut,
|
|
365
|
-
onPointerDown,
|
|
366
|
-
onPointerUp,
|
|
367
|
-
onPointerLeave,
|
|
368
|
-
onPointerCancel,
|
|
369
|
-
onMouseEnter,
|
|
370
|
-
onMouseLeave,
|
|
371
|
-
onFocus,
|
|
372
|
-
onBlur,
|
|
373
|
-
...rest
|
|
374
|
-
} = props;
|
|
375
|
-
const disabled = !!props.disabled;
|
|
376
|
-
const checked = !!props.checked;
|
|
377
|
-
const { config } = useTheme();
|
|
378
|
-
const isWebPlatform = getEffectiveIsWeb();
|
|
379
|
-
const isDark = useConditionalGlobalDarkMode(!isWebPlatform);
|
|
380
|
-
const [pressed, setPressed] = useState2(false);
|
|
381
|
-
const [hovered, setHovered] = useState2(false);
|
|
382
|
-
const [focused, setFocused] = useState2(false);
|
|
383
|
-
const handlePressIn = useCallback2(chain(onPressIn, () => {
|
|
384
|
-
if (!isWebPlatform) setPressed(true);
|
|
385
|
-
}), [onPressIn, isWebPlatform]);
|
|
386
|
-
const handlePressOut = useCallback2(chain(onPressOut, () => {
|
|
387
|
-
if (!isWebPlatform) setPressed(false);
|
|
388
|
-
}), [onPressOut, isWebPlatform]);
|
|
389
|
-
const handlePointerDown = useCallback2(chain(onPointerDown, () => {
|
|
390
|
-
if (!isWebPlatform) setPressed(true);
|
|
391
|
-
}), [onPointerDown, isWebPlatform]);
|
|
392
|
-
const handlePointerUp = useCallback2(chain(onPointerUp, () => {
|
|
393
|
-
if (!isWebPlatform) setPressed(false);
|
|
394
|
-
}), [onPointerUp, isWebPlatform]);
|
|
395
|
-
const handlePointerLeave = useCallback2(chain(onPointerLeave, () => {
|
|
396
|
-
if (!isWebPlatform) setPressed(false);
|
|
397
|
-
}), [onPointerLeave, isWebPlatform]);
|
|
398
|
-
const handlePointerCancel = useCallback2(chain(onPointerCancel, () => {
|
|
399
|
-
if (!isWebPlatform) setPressed(false);
|
|
400
|
-
}), [onPointerCancel, isWebPlatform]);
|
|
401
|
-
const handleMouseEnter = useCallback2(chain(onMouseEnter, () => {
|
|
402
|
-
if (!isWebPlatform) setHovered(true);
|
|
403
|
-
}), [onMouseEnter, isWebPlatform]);
|
|
404
|
-
const handleMouseLeave = useCallback2(chain(onMouseLeave, () => {
|
|
405
|
-
if (!isWebPlatform) setHovered(false);
|
|
406
|
-
}), [onMouseLeave, isWebPlatform]);
|
|
407
|
-
const handleFocus = useCallback2(chain(onFocus, () => {
|
|
408
|
-
if (!isWebPlatform) setFocused(true);
|
|
409
|
-
}), [onFocus, isWebPlatform]);
|
|
410
|
-
const handleBlur = useCallback2(chain(onBlur, () => {
|
|
411
|
-
if (!isWebPlatform) setFocused(false);
|
|
412
|
-
}), [onBlur, isWebPlatform]);
|
|
413
|
-
const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
|
|
414
|
-
const firstPassResolved = useMemo4(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
|
|
415
|
-
const webTag = isWebPlatform ? getWebTag(Component, rest) : null;
|
|
416
|
-
const impliedClasses = getImpliedRNClasses(webTag, firstPassResolved.base);
|
|
417
|
-
const finalClassStr = impliedClasses ? `${combined} ${impliedClasses}` : combined;
|
|
418
|
-
const resolved = useMemo4(() => resolve(finalClassStr, config.theme, config.darkMode), [finalClassStr, config.theme, config.darkMode]);
|
|
419
|
-
const hasInteractive = useMemo4(() => hasInteractiveBuckets(resolved), [resolved]);
|
|
420
|
-
const needsWidth = hasResponsiveBuckets(resolved);
|
|
421
|
-
const width = useConditionalWidth(needsWidth && !isWebPlatform);
|
|
422
|
-
const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
|
|
423
|
-
const screens = getGlobalScreens();
|
|
424
|
-
const computedStyle = useMemo4(
|
|
425
|
-
() => isWebPlatform ? {} : flatten(resolved, isDark, { pressed, hover: hovered, focus: focused, disabled, checked }, breakpoints),
|
|
426
|
-
[resolved, isDark, pressed, hovered, focused, disabled, checked, width, screens]
|
|
427
|
-
// eslint-disable-line react-hooks/exhaustive-deps
|
|
428
|
-
);
|
|
429
|
-
const finalStyle = isWebPlatform ? (Array.isArray(styleProp) ? Object.assign({}, ...styleProp) : styleProp) ?? void 0 : composeNativeStyle(computedStyle, styleProp);
|
|
430
|
-
const effectiveComponent = webTag ?? Component;
|
|
431
|
-
const componentName2 = Component.displayName ?? Component.name ?? "";
|
|
432
|
-
const effectiveRest = webTag && componentName2 ? transformToWebProps(componentName2, webTag, rest) : rest;
|
|
433
|
-
return React2.createElement(effectiveComponent, {
|
|
434
|
-
ref,
|
|
435
|
-
...effectiveRest,
|
|
436
|
-
style: finalStyle,
|
|
437
|
-
// className lets injected CSS rules (group-hover:, before:, print:, etc.) match the element.
|
|
438
|
-
...isWebPlatform && finalClassStr ? { className: normalizeClassString(finalClassStr) } : {},
|
|
439
|
-
// Only attach state-tracking handlers when interactive modifiers are present.
|
|
440
|
-
// Always forward user-provided handlers to avoid silently swallowing them.
|
|
441
|
-
// Gated on isNative, not isWeb: isWeb is false during SSR too (no `window` there),
|
|
442
|
-
// and onPressIn/onPressOut must stay excluded there as well — SSR is exactly where
|
|
443
|
-
// getWebTag() above is skipped (it's isWeb-gated), so effectiveComponent may still be
|
|
444
|
-
// a non-string RN-style reference server-side, and this used to forward onPressIn/
|
|
445
|
-
// onPressOut to it unconditionally in that case (isWeb ? ... : ...'s false branch
|
|
446
|
-
// covered both native AND SSR). onPressIn/onPressOut are RN-only prop names — a
|
|
447
|
-
// non-string effectiveComponent on the web/SSR side is just as likely to be an
|
|
448
|
-
// ordinary web component (React Router's <Link>, Next.js's <Link>, any custom
|
|
449
|
-
// wrapper) as an actual react-native-web primitive, and "not a string" alone isn't a
|
|
450
|
-
// reliable signal either way. Forwarding them made React DOM warn "Unknown event
|
|
451
|
-
// handler property" the moment any such component got an interactive modifier
|
|
452
|
-
// (hover:, active:, …), which is the common case, not the exception.
|
|
453
|
-
...!isNative ? hasInteractive ? {
|
|
454
|
-
onPointerDown: handlePointerDown,
|
|
455
|
-
onPointerUp: handlePointerUp,
|
|
456
|
-
onPointerLeave: handlePointerLeave,
|
|
457
|
-
onPointerCancel: handlePointerCancel
|
|
458
|
-
} : {
|
|
459
|
-
onPointerDown,
|
|
460
|
-
onPointerUp,
|
|
461
|
-
onPointerLeave,
|
|
462
|
-
onPointerCancel
|
|
463
|
-
} : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
|
|
464
|
-
onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
|
|
465
|
-
onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
|
|
466
|
-
onFocus: hasInteractive ? handleFocus : onFocus,
|
|
467
|
-
onBlur: hasInteractive ? handleBlur : onBlur
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
const componentName = Component.displayName ?? Component.name ?? "Component";
|
|
472
|
-
Styled.displayName = `Styled(${componentName})`;
|
|
473
|
-
return Styled;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/useStyles.ts
|
|
477
|
-
import { useMemo as useMemo5 } from "react";
|
|
478
|
-
function useStyles(classString, state = {}) {
|
|
479
|
-
const { isDark, config } = useTheme();
|
|
480
|
-
const width = useGlobalWidth();
|
|
481
|
-
const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
|
|
482
|
-
return useMemo5(() => {
|
|
483
|
-
const resolved = resolve(normalised, config.theme, config.darkMode);
|
|
484
|
-
const breakpoints = getActiveBreakpoints(width);
|
|
485
|
-
return flatten(resolved, isDark, state, breakpoints);
|
|
486
|
-
}, [normalised, isDark, config.theme, config.darkMode, width, state.hover, state.focus, state.pressed, state.active, state.disabled, state.checked, state.visited, state.placeholder]);
|
|
487
|
-
}
|
|
488
|
-
function useResolvedStyle(classString) {
|
|
489
|
-
const { config } = useTheme();
|
|
490
|
-
const normalised = Array.isArray(classString) ? classString.join(" ") : classString;
|
|
491
|
-
return useMemo5(
|
|
492
|
-
() => resolve(normalised, config.theme, config.darkMode),
|
|
493
|
-
[normalised, config]
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// src/useBreakpoint.ts
|
|
498
|
-
import { useMemo as useMemo6 } from "react";
|
|
499
|
-
function toNumericScreens(screens) {
|
|
500
|
-
const out = {};
|
|
501
|
-
for (const [name, v] of Object.entries(screens)) {
|
|
502
|
-
const n = typeof v === "number" ? v : parseInt(String(v), 10);
|
|
503
|
-
if (!Number.isNaN(n)) out[name] = n;
|
|
504
|
-
}
|
|
505
|
-
return out;
|
|
506
|
-
}
|
|
507
|
-
function useBreakpoint() {
|
|
508
|
-
const { config } = useTheme();
|
|
509
|
-
const width = useGlobalWidth();
|
|
510
|
-
const screens = config.theme.screens;
|
|
511
|
-
const numericScreens = useMemo6(() => toNumericScreens(screens), [screens]);
|
|
512
|
-
return useMemo6(() => {
|
|
513
|
-
const active = getActiveBreakpoints(width, numericScreens);
|
|
514
|
-
let best = null;
|
|
515
|
-
let bestMinW = -Infinity;
|
|
516
|
-
for (const name of active) {
|
|
517
|
-
const minW = numericScreens[name];
|
|
518
|
-
if (minW > bestMinW) {
|
|
519
|
-
bestMinW = minW;
|
|
520
|
-
best = name;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return best ?? "xs";
|
|
524
|
-
}, [numericScreens, width]);
|
|
525
|
-
}
|
|
526
|
-
function useResponsive() {
|
|
527
|
-
const { config } = useTheme();
|
|
528
|
-
const width = useGlobalWidth();
|
|
529
|
-
const screens = config.theme.screens;
|
|
530
|
-
const numericScreens = useMemo6(() => toNumericScreens(screens), [screens]);
|
|
531
|
-
return useMemo6(() => {
|
|
532
|
-
const active = getActiveBreakpoints(width, numericScreens);
|
|
533
|
-
const result = {};
|
|
534
|
-
for (const name of Object.keys(numericScreens)) {
|
|
535
|
-
result[name] = active.has(name);
|
|
536
|
-
}
|
|
537
|
-
return result;
|
|
538
|
-
}, [numericScreens, width]);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// src/kb.ts
|
|
542
|
-
function kb(classString, isDark = false) {
|
|
543
|
-
const config = getConfig();
|
|
544
|
-
const resolved = resolve(classString, config.theme, config.darkMode);
|
|
545
|
-
if (getEffectiveIsWeb()) {
|
|
546
|
-
return classString;
|
|
547
|
-
}
|
|
548
|
-
return flatten(resolved, isDark);
|
|
549
|
-
}
|
|
550
|
-
function cx(...classes) {
|
|
551
|
-
return classes.filter(Boolean).join(" ");
|
|
162
|
+
return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value, children });
|
|
552
163
|
}
|
|
553
164
|
export {
|
|
554
|
-
InteractiveWrapper,
|
|
555
165
|
KbachReset,
|
|
556
|
-
ThemeContext,
|
|
557
166
|
ThemeProvider,
|
|
558
|
-
buildConfig,
|
|
559
|
-
clearCache,
|
|
560
|
-
cx,
|
|
561
|
-
defaultColors,
|
|
562
167
|
defaultTheme,
|
|
563
168
|
disableRuntimeCSS,
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
169
|
+
getGlobalDarkMode,
|
|
170
|
+
getGlobalThemeMode,
|
|
171
|
+
getTheme,
|
|
172
|
+
initKbach,
|
|
173
|
+
isKbachReady,
|
|
174
|
+
isRuntimeCSSDisabled,
|
|
568
175
|
kb,
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
resolve,
|
|
574
|
-
setResolveTarget,
|
|
575
|
-
splitClassTokens,
|
|
576
|
-
styled,
|
|
577
|
-
updateConfig,
|
|
578
|
-
useBreakpoint,
|
|
579
|
-
useColors,
|
|
176
|
+
setGlobalThemeMode,
|
|
177
|
+
setTheme,
|
|
178
|
+
subscribeGlobalDarkMode,
|
|
179
|
+
toggleGlobalDarkMode,
|
|
580
180
|
useGlobalDarkMode,
|
|
581
|
-
|
|
582
|
-
useResolvedStyle,
|
|
583
|
-
useResponsive,
|
|
584
|
-
useSpacing,
|
|
585
|
-
useStyles,
|
|
586
|
-
useTheme,
|
|
587
|
-
wrapColors,
|
|
588
|
-
wrapSpacing
|
|
181
|
+
useTheme
|
|
589
182
|
};
|