@jxsuite/runtime 0.22.1 → 0.23.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.
package/dist/runtime.js CHANGED
@@ -1258,6 +1258,239 @@ function computed(getterOrOptions, debugOptions, isSSR = false) {
1258
1258
  return cRef;
1259
1259
  }
1260
1260
 
1261
+ // src/expression.js
1262
+ var MUTATING_OPS = new Set([
1263
+ "=",
1264
+ "+=",
1265
+ "-=",
1266
+ "*=",
1267
+ "/=",
1268
+ "push",
1269
+ "pop",
1270
+ "shift",
1271
+ "unshift",
1272
+ "splice"
1273
+ ]);
1274
+ var UNARY_OPS = new Set(["!", "-"]);
1275
+ var BINARY_OPS = new Set([
1276
+ "+",
1277
+ "-",
1278
+ "*",
1279
+ "/",
1280
+ "%",
1281
+ "===",
1282
+ "!==",
1283
+ "<",
1284
+ "<=",
1285
+ ">",
1286
+ ">=",
1287
+ "&&",
1288
+ "||"
1289
+ ]);
1290
+ var ASSIGNMENT_OPS = new Set(["=", "+=", "-=", "*=", "/="]);
1291
+ var ARRAY_METHOD_OPS = new Set(["push", "pop", "shift", "unshift", "splice"]);
1292
+ var AGGREGATE_OPS = new Set(["reduce", "map", "filter"]);
1293
+ var BLESSED_OPERATORS = new Set([
1294
+ ...MUTATING_OPS,
1295
+ ...UNARY_OPS,
1296
+ ...BINARY_OPS,
1297
+ ...AGGREGATE_OPS
1298
+ ]);
1299
+ function isMutating(op) {
1300
+ return MUTATING_OPS.has(op);
1301
+ }
1302
+ function resolveOperand(operand, state, event, iterCtx) {
1303
+ if (operand === null || operand === undefined)
1304
+ return operand;
1305
+ if (typeof operand === "object" && !Array.isArray(operand) && "operator" in operand) {
1306
+ return evaluateExpression(operand, state, event, iterCtx);
1307
+ }
1308
+ if (typeof operand === "object" && !Array.isArray(operand) && "$ref" in operand) {
1309
+ return resolveExprRef(operand.$ref, state, event, iterCtx);
1310
+ }
1311
+ if (Array.isArray(operand)) {
1312
+ return operand.map((o) => resolveOperand(o, state, event, iterCtx));
1313
+ }
1314
+ return operand;
1315
+ }
1316
+ function resolveExprRef(ref2, state, event, iterCtx) {
1317
+ if (ref2 === "$reduce/acc") {
1318
+ return iterCtx?.acc;
1319
+ }
1320
+ if (ref2.startsWith("$reduce/")) {
1321
+ return iterCtx?.acc;
1322
+ }
1323
+ if (ref2.startsWith("event#/")) {
1324
+ const path = ref2.slice("event#/".length);
1325
+ return getPath(event, path);
1326
+ }
1327
+ if (ref2.startsWith("$map/")) {
1328
+ const parts = ref2.split("/");
1329
+ const key = parts[1];
1330
+ let base;
1331
+ if (key === "item")
1332
+ base = iterCtx?.item ?? state.$map?.item ?? state["$map/item"];
1333
+ else if (key === "index")
1334
+ base = iterCtx?.index ?? state.$map?.index ?? state["$map/index"];
1335
+ else
1336
+ base = state.$map?.[key] ?? state["$map/" + key];
1337
+ return parts.length > 2 ? getPath(base, parts.slice(2).join("/")) : base;
1338
+ }
1339
+ if (ref2.startsWith("#/state/")) {
1340
+ const sub = ref2.slice("#/state/".length);
1341
+ const slash = sub.indexOf("/");
1342
+ if (slash < 0)
1343
+ return state[sub];
1344
+ return getPath(state[sub.slice(0, slash)], sub.slice(slash + 1));
1345
+ }
1346
+ if (ref2.startsWith("parent#/"))
1347
+ return state[ref2.slice("parent#/".length)];
1348
+ if (ref2.startsWith("window#/"))
1349
+ return getPath(globalThis.window, ref2.slice("window#/".length));
1350
+ if (ref2.startsWith("document#/"))
1351
+ return getPath(globalThis.document, ref2.slice("document#/".length));
1352
+ return state[ref2] ?? null;
1353
+ }
1354
+ function resolveWritableRef(ref2, state, event, iterCtx) {
1355
+ if (ref2.startsWith("$map/")) {
1356
+ const parts = ref2.split("/");
1357
+ const key = parts[1];
1358
+ let base;
1359
+ if (key === "item")
1360
+ base = iterCtx?.item ?? state.$map?.item ?? state["$map/item"];
1361
+ else if (key === "index")
1362
+ base = iterCtx?.index ?? state.$map?.index ?? state["$map/index"];
1363
+ else
1364
+ base = state.$map?.[key] ?? state["$map/" + key];
1365
+ if (parts.length > 2) {
1366
+ const pathParts = parts.slice(2);
1367
+ const lastKey = pathParts.pop();
1368
+ const obj = pathParts.length > 0 ? getPath(base, pathParts.join("/")) : base;
1369
+ return { obj, key: lastKey };
1370
+ }
1371
+ return { obj: state.$map ?? state, key: key === "item" ? "$map/item" : "$map/index" };
1372
+ }
1373
+ if (ref2.startsWith("#/state/")) {
1374
+ const sub = ref2.slice("#/state/".length);
1375
+ const slash = sub.indexOf("/");
1376
+ if (slash < 0)
1377
+ return { obj: state, key: sub };
1378
+ const parts = sub.split("/");
1379
+ const lastKey = parts.pop();
1380
+ let obj = state;
1381
+ for (const p of parts)
1382
+ obj = obj[p];
1383
+ return { obj, key: lastKey };
1384
+ }
1385
+ return { obj: state, key: ref2 };
1386
+ }
1387
+ function evaluateExpression(node, state, event, iterCtx) {
1388
+ const { operator, target, value, initial } = node;
1389
+ if (!BLESSED_OPERATORS.has(operator)) {
1390
+ throw new Error(`$expression: unknown operator "${operator}"`);
1391
+ }
1392
+ if (UNARY_OPS.has(operator) && !("value" in node)) {
1393
+ const operand = resolveOperand(target, state, event, iterCtx);
1394
+ if (operator === "!")
1395
+ return !operand;
1396
+ if (operator === "-")
1397
+ return -operand;
1398
+ }
1399
+ if (BINARY_OPS.has(operator)) {
1400
+ const left = resolveOperand(target, state, event, iterCtx);
1401
+ const right = resolveOperand(value, state, event, iterCtx);
1402
+ switch (operator) {
1403
+ case "+":
1404
+ return left + right;
1405
+ case "-":
1406
+ return left - right;
1407
+ case "*":
1408
+ return left * right;
1409
+ case "/":
1410
+ return left / right;
1411
+ case "%":
1412
+ return left % right;
1413
+ case "===":
1414
+ return left === right;
1415
+ case "!==":
1416
+ return left !== right;
1417
+ case "<":
1418
+ return left < right;
1419
+ case "<=":
1420
+ return left <= right;
1421
+ case ">":
1422
+ return left > right;
1423
+ case ">=":
1424
+ return left >= right;
1425
+ case "&&":
1426
+ return left && right;
1427
+ case "||":
1428
+ return left || right;
1429
+ }
1430
+ }
1431
+ if (ASSIGNMENT_OPS.has(operator)) {
1432
+ const rhs = resolveOperand(value, state, event, iterCtx);
1433
+ const { obj, key } = resolveWritableRef(target.$ref, state, event, iterCtx);
1434
+ switch (operator) {
1435
+ case "=":
1436
+ obj[key] = rhs;
1437
+ break;
1438
+ case "+=":
1439
+ obj[key] += rhs;
1440
+ break;
1441
+ case "-=":
1442
+ obj[key] -= rhs;
1443
+ break;
1444
+ case "*=":
1445
+ obj[key] *= rhs;
1446
+ break;
1447
+ case "/=":
1448
+ obj[key] /= rhs;
1449
+ break;
1450
+ }
1451
+ return;
1452
+ }
1453
+ if (ARRAY_METHOD_OPS.has(operator)) {
1454
+ const arr = resolveOperand(target, state, event, iterCtx);
1455
+ switch (operator) {
1456
+ case "push":
1457
+ return arr.push(resolveOperand(value, state, event, iterCtx));
1458
+ case "unshift":
1459
+ return arr.unshift(resolveOperand(value, state, event, iterCtx));
1460
+ case "pop":
1461
+ return arr.pop();
1462
+ case "shift":
1463
+ return arr.shift();
1464
+ case "splice": {
1465
+ const args = resolveOperand(value, state, event, iterCtx);
1466
+ return arr.splice(...args);
1467
+ }
1468
+ }
1469
+ }
1470
+ if (AGGREGATE_OPS.has(operator)) {
1471
+ const arr = resolveOperand(target, state, event, iterCtx);
1472
+ if (operator === "reduce") {
1473
+ const seed = resolveOperand(initial, state, event, iterCtx);
1474
+ return arr.reduce((acc, item, index) => {
1475
+ return evaluateExpression(value, state, event, { acc, item, index });
1476
+ }, seed);
1477
+ }
1478
+ if (operator === "map") {
1479
+ return arr.map((item, index) => {
1480
+ return evaluateExpression(value, state, event, { ...iterCtx, item, index });
1481
+ });
1482
+ }
1483
+ if (operator === "filter") {
1484
+ return arr.filter((item, index) => {
1485
+ return evaluateExpression(value, state, event, { ...iterCtx, item, index });
1486
+ });
1487
+ }
1488
+ }
1489
+ }
1490
+ function getPath(obj, path) {
1491
+ return path.split(/[./]/).reduce((o, k) => o?.[k], obj);
1492
+ }
1493
+
1261
1494
  // src/runtime.js
