@kbach/react 0.3.4 → 0.3.6

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.
@@ -540,10 +540,14 @@ var defaultTheme = {
540
540
  }
541
541
  },
542
542
  screens: {
543
- sm: 640,
543
+ sm: 390,
544
+ // large phones (iPhone 12+, most Android flagships)
544
545
  md: 768,
546
+ // tablets (iPad and up)
545
547
  lg: 1024,
548
+ // large tablets (iPad Pro)
546
549
  xl: 1280,
550
+ // desktop
547
551
  "2xl": 1536
548
552
  }
549
553
  };
@@ -1871,6 +1875,36 @@ function getPluginStandaloneMap() {
1871
1875
  return PLUGIN_STANDALONE;
1872
1876
  }
1873
1877
 
1878
+ // src/core/responsiveStore.ts
1879
+ var RESPONSIVE_KEY = "__kbach_responsive__";
1880
+ function getStore() {
1881
+ const g = globalThis;
1882
+ if (!g[RESPONSIVE_KEY]) {
1883
+ g[RESPONSIVE_KEY] = { width: 0, screens: {}, listeners: /* @__PURE__ */ new Set() };
1884
+ }
1885
+ return g[RESPONSIVE_KEY];
1886
+ }
1887
+ function getGlobalScreens() {
1888
+ return getStore().screens;
1889
+ }
1890
+ function getGlobalWidth() {
1891
+ return getStore().width;
1892
+ }
1893
+ function subscribeGlobalWidth(listener) {
1894
+ const store = getStore();
1895
+ store.listeners.add(listener);
1896
+ return () => store.listeners.delete(listener);
1897
+ }
1898
+ function getActiveBreakpoints(width) {
1899
+ const store = getStore();
1900
+ const w = width ?? store.width;
1901
+ const active = /* @__PURE__ */ new Set();
1902
+ for (const [name, minW] of Object.entries(store.screens)) {
1903
+ if (typeof minW === "number" && w >= minW) active.add(name);
1904
+ }
1905
+ return active;
1906
+ }
1907
+
1874
1908
  // src/core/resolver.ts
1875
1909
  var styleCache = new LRUCache(1e4);
1876
1910
  var _styleEl = null;
@@ -1921,6 +1955,7 @@ var GROUP_PEER_PREFIX = {
1921
1955
  "peer-focus": ".peer:focus ~"
1922
1956
  };
1923
1957
  var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
1958
+ var RESPONSIVE_MODS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
1924
1959
  function injectClassRule(cls, bucketKey, styles, darkMode) {
1925
1960
  const cssDecls = styleValueToCSS(styles);
1926
1961
  if (!cssDecls) return;
@@ -1930,10 +1965,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1930
1965
  return;
1931
1966
  }
1932
1967
  const mods = bucketKey.split(":");
1933
- const hasDark = mods.includes("dark") || mods.includes("not-light");
1934
- const hasLight = mods.includes("light") || mods.includes("not-dark");
1935
- const ancestorMods = mods.filter((m) => m in GROUP_PEER_PREFIX);
1936
- const pseudoMods = mods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
1968
+ const responsiveMods = mods.filter((m) => RESPONSIVE_MODS.has(m));
1969
+ const nonResponsiveMods = mods.filter((m) => !RESPONSIVE_MODS.has(m));
1970
+ const hasDark = nonResponsiveMods.includes("dark") || nonResponsiveMods.includes("not-light");
1971
+ const hasLight = nonResponsiveMods.includes("light") || nonResponsiveMods.includes("not-dark");
1972
+ const ancestorMods = nonResponsiveMods.filter((m) => m in GROUP_PEER_PREFIX);
1973
+ const pseudoMods = nonResponsiveMods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
1937
1974
  const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
1938
1975
  const elementSelector = `.${escaped}${pseudoSuffix}`;
1939
1976
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
@@ -1958,6 +1995,13 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1958
1995
  } else {
1959
1996
  rule = `${selector} { ${cssDecls} }`;
1960
1997
  }
1998
+ if (responsiveMods.length > 0) {
1999
+ const screens = getGlobalScreens();
2000
+ const minWidth = Math.max(...responsiveMods.map((m) => screens[m] ?? 0));
2001
+ if (minWidth > 0) {
2002
+ rule = `@media (min-width: ${minWidth}px) { ${rule} }`;
2003
+ }
2004
+ }
1961
2005
  injectRule(rule);
1962
2006
  }
1963
2007
  var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
