@kbach/react 0.6.0 → 0.6.2

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/index.mjs CHANGED
@@ -1,38 +1,42 @@
1
+ 'use client';
1
2
  import {
2
3
  EMPTY_BREAKPOINTS,
3
4
  InteractiveWrapper,
4
- getWebTag,
5
- hasInteractiveBuckets,
6
- hasResponsiveBuckets,
7
- registerWebElement,
8
- setGlobalDarkMode,
9
- syncGlobalDarkMode,
10
- transformToWebProps,
11
- useConditionalWidth,
12
- useGlobalDarkMode,
13
- useGlobalWidth
14
- } from "./chunk-D4LJICWZ.mjs";
15
- import {
16
5
  buildConfig,
6
+ chain,
7
+ clearCache,
17
8
  defaultColors,
18
9
  defaultTheme,
19
10
  disableRuntimeCSS,
20
11
  flatten,
21
12
  getActiveBreakpoints,
22
13
  getConfig,
14
+ getWebTag,
15
+ hasInteractiveBuckets,
16
+ hasResponsiveBuckets,
23
17
  initConfig,
24
18
  isWeb,
25
19
  normalizeClassString,
26
20
  onConfigChange,
27
21
  parseClass,
28
22
  parseClasses,
23
+ parseHexRgb,
24
+ registerWebElement,
29
25
  resolve,
26
+ setGlobalDarkMode,
30
27
  setGlobalWidth,
28
+ setResolveTarget,
31
29
  splitClassTokens,
30
+ syncGlobalDarkMode,
32
31
  syncGlobalScreens,
33
32
  syncGlobalWidth,
34
- updateConfig
35
- } from "./chunk-6NED62DB.mjs";
33
+ transformToWebProps,
34
+ updateConfig,
35
+ useConditionalWidth,
36
+ useGlobalDarkMode,
37
+ useGlobalWidth,
38
+ useSyncExternalStore
39
+ } from "./chunk-GRZ6MVIL.mjs";
36
40
 
37
41
  // src/context.tsx
38
42
  import { createContext, useContext } from "react";
@@ -55,22 +59,10 @@ import { useMemo } from "react";
55
59
  function applyOpacity(color, opacity) {
56
60
  const a = Math.max(0, Math.min(1, opacity / 100));
57
61
  if (color.startsWith("#")) {
58
- const h = color.slice(1);
59
- let rs, gs, bs;
60
- if (h.length === 3 || h.length === 4) {
61
- rs = h[0] + h[0];
62
- gs = h[1] + h[1];
63
- bs = h[2] + h[2];
64
- } else if (h.length === 8) {
65
- rs = h.slice(0, 2);
66
- gs = h.slice(2, 4);
67
- bs = h.slice(4, 6);
68
- } else {
69
- rs = h.slice(0, 2);
70
- gs = h.slice(2, 4);
71
- bs = h.slice(4, 6);
72
- }
73
- return `rgba(${parseInt(rs, 16)},${parseInt(gs, 16)},${parseInt(bs, 16)},${a})`;
62
+ const rgb = parseHexRgb(color);
63
+ if (!rgb) return color;
64
+ const [r, g, b] = rgb;
65
+ return `rgba(${r},${g},${b},${a})`;
74
66
  }
75
67
  if (color.startsWith("rgb(")) return color.replace("rgb(", "rgba(").replace(")", `,${a})`);
76
68
  if (color.startsWith("rgba(")) return color.replace(/,\s*[\d.]+\)$/, `,${a})`);
@@ -144,6 +136,22 @@ function persistMode(mode) {
144
136
  } catch {
145
137
  }
146
138
  }