1262
1495
  async function Jx(source, target = document.body, options) {
1263
1496
  const base = typeof source === "string" ? new URL(source, location.href).href : location.href;
@@ -1339,6 +1572,8 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1339
1572
  if (typeof def2 === "object") {
1340
1573
  if (def2.$prototype)
1341
1574
  continue;
1575
+ if ("$expression" in def2)
1576
+ continue;
1342
1577
  if (def2.timing === "server" && def2.$src && def2.$export)
1343
1578
  continue;
1344
1579
  if ("default" in def2) {
@@ -1356,6 +1591,16 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1356
1591
  state[key] = computed(() => evaluateTemplate(def2, state));
1357
1592
  }
1358
1593
  }
1594
+ for (const [key, def2] of Object.entries(defs)) {
1595
+ if (def2 && typeof def2 === "object" && !Array.isArray(def2) && "$expression" in def2) {
1596
+ const node = def2.$expression;
1597
+ if (isMutating(node.operator)) {
1598
+ state[key] = (s, event) => evaluateExpression(node, s, event);
1599
+ } else {
1600
+ state[key] = computed(() => evaluateExpression(node, state, null));
1601
+ }
1602
+ }
1603
+ }
1359
1604
  for (const [key, def2] of Object.entries(defs)) {
1360
1605
  if (typeof def2 === "object" && def2?.$prototype === "Function") {
1361
1606
  state[key] = await resolveFunction(def2, state, key, base);
@@ -1560,6 +1805,12 @@ function applyProperties(el, def2, state) {
1560
1805
  el.addEventListener(key.slice(2), (e) => fn(scope, e));
1561
1806
  continue;
1562
1807
  }
1808
+ if (val && typeof val === "object" && "$expression" in val) {
1809
+ const node = val.$expression;
1810
+ const scope = state;
1811
+ el.addEventListener(key.slice(2), (e) => evaluateExpression(node, scope, e));
1812
+ continue;
1813
+ }
1563
1814
  }
1564
1815
  bindProperty(el, key, val, state);
1565
1816
  }
@@ -1587,6 +1838,17 @@ function bindProperty(el, key, val, state) {
1587
1838
  function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
1588
1839
  const nested = {};
1589
1840
  const media = {};
1841
+ const baseDecls = {};
1842
+ const mediaOverriddenProps = new Set;
1843
+ for (const [prop, val] of Object.entries(styleDef)) {
1844
+ if (prop.startsWith("@") && val && typeof val === "object") {
1845
+ for (const k of Object.keys(val)) {
1846
+ if (!k.startsWith(":") && !k.startsWith(".") && !k.startsWith("&") && !k.startsWith("[") && !k.startsWith("@")) {
1847
+ mediaOverriddenProps.add(k);
1848
+ }
1849
+ }
1850
+ }
1851
+ }
1590
1852
  for (const [prop, val] of Object.entries(styleDef)) {
1591
1853
  if (prop.startsWith("@"))
1592
1854
  media[prop] = val;
@@ -1605,16 +1867,23 @@ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
1605
1867
  effect(() => {
1606
1868
  el.style[prop] = evaluateTemplate(val, state);
1607
1869
  });
1870
+ else if (mediaOverriddenProps.has(prop))
1871
+ baseDecls[prop] = val;
1608
1872
  else
1609
1873
  el.style[prop] = val;
1610
1874
  }
1611
1875
  const hasNested = Object.keys(nested).length > 0;
1612
1876
  const hasMedia = Object.keys(media).length > 0;
1613
- if (!hasNested && !hasMedia)
1877
+ const hasBaseDecls = Object.keys(baseDecls).length > 0;
1878
+ if (!hasNested && !hasMedia && !hasBaseDecls)
1614
1879
  return;
1615
1880
  const uid = `jx-${Math.random().toString(36).slice(2, 7)}`;
1616
1881
  el.dataset.jx = uid;
1617
1882
  let css = "";
1883
+ const baseCSS = toCSSText(baseDecls);
1884
+ if (baseCSS)
1885
+ css += `[data-jx="${uid}"] { ${baseCSS} }
1886
+ `;
1618
1887
  function emitNested(scope, rules) {
1619
1888
  const props = toCSSText(rules);
1620
1889
  if (props)
@@ -1679,8 +1948,7 @@ function renderMappedArray(def2, state, options) {
1679
1948
  applyProperties(container, def2, state);
1680
1949
  applyStyle(container, def2.style ?? {}, state["$media"] ?? {}, state);
1681
1950
  applyAttributes(container, def2.attributes ?? {}, state);
1682
- const { items: _items, of: _of, map: mapDef, filter: filterRef, sort: sortRef } = def2.children;
1683
- const itemsSrc = _items ?? _of;
1951
+ const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef } = def2.children;
1684
1952
  effect(() => {
1685
1953
  container.innerHTML = "";
1686
1954
  let items;
@@ -2221,21 +2489,21 @@ function resolveRef(ref2, state) {
2221
2489
  const parts = ref2.split("/");
2222
2490
  const key = parts[1];
2223
2491
  const base = state.$map?.[key] ?? state["$map/" + key];
2224
- return parts.length > 2 ? getPath(base, parts.slice(2).join("/")) : base;
2492
+ return parts.length > 2 ? getPath2(base, parts.slice(2).join("/")) : base;
2225
2493
  }
2226
2494
  if (ref2.startsWith("#/state/")) {
2227
2495
  const sub = ref2.slice("#/state/".length);
2228
2496
  const slash = sub.indexOf("/");
2229
2497
  if (slash < 0)
2230
2498
  return state[sub];
2231
- return getPath(state[sub.slice(0, slash)], sub.slice(slash + 1));
2499
+ return getPath2(state[sub.slice(0, slash)], sub.slice(slash + 1));
2232
2500
  }
2233
2501
  if (ref2.startsWith("parent#/"))
2234
2502
  return state[ref2.slice("parent#/".length)];
2235
2503
  if (ref2.startsWith("window#/"))
2236
- return getPath(globalThis.window, ref2.slice("window#/".length));
2504
+ return getPath2(globalThis.window, ref2.slice("window#/".length));
2237
2505
  if (ref2.startsWith("document#/"))
2238
- return getPath(globalThis.document, ref2.slice("document#/".length));
2506
+ return getPath2(globalThis.document, ref2.slice("document#/".length));
2239
2507
  return state[ref2] ?? null;
2240
2508
  }
2241
2509
  function isSignal(v) {
@@ -2247,7 +2515,7 @@ function isRefObj(v) {
2247
2515
  function isNestedSelector(k) {
2248
2516
  return k.startsWith(":") || k.startsWith(".") || k.startsWith("&") || k.startsWith("[");
2249
2517
  }
2250
- function getPath(obj, path) {
2518
+ function getPath2(obj, path) {
2251
2519
  return path.split(/[./]/).reduce((o, k) => o?.[k], obj);
2252
2520
  }
2253
2521
  function mergeProps(def2, parentState) {
@@ -2487,5 +2755,5 @@ export {
2487
2755
  Jx
2488
2756
  };
2489
2757
 
2490
- //# debugId=9451B6325702042164756E2164756E21
2758
+ //# debugId=37F989EF6650619C64756E2164756E21
2491
2759
  //# sourceMappingURL=runtime.js.map