@kbach/react 0.2.8 → 0.3.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.
@@ -597,6 +597,13 @@ function escapeCSSSelector(cls) {
597
597
  }
598
598
 
599
599
  // src/core/parser.ts
600
+ var PLUGIN_MODIFIERS = /* @__PURE__ */ new Set();
601
+ function registerModifier(name) {
602
+ PLUGIN_MODIFIERS.add(name);
603
+ }
604
+ function clearPluginModifiers() {
605
+ PLUGIN_MODIFIERS.clear();
606
+ }
600
607
  var MODIFIERS = /* @__PURE__ */ new Set([
601
608
  // Theme
602
609
  "dark",
@@ -1026,7 +1033,7 @@ function parseClass(className) {
1026
1033
  const colonIdx = findOuterColon(remaining);
1027
1034
  if (colonIdx === -1) break;
1028
1035
  const candidate = remaining.slice(0, colonIdx);
1029
- if (MODIFIERS.has(candidate)) {
1036
+ if (MODIFIERS.has(candidate) || PLUGIN_MODIFIERS.has(candidate)) {
1030
1037
  modifiers.push(candidate);
1031
1038
  remaining = remaining.slice(colonIdx + 1);
1032
1039
  } else {
@@ -1492,43 +1499,64 @@ var RESOLVERS = {
1492
1499
  // ── Border width ───────────────────────────────────────────────────────────
1493
1500
  border: ({ value, isArbitrary }, { colors, borderWidth, spacing }) => {
1494
1501
  if (!value) return { borderWidth: borderWidth["DEFAULT"] ?? 1 };
1495
- const color = resolveColor(value, colors, isArbitrary);
1496
- if (color) return { borderColor: color };
1497
1502
  if (isArbitrary) {
1498
1503
  const w2 = toNativeValue(value);
1499
- return { borderWidth: typeof w2 === "number" ? w2 : 1 };
1504
+ if (typeof w2 === "number") return { borderWidth: w2 };
1505
+ return { borderColor: value };
1500
1506
  }
1507
+ const color = resolveColor(value, colors, false);
1508
+ if (color) return { borderColor: color };
1501
1509
  const w = borderWidth[value] ?? spacing[value];
1502
1510
  if (w !== void 0) return { borderWidth: typeof w === "number" ? w : parseFloat(String(w)) };
1503
1511
  return null;
1504
1512
  },
1505
1513
  "border-t": ({ value, isArbitrary }, { colors, borderWidth }) => {
1506
1514
  if (!value) return { borderTopWidth: 1 };
1507
- const color = resolveColor(value, colors, isArbitrary);
1515
+ if (isArbitrary) {
1516
+ const w2 = toNativeValue(value);
1517
+ if (typeof w2 === "number") return { borderTopWidth: w2 };
1518
+ return { borderTopColor: value };
1519
+ }
1520
+ const color = resolveColor(value, colors, false);
1508
1521
  if (color) return { borderTopColor: color };
1509
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1510
- return w !== null ? { borderTopWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1522
+ const w = borderWidth[value];
1523
+ return w !== void 0 ? { borderTopWidth: w } : null;
1511
1524
  },
1512
1525
  "border-r": ({ value, isArbitrary }, { colors, borderWidth }) => {
1513
1526
  if (!value) return { borderRightWidth: 1 };
1514
- const color = resolveColor(value, colors, isArbitrary);
1527
+ if (isArbitrary) {
1528
+ const w2 = toNativeValue(value);
1529
+ if (typeof w2 === "number") return { borderRightWidth: w2 };
1530
+ return { borderRightColor: value };
1531
+ }
1532
+ const color = resolveColor(value, colors, false);
1515
1533
  if (color) return { borderRightColor: color };
1516
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1517
- return w !== null ? { borderRightWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1534
+ const w = borderWidth[value];
1535
+ return w !== void 0 ? { borderRightWidth: w } : null;
1518
1536
  },
1519
1537
  "border-b": ({ value, isArbitrary }, { colors, borderWidth }) => {
1520
1538
  if (!value) return { borderBottomWidth: 1 };
1521
- const color = resolveColor(value, colors, isArbitrary);
1539
+ if (isArbitrary) {
1540
+ const w2 = toNativeValue(value);
1541
+ if (typeof w2 === "number") return { borderBottomWidth: w2 };
1542
+ return { borderBottomColor: value };
1543
+ }
1544
+ const color = resolveColor(value, colors, false);
1522
1545
  if (color) return { borderBottomColor: color };
1523
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1524
- return w !== null ? { borderBottomWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1546
+ const w = borderWidth[value];
1547
+ return w !== void 0 ? { borderBottomWidth: w } : null;
1525
1548
  },
1526
1549
  "border-l": ({ value, isArbitrary }, { colors, borderWidth }) => {
1527
1550
  if (!value) return { borderLeftWidth: 1 };
1528
- const color = resolveColor(value, colors, isArbitrary);
1551
+ if (isArbitrary) {
1552
+ const w2 = toNativeValue(value);
1553
+ if (typeof w2 === "number") return { borderLeftWidth: w2 };
1554
+ return { borderLeftColor: value };
1555
+ }
1556
+ const color = resolveColor(value, colors, false);
1529
1557
  if (color) return { borderLeftColor: color };
1530
- const w = borderWidth[value] ?? (isArbitrary ? toNativeValue(value) : null);
1531
- return w !== null ? { borderLeftWidth: typeof w === "number" ? w : parseFloat(String(w)) } : null;
1558
+ const w = borderWidth[value];
1559
+ return w !== void 0 ? { borderLeftWidth: w } : null;
1532
1560
  },
1533
1561
  // ── Border radius ──────────────────────────────────────────────────────────
1534
1562
  rounded: ({ value, isArbitrary }, { borderRadius }) => {
@@ -1593,7 +1621,6 @@ var RESOLVERS = {
1593
1621
  return { flexShrink: isNaN(n) ? 1 : n };
1594
1622
  },
1595
1623
  order: ({ value, isArbitrary }, _) => {
1596
- if (isArbitrary) return { order: parseInt(value) };
1597
1624
  const n = parseInt(value);
1598
1625
  return isNaN(n) ? null : { order: n };
1599
1626
  },
@@ -1641,9 +1668,15 @@ var RESOLVERS = {
1641
1668
  },
1642
1669
  // ── Z-index ────────────────────────────────────────────────────────────────
1643
1670
  z: ({ value, isArbitrary }, { zIndex }) => {
1644
- if (isArbitrary) return { zIndex: parseInt(value) };
1671
+ if (isArbitrary) {
1672
+ const n2 = parseInt(value);
1673
+ return isNaN(n2) ? null : { zIndex: n2 };
1674
+ }
1645
1675
  const v = zIndex[value];
1646
- if (v !== void 0) return { zIndex: v };
1676
+ if (v !== void 0) {
1677
+ if (v === "auto") return isWeb ? { zIndex: "auto" } : null;
1678
+ return { zIndex: v };
1679
+ }
1647
1680
  const n = parseInt(value);
1648
1681
  return isNaN(n) ? null : { zIndex: n };
1649
1682
  },
@@ -1686,25 +1719,30 @@ var RESOLVERS = {
1686
1719
  return shadow[key] ?? null;
1687
1720
  },
1688
1721
  // ── Scale ──────────────────────────────────────────────────────────────────
1722
+ // Non-arbitrary: scale-150 → value='150' → 150/100 = 1.5
1723
+ // Arbitrary: scale-[1.5] → value='1.5' → used as-is (already the factor)
1689
1724
  scale: ({ value, isArbitrary }) => {
1690
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1725
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1691
1726
  if (isNaN(n)) return null;
1692
1727
  return isWeb ? { transform: `scale(${n})` } : { transform: [{ scale: n }] };
1693
1728
  },
1694
1729
  "scale-x": ({ value, isArbitrary }) => {
1695
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1730
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1696
1731
  if (isNaN(n)) return null;
1697
1732
  return isWeb ? { transform: `scaleX(${n})` } : { transform: [{ scaleX: n }] };
1698
1733
  },
1699
1734
  "scale-y": ({ value, isArbitrary }) => {
1700
- const n = isArbitrary ? parseFloat(value) / 100 : parseFloat(value) / 100;
1735
+ const n = isArbitrary ? parseFloat(value) : parseFloat(value) / 100;
1701
1736
  if (isNaN(n)) return null;
1702
1737
  return isWeb ? { transform: `scaleY(${n})` } : { transform: [{ scaleY: n }] };
1703
1738
  },
1704
1739
  // ── Rotate ─────────────────────────────────────────────────────────────────
1705
1740
  rotate: ({ value, negative, isArbitrary }) => {
1706
- const raw = isArbitrary ? value : `${value}deg`;
1707
- const deg = parseFloat(raw);
1741
+ if (isArbitrary) {
1742
+ const finalValue = negative ? `-${value}` : value;
1743
+ return isWeb ? { transform: `rotate(${finalValue})` } : { transform: [{ rotate: finalValue }] };
1744
+ }
1745
+ const deg = parseFloat(value);
1708
1746
  if (isNaN(deg)) return null;
1709
1747
  const finalDeg = negative ? -deg : deg;
1710
1748
  return isWeb ? { transform: `rotate(${finalDeg}deg)` } : { transform: [{ rotate: `${finalDeg}deg` }] };
@@ -1713,18 +1751,31 @@ var RESOLVERS = {
1713
1751
  "translate-x": ({ value, negative, isArbitrary }, { spacing }) => {
1714
1752
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
1715
1753
  if (v === null) return null;
1716
- return isWeb ? { transform: `translateX(${v})` } : { transform: [{ translateX: typeof v === "string" ? parseFloat(v) : v }] };
1754
+ if (isWeb) return { transform: `translateX(${v})` };
1755
+ if (typeof v === "string") {
1756
+ const n = parseFloat(v);
1757
+ return isNaN(n) ? null : { transform: [{ translateX: n }] };
1758
+ }
1759
+ return { transform: [{ translateX: v }] };
1717
1760
  },
1718
1761
  "translate-y": ({ value, negative, isArbitrary }, { spacing }) => {
1719
1762
  const v = resolveSpacing(value, negative, spacing, isArbitrary);
1720
1763
  if (v === null) return null;
1721
- return isWeb ? { transform: `translateY(${v})` } : { transform: [{ translateY: typeof v === "string" ? parseFloat(v) : v }] };
1764
+ if (isWeb) return { transform: `translateY(${v})` };
1765
+ if (typeof v === "string") {
1766
+ const n = parseFloat(v);
1767
+ return isNaN(n) ? null : { transform: [{ translateY: n }] };
1768
+ }
1769
+ return { transform: [{ translateY: v }] };
1722
1770
  },
1723
1771
  // ── Aspect ratio ───────────────────────────────────────────────────────────
1724
1772
  aspect: ({ value, isArbitrary }) => {
1725
1773
  if (isArbitrary) return { aspectRatio: value };
1726
1774
  const presets = { auto: "auto", square: 1, video: 16 / 9 };
1727
- return value in presets ? { aspectRatio: presets[value] } : null;
1775
+ if (!(value in presets)) return null;
1776
+ const v = presets[value];
1777
+ if (v === "auto") return isWeb ? { aspectRatio: "auto" } : null;
1778
+ return { aspectRatio: v };
1728
1779
  },
1729
1780
  // ── Transition (web-only; use Animated API on native) ─────────────────────
1730
1781
  transition: ({ value }) => {
@@ -1801,18 +1852,25 @@ var RESOLVERS = {
1801
1852
  }
1802
1853
  };
1803
1854
  function resolveUtility(parsed, theme) {
1804
- if (!parsed.value && parsed.utility in STANDALONE) {
1805
- return STANDALONE[parsed.utility] ?? null;
1855
+ if (!parsed.value) {
1856
+ if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
1857
+ if (parsed.utility in STANDALONE) return STANDALONE[parsed.utility] ?? null;
1806
1858
  }
1807
- const resolver = RESOLVERS[parsed.utility];
1859
+ const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
1808
1860
  if (resolver) return resolver(parsed, theme);
1809
1861
  return null;
1810
1862
  }
1811
- function getStandaloneMap() {
1812
- return STANDALONE;
1863
+ var PLUGIN_STANDALONE = {};
1864
+ var PLUGIN_RESOLVERS = {};
1865
+ function clearPluginUtilities() {
1866
+ for (const key of Object.keys(PLUGIN_STANDALONE)) delete PLUGIN_STANDALONE[key];
1867
+ for (const key of Object.keys(PLUGIN_RESOLVERS)) delete PLUGIN_RESOLVERS[key];
1813
1868
  }
1814
- function getResolverMap() {
1815
- return RESOLVERS;
1869
+ function getPluginStandaloneMap() {
1870
+ return PLUGIN_STANDALONE;
1871
+ }
1872
+ function getPluginResolverMap() {
1873
+ return PLUGIN_RESOLVERS;
1816
1874
  }
1817
1875
 
1818
1876
  // src/core/resolver.ts
@@ -1834,6 +1892,37 @@ function injectRule(rule) {
1834
1892
  } catch {
1835
1893
  }
1836
1894
  }
1895
+ var MODIFIER_TO_PSEUDO = {
1896
+ hover: ":hover",
1897
+ focus: ":focus",
1898
+ "focus-within": ":focus-within",
1899
+ "focus-visible": ":focus-visible",
1900
+ active: ":active",
1901
+ pressed: ":active",
1902
+ disabled: ":disabled",
1903
+ checked: ":checked",
1904
+ visited: ":visited",
1905
+ placeholder: "::placeholder",
1906
+ first: ":first-child",
1907
+ last: ":last-child",
1908
+ odd: ":nth-child(odd)",
1909
+ even: ":nth-child(even)",
1910
+ only: ":only-child",
1911
+ "not-hover": ":not(:hover)",
1912
+ "not-focus": ":not(:focus)",
1913
+ "not-active": ":not(:active)",
1914
+ "not-pressed": ":not(:active)",
1915
+ "not-disabled": ":not(:disabled)",
1916
+ "not-checked": ":not(:checked)",
1917
+ "not-visited": ":not(:visited)"
1918
+ };
1919
+ var GROUP_PEER_PREFIX = {
1920
+ "group-hover": ".group:hover",
1921
+ "group-focus": ".group:focus",
1922
+ "peer-hover": ".peer:hover ~",
1923
+ "peer-focus": ".peer:focus ~"
1924
+ };
1925
+ var MODE_MODS = /* @__PURE__ */ new Set(["dark", "light", "not-dark", "not-light"]);
1837
1926
  function injectClassRule(cls, bucketKey, styles, darkMode) {
1838
1927
  const cssDecls = styleValueToCSS(styles);
1839
1928
  if (!cssDecls) return;
@@ -1843,10 +1932,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1843
1932
  return;
1844
1933
  }
1845
1934
  const mods = bucketKey.split(":");
1846
- const pseudos = mods.filter((m) => !["dark", "light"].includes(m));
1847
- const hasDark = mods.includes("dark");
1848
- const pseudoSuffix = pseudos.map((p) => `:${p}`).join("");
1849
- const selector = `.${escaped}${pseudoSuffix}`;
1935
+ const hasDark = mods.includes("dark") || mods.includes("not-light");
1936
+ const hasLight = mods.includes("light") || mods.includes("not-dark");
1937
+ const ancestorMods = mods.filter((m) => m in GROUP_PEER_PREFIX);
1938
+ const pseudoMods = mods.filter((m) => !MODE_MODS.has(m) && !(m in GROUP_PEER_PREFIX));
1939
+ const pseudoSuffix = pseudoMods.map((p) => MODIFIER_TO_PSEUDO[p] ?? `:${p}`).join("");
1940
+ const elementSelector = `.${escaped}${pseudoSuffix}`;
1941
+ const ancestorPrefix = ancestorMods.length ? ancestorMods.map((m) => GROUP_PEER_PREFIX[m]).join(" ") + " " : "";
1942
+ const selector = `${ancestorPrefix}${elementSelector}`;
1850
1943
  let rule;
1851
1944
  if (hasDark) {
1852
1945
  if (darkMode === "media") {
@@ -1856,6 +1949,14 @@ function injectClassRule(cls, bucketKey, styles, darkMode) {
1856
1949
  } else {
1857
1950
  rule = `[data-theme="dark"] ${selector} { ${cssDecls} }`;
1858
1951
  }
1952
+ } else if (hasLight) {
1953
+ if (darkMode === "media") {
1954
+ rule = `@media (prefers-color-scheme: light) { ${selector} { ${cssDecls} } }`;
1955
+ } else if (darkMode === "class") {
1956
+ rule = `:not(.dark) ${selector} { ${cssDecls} }`;
1957
+ } else {
1958
+ rule = `[data-theme="light"] ${selector} { ${cssDecls} }`;
1959
+ }
1859
1960
  } else {
1860
1961
  rule = `${selector} { ${cssDecls} }`;
1861
1962
  }
@@ -2036,20 +2137,24 @@ function buildConfig(userConfig) {
2036
2137
  theme,
2037
2138
  plugins: userConfig.plugins ?? []
2038
2139
  };
2140
+ clearPluginUtilities();
2141
+ clearPluginModifiers();
2142
+ const pluginAPI = makePluginAPI(resolved.theme);
2039
2143
  for (const plugin of resolved.plugins) {
2040
- plugin(makePluginAPI(resolved.theme));
2144
+ plugin(pluginAPI);
2041
2145
  }
2042
2146
  return resolved;
2043
2147
  }
2044
2148
  function makePluginAPI(theme) {
2045
- const standalone = getStandaloneMap();
2046
- const resolvers = getResolverMap();
2149
+ const standalone = getPluginStandaloneMap();
2150
+ const resolvers = getPluginResolverMap();
2047
2151
  return {
2048
2152
  addUtility(name, styles) {
2049
2153
  standalone[name] = styles;
2050
2154
  },
2051
- addVariant(name, _selector) {
2052
- customVariants[name] = _selector;
2155
+ addVariant(name, selector) {
2156
+ customVariants[name] = selector;
2157
+ registerModifier(name);
2053
2158
  },
2054
2159
  theme(path, defaultValue) {
2055
2160
  const parts = path.replace(/\[([^\]]+)\]/g, ".$1").split(".");
@@ -2165,8 +2270,8 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
2165
2270
  ...restWithoutChildren,
2166
2271
  style: finalStyle,
2167
2272
  ...isWeb && className ? { className } : {},
2168
- onPressIn: handlePressIn,
2169
- onPressOut: handlePressOut,
2273
+ // onPressIn / onPressOut are React Native-only; skip on web to avoid DOM warnings.
2274
+ ...isWeb ? {} : { onPressIn: handlePressIn, onPressOut: handlePressOut },
2170
2275
  onMouseEnter: handleMouseEnter,
2171
2276
  onMouseLeave: handleMouseLeave,
2172
2277
  onFocus: handleFocus,
@@ -2,8 +2,8 @@ import {
2
2
  Fragment,
3
3
  jsx,
4
4
  jsxs
5
- } from "./chunk-YDKLJFFY.mjs";
6
- import "./chunk-GN4JPJHC.mjs";
5
+ } from "./chunk-Q5RMVTAU.mjs";
6
+ import "./chunk-V5KHX4CZ.mjs";
7
7
  export {
8
8
  Fragment,
9
9
  jsx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./dist/index.js",
@@ -52,5 +52,7 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "files": ["dist"]
55
+ "files": [
56
+ "dist"
57
+ ]
56
58
  }