@wildwinter/expr-editor 0.3.0 → 0.4.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
@@ -43,6 +43,13 @@ Notable options beyond the basics:
43
43
  `document.body`). Pass a container inside a focus-trapping dialog (Radix,
44
44
  etc.) so opening a pill's popover counts as "inside" that layer and does not
45
45
  dismiss the dialog; the popover is then positioned relative to that container.
46
+ - `requireNonEmpty` — block a delete that would empty the whole expression
47
+ (single-value fields that must always hold a term). Conditions leave it off
48
+ (an empty condition is the valid "always" state).
49
+ - `valueEnumValues` + `valueField` — flat single-value editing for an
50
+ outcome-style value cell: `valueEnumValues` offers a target's enum values on
51
+ the root literal, and `valueField` makes an empty value render one editable
52
+ "set a value…" pill instead of the condition clause-menu empty state.
46
53
  - `setText(on)` — the host's raw-text toggle (`</>`); unparseable input falls
47
54
  back to raw text automatically.
48
55
 
package/dist/index.cjs CHANGED
@@ -490,11 +490,14 @@ function numberEditor(ctx, path, value, anchor) {
490
490
  function stringEditor(ctx, path, node, anchor) {
491
491
  const peer = findEnumPeer(ctx.getAst(), path);
492
492
  const enumEntry = peer ? lookup(ctx.catalogue, peer.scope, peer.name) : null;
493
+ const rootValueEnums = path.length === 0 && ctx.valueEnumValues?.length ? ctx.valueEnumValues : null;
494
+ const enumValues = enumEntry?.enumValues?.length ? enumEntry.enumValues : rootValueEnums;
495
+ const enumHeadLabel = enumEntry ? `${displayName(enumEntry, ctx.defaultScope)} value` : "Value";
493
496
  ctx.openPopover(anchor, (close) => {
494
- if (enumEntry?.enumValues?.length) {
497
+ if (enumValues?.length) {
495
498
  const wrap = el("div", "exed-menu");
496
- wrap.append(el("div", "exed-menu-head", [`${displayName(enumEntry, ctx.defaultScope)} value`]));
497
- for (const v of enumEntry.enumValues) {
499
+ wrap.append(el("div", "exed-menu-head", [enumHeadLabel]));
500
+ for (const v of enumValues) {
498
501
  wrap.append(button(`exed-opt${v === node.value ? " sel" : ""}`, v, () => {
499
502
  replace(ctx, path, strLit(v));
500
503
  close();
@@ -566,6 +569,7 @@ function flagEditor(ctx, path, node, anchor) {
566
569
  return wrap;
567
570
  });
568
571
  }
572
+ var canDelete = (ctx, path) => !(ctx.requireNonEmpty && path.length === 0);
569
573
  function callEditor(ctx, path, node, anchor) {
570
574
  ctx.openPopover(anchor, (close) => {
571
575
  const wrap = el("div", "exed-menu");
@@ -577,10 +581,12 @@ function callEditor(ctx, path, node, anchor) {
577
581
  close();
578
582
  }));
579
583
  }
580
- wrap.append(button("exed-opt danger", "Delete", () => {
581
- ctx.apply(deleteAt(ctx.getAst(), path));
582
- close();
583
- }));
584
+ if (canDelete(ctx, path)) {
585
+ wrap.append(button("exed-opt danger", "Delete", () => {
586
+ ctx.apply(deleteAt(ctx.getAst(), path));
587
+ close();
588
+ }));
589
+ }
584
590
  return wrap;
585
591
  });
586
592
  }
@@ -591,10 +597,10 @@ function variableEditor(ctx, path, node, anchor) {
591
597
  replace(ctx, path, scopedVar(entry.scope, entry.name));
592
598
  close();
593
599
  },
594
- footer: button("exed-opt danger", "Delete", () => {
600
+ ...canDelete(ctx, path) ? { footer: button("exed-opt danger", "Delete", () => {
595
601
  ctx.apply(deleteAt(ctx.getAst(), path));
596
602
  close();
597
- })
603
+ }) } : {}
598
604
  }));
599
605
  }
600
606
  function propertyPicker(ctx, opts) {
@@ -1059,6 +1065,8 @@ function mountExpressionEditor(host, opts) {
1059
1065
  requestFocus: (p) => {
1060
1066
  pendingFocus = p;
1061
1067
  },
1068
+ ...opts.requireNonEmpty ? { requireNonEmpty: true } : {},
1069
+ ...opts.valueEnumValues ? { valueEnumValues: opts.valueEnumValues } : {},
1062
1070
  ...opts.pickNode ? { pickNode: opts.pickNode } : {},
1063
1071
  ...opts.nodeLabel ? { nodeLabel: opts.nodeLabel } : {}
1064
1072
  };
@@ -1070,7 +1078,7 @@ function mountExpressionEditor(host, opts) {
1070
1078
  if (raw || v.unparseable) {
1071
1079
  host.append(rawArea());
1072
1080
  } else if (!src.trim() || !v.ast) {
1073
- host.append(emptyState());
1081
+ host.append(opts.valueField ? valueEmptyState() : emptyState());
1074
1082
  } else {
1075
1083
  const ctx = buildCtx(v.ast);
1076
1084
  const body = el("div", "exed-body");
@@ -1114,6 +1122,10 @@ function mountExpressionEditor(host, opts) {
1114
1122
  }));
1115
1123
  return wrap;
1116
1124
  }
1125
+ function valueEmptyState() {
1126
+ const ctx = buildCtx(strLit(""));
1127
+ return el("div", "exed-flat", [renderNode(strLit(""), [], ctx)]);
1128
+ }
1117
1129
  function addTermControl(ctx) {
1118
1130
  const boolean = opts.addTerm === "boolean";
1119
1131
  const ops = boolean ? ["and", "or"] : ARITHMETIC_OPS;