@wildwinter/expr-editor 0.10.3 → 0.11.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/README.md CHANGED
@@ -35,6 +35,14 @@ Notable options beyond the basics:
35
35
  multi-step flow (e.g. tag → operator → threshold) with no editor changes.
36
36
  Templates without a wizard insert-then-refine: the editor auto-opens the
37
37
  first unfilled slot of the inserted clause.
38
+ - **Qualities** (the `quality` property type) need nothing from the host beyond a
39
+ catalogue entry carrying `stages` — the ladder, in order. The editor then offers
40
+ the property in a comparison clause, gives it the **ordering** operators (a
41
+ ladder exists for "at or past a stage"), lists its stages wherever a value is
42
+ picked, labels that list "stage", and seeds an outcome on it with
43
+ `advance(@ref)` — the form that keeps a story insertion-safe, because it names
44
+ no destination. A host still passing a ladder through `enumValues` keeps its
45
+ value picker, but not the ordering affordances.
38
46
  - `onEditingChange(editing)` — fires as popover micro-editors open/close, so the
39
47
  host can suppress its own validation display mid-edit.
40
48
  - `messages: false` — hide the editor's internal validation list when the host
package/dist/index.cjs CHANGED
@@ -222,6 +222,10 @@ var BINARY_LABEL = {
222
222
  var UNARY_LABEL = { not: "NOT", neg: "\u2212" };
223
223
  var COMPARISON_OPS = ["==", "!=", ">", ">=", "<", "<="];
224
224
  var ARITHMETIC_OPS = ["+", "-", "*", "/"];
225
+ var COMPARABLE_TYPES = ["boolean", "number", "string", "enum", "quality"];
226
+ var comparisonOpsFor = (t) => t === "number" || t === "quality" ? COMPARISON_OPS : EQUALITY_OPS;
227
+ var EQUALITY_OPS = ["==", "!="];
228
+ var rhsTypesFor = (t) => t === "number" ? ["number"] : t === "boolean" ? ["boolean"] : t === "quality" ? ["quality"] : t === "enum" ? ["enum", "string"] : ["string", "enum"];
225
229
  function opSwapGroup(op) {
226
230
  if (COMPARISON_OPS.includes(op)) return COMPARISON_OPS;
227
231
  if (ARITHMETIC_OPS.includes(op)) return ARITHMETIC_OPS;
@@ -251,6 +255,12 @@ function formatNumber(n) {
251
255
  }
252
256
 
253
257
  // src/schema.ts
258
+ var choicesOf = (e) => {
259
+ if (!e) return void 0;
260
+ if (e.type === "quality") return e.stages?.length ? e.stages : e.enumValues;
261
+ if (e.type === "enum") return e.enumValues;
262
+ return void 0;
263
+ };
254
264
  var refOf = (e, defaultScope) => e.scope === defaultScope ? `@${e.name}` : `@${e.scope}.${e.name}`;
255
265
  var displayName = (e, defaultScope) => e.scope === defaultScope ? e.name : `${e.scope}.${e.name}`;
256
266
  function filterCatalogue(entries, filter = {}) {
@@ -554,8 +564,9 @@ function stringEditor(ctx, path, node, anchor) {
554
564
  const peer = findEnumPeer(ctx.getAst(), path);
555
565
  const enumEntry = peer ? lookup(ctx.catalogue, peer.scope, peer.name) : null;
556
566
  const rootValueEnums = path.length === 0 && ctx.valueEnumValues?.length ? ctx.valueEnumValues : null;
557
- const enumValues = enumEntry?.enumValues?.length ? enumEntry.enumValues : rootValueEnums;
558
- const enumHeadLabel = enumEntry ? `${displayName(enumEntry, ctx.defaultScope)} value` : "Value";
567
+ const peerChoices = choicesOf(enumEntry);
568
+ const enumValues = peerChoices?.length ? peerChoices : rootValueEnums;
569
+ const enumHeadLabel = enumEntry ? `${displayName(enumEntry, ctx.defaultScope)} ${enumEntry.type === "quality" ? "stage" : "value"}` : "Value";
559
570
  ctx.openPopover(anchor, (close) => {
560
571
  if (enumValues?.length) {
561
572
  const wrap = el("div", "exed-menu");
@@ -705,7 +716,6 @@ function propertyPicker(ctx, opts) {
705
716
  }
706
717
 
707
718
  // src/clausewizard.ts
708
- var EQUALITY = ["==", "!="];
709
719
  var OP_WORD = {
710
720
  "==": "equals",
711
721
  "!=": "not equal to",
@@ -719,8 +729,6 @@ var opButton = (o, onClick) => {
719
729
  b.append(el("span", "exed-opt-name", [BINARY_LABEL[o]]), el("span", "exed-opt-purpose", [OP_WORD[o] ?? ""]));
720
730
  return b;
721
731
  };
722
- var opsForType = (t) => t === "number" ? COMPARISON_OPS : EQUALITY;
723
- var rhsTypesFor = (t) => t === "number" ? ["number"] : t === "boolean" ? ["boolean"] : t === "enum" ? ["enum", "string"] : ["string", "enum"];
724
732
  function header(host, title, back, cancel) {
725
733
  const h = el("div", "exed-vwiz-head");
726
734
  if (back) h.append(button("exed-vwiz-back", "\u2190", back, "Back"));
@@ -751,8 +759,8 @@ function valueStep(host, w, lhs, op, back, cancel, commit) {
751
759
  const row = el("div", "exed-field-row");
752
760
  row.append(button("exed-opt", "true", () => done(boolLit(true))), button("exed-opt", "false", () => done(boolLit(false))));
753
761
  body.append(row);
754
- } else if (lhs.type === "enum" && lhs.enumValues?.length) {
755
- for (const v of lhs.enumValues) body.append(button("exed-opt", v, () => done(strLit(v))));
762
+ } else if (choicesOf(lhs)?.length) {
763
+ for (const v of choicesOf(lhs)) body.append(button("exed-opt", v, () => done(strLit(v))));
756
764
  } else if (lhs.type === "number") {
757
765
  body.append(textField({ caption: "Number", placeholder: "e.g. 3", validate: (v) => v.trim() !== "" && Number.isFinite(Number(v)), onCommit: (v) => done(numLit(Number(v))) }));
758
766
  } else {
@@ -774,8 +782,13 @@ function comparisonWizard(host, w, commit, cancel) {
774
782
  const body = el("div", "exed-vwiz-body");
775
783
  host.append(body);
776
784
  body.append(propertyPicker(pickCtxOf(w), {
777
- accept: ["boolean", "number", "string", "enum"],
778
- onPick: (e) => pickOp({ ref: refDisplay(w, e), type: e.type, ...e.enumValues ? { enumValues: e.enumValues } : {} })
785
+ accept: COMPARABLE_TYPES,
786
+ onPick: (e) => pickOp({
787
+ ref: refDisplay(w, e),
788
+ type: e.type,
789
+ ...e.enumValues ? { enumValues: e.enumValues } : {},
790
+ ...e.stages ? { stages: e.stages } : {}
791
+ })
779
792
  }));
780
793
  };
781
794
  const pickOp = (lhs) => {
@@ -783,7 +796,7 @@ function comparisonWizard(host, w, commit, cancel) {
783
796
  header(host, el("span", void 0, ["Operator for ", mono(lhs.ref)]), pickLhs, cancel);
784
797
  const body = el("div", "exed-vwiz-body");
785
798
  host.append(body);
786
- for (const o of opsForType(lhs.type)) {
799
+ for (const o of comparisonOpsFor(lhs.type)) {
787
800
  body.append(opButton(o, () => valueStep(host, w, lhs, o, () => pickOp(lhs), cancel, commit)));
788
801
  }
789
802
  };
@@ -1435,14 +1448,16 @@ function removeArgAt(list, i, argIdx) {
1435
1448
  cur.args.splice(argIdx, 1);
1436
1449
  return next;
1437
1450
  }
1438
- function seedValueSrc(type, enumValues) {
1451
+ function seedValueSrc(type, values, ref) {
1439
1452
  switch (type) {
1440
1453
  case "boolean":
1441
1454
  return "true";
1442
1455
  case "number":
1443
1456
  return "0";
1444
1457
  case "enum":
1445
- return JSON.stringify(enumValues?.[0] ?? "");
1458
+ return JSON.stringify(values?.[0] ?? "");
1459
+ case "quality":
1460
+ return ref ? `advance(${ref})` : JSON.stringify(values?.[0] ?? "");
1446
1461
  case "string":
1447
1462
  return '""';
1448
1463
  default:
@@ -1540,7 +1555,8 @@ function mountEffectsEditor(host, opts) {
1540
1555
  const row = el("div", "exed-effect exed-effect-set");
1541
1556
  const targetBtn = button("exed-pill exed-pill-prop", eff.target || "(pick property)", (e) => {
1542
1557
  pickTarget(e.currentTarget, void 0, (entry) => {
1543
- commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value: seedValueSrc(entry.type, entry.enumValues) }));
1558
+ const ref = refOf(entry, defaultScope);
1559
+ commit(updateAt(effects, i, { target: ref, value: seedValueSrc(entry.type, choicesOf(entry), ref) }));
1544
1560
  });
1545
1561
  }, "change the target property");
1546
1562
  row.append(targetBtn, el("span", "exed-effect-eq", ["="]));
@@ -1716,7 +1732,9 @@ function renderEffectsPreview(effects, o) {
1716
1732
 
1717
1733
  exports.ARITHMETIC_OPS = ARITHMETIC_OPS;
1718
1734
  exports.BINARY_LABEL = BINARY_LABEL;
1735
+ exports.COMPARABLE_TYPES = COMPARABLE_TYPES;
1719
1736
  exports.COMPARISON_OPS = COMPARISON_OPS;
1737
+ exports.EQUALITY_OPS = EQUALITY_OPS;
1720
1738
  exports.UNARY_LABEL = UNARY_LABEL;
1721
1739
  exports.addArg = addArg;
1722
1740
  exports.addChildToContainer = addChildToContainer;
@@ -1727,6 +1745,8 @@ exports.binary = binary;
1727
1745
  exports.boolLit = boolLit;
1728
1746
  exports.buildSubGroupClause = buildSubGroupClause;
1729
1747
  exports.callNode = callNode;
1748
+ exports.choicesOf = choicesOf;
1749
+ exports.comparisonOpsFor = comparisonOpsFor;
1730
1750
  exports.deleteAt = deleteAt;
1731
1751
  exports.displayName = displayName;
1732
1752
  exports.filterCatalogue = filterCatalogue;
@@ -1759,6 +1779,7 @@ exports.removeArgAt = removeArgAt;
1759
1779
  exports.removeAt = removeAt;
1760
1780
  exports.renderConditionPreview = renderConditionPreview;
1761
1781
  exports.renderEffectsPreview = renderEffectsPreview;
1782
+ exports.rhsTypesFor = rhsTypesFor;
1762
1783
  exports.scopedVar = scopedVar;
1763
1784
  exports.searchCatalogue = searchCatalogue;
1764
1785
  exports.seedValueSrc = seedValueSrc;