@kbach/react-native 1.0.0-beta.16 → 1.0.0-beta.17

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.
@@ -1,10 +1,10 @@
1
- import "./chunk-XGQWM65X.mjs";
1
+ import "./chunk-45GOGUHJ.mjs";
2
2
  import {
3
3
  Fragment,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-6ML62O2R.mjs";
7
- import "./chunk-3ED4FIO2.mjs";
6
+ } from "./chunk-MJWWZJMH.mjs";
7
+ import "./chunk-OGFB3OXS.mjs";
8
8
 
9
9
  // src/jsxRuntimeCore.ts
10
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -31,12 +31,21 @@ var import_jsx_runtime = require("react/jsx-runtime");
31
31
  var import_react = require("react");
32
32
  var import_react_native = require("react-native");
33
33
  var import_darkModeStore = require("./darkModeStore");
34
+ var import_dynamicTokens = require("./dynamicTokens");
34
35
  var import_theme = require("./theme");
36
+ var DYNAMIC_TOKEN_MATCH_RE = /var\(--([a-zA-Z0-9-]+)\)/g;
37
+ function substituteDynamicTokens(classStrRaw) {
38
+ if (!classStrRaw.includes("var(--")) return classStrRaw;
39
+ return classStrRaw.replace(DYNAMIC_TOKEN_MATCH_RE, (whole, name) => {
40
+ const value = (0, import_dynamicTokens.getDynamicToken)(name);
41
+ return value ?? whole;
42
+ });
43
+ }
35
44
  function makeElement(isStaticChildren, type, props, key) {
36
45
  return (isStaticChildren ? import_jsx_runtime.jsxs : import_jsx_runtime.jsx)(type, props, key);
37
46
  }
