@kbach/react 0.3.3 → 0.3.5

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.
@@ -1871,6 +1871,36 @@ function getPluginStandaloneMap() {
1871
1871
  return PLUGIN_STANDALONE;
1872
1872
  }
1873
1873
 
1874
+ // src/core/responsiveStore.ts
1875
+ var RESPONSIVE_KEY = "__kbach_responsive__";
1876
+ function getStore() {
1877
+ const g = globalThis;
1878
+ if (!g[RESPONSIVE_KEY]) {
1879
+ g[RESPONSIVE_KEY] = { width: 0, screens: {}, listeners: /* @__PURE__ */ new Set() };
1880
+ }
1881
+ return g[RESPONSIVE_KEY];
1882
+ }
1883
+ function getGlobalScreens() {
1884
+ return getStore().screens;
1885
+ }
1886
+ function getGlobalWidth() {
1887
+ return getStore().width;
1888
+ }
1889
+ function subscribeGlobalWidth(listener) {
1890
+ const store = getStore();
1891
+ store.listeners.add(listener);
1892
+ return () => store.listeners.delete(listener);
1893
+ }
1894
+ function getActiveBreakpoints(width) {
1895
+ const store = getStore();
1896
+ const w = width ?? store.width;
1897
+ const active = /* @__PURE__ */ new Set();
1898
+ for (const [name, minW] of Object.entries(store.screens)) {
1899
+ if (typeof minW === "number" && w >= minW) active.add(name);
1900
+ }
1901
+ return active;
1902
+ }
1903
+
1874
1904
  // src/core/resolver.ts
1875
1905
  var styleCache = new LRUCache(1e4);
1876
1906
  var _styleEl = null;
@@ -1921,6 +1951,7 @@ var GROUP_PEER_PREFIX = {
1921
1951
  "peer-focus": ".peer:focus ~"
1922
1952
  };
1923
1953
  var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
1954
+ var RESPONSIVE_MODS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
1924
1955
  function injectClassRule(cls, bucketKey, styles, darkMode) {
1925
1956
  const cssDecls = styleValueToCSS(styles);
1926
1957
  if (!cssDecls) return;
@@ -1930,10 +1961,12 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1930
1961
  return;
1931
1962
  }
1932
1963
  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));
1964
+ const responsiveMods = mods.filter((m) => RESPONSIVE_MODS.has(m));
1965
+ const nonResponsiveMods = mods.filter((m) => !RESPONSIVE_MODS.has(m));
1966
+ const hasDark = nonResponsiveMods.includes("dark") || nonResponsiveMods.includes("not-light");
1967
+ const hasLight = nonResponsiveMods.includes("light") || nonResponsiveMods.includes("not-dark");
1968
+ const ancestorMods = nonResponsiveMods.filter((m) => m in GROUP_PEER_PREFIX);
1969
+ const pseudoMods = nonResponsiveMods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
1937
1970
  const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
1938
1971
  const elementSelector = `.${escaped}${pseudoSuffix}`;
1939
1972
  const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
@@ -1958,6 +1991,13 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1958
1991
  } else {
1959
1992
  rule = `${selector} { ${cssDecls} }`;
1960
1993
  }
1994
+ if (responsiveMods.length > 0) {
1995
+ const screens = getGlobalScreens();
1996
+ const minWidth = Math.max(...responsiveMods.map((m) => screens[m] ?? 0));
1997
+ if (minWidth > 0) {
1998
+ rule = `@media (min-width: ${minWidth}px) { ${rule} }`;
1999
+ }
2000
+ }
1961
2001
  injectRule(rule);
1962
2002
  }