@@ -2026,18 +2070,18 @@ function resolve(classString, theme, darkMode = "attribute") {
2026
2070
  }
2027
2071
  return result;
2028
2072
  }
2029
- function flatten(resolved, isDark, state = {}) {
2073
+ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
2030
2074
  const result = { ...resolved.base ?? {} };
2031
2075
  for (const [key, styles] of Object.entries(resolved)) {
2032
2076
  if (key === "base" || !styles) continue;
2033
2077
  const mods = key.split(":");
2034
- if (matchesMods(mods, isDark, state)) {
2078
+ if (matchesMods(mods, isDark, state, breakpoints)) {
2035
2079
  Object.assign(result, styles);
2036
2080
  }
2037
2081
  }
2038
2082
  return result;
2039
2083
  }
2040
- function matchesMods(mods, isDark, state) {
2084
+ function matchesMods(mods, isDark, state, breakpoints) {
2041
2085
  return mods.every((mod) => {
2042
2086
  switch (mod) {
2043
2087
  case "dark":
@@ -2075,6 +2119,12 @@ function matchesMods(mods, isDark, state) {
2075
2119
  return !state.visited;
2076
2120
  case "placeholder":
2077
2121
  return !!state.placeholder;
2122
+ case "sm":
2123
+ case "md":
2124
+ case "lg":
2125
+ case "xl":
2126
+ case "2xl":
2127
+ return breakpoints.has(mod);
2078
2128
  default:
2079
2129
  return false;
2080
2130
  }
@@ -2213,7 +2263,7 @@ var customVariants = new Proxy(
2213
2263
 
2214
2264
  // src/core/darkModeStore.ts
2215
2265
  var KEY = "__kbach_dark_store__";
2216
- function getStore() {
2266
+ function getStore2() {
2217
2267
  const g = globalThis;
2218
2268
  if (!g[KEY]) {
2219
2269
  g[KEY] = { isDark: false, lastNotified: false, subscribers: /* @__PURE__ */ new Set() };
@@ -2221,16 +2271,16 @@ function getStore() {
2221
2271
  return g[KEY];
2222
2272
  }
2223
2273
  function getGlobalDarkMode() {
2224
- return getStore().isDark;
2274
+ return getStore2().isDark;
2225
2275
  }
2226
2276
  function subscribeGlobalDarkMode(callback) {
2227
- const store = getStore();
2277
+ const store = getStore2();
2228
2278
  store.subscribers.add(callback);
2229
2279
  return () => store.subscribers.delete(callback);
2230
2280
  }
2231
2281
 
2232
2282
  // src/InteractiveWrapper.tsx
2233
- var import_react2 = __toESM(require("react"));
2283
+ var import_react3 = __toESM(require("react"));
2234
2284
 
2235
2285
  // src/useGlobalDarkMode.ts
2236
2286
  var import_react = __toESM(require("react"));
@@ -2248,6 +2298,25 @@ function useGlobalDarkMode() {
2248
2298
  );
2249
2299
  }
2250
2300
 
2301
+ // src/useGlobalWidth.ts
2302
+ var import_react2 = __toESM(require("react"));
2303
+ var useSyncExternalStore2 = import_react2.default.useSyncExternalStore ?? function useSyncExternalStoreFallback2(subscribe, getSnapshot, _getServerSnapshot) {
2304
+ const [, forceUpdate] = import_react2.default.useReducer((n) => n + 1, 0);
2305
+ import_react2.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2306
+ return getSnapshot();
2307
+ };
2308
+ var NOOP_SUB = (_) => () => {
2309
+ };
2310
+ var ZERO_SNAP = () => 0;
2311
+ var EMPTY_BREAKPOINTS = /* @__PURE__ */ new Set();
2312
+ function useConditionalWidth(active) {
2313
+ return useSyncExternalStore2(
2314
+ active ? subscribeGlobalWidth : NOOP_SUB,
2315
+ active ? getGlobalWidth : ZERO_SNAP,
2316
+ ZERO_SNAP
2317
+ );
2318
+ }
2319
+
2251
2320
  // src/InteractiveWrapper.tsx
2252
2321
  function chain(original, extra) {
2253
2322
  return (...args) => {
@@ -2255,7 +2324,17 @@ function chain(original, extra) {
2255
2324
  extra();
2256
2325
  };
2257
2326
  }
2258
- var InteractiveWrapper = (0, import_react2.forwardRef)(
2327
+ var RESPONSIVE_MODS2 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2328
+ function hasResponsiveBuckets(resolved) {
2329
+ for (const key of Object.keys(resolved)) {
2330
+ if (key === "base") continue;
2331
+ for (const mod of key.split(":")) {
2332
+ if (RESPONSIVE_MODS2.has(mod)) return true;
2333
+ }
2334
+ }
2335
+ return false;
2336
+ }
2337
+ var InteractiveWrapper = (0, import_react3.forwardRef)(
2259
2338
  function InteractiveWrapper2({
2260
2339
  Component,
2261
2340
  resolvedStyle,
@@ -2270,18 +2349,22 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
2270
2349
  ...rest
2271
2350
  }, ref) {
2272
2351
  const isDark = useGlobalDarkMode();
2273
- const [pressed, setPressed] = (0, import_react2.useState)(false);
2274
- const [hovered, setHovered] = (0, import_react2.useState)(false);
2275
- const [focused, setFocused] = (0, import_react2.useState)(false);
2276
- const handlePressIn = (0, import_react2.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2277
- const handlePressOut = (0, import_react2.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2278
- const handleMouseEnter = (0, import_react2.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2279
- const handleMouseLeave = (0, import_react2.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2280
- const handleFocus = (0, import_react2.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2281
- const handleBlur = (0, import_react2.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2282
- const computedStyle = (0, import_react2.useMemo)(
2283
- () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }),
2284
- [resolvedStyle, isDark, pressed, hovered, focused]
2352
+ const needsWidth = !isWeb && hasResponsiveBuckets(resolvedStyle);
2353
+ const width = useConditionalWidth(needsWidth);
2354
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
2355
+ const [pressed, setPressed] = (0, import_react3.useState)(false);
2356
+ const [hovered, setHovered] = (0, import_react3.useState)(false);
2357
+ const [focused, setFocused] = (0, import_react3.useState)(false);
2358
+ const handlePressIn = (0, import_react3.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2359
+ const handlePressOut = (0, import_react3.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2360
+ const handleMouseEnter = (0, import_react3.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2361
+ const handleMouseLeave = (0, import_react3.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2362
+ const handleFocus = (0, import_react3.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2363
+ const handleBlur = (0, import_react3.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2364
+ const computedStyle = (0, import_react3.useMemo)(
2365
+ () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }, breakpoints),
2366
+ [resolvedStyle, isDark, pressed, hovered, focused, width]
2367
+ // eslint-disable-line react-hooks/exhaustive-deps
2285
2368
  );
2286
2369
  const finalStyle = styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
2287
2370
  const { children, ...restWithoutChildren } = rest;
@@ -2297,33 +2380,88 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
2297
2380
  onFocus: handleFocus,
2298
2381
  onBlur: handleBlur
2299
2382
  };
2300
- return Array.isArray(children) ? import_react2.default.createElement(Component, componentProps, ...children) : import_react2.default.createElement(Component, componentProps, children);
2383
+ return Array.isArray(children) ? import_react3.default.createElement(Component, componentProps, ...children) : import_react3.default.createElement(Component, componentProps, children);
2301
2384
  }
2302
2385
  );
2303
2386
  InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
2304
2387
 
2305
2388
  // src/DarkWrapper.tsx
2306
- var import_react3 = __toESM(require("react"));
2307
- var DarkWrapper = import_react3.default.forwardRef(
2389
+ var import_react4 = __toESM(require("react"));
2390
+ var RESPONSIVE_MODS3 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2391
+ function hasResponsiveBuckets2(resolved) {
2392
+ for (const key of Object.keys(resolved)) {
2393
+ if (key === "base") continue;
2394
+ for (const mod of key.split(":")) {
2395
+ if (RESPONSIVE_MODS3.has(mod)) return true;
2396
+ }
2397
+ }
2398
+ return false;
2399
+ }
2400
+ var DarkWrapper = import_react4.default.forwardRef(
2308
2401
  function DarkWrapper2({ Component, resolvedStyle, style: styleProp, children, ...rest }, ref) {
2309
2402
  const isDark = useGlobalDarkMode();
2310
- const computedStyle = flatten(resolvedStyle, isDark);
2403
+ const needsWidth = !isWeb && hasResponsiveBuckets2(resolvedStyle);
2404
+ const width = useConditionalWidth(needsWidth);
2405
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
2406
+ const computedStyle = flatten(resolvedStyle, isDark, {}, breakpoints);
2311
2407
  const finalStyle = styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
2312
2408
  const props = { ref, ...rest, style: finalStyle };
2313
- return Array.isArray(children) ? import_react3.default.createElement(Component, props, ...children) : import_react3.default.createElement(Component, props, children);
2409
+ return Array.isArray(children) ? import_react4.default.createElement(Component, props, ...children) : import_react4.default.createElement(Component, props, children);
2314
2410
  }
2315
2411
  );
2316
2412
  DarkWrapper.displayName = "Kbach.DarkWrapper";
2317
2413
 
2318
2414
  // src/jsx-runtime.tsx
2319
- var MODE_BUCKET_RE = /^(not-)?(dark|light)(:(not-)?(dark|light))*$/;
2320
- function hasInteractiveBuckets(resolved) {
2321
- return Object.keys(resolved).some((key) => {
2322
- if (key === "base") return false;
2323
- if (/^(sm|md|lg|xl|2xl)$/.test(key)) return false;
2324
- if (MODE_BUCKET_RE.test(key)) return false;
2325
- return true;
2326
- });
2415
+ var INTERACTIVE_MODS = /* @__PURE__ */ new Set([
2416
+ "hover",
2417
+ "not-hover",
2418
+ "focus",
2419
+ "not-focus",
2420
+ "active",
2421
+ "not-active",
2422
+ "pressed",
2423
+ "not-pressed",
2424
+ "disabled",
2425
+ "not-disabled",
2426
+ "checked",
2427
+ "not-checked",
2428
+ "visited",
2429
+ "not-visited",
2430
+ "placeholder",
2431
+ "focus-within",
2432
+ "focus-visible",
2433
+ "group-hover",
2434
+ "group-focus",
2435
+ "peer-hover",
2436
+ "peer-focus",
2437
+ "first",
2438
+ "last",
2439
+ "odd",
2440
+ "even",
2441
+ "only"
2442
+ ]);
2443
+ var RESPONSIVE_MODS4 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2444
+ var MODE_MODS_JSX = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
2445
+ function bucketMods(resolved) {
2446
+ let interactive = false;
2447
+ let modeOrResponsive = false;
2448
+ for (const key of Object.keys(resolved)) {
2449
+ if (key === "base") continue;
2450
+ const parts = key.split(":");
2451
+ for (const mod of parts) {
2452
+ if (INTERACTIVE_MODS.has(mod)) {
2453
+ interactive = true;
2454
+ break;
2455
+ }
2456
+ }
2457
+ for (const mod of parts) {
2458
+ if (MODE_MODS_JSX.has(mod) || RESPONSIVE_MODS4.has(mod)) {
2459
+ modeOrResponsive = true;
2460
+ break;
2461
+ }
2462
+ }
2463
+ }
2464
+ return { interactive, modeOrResponsive };
2327
2465
  }
2328
2466
  var CONSUMED_PROPS = /* @__PURE__ */ new Set(["className", "kb", "__kbachStyles", "__kbachClasses"]);
2329
2467
  function omitConsumed(props) {
@@ -2351,7 +2489,8 @@ function processElement(type, rawProps, key, isStaticChildren) {
2351
2489
  const config = getConfig();
2352
2490
  const resolved = __kbachStyles ?? (classStr ? resolve(classStr, config.theme, config.darkMode) : {});
2353
2491
  const { style: userStyle, ...passProps } = omitConsumed(rawProps);
2354
- if (hasInteractiveBuckets(resolved)) {
2492
+ const { interactive, modeOrResponsive } = bucketMods(resolved);
2493
+ if (interactive) {
2355
2494
  return (0, import_jsx_runtime.jsx)(InteractiveWrapper, {
2356
2495
  Component: type,
2357
2496
  resolvedStyle: resolved,
@@ -2360,10 +2499,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
2360
2499
  ...passProps
2361
2500
  }, key);
2362
2501
  }
2363
- const hasModeVariants = Object.keys(resolved).some(
2364
- (k) => k !== "base" && MODE_BUCKET_RE.test(k)
2365
- );
2366
- if (hasModeVariants) {
2502
+ if (modeOrResponsive) {
2367
2503
  return (0, import_jsx_runtime.jsx)(DarkWrapper, {
2368
2504
  Component: type,
2369
2505
  resolvedStyle: resolved,
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-J322Q3UR.mjs";
6
- import "./chunk-ADRZPFQC.mjs";
5
+ } from "./chunk-ALKTUEAA.mjs";
6
+ import "./chunk-UFSME6UC.mjs";
7
7
 
8
8
  // src/jsx-dev-runtime.tsx
9
9
  function jsxDEV(type, props, key, isStaticChildren, _source, _self) {