@jxsuite/runtime 0.22.0 → 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;
@@ -1268,6 +1501,9 @@ async function Jx(source, target = document.body, options) {
1268
1501
  if (doc.$head) {
1269
1502
  injectHead(doc.$head, base);
1270
1503
  }
1504
+ if (doc.$media) {
1505
+ _rootMedia = doc.$media;
1506
+ }
1271
1507
  const state = await buildScope(doc, {}, base);
1272
1508
  target.appendChild(renderNode(doc, state, options));
1273
1509
  if (typeof state.onMount === "function")
@@ -1336,6 +1572,8 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1336
1572
  if (typeof def2 === "object") {
1337
1573
  if (def2.$prototype)
1338
1574
  continue;
1575
+ if ("$expression" in def2)
1576
+ continue;
1339
1577
  if (def2.timing === "server" && def2.$src && def2.$export)
1340
1578
  continue;
1341
1579
  if ("default" in def2) {
@@ -1353,6 +1591,16 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1353
1591
  state[key] = computed(() => evaluateTemplate(def2, state));
1354
1592
  }
1355
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
+ }
1356
1604
  for (const [key, def2] of Object.entries(defs)) {
1357
1605
  if (typeof def2 === "object" && def2?.$prototype === "Function") {
1358
1606
  state[key] = await resolveFunction(def2, state, key, base);
@@ -1372,6 +1620,8 @@ async function buildScope(doc, parentScope = {}, base = location.href) {
1372
1620
  }
1373
1621
  if (doc.$media) {
1374
1622
  state["$media"] = doc.$media;
1623
+ } else if (!state["$media"] && Object.keys(_rootMedia).length > 0) {
1624
+ state["$media"] = _rootMedia;
1375
1625
  }
1376
1626
  return state;
1377
1627
  }
@@ -1555,6 +1805,12 @@ function applyProperties(el, def2, state) {
1555
1805
  el.addEventListener(key.slice(2), (e) => fn(scope, e));
1556
1806
  continue;
1557
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
+ }
1558
1814
  }
1559
1815
  bindProperty(el, key, val, state);
1560
1816
  }
@@ -1582,6 +1838,17 @@ function bindProperty(el, key, val, state) {
1582
1838
  function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
1583
1839
  const nested = {};
1584
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
+ }
1585
1852
  for (const [prop, val] of Object.entries(styleDef)) {
1586
1853
  if (prop.startsWith("@"))
1587
1854
  media[prop] = val;
@@ -1600,16 +1867,23 @@ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
1600
1867
  effect(() => {
1601
1868
  el.style[prop] = evaluateTemplate(val, state);
1602
1869
  });
1870
+ else if (mediaOverriddenProps.has(prop))
1871
+ baseDecls[prop] = val;
1603
1872
  else
1604
1873
  el.style[prop] = val;
1605
1874
  }
1606
1875
  const hasNested = Object.keys(nested).length > 0;
1607
1876
  const hasMedia = Object.keys(media).length > 0;
1608
- if (!hasNested && !hasMedia)
1877
+ const hasBaseDecls = Object.keys(baseDecls).length > 0;
1878
+ if (!hasNested && !hasMedia && !hasBaseDecls)
1609
1879
  return;
1610
1880
  const uid = `jx-${Math.random().toString(36).slice(2, 7)}`;
1611
1881
  el.dataset.jx = uid;
1612
1882
  let css = "";
1883
+ const baseCSS = toCSSText(baseDecls);
1884
+ if (baseCSS)
1885
+ css += `[data-jx="${uid}"] { ${baseCSS} }
1886
+ `;
1613
1887
  function emitNested(scope, rules) {
1614
1888
  const props = toCSSText(rules);
1615
1889
  if (props)
@@ -1638,16 +1912,16 @@ function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
1638
1912
  const resolved = sel.startsWith("&") ? sel.replace("&", parentSel) : sel.startsWith("[") ? `${parentSel}${sel}` : sel.startsWith(":") || sel.startsWith(".") ? `${parentSel}${sel}` : `${parentSel} ${sel}`;
1639
1913
  const props = toCSSText(sub);
1640
1914
  if (props)
1641
- css += `@media ${query} { ${resolved} { ${props} } }
1915
+ css += `${atRule} { ${resolved} { ${props} } }
1642
1916
  `;
1643
1917
  emitMediaNested(resolved, sub);
1644
1918
  }
1645
1919
  };
1646
1920
  if (key === "@--")
1647
1921
  continue;
1648
- const query = key.startsWith("@--") ? mediaQueries[key.slice(1)] ?? key.slice(1) : key.slice(1);
1922
+ const atRule = key.startsWith("@--") ? `@media ${mediaQueries[key.slice(1)] ?? key.slice(1)}` : key.startsWith("@(") ? `@media ${key.slice(1)}` : key;
1649
1923
  const scope = `[data-jx="${uid}"]`;
1650
- css += `@media ${query} { ${scope} { ${toCSSText(rules)} } }
1924
+ css += `${atRule} { ${scope} { ${toCSSText(rules)} } }
1651
1925
  `;
1652
1926
  emitMediaNested(scope, rules);
1653
1927
  }
