@getforma/core 1.2.0 → 1.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.
package/dist/runtime.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkE3FV2VSU_cjs = require('./chunk-E3FV2VSU.cjs');
3
+ var chunkFBM7V4NE_cjs = require('./chunk-FBM7V4NE.cjs');
4
4
  var chunk263HW3KN_cjs = require('./chunk-263HW3KN.cjs');
5
5
  var chunkKUXNZ5MG_cjs = require('./chunk-KUXNZ5MG.cjs');
6
6
  var chunkH7MDUHS5_cjs = require('./chunk-H7MDUHS5.cjs');
@@ -275,12 +275,12 @@ function findInsertionPoint(parent, newChild, newChildren) {
275
275
  return null;
276
276
  }
277
277
  function createReconciler(config) {
278
- let _lastHtml = "";
278
+ const lastHtmlByContainer = /* @__PURE__ */ new WeakMap();
279
279
  return function reconcile2(container, html) {
280
280
  const trimmed = html.trim();
281
281
  if (!trimmed) return;
282
- if (trimmed === _lastHtml && container.hasChildNodes()) return;
283
- _lastHtml = trimmed;
282
+ if (lastHtmlByContainer.get(container) === trimmed && container.hasChildNodes()) return;
283
+ lastHtmlByContainer.set(container, trimmed);
284
284
  config.disconnectObserver();
285
285
  try {
286
286
  if (!container.hasChildNodes() || container.children.length === 0) {
@@ -571,9 +571,97 @@ var RE_TERNARY = /^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/;
571
571
  var RE_NULLISH = /^(.+?)\s*\?\?\s*(.+)$/;
572
572
  var RE_AND = /^(.+?)\s*&&\s*(.+)$/;
573
573
  var RE_OR = /^(.+?)\s*\|\|\s*(.+)$/;
574
- var RE_COMPARISON = /^(.+?)\s*(===|!==|==|!=|>=|<=|>|<)\s*(.+)$/;
575
- var RE_MUL = /^(.+?)\s*([*/%])\s*(.+)$/;
576
- var RE_ADD = /^(.+?)\s*([+-])\s*(.+)$/;
574
+ var UNARY_PRECEDERS = "+-*/%<>=!&|^~(,[{?:";
575
+ function matchBinaryOp(expr, ops) {
576
+ const SQ = String.fromCharCode(39);
577
+ const DQ = String.fromCharCode(34);
578
+ const BT = String.fromCharCode(96);
579
+ const BS = String.fromCharCode(92);
580
+ let depth = 0;
581
+ let inSingle = false, inDouble = false, inTemplate = false, escaped = false;
582
+ let prevSig = "";
583
+ let bestIdx = -1;
584
+ let bestOp = "";
585
+ for (let i = 0; i < expr.length; i++) {
586
+ const ch = expr[i];
587
+ if (escaped) {
588
+ escaped = false;
589
+ continue;
590
+ }
591
+ if (ch === BS && (inSingle || inDouble || inTemplate)) {
592
+ escaped = true;
593
+ continue;
594
+ }
595
+ if (inSingle) {
596
+ if (ch === SQ) {
597
+ inSingle = false;
598
+ prevSig = DQ;
599
+ }
600
+ continue;
601
+ }
602
+ if (inDouble) {
603
+ if (ch === DQ) {
604
+ inDouble = false;
605
+ prevSig = DQ;
606
+ }
607
+ continue;
608
+ }
609
+ if (inTemplate) {
610
+ if (ch === BT) {
611
+ inTemplate = false;
612
+ prevSig = DQ;
613
+ }
614
+ continue;
615
+ }
616
+ if (ch === SQ) {
617
+ inSingle = true;
618
+ continue;
619
+ }
620
+ if (ch === DQ) {
621
+ inDouble = true;
622
+ continue;
623
+ }
624
+ if (ch === BT) {
625
+ inTemplate = true;
626
+ continue;
627
+ }
628
+ if (ch === "(" || ch === "[" || ch === "{") {
629
+ depth++;
630
+ prevSig = ch;
631
+ continue;
632
+ }
633
+ if (ch === ")" || ch === "]" || ch === "}") {
634
+ if (depth > 0) depth--;
635
+ prevSig = ch;
636
+ continue;
637
+ }
638
+ if (depth !== 0) continue;
639
+ if (ch === " " || ch === " " || ch === "\n") continue;
640
+ if (i > 0) {
641
+ let matchedOp = "";
642
+ for (const op of ops) {
643
+ if (expr.startsWith(op, i)) {
644
+ matchedOp = op;
645
+ break;
646
+ }
647
+ }
648
+ if (matchedOp) {
649
+ const isPlusMinus = matchedOp === "+" || matchedOp === "-";
650
+ const unary = isPlusMinus && (prevSig === "" || UNARY_PRECEDERS.includes(prevSig));
651
+ if (!unary) {
652
+ bestIdx = i;
653
+ bestOp = matchedOp;
654
+ }
655
+ i += matchedOp.length - 1;
656
+ prevSig = matchedOp[matchedOp.length - 1];
657
+ continue;
658
+ }
659
+ }
660
+ prevSig = ch;
661
+ }
662
+ if (bestIdx === -1) return null;
663
+ return { left: expr.slice(0, bestIdx).trim(), op: bestOp, right: expr.slice(bestIdx + bestOp.length).trim() };
664
+ }
577
665
  var RE_TEMPLATE_LIT = /^`([^`]*)`$/;
578
666
  var RE_TEMPLATE_INTERP = /\$\{([^}]+)\}/g;
579
667
  var RE_GROUP_METHOD_CALL = /^\((.+)\)\.(\w+)\((.*)\)$/;
@@ -1568,12 +1656,12 @@ function parseExpressionUncached(expr, scope) {
1568
1656
  return () => left() && right();
1569
1657
  }
1570
1658
  }
1571
- const compMatch = expr.match(RE_COMPARISON);
1659
+ const compMatch = matchBinaryOp(expr, ["===", "!==", "==", "!=", ">=", "<=", ">", "<"]);
1572
1660
  if (compMatch) {
1573
- const left = parseExpression(compMatch[1].trim(), scope);
1574
- const right = parseExpression(compMatch[3].trim(), scope);
1661
+ const left = parseExpression(compMatch.left, scope);
1662
+ const right = parseExpression(compMatch.right, scope);
1575
1663
  if (left && right) {
1576
- const op = compMatch[2];
1664
+ const op = compMatch.op;
1577
1665
  return () => {
1578
1666
  const l = left(), r = right();
1579
1667
  switch (op) {
@@ -1597,12 +1685,12 @@ function parseExpressionUncached(expr, scope) {
1597
1685
  };
1598
1686
  }
1599
1687
  }
1600
- const addMatch = expr.match(RE_ADD);
1688
+ const addMatch = matchBinaryOp(expr, ["+", "-"]);
1601
1689
  if (addMatch) {
1602
- const left = parseExpression(addMatch[1].trim(), scope);
1603
- const right = parseExpression(addMatch[3].trim(), scope);
1690
+ const left = parseExpression(addMatch.left, scope);
1691
+ const right = parseExpression(addMatch.right, scope);
1604
1692
  if (left && right) {
1605
- const op = addMatch[2];
1693
+ const op = addMatch.op;
1606
1694
  return () => {
1607
1695
  const l = left(), r = right();
1608
1696
  if (op === "+") return l + r;
@@ -1610,12 +1698,12 @@ function parseExpressionUncached(expr, scope) {
1610
1698
  };
1611
1699
  }
1612
1700
  }
1613
- const mulMatch = expr.match(RE_MUL);
1701
+ const mulMatch = matchBinaryOp(expr, ["*", "/", "%"]);
1614
1702
  if (mulMatch) {
1615
- const left = parseExpression(mulMatch[1].trim(), scope);
1616
- const right = parseExpression(mulMatch[3].trim(), scope);
1703
+ const left = parseExpression(mulMatch.left, scope);
1704
+ const right = parseExpression(mulMatch.right, scope);
1617
1705
  if (left && right) {
1618
- const op = mulMatch[2];
1706
+ const op = mulMatch.op;
1619
1707
  return () => {
1620
1708
  const l = left(), r = right();
1621
1709
  switch (op) {
@@ -2110,25 +2198,61 @@ function bindElement(el, scope, disposers) {
2110
2198
  const modelExpr = !known || known.has("data-model") ? el.getAttribute("data-model") : null;
2111
2199
  if (modelExpr) {
2112
2200
  const prop = modelExpr.replace(RE_STRIP_BRACES, "").trim();
2113
- const getter = scope.getters[prop];
2114
- const setter = scope.setters[prop];
2201
+ let getter = scope.getters[prop];
2202
+ let setter = scope.setters[prop];
2203
+ if ((!getter || !setter) && prop.includes(".")) {
2204
+ getter = buildEvaluator(prop, scope);
2205
+ const lastDot = prop.lastIndexOf(".");
2206
+ const basePath = prop.slice(0, lastDot);
2207
+ const key = prop.slice(lastDot + 1);
2208
+ const baseGet = buildEvaluator(basePath, scope);
2209
+ setter = (v) => {
2210
+ const base = baseGet();
2211
+ if (base != null && typeof base === "object") base[key] = v;
2212
+ };
2213
+ }
2115
2214
  if (getter && setter) {
2116
2215
  const input = el;
2216
+ const tag = input.tagName;
2117
2217
  const dispose = chunkKUXNZ5MG_cjs.internalEffect(() => {
2118
2218
  const val = getter();
2119
- if (input.type === "checkbox") {
2219
+ const type = input.type;
2220
+ if (type === "checkbox") {
2120
2221
  input.checked = !!val;
2222
+ const indetExpr = el.getAttribute("data-model-indeterminate");
2223
+ if (indetExpr) {
2224
+ const indetGetter = scope.getters[indetExpr.replace(RE_STRIP_BRACES, "").trim()];
2225
+ if (indetGetter) input.indeterminate = !!indetGetter();
2226
+ }
2227
+ } else if (type === "radio") {
2228
+ input.checked = String(val) === input.value;
2229
+ } else if (tag === "SELECT" && input.multiple) {
2230
+ const sel = input;
2231
+ const arr = Array.isArray(val) ? val.map(String) : [];
2232
+ for (const opt of Array.from(sel.options)) opt.selected = arr.includes(opt.value);
2121
2233
  } else {
2122
2234
  input.value = String(val ?? "");
2123
2235
  }
2124
2236
  });
2125
2237
  disposers.push(dispose);
2126
- const event = input.type === "checkbox" ? "change" : "input";
2238
+ const event = input.type === "checkbox" || input.type === "radio" || tag === "SELECT" ? "change" : "input";
2127
2239
  const onModelInput = () => {
2128
- if (input.type === "checkbox") {
2240
+ const type = input.type;
2241
+ if (type === "checkbox") {
2129
2242
  setter(input.checked);
2130
- } else if (input.type === "number" || input.type === "range") {
2131
- setter(Number(input.value));
2243
+ } else if (type === "radio") {
2244
+ if (input.checked) setter(input.value);
2245
+ } else if (tag === "SELECT" && input.multiple) {
2246
+ const sel = input;
2247
+ setter(Array.from(sel.selectedOptions).map((o) => o.value));
2248
+ } else if (type === "number" || type === "range") {
2249
+ const raw = input.value;
2250
+ if (raw === "") {
2251
+ setter(null);
2252
+ } else {
2253
+ const n = Number(raw);
2254
+ if (!Number.isNaN(n)) setter(n);
2255
+ }
2132
2256
  } else {
2133
2257
  setter(input.value);
2134
2258
  }
@@ -2320,59 +2444,30 @@ function bindElement(el, scope, disposers) {
2320
2444
  }
2321
2445
  }
2322
2446
  const prevNodes = new Set(oldNodes);
2323
- if (keyProp) {
2324
- const result = chunkE3FV2VSU_cjs.reconcileList(
2325
- el,
2326
- oldItems,
2327
- rawItems,
2328
- oldNodes,
2329
- (item) => String(item?.[keyProp] ?? ""),
2330
- (item) => {
2331
- const idx = rawItems.indexOf(item);
2332
- return createBoundClone2(item, idx);
2333
- },
2334
- (node, item) => {
2335
- const idx = rawItems.indexOf(item);
2336
- updateBoundClone2(node, item, idx);
2337
- },
2338
- void 0,
2339
- // beforeNode
2340
- listHooks
2341
- );
2342
- const nextNodes = new Set(result.nodes);
2343
- for (const n of prevNodes) {
2344
- if (!nextNodes.has(n)) {
2345
- if (n.hasAttribute?.("data-forma-leaving")) continue;
2346
- disposeCloneBindings2(n);
2347
- }
2348
- }
2349
- oldItems = result.items;
2350
- oldNodes = result.nodes;
2351
- } else {
2352
- const wrapped = rawItems.map((item, i) => ({ __idx: i, __item: item }));
2353
- const oldWrapped = oldItems;
2354
- const result = chunkE3FV2VSU_cjs.reconcileList(
2355
- el,
2356
- oldWrapped,
2357
- wrapped,
2358
- oldNodes,
2359
- (w) => w.__idx,
2360
- (w) => createBoundClone2(w.__item, w.__idx),
2361
- (node, w) => updateBoundClone2(node, w.__item, w.__idx),
2362
- void 0,
2363
- // beforeNode
2364
- listHooks
2365
- );
2366
- const nextNodes = new Set(result.nodes);
2367
- for (const n of prevNodes) {
2368
- if (!nextNodes.has(n)) {
2369
- if (n.hasAttribute?.("data-forma-leaving")) continue;
2370
- disposeCloneBindings2(n);
2371
- }
2447
+ const wrapped = rawItems.map((item, i) => ({ __idx: i, __item: item }));
2448
+ const oldWrapped = oldItems;
2449
+ const keyFn = keyProp ? (w) => String(w.__item?.[keyProp] ?? "") : (w) => w.__idx;
2450
+ const result = chunkFBM7V4NE_cjs.reconcileList(
2451
+ el,
2452
+ oldWrapped,
2453
+ wrapped,
2454
+ oldNodes,
2455
+ keyFn,
2456
+ (w) => createBoundClone2(w.__item, w.__idx),
2457
+ (node, w) => updateBoundClone2(node, w.__item, w.__idx),
2458
+ void 0,
2459
+ // beforeNode
2460
+ listHooks
2461
+ );
2462
+ const nextNodes = new Set(result.nodes);
2463
+ for (const n of prevNodes) {
2464
+ if (!nextNodes.has(n)) {
2465
+ if (n.hasAttribute?.("data-forma-leaving")) continue;
2466
+ disposeCloneBindings2(n);
2372
2467
  }
2373
- oldItems = result.items;
2374
- oldNodes = result.nodes;
2375
2468
  }
2469
+ oldItems = result.items;
2470
+ oldNodes = result.nodes;
2376
2471
  });
2377
2472
  disposers.push(dispose);
2378
2473
  }