139
+ function subscribeSystemScheme(callback) {
140
+ if (!isWeb || typeof window === "undefined") return () => {
141
+ };
142
+ const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
143
+ if (!mq) return () => {
144
+ };
145
+ mq.addEventListener("change", callback);
146
+ return () => mq.removeEventListener("change", callback);
147
+ }
148
+ function getSystemScheme() {
149
+ if (!isWeb || typeof window === "undefined") return "light";
150
+ return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
151
+ }
152
+ function getSystemSchemeServerSnapshot() {
153
+ return "light";
154
+ }
147
155
  function applyWebTheme(resolvedMode, strategy) {
148
156
  if (!isWeb) return;
149
157
  const root = document.documentElement;
@@ -165,9 +173,7 @@ function ThemeProvider({
165
173
  const [resolvedConfig, setResolvedConfig] = useState(
166
174
  () => configOverride ? buildConfig(configOverride) : getConfig()
167
175
  );
168
- const [webWidth, setWebWidth] = useState(
169
- () => isWeb && typeof window !== "undefined" ? window.innerWidth : 0
170
- );
176
+ const [webWidth, setWebWidth] = useState(0);
171
177
  const effectiveWidth = windowWidthProp ?? (isWeb ? webWidth : 0);
172
178
  const numericScreens = useMemo2(() => {
173
179
  const out = {};
@@ -191,6 +197,7 @@ function ThemeProvider({
191
197
  }, [effectiveWidth]);
192
198
  useEffect(() => {
193
199
  if (!isWeb || typeof window === "undefined") return;
200
+ setWebWidth(window.innerWidth);
194
201
  let raf = 0;
195
202
  const update = () => {
196
203
  cancelAnimationFrame(raf);
@@ -206,20 +213,18 @@ function ThemeProvider({
206
213
  cancelAnimationFrame(raf);
207
214
  };
208
215
  }, []);
209
- const [webScheme, setWebScheme] = useState(() => {
210
- if (isWeb && typeof window !== "undefined") {
211
- return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
212
- }
213
- return "light";
214
- });
216
+ const webScheme = useSyncExternalStore(
217
+ subscribeSystemScheme,
218
+ getSystemScheme,
219
+ getSystemSchemeServerSnapshot
220
+ );
215
221
  const systemScheme = isWeb ? webScheme : colorScheme === "dark" ? "dark" : "light";
216
- const [mode, _setMode] = useState(() => {
217
- if (!disablePersistence) {
218
- const persisted = loadPersistedMode();
219
- if (persisted) return persisted;
220
- }
221
- return defaultMode;
222
- });
222
+ const [mode, _setMode] = useState(defaultMode);
223
+ useEffect(() => {
224
+ if (disablePersistence) return;
225
+ const persisted = loadPersistedMode();
226
+ if (persisted) _setMode(persisted);
227
+ }, []);
223
228
  const setMode = useCallback((next) => {
224
229
  _setMode(next);
225
230
  if (!disablePersistence) persistMode(next);
@@ -234,18 +239,6 @@ function ThemeProvider({
234
239
  applyWebTheme(resolvedMode, resolvedConfig.darkMode);
235
240
  setGlobalDarkMode(isDark);
236
241
  }, [isDark, resolvedMode, resolvedConfig.darkMode]);
237
- const setWebSchemeRef = useRef(setWebScheme);
238
- setWebSchemeRef.current = setWebScheme;
239
- useEffect(() => {
240
- if (!isWeb || typeof window === "undefined") return;
241
- const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
242
- if (!mq) return;
243
- const handler = (e) => {
244
- setWebSchemeRef.current(e.matches ? "dark" : "light");
245
- };
246
- mq.addEventListener("change", handler);
247
- return () => mq.removeEventListener("change", handler);
248
- }, []);
249
242
  useEffect(() => {
250
243
  if (!configOverride) return;
251
244
  updateConfig(configOverride);
@@ -287,6 +280,7 @@ function styled(Component, baseClasses = "") {
287
280
  onPointerDown,
288
281
  onPointerUp,
289
282
  onPointerLeave,
283
+ onPointerCancel,
290
284
  onMouseEnter,
291
285
  onMouseLeave,
292
286
  onFocus,
@@ -300,42 +294,16 @@ function styled(Component, baseClasses = "") {
300
294
  const [pressed, setPressed] = useState2(false);
301
295
  const [hovered, setHovered] = useState2(false);
302
296
  const [focused, setFocused] = useState2(false);
303
- const handlePressIn = useCallback2((e) => {
304
- setPressed(true);
305
- onPressIn?.(e);
306
- }, [onPressIn]);
307
- const handlePressOut = useCallback2((e) => {
308
- setPressed(false);
309
- onPressOut?.(e);
310
- }, [onPressOut]);
311
- const handlePointerDown = useCallback2((e) => {
312
- setPressed(true);
313
- onPointerDown?.(e);
314
- }, [onPointerDown]);
315
- const handlePointerUp = useCallback2((e) => {
316
- setPressed(false);
317
- onPointerUp?.(e);
318
- }, [onPointerUp]);
319
- const handlePointerLeave = useCallback2((e) => {
320
- setPressed(false);
321
- onPointerLeave?.(e);
322
- }, [onPointerLeave]);
323
- const handleMouseEnter = useCallback2((e) => {
324
- setHovered(true);
325
- onMouseEnter?.(e);
326
- }, [onMouseEnter]);
327
- const handleMouseLeave = useCallback2((e) => {
328
- setHovered(false);
329
- onMouseLeave?.(e);
330
- }, [onMouseLeave]);
331
- const handleFocus = useCallback2((e) => {
332
- setFocused(true);
333
- onFocus?.(e);
334
- }, [onFocus]);
335
- const handleBlur = useCallback2((e) => {
336
- setFocused(false);
337
- onBlur?.(e);
338
- }, [onBlur]);
297
+ const handlePressIn = useCallback2(chain(onPressIn, () => setPressed(true)), [onPressIn]);
298
+ const handlePressOut = useCallback2(chain(onPressOut, () => setPressed(false)), [onPressOut]);
299
+ const handlePointerDown = useCallback2(chain(onPointerDown, () => setPressed(true)), [onPointerDown]);
300
+ const handlePointerUp = useCallback2(chain(onPointerUp, () => setPressed(false)), [onPointerUp]);
301
+ const handlePointerLeave = useCallback2(chain(onPointerLeave, () => setPressed(false)), [onPointerLeave]);
302
+ const handlePointerCancel = useCallback2(chain(onPointerCancel, () => setPressed(false)), [onPointerCancel]);
303
+ const handleMouseEnter = useCallback2(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
304
+ const handleMouseLeave = useCallback2(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
305
+ const handleFocus = useCallback2(chain(onFocus, () => setFocused(true)), [onFocus]);
306
+ const handleBlur = useCallback2(chain(onBlur, () => setFocused(false)), [onBlur]);
339
307
  const combined = extraClasses ? `${baseClasses} ${extraClasses}` : baseClasses;
340
308
  const resolved = useMemo3(() => resolve(combined, config.theme, config.darkMode), [combined, config.theme, config.darkMode]);
341
309
  const hasInteractive = useMemo3(() => hasInteractiveBuckets(resolved), [resolved]);
@@ -363,7 +331,7 @@ function styled(Component, baseClasses = "") {
363
331
  // Always forward user-provided handlers to avoid silently swallowing them.
364
332
  // onPressIn/onPressOut are forwarded on web too (not just native) since
365
333
  // react-native-web components rely on receiving them directly.
366
- ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerLeave, onPressIn, onPressOut } : { onPointerDown, onPointerUp, onPointerLeave, onPressIn, onPressOut } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
334
+ ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPointerDown, onPointerUp, onPointerLeave, onPointerCancel, onPressIn, onPressOut } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
367
335
  onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
368
336
  onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
369
337
  onFocus: hasInteractive ? handleFocus : onFocus,
@@ -404,7 +372,7 @@ function useBreakpoint() {
404
372
  const width = useGlobalWidth();
405
373
  const screens = config.theme.screens;
406
374
  const sorted = useMemo5(
407
- () => Object.entries(screens).filter((entry) => typeof entry[1] === "number").sort(([, a], [, b]) => b - a),
375
+ () => Object.entries(screens).map(([name, v]) => [name, typeof v === "number" ? v : parseInt(String(v), 10)]).filter(([, minW]) => !Number.isNaN(minW)).sort(([, a], [, b]) => b - a),
408
376
  [screens]
409
377
  );
410
378
  for (const [name, minW] of sorted) {
@@ -418,8 +386,9 @@ function useResponsive() {
418
386
  const screens = config.theme.screens;
419
387
  return useMemo5(() => {
420
388
  const result = {};
421
- for (const [name, minW] of Object.entries(screens)) {
422
- if (typeof minW === "number") result[name] = width >= minW;
389
+ for (const [name, v] of Object.entries(screens)) {
390
+ const minW = typeof v === "number" ? v : parseInt(String(v), 10);
391
+ if (!Number.isNaN(minW)) result[name] = width >= minW;
423
392
  }
424
393
  return result;
425
394
  }, [screens, width]);
@@ -442,6 +411,7 @@ export {
442
411
  ThemeContext,
443
412
  ThemeProvider,
444
413
  buildConfig,
414
+ clearCache,
445
415
  cx,
446
416
  defaultColors,
447
417
  defaultTheme,
@@ -455,6 +425,7 @@ export {
455
425
  parseClasses,
456
426
  registerWebElement,
457
427
  resolve,
428
+ setResolveTarget,
458
429
  splitClassTokens,
459
430
  styled,
460
431
  updateConfig,