@@ -1674,8 +1948,7 @@ function renderMappedArray(def2, state, options) {
1674
1948
  applyProperties(container, def2, state);
1675
1949
  applyStyle(container, def2.style ?? {}, state["$media"] ?? {}, state);
1676
1950
  applyAttributes(container, def2.attributes ?? {}, state);
1677
- const { items: _items, of: _of, map: mapDef, filter: filterRef, sort: sortRef } = def2.children;
1678
- const itemsSrc = _items ?? _of;
1951
+ const { items: itemsSrc, map: mapDef, filter: filterRef, sort: sortRef } = def2.children;
1679
1952
  effect(() => {
1680
1953
  container.innerHTML = "";
1681
1954
  let items;
@@ -2216,21 +2489,21 @@ function resolveRef(ref2, state) {
2216
2489
  const parts = ref2.split("/");
2217
2490
  const key = parts[1];
2218
2491
  const base = state.$map?.[key] ?? state["$map/" + key];
2219
- return parts.length > 2 ? getPath(base, parts.slice(2).join("/")) : base;
2492
+ return parts.length > 2 ? getPath2(base, parts.slice(2).join("/")) : base;
2220
2493
  }
2221
2494
  if (ref2.startsWith("#/state/")) {
2222
2495
  const sub = ref2.slice("#/state/".length);
2223
2496
  const slash = sub.indexOf("/");
2224
2497
  if (slash < 0)
2225
2498
  return state[sub];
2226
- return getPath(state[sub.slice(0, slash)], sub.slice(slash + 1));
2499
+ return getPath2(state[sub.slice(0, slash)], sub.slice(slash + 1));
2227
2500
  }
2228
2501
  if (ref2.startsWith("parent#/"))
2229
2502
  return state[ref2.slice("parent#/".length)];
2230
2503
  if (ref2.startsWith("window#/"))
2231
- return getPath(globalThis.window, ref2.slice("window#/".length));
2504
+ return getPath2(globalThis.window, ref2.slice("window#/".length));
2232
2505
  if (ref2.startsWith("document#/"))
2233
- return getPath(globalThis.document, ref2.slice("document#/".length));
2506
+ return getPath2(globalThis.document, ref2.slice("document#/".length));
2234
2507
  return state[ref2] ?? null;
2235
2508
  }
2236
2509
  function isSignal(v) {
@@ -2242,7 +2515,7 @@ function isRefObj(v) {
2242
2515
  function isNestedSelector(k) {
2243
2516
  return k.startsWith(":") || k.startsWith(".") || k.startsWith("&") || k.startsWith("[");
2244
2517
  }
2245
- function getPath(obj, path) {
2518
+ function getPath2(obj, path) {
2246
2519
  return path.split(/[./]/).reduce((o, k) => o?.[k], obj);
2247
2520
  }
2248
2521
  function mergeProps(def2, parentState) {
@@ -2258,6 +2531,7 @@ function camelToKebab(s) {
2258
2531
  function toCSSText(rules) {
2259
2532
  return Object.entries(rules).filter(([k, v]) => !isNestedSelector(k) && (v === null || typeof v !== "object")).map(([p, v]) => `${camelToKebab(p)}: ${v}`).join("; ");
2260
2533
  }
2534
+ var _rootMedia = {};
2261
2535
  var _elementDefs = new Map;
2262
2536
  async function registerElements(elements, base) {
2263
2537
  for (const entry of elements) {
@@ -2481,5 +2755,5 @@ export {
2481
2755
  Jx
2482
2756
  };
2483
2757
 
2484
- //# debugId=10A56E6D287D37B364756E2164756E21
2758
+ //# debugId=37F989EF6650619C64756E2164756E21
2485
2759
  //# sourceMappingURL=runtime.js.map