1963
2003
  var RN_ONLY_PROPS = /* @__PURE__ */ new Set([
@@ -2026,18 +2066,18 @@ function resolve(classString, theme, darkMode = "attribute") {
2026
2066
  }
2027
2067
  return result;
2028
2068
  }
2029
- function flatten(resolved, isDark, state = {}) {
2069
+ function flatten(resolved, isDark, state = {}, breakpoints = /* @__PURE__ */ new Set()) {
2030
2070
  const result = { ...resolved.base ?? {} };
2031
2071
  for (const [key, styles] of Object.entries(resolved)) {
2032
2072
  if (key === "base" || !styles) continue;
2033
2073
  const mods = key.split(":");
2034
- if (matchesMods(mods, isDark, state)) {
2074
+ if (matchesMods(mods, isDark, state, breakpoints)) {
2035
2075
  Object.assign(result, styles);
2036
2076
  }
2037
2077
  }
2038
2078
  return result;
2039
2079
  }
2040
- function matchesMods(mods, isDark, state) {
2080
+ function matchesMods(mods, isDark, state, breakpoints) {
2041
2081
  return mods.every((mod) => {
2042
2082
  switch (mod) {
2043
2083
  case "dark":
@@ -2075,6 +2115,12 @@ function matchesMods(mods, isDark, state) {
2075
2115
  return !state.visited;
2076
2116
  case "placeholder":
2077
2117
  return !!state.placeholder;
2118
+ case "sm":
2119
+ case "md":
2120
+ case "lg":
2121
+ case "xl":
2122
+ case "2xl":
2123
+ return breakpoints.has(mod);
2078
2124
  default:
2079
2125
  return false;
2080
2126
  }
@@ -2112,6 +2158,28 @@ function getConfig() {
2112
2158
  store.resolved = buildConfig({});
2113
2159
  return store.resolved;
2114
2160
  }
2161
+ function resolveOneRef(ref, colors) {
2162
+ const m = /^([a-z][a-z0-9]*)-(\d+)$/.exec(ref);
2163
+ if (!m) return null;
2164
+ const entry = colors[m[1]];
2165
+ if (!entry || typeof entry !== "object") return null;
2166
+ return entry[m[2]] ?? null;
2167
+ }
2168
+ function resolveColorRefs(colors) {
2169
+ const out = {};
2170
+ for (const [key, val] of Object.entries(colors)) {
2171
+ if (typeof val === "string") {
2172
+ out[key] = resolveOneRef(val, colors) ?? val;
2173
+ } else {
2174
+ const shades = {};
2175
+ for (const [shade, hex] of Object.entries(val)) {
2176
+ shades[shade] = resolveOneRef(hex, colors) ?? hex;
2177
+ }
2178
+ out[key] = shades;
2179
+ }
2180
+ }
2181
+ return out;
2182
+ }
2115
2183
  function buildConfig(userConfig) {
2116
2184
  let theme = { ...defaultTheme };
2117
2185
  if (userConfig.theme) {
@@ -2130,6 +2198,7 @@ function buildConfig(userConfig) {
2130
2198
  }
2131
2199
  }
2132
2200
  }
2201
+ theme = { ...theme, colors: resolveColorRefs(theme.colors) };
2133
2202
  const resolved = {
2134
2203
  darkMode: userConfig.darkMode ?? "attribute",
2135
2204
  theme,
@@ -2190,7 +2259,7 @@ var customVariants = new Proxy(
2190
2259
 
2191
2260
  // src/core/darkModeStore.ts
2192
2261
  var KEY = "__kbach_dark_store__";
2193
- function getStore() {
2262
+ function getStore2() {
2194
2263
  const g = globalThis;
2195
2264
  if (!g[KEY]) {
2196
2265
  g[KEY] = { isDark: false, lastNotified: false, subscribers: /* @__PURE__ */ new Set() };
@@ -2198,16 +2267,16 @@ function getStore() {
2198
2267
  return g[KEY];
2199
2268
  }
2200
2269
  function getGlobalDarkMode() {
2201
- return getStore().isDark;
2270
+ return getStore2().isDark;
2202
2271
  }
2203
2272
  function subscribeGlobalDarkMode(callback) {
2204
- const store = getStore();
2273
+ const store = getStore2();
2205
2274
  store.subscribers.add(callback);
2206
2275
  return () => store.subscribers.delete(callback);
2207
2276
  }
2208
2277
 
2209
2278
  // src/InteractiveWrapper.tsx
2210
- var import_react2 = __toESM(require("react"));
2279
+ var import_react3 = __toESM(require("react"));
2211
2280
 
2212
2281
  // src/useGlobalDarkMode.ts
2213
2282
  var import_react = __toESM(require("react"));
@@ -2225,6 +2294,25 @@ function useGlobalDarkMode() {
2225
2294
  );
2226
2295
  }
2227
2296
 
2297
+ // src/useGlobalWidth.ts
2298
+ var import_react2 = __toESM(require("react"));
2299
+ var useSyncExternalStore2 = import_react2.default.useSyncExternalStore ?? function useSyncExternalStoreFallback2(subscribe, getSnapshot, _getServerSnapshot) {
2300
+ const [, forceUpdate] = import_react2.default.useReducer((n) => n + 1, 0);
2301
+ import_react2.default.useEffect(() => subscribe(forceUpdate), [subscribe]);
2302
+ return getSnapshot();
2303
+ };
2304
+ var NOOP_SUB = (_) => () => {
2305
+ };
2306
+ var ZERO_SNAP = () => 0;
2307
+ var EMPTY_BREAKPOINTS = /* @__PURE__ */ new Set();
2308
+ function useConditionalWidth(active) {
2309
+ return useSyncExternalStore2(
2310
+ active ? subscribeGlobalWidth : NOOP_SUB,
2311
+ active ? getGlobalWidth : ZERO_SNAP,
2312
+ ZERO_SNAP
2313
+ );
2314
+ }
2315
+
2228
2316
  // src/InteractiveWrapper.tsx
2229
2317
  function chain(original, extra) {
2230
2318
  return (...args) => {
@@ -2232,7 +2320,17 @@ function chain(original, extra) {
2232
2320
  extra();
2233
2321
  };
2234
2322
  }
2235
- var InteractiveWrapper = (0, import_react2.forwardRef)(
2323
+ var RESPONSIVE_MODS2 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2324
+ function hasResponsiveBuckets(resolved) {
2325
+ for (const key of Object.keys(resolved)) {
2326
+ if (key === "base") continue;
2327
+ for (const mod of key.split(":")) {
2328
+ if (RESPONSIVE_MODS2.has(mod)) return true;
2329
+ }
2330
+ }
2331
+ return false;
2332
+ }
2333
+ var InteractiveWrapper = (0, import_react3.forwardRef)(
2236
2334
  function InteractiveWrapper2({
2237
2335
  Component,
2238
2336
  resolvedStyle,
@@ -2247,18 +2345,22 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
2247
2345
  ...rest
2248
2346
  }, ref) {
2249
2347
  const isDark = useGlobalDarkMode();
2250
- const [pressed, setPressed] = (0, import_react2.useState)(false);
2251
- const [hovered, setHovered] = (0, import_react2.useState)(false);
2252
- const [focused, setFocused] = (0, import_react2.useState)(false);
2253
- const handlePressIn = (0, import_react2.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2254
- const handlePressOut = (0, import_react2.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2255
- const handleMouseEnter = (0, import_react2.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2256
- const handleMouseLeave = (0, import_react2.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2257
- const handleFocus = (0, import_react2.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2258
- const handleBlur = (0, import_react2.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2259
- const computedStyle = (0, import_react2.useMemo)(
2260
- () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }),
2261
- [resolvedStyle, isDark, pressed, hovered, focused]
2348
+ const needsWidth = !isWeb && hasResponsiveBuckets(resolvedStyle);
2349
+ const width = useConditionalWidth(needsWidth);
2350
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
2351
+ const [pressed, setPressed] = (0, import_react3.useState)(false);
2352
+ const [hovered, setHovered] = (0, import_react3.useState)(false);
2353
+ const [focused, setFocused] = (0, import_react3.useState)(false);
2354
+ const handlePressIn = (0, import_react3.useCallback)(chain(onPressIn, () => setPressed(true)), [onPressIn]);
2355
+ const handlePressOut = (0, import_react3.useCallback)(chain(onPressOut, () => setPressed(false)), [onPressOut]);
2356
+ const handleMouseEnter = (0, import_react3.useCallback)(chain(onMouseEnter, () => setHovered(true)), [onMouseEnter]);
2357
+ const handleMouseLeave = (0, import_react3.useCallback)(chain(onMouseLeave, () => setHovered(false)), [onMouseLeave]);
2358
+ const handleFocus = (0, import_react3.useCallback)(chain(onFocus, () => setFocused(true)), [onFocus]);
2359
+ const handleBlur = (0, import_react3.useCallback)(chain(onBlur, () => setFocused(false)), [onBlur]);
2360
+ const computedStyle = (0, import_react3.useMemo)(
2361
+ () => flatten(resolvedStyle, isDark, { pressed, hover: hovered, focus: focused }, breakpoints),
2362
+ [resolvedStyle, isDark, pressed, hovered, focused, width]
2363
+ // eslint-disable-line react-hooks/exhaustive-deps
2262
2364
  );
2263
2365
  const finalStyle = styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
2264
2366
  const { children, ...restWithoutChildren } = rest;
@@ -2274,33 +2376,88 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
2274
2376
  onFocus: handleFocus,
2275
2377
  onBlur: handleBlur
2276
2378
  };
2277
- return Array.isArray(children) ? import_react2.default.createElement(Component, componentProps, ...children) : import_react2.default.createElement(Component, componentProps, children);
2379
+ return Array.isArray(children) ? import_react3.default.createElement(Component, componentProps, ...children) : import_react3.default.createElement(Component, componentProps, children);
2278
2380
  }
2279
2381
  );
2280
2382
  InteractiveWrapper.displayName = "Kbach.InteractiveWrapper";
2281
2383
 
2282
2384
  // src/DarkWrapper.tsx
2283
- var import_react3 = __toESM(require("react"));
2284
- var DarkWrapper = import_react3.default.forwardRef(
2385
+ var import_react4 = __toESM(require("react"));
2386
+ var RESPONSIVE_MODS3 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2387
+ function hasResponsiveBuckets2(resolved) {
2388
+ for (const key of Object.keys(resolved)) {
2389
+ if (key === "base") continue;
2390
+ for (const mod of key.split(":")) {
2391
+ if (RESPONSIVE_MODS3.has(mod)) return true;
2392
+ }
2393
+ }
2394
+ return false;
2395
+ }
2396
+ var DarkWrapper = import_react4.default.forwardRef(
2285
2397
  function DarkWrapper2({ Component, resolvedStyle, style: styleProp, children, ...rest }, ref) {
2286
2398
  const isDark = useGlobalDarkMode();
2287
- const computedStyle = flatten(resolvedStyle, isDark);
2399
+ const needsWidth = !isWeb && hasResponsiveBuckets2(resolvedStyle);
2400
+ const width = useConditionalWidth(needsWidth);
2401
+ const breakpoints = needsWidth ? getActiveBreakpoints(width) : EMPTY_BREAKPOINTS;
2402
+ const computedStyle = flatten(resolvedStyle, isDark, {}, breakpoints);
2288
2403
  const finalStyle = styleProp ? Array.isArray(styleProp) ? Object.assign({}, computedStyle, ...styleProp) : { ...computedStyle, ...styleProp } : computedStyle;
2289
2404
  const props = { ref, ...rest, style: finalStyle };
2290
- return Array.isArray(children) ? import_react3.default.createElement(Component, props, ...children) : import_react3.default.createElement(Component, props, children);
2405
+ return Array.isArray(children) ? import_react4.default.createElement(Component, props, ...children) : import_react4.default.createElement(Component, props, children);
2291
2406
  }
2292
2407
  );
2293
2408
  DarkWrapper.displayName = "Kbach.DarkWrapper";
2294
2409
 
2295
2410
  // src/jsx-runtime.tsx
2296
- var MODE_BUCKET_RE = /^(not-)?(dark|light)(:(not-)?(dark|light))*$/;
2297
- function hasInteractiveBuckets(resolved) {
2298
- return Object.keys(resolved).some((key) => {
2299
- if (key === "base") return false;
2300
- if (/^(sm|md|lg|xl|2xl)$/.test(key)) return false;
2301
- if (MODE_BUCKET_RE.test(key)) return false;
2302
- return true;
2303
- });
2411
+ var INTERACTIVE_MODS = /* @__PURE__ */ new Set([
2412
+ "hover",
2413
+ "not-hover",
2414
+ "focus",
2415
+ "not-focus",
2416
+ "active",
2417
+ "not-active",
2418
+ "pressed",
2419
+ "not-pressed",
2420
+ "disabled",
2421
+ "not-disabled",
2422
+ "checked",
2423
+ "not-checked",
2424
+ "visited",
2425
+ "not-visited",
2426
+ "placeholder",
2427
+ "focus-within",
2428
+ "focus-visible",
2429
+ "group-hover",
2430
+ "group-focus",
2431
+ "peer-hover",
2432
+ "peer-focus",
2433
+ "first",
2434
+ "last",
2435
+ "odd",
2436
+ "even",
2437
+ "only"
2438
+ ]);
2439
+ var RESPONSIVE_MODS4 = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
2440
+ var MODE_MODS_JSX = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
2441
+ function bucketMods(resolved) {
2442
+ let interactive = false;
2443
+ let modeOrResponsive = false;
2444
+ for (const key of Object.keys(resolved)) {
2445
+ if (key === "base") continue;
2446
+ const parts = key.split(":");
2447
+ for (const mod of parts) {
2448
+ if (INTERACTIVE_MODS.has(mod)) {
2449
+ interactive = true;
2450
+ break;
2451
+ }
2452
+ }
2453
+ for (const mod of parts) {
2454
+ if (MODE_MODS_JSX.has(mod) || RESPONSIVE_MODS4.has(mod)) {
2455
+ modeOrResponsive = true;
2456
+ break;
2457
+ }
2458
+ }
2459
+ }
2460
+ return { interactive, modeOrResponsive };
2304
2461
  }
2305
2462
  var CONSUMED_PROPS = /* @__PURE__ */ new Set(["className", "kb", "__kbachStyles", "__kbachClasses"]);
2306
2463
  function omitConsumed(props) {
@@ -2328,7 +2485,8 @@ function processElement(type, rawProps, key, isStaticChildren) {
2328
2485
  const config = getConfig();
2329
2486
  const resolved = __kbachStyles ?? (classStr ? resolve(classStr, config.theme, config.darkMode) : {});
2330
2487
  const { style: userStyle, ...passProps } = omitConsumed(rawProps);
2331
- if (hasInteractiveBuckets(resolved)) {
2488
+ const { interactive, modeOrResponsive } = bucketMods(resolved);
2489
+ if (interactive) {
2332
2490
  return (0, import_jsx_runtime.jsx)(InteractiveWrapper, {
2333
2491
  Component: type,
2334
2492
  resolvedStyle: resolved,
@@ -2337,10 +2495,7 @@ function processElement(type, rawProps, key, isStaticChildren) {
2337
2495
  ...passProps
2338
2496
  }, key);
2339
2497
  }
2340
- const hasModeVariants = Object.keys(resolved).some(
2341
- (k) => k !== "base" && MODE_BUCKET_RE.test(k)
2342
- );
2343
- if (hasModeVariants) {
2498
+ if (modeOrResponsive) {
2344
2499
  return (0, import_jsx_runtime.jsx)(DarkWrapper, {
2345
2500
  Component: type,
2346
2501
  resolvedStyle: resolved,
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-NFPYOE5K.mjs";
6
- import "./chunk-7XL6VQDW.mjs";
5
+ } from "./chunk-2WECG35P.mjs";
6
+ import "./chunk-6YT4ECUI.mjs";
7
7
 
8
8
  // src/jsx-dev-runtime.tsx
9
9
  function jsxDEV(type, props, key, isStaticChildren, _source, _self) {