38
47
  function resolvedStyleFor(resolveStyle2, classStrRaw, userStyle, pressed) {
39
- const resolved = resolveStyle2(classStrRaw, pressed);
48
+ const resolved = resolveStyle2(substituteDynamicTokens(classStrRaw), pressed);
40
49
  if (userStyle === void 0) {
41
50
  return resolved;
42
51
  }
@@ -61,6 +70,12 @@ function breakpointKeySuffix(classStrRaw, width) {
61
70
  const state = present.map((name) => screens[name] !== void 0 && width >= screens[name] ? "1" : "0").join("");
62
71
  return `:kb-bp-${state}`;
63
72
  }
73
+ function dynamicTokenKeySuffix(classStrRaw) {
74
+ const matches = classStrRaw.match(DYNAMIC_TOKEN_MATCH_RE);
75
+ if (!matches) return "";
76
+ const values = matches.map((m) => (0, import_dynamicTokens.getDynamicToken)(m.slice("var(--".length, -1)) ?? "");
77
+ return `:kb-dt-${values.join("|")}`;
78
+ }
64
79
  function ReactiveElement({
65
80
  hostType,
66
81
  hostRest,
@@ -71,9 +86,10 @@ function ReactiveElement({
71
86
  isStaticChildren
72
87
  }) {
73
88
  (0, import_react.useSyncExternalStore)(import_darkModeStore.subscribeGlobalDarkMode, import_darkModeStore.getGlobalDarkMode);
89
+ (0, import_react.useSyncExternalStore)(import_dynamicTokens.subscribeDynamicTokens, import_dynamicTokens.getDynamicTokensVersion);
74
90
  const { width } = (0, import_react_native.useWindowDimensions)();
75
91
  const finalStyle = hostType === import_react_native.Pressable ? (state) => resolvedStyleFor(resolveStyle2, classStrRaw, userStyle, state.pressed) : resolvedStyleFor(resolveStyle2, classStrRaw, userStyle, false);
76
- const suffix = darkModeKeySuffix(classStrRaw) + breakpointKeySuffix(classStrRaw, width);
92
+ const suffix = darkModeKeySuffix(classStrRaw) + breakpointKeySuffix(classStrRaw, width) + dynamicTokenKeySuffix(classStrRaw);
77
93
  const finalKey = suffix ? `${elementKey ?? ""}${suffix}` : elementKey;
78
94
  return makeElement(isStaticChildren, hostType, { ...hostRest, style: finalStyle }, finalKey);
79
95
  }
@@ -85,7 +101,7 @@ function processElement(resolveStyle2, type, rawProps, key, isStaticChildren) {
85
101
  if (typeof classStrRaw !== "string") {
86
102
  return makeElement(isStaticChildren, type, rawProps, key);
87
103
  }
88
- if (DARK_MODIFIER_RE.test(classStrRaw) || BREAKPOINT_MODIFIER_RE.test(classStrRaw)) {
104
+ if (DARK_MODIFIER_RE.test(classStrRaw) || BREAKPOINT_MODIFIER_RE.test(classStrRaw) || classStrRaw.includes("var(--")) {
89
105
  return (0, import_jsx_runtime.jsx)(
90
106
  ReactiveElement,
91
107
  { hostType: type, hostRest: rest, classStrRaw, userStyle, resolveStyle: resolveStyle2, elementKey: key, isStaticChildren },
@@ -1206,6 +1222,153 @@ function resolveUtilityNative(parsed, theme) {
1206
1222
  return resolveFlex(parsed, false) ?? resolve(parsed, theme) ?? resolve2(parsed, theme) ?? resolve3(parsed, theme) ?? resolveColorNative(parsed, theme) ?? resolveLeadingTrackingNative(parsed) ?? resolveTypographyNative(parsed) ?? nativeShadowDeclarations(parsed);
1207
1223
  }
1208
1224
 
1225
+ // src/jsEngine/calc.ts
1226
+ function reduceConstantMath(raw) {
1227
+ const trimmed = raw.trim();
1228
+ const calc = stripCall(trimmed, "calc");
1229
+ if (calc !== null) {
1230
+ return new Evaluator(calc).parseExprToEnd();
1231
+ }
1232
+ const clamp = stripCall(trimmed, "clamp");
1233
+ if (clamp !== null) {
1234
+ const args = splitTopLevelCommas(clamp);
1235
+ if (args.length !== 3) return null;
1236
+ const [min2, preferred, max2] = args.map((a) => new Evaluator(a).parseExprToEnd());
1237
+ if (min2 === null || preferred === null || max2 === null) return null;
1238
+ return Math.max(min2, Math.min(preferred, max2));
1239
+ }
1240
+ const min = stripCall(trimmed, "min");
1241
+ if (min !== null) {
1242
+ const values = splitTopLevelCommas(min).map((a) => new Evaluator(a).parseExprToEnd());
1243
+ return values.some((v) => v === null) ? null : Math.min(...values);
1244
+ }
1245
+ const max = stripCall(trimmed, "max");
1246
+ if (max !== null) {
1247
+ const values = splitTopLevelCommas(max).map((a) => new Evaluator(a).parseExprToEnd());
1248
+ return values.some((v) => v === null) ? null : Math.max(...values);
1249
+ }
1250
+ return null;
1251
+ }
1252
+ function stripCall(raw, name) {
1253
+ if (!raw.startsWith(name)) return null;
1254
+ const afterName = raw.slice(name.length).trimStart();
1255
+ if (!afterName.startsWith("(") || !afterName.endsWith(")")) return null;
1256
+ return afterName.slice(1, -1);
1257
+ }
1258
+ function splitTopLevelCommas(s) {
1259
+ const parts = [];
1260
+ let depth = 0;
1261
+ let start = 0;
1262
+ for (let i = 0; i < s.length; i++) {
1263
+ const ch = s[i];
1264
+ if (ch === "(") depth++;
1265
+ else if (ch === ")") depth--;
1266
+ else if (ch === "," && depth === 0) {
1267
+ parts.push(s.slice(start, i).trim());
1268
+ start = i + 1;
1269
+ }
1270
+ }
1271
+ parts.push(s.slice(start).trim());
1272
+ return parts;
1273
+ }
1274
+ var Evaluator = class {
1275
+ constructor(s) {
1276
+ this.pos = 0;
1277
+ this.s = s;
1278
+ }
1279
+ parseExprToEnd() {
1280
+ const value = this.expr();
1281
+ if (value === null) return null;
1282
+ this.skipWs();
1283
+ return this.pos === this.s.length ? value : null;
1284
+ }
1285
+ skipWs() {
1286
+ while (this.pos < this.s.length && /\s/.test(this.s[this.pos])) this.pos++;
1287
+ }
1288
+ peek() {
1289
+ this.skipWs();
1290
+ return this.s[this.pos];
1291
+ }
1292
+ expr() {
1293
+ let value = this.term();
1294
+ if (value === null) return null;
1295
+ for (; ; ) {
1296
+ const ch = this.peek();
1297
+ if (ch === "+") {
1298
+ this.pos++;
1299
+ const rhs = this.term();
1300
+ if (rhs === null) return null;
1301
+ value += rhs;
1302
+ } else if (ch === "-") {
1303
+ this.pos++;
1304
+ const rhs = this.term();
1305
+ if (rhs === null) return null;
1306
+ value -= rhs;
1307
+ } else {
1308
+ return value;
1309
+ }
1310
+ }
1311
+ }
1312
+ term() {
1313
+ let value = this.factor();
1314
+ if (value === null) return null;
1315
+ for (; ; ) {
1316
+ const ch = this.peek();
1317
+ if (ch === "*") {
1318
+ this.pos++;
1319
+ const rhs = this.factor();
1320
+ if (rhs === null) return null;
1321
+ value *= rhs;
1322
+ } else if (ch === "/") {
1323
+ this.pos++;
1324
+ const divisor = this.factor();
1325
+ if (divisor === null || divisor === 0) return null;
1326
+ value /= divisor;
1327
+ } else {
1328
+ return value;
1329
+ }
1330
+ }
1331
+ }
1332
+ factor() {
1333
+ const ch = this.peek();
1334
+ if (ch === void 0) return null;
1335
+ if (ch === "(") {
1336
+ this.pos++;
1337
+ const value = this.expr();
1338
+ if (value === null || this.peek() !== ")") return null;
1339
+ this.pos++;
1340
+ return value;
1341
+ }
1342
+ if (ch === "-") {
1343
+ this.pos++;
1344
+ const value = this.factor();
1345
+ return value === null ? null : -value;
1346
+ }
1347
+ return this.numberWithUnit();
1348
+ }
1349
+ numberWithUnit() {
1350
+ this.skipWs();
1351
+ const start = this.pos;
1352
+ while (this.pos < this.s.length && (/[0-9]/.test(this.s[this.pos]) || this.s[this.pos] === ".")) this.pos++;
1353
+ if (this.pos === start) return null;
1354
+ const n = Number(this.s.slice(start, this.pos));
1355
+ if (!Number.isFinite(n)) return null;
1356
+ const unitStart = this.pos;
1357
+ while (this.pos < this.s.length && /[a-zA-Z]/.test(this.s[this.pos])) this.pos++;
1358
+ const unit = this.s.slice(unitStart, this.pos);
1359
+ switch (unit) {
1360
+ case "":
1361
+ return n;
1362
+ case "px":
1363
+ return n;
1364
+ case "rem":
1365
+ return n * 16;
1366
+ default:
1367
+ return null;
1368
+ }
1369
+ }
1370
+ };
1371
+
1209
1372
  // src/jsEngine/resolveStyle.ts
1210
1373
  var RESPONSIVE_BREAKPOINTS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "2xl"]);
1211
1374
  function nativeModifierState(modifier, theme, colorScheme, pressed, width) {
@@ -1275,25 +1438,41 @@ var NUMERIC_LENGTH_PROPS = /* @__PURE__ */ new Set([
1275
1438
  "outline-width",
1276
1439
  "outline-offset"
1277
1440
  ]);
1441
+ function isPlainPercentage(value) {
1442
+ if (!value.endsWith("%")) return false;
1443
+ let digits = value.slice(0, -1);
1444
+ if (digits.startsWith("-")) digits = digits.slice(1);
1445
+ return digits.length > 0 && /^[0-9.]+$/.test(digits);
1446
+ }
1278
1447
  function rnStyleValue(property, value) {
1279
- if (NUMERIC_LENGTH_PROPS.has(property)) {
1280
- let px = null;
1281
- if (value.endsWith("px")) {
1282
- const n = Number(value.slice(0, -2));
1283
- px = Number.isFinite(n) ? n : null;
1284
- } else if (value.endsWith("rem")) {
1285
- const n = Number(value.slice(0, -3));
1286
- px = Number.isFinite(n) ? n * 16 : null;
1287
- } else {
1288
- const n = Number(value);
1289
- px = value.trim() !== "" && Number.isFinite(n) ? n : null;
1290
- }
1291
- if (px !== null) return px;
1448
+ if (!NUMERIC_LENGTH_PROPS.has(property)) {
1449
+ return { value, warning: null };
1450
+ }
1451
+ let px = null;
1452
+ if (value.endsWith("px")) {
1453
+ const n = Number(value.slice(0, -2));
1454
+ px = Number.isFinite(n) ? n : null;
1455
+ } else if (value.endsWith("rem")) {
1456
+ const n = Number(value.slice(0, -3));
1457
+ px = Number.isFinite(n) ? n * 16 : null;
1458
+ } else {
1459
+ const n = Number(value);
1460
+ px = value.trim() !== "" && Number.isFinite(n) ? n : null;
1461
+ }
1462
+ if (px !== null) return { value: px, warning: null };
1463
+ if (isPlainPercentage(value)) {
1464
+ return { value, warning: null };
1465
+ }
1466
+ const reduced = reduceConstantMath(value);
1467
+ if (reduced !== null) {
1468
+ return { value: reduced, warning: null };
1292
1469
  }
1293
- return value;
1470
+ const warning = `Kbach: "${value}" is not a valid native value for "${property}" \u2014 dropped. calc()/clamp()/min()/max() only resolve on native when every operand is a constant px/rem length (no %, vw, vh, var(), or other viewport/CSS-variable units \u2014 those need real layout/DOM, which doesn't exist on native at paint time). Use a plain px/rem calc, a fraction utility (e.g. w-1/2), or resolve this value in JS instead.`;
1471
+ return { value: null, warning };
1294
1472
  }
1295
- function resolveStyleJs(classString, theme, colorScheme, pressed, width) {
1473
+ function resolveStyleJsWithWarnings(classString, theme, colorScheme, pressed, width) {
1296
1474
  const style = {};
1475
+ const warnings = [];
1297
1476
  let shadowOffset = null;
1298
1477
  for (const token of classString.split(/\s+/).filter(Boolean)) {
1299
1478
  const parsed = parseClass(token);
@@ -1314,13 +1493,15 @@ function resolveStyleJs(classString, theme, colorScheme, pressed, width) {
1314
1493
  shadowOffset[d.property === "shadow-offset-x" ? "width" : "height"] = n;
1315
1494
  continue;
1316
1495
  }
1317
- style[kebabToCamel(d.property)] = rnStyleValue(d.property, d.value);
1496
+ const { value, warning } = rnStyleValue(d.property, d.value);
1497
+ if (warning !== null) warnings.push(warning);
1498
+ if (value !== null) style[kebabToCamel(d.property)] = value;
1318
1499
  }
1319
1500
  }
1320
1501
  if (shadowOffset !== null) {
1321
1502
  style.shadowOffset = { width: shadowOffset.width ?? 0, height: shadowOffset.height ?? 0 };
1322
1503
  }
1323
- return style;
1504
+ return { style, warnings };
1324
1505
  }
1325
1506
 
1326
1507
  // src/nativeBridge.ts
@@ -1329,10 +1510,29 @@ function resolveStyle(classString, pressed = false) {
1329
1510
  const width = import_react_native2.Dimensions.get("window").width;
1330
1511
  const KbachModule = import_react_native2.TurboModuleRegistry.get("KbachModule");
1331
1512
  if (!KbachModule) {
1332
- return resolveStyleJs(classString, (0, import_theme2.getTheme)(), colorScheme, pressed, width);
1513
+ const { style: style2, warnings: warnings2 } = resolveStyleJsWithWarnings(classString, (0, import_theme2.getTheme)(), colorScheme, pressed, width);
1514
+ warnIfDev(classString, warnings2);
1515
+ return style2;
1333
1516
  }
1334
1517
  const json = KbachModule.resolveStyle(classString, (0, import_theme2.getThemeJson)(), colorScheme, pressed, width);
1335
- return JSON.parse(json);
1518
+ const style = JSON.parse(json);
1519
+ const warnings = style.__kbachWarnings;
1520
+ if (warnings !== void 0) {
1521
+ delete style.__kbachWarnings;
1522
+ warnIfDev(classString, warnings);
1523
+ }
1524
+ return style;
1525
+ }
1526
+ var warnedMessages = /* @__PURE__ */ new Set();
1527
+ function warnIfDev(classString, warnings) {
1528
+ if (warnings.length === 0) return;
1529
+ if (typeof __DEV__ !== "undefined" && !__DEV__) return;
1530
+ for (const warning of warnings) {
1531
+ const message = `${warning} (from className "${classString}")`;
1532
+ if (warnedMessages.has(message)) continue;
1533
+ warnedMessages.add(message);
1534
+ console.warn(message);
1535
+ }
1336
1536
  }
1337
1537
 
1338
1538
  // src/jsx-runtime.tsx