@wildwinter/expr-editor 0.5.0 → 0.7.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
@@ -50,11 +50,11 @@ Notable options beyond the basics:
50
50
  outcome-style value cell: `valueEnumValues` offers a target's enum values on
51
51
  the root literal, and `valueField` makes an empty value render one editable
52
52
  "set a value…" pill instead of the condition clause-menu empty state.
53
- - `flagValue: { target }` — flat compact editor for a `set_flags(@target, …)`
54
- value whose target is implied by context: renders only the flag-delta pills
55
- plus an "+ flag" chip, hiding the function name and target arg, and seeds the
56
- call on the first flag add. Pair with `requireNonEmpty` so the last flag
57
- can't be removed.
53
+ - `flagValue: { target, flags }` — flat compact editor for a
54
+ `set_flags(@target, …)` value whose target is implied by context: renders only
55
+ the flag-delta pills plus an "+ flag" chip (which picks sign + name from
56
+ `flags`), hiding the function name and target arg, and seeds the call on the
57
+ first flag add. Pair with `requireNonEmpty` so the last flag can't be removed.
58
58
  - `setText(on)` — the host's raw-text toggle (`</>`); unparseable input falls
59
59
  back to raw text automatically.
60
60
 
package/dist/index.cjs CHANGED
@@ -458,6 +458,14 @@ function renderNode(node, path, ctx, parentOp, side) {
458
458
  }
459
459
  }
460
460
  }
461
+ function signToggle(current, onPick) {
462
+ const row = el("div", "exed-sign");
463
+ ["+", "-"].forEach((s) => {
464
+ const cls = `exed-signbtn${s === current ? s === "+" ? " sel-pos" : " sel-neg" : ""}`;
465
+ row.append(button(cls, s === "+" ? "+ set" : "\u2212 unset", () => onPick(s)));
466
+ });
467
+ return row;
468
+ }
461
469
  var deleteOrUnwrapNot = (ctx, path, node) => (
462
470
  // clicking the NOT pill strips it (keeps the operand)
463
471
  setNodeAt(ctx.getAst(), path, node.operand)
@@ -547,14 +555,10 @@ function flagEditor(ctx, path, node, anchor) {
547
555
  ctx.openPopover(anchor, (close) => {
548
556
  const wrap = el("div", "exed-menu");
549
557
  wrap.append(el("div", "exed-menu-head", ["Flag set?"]));
550
- const signRow = el("div", "exed-field-row");
551
- for (const s of ["+", "-"]) {
552
- signRow.append(button(`exed-opt${s === node.sign ? " sel" : ""}`, s === "+" ? "+ set" : "\u2212 unset", () => {
553
- replace(ctx, path, flagDelta(s, node.name));
554
- close();
555
- }));
556
- }
557
- wrap.append(signRow);
558
+ wrap.append(signToggle(node.sign, (s) => {
559
+ replace(ctx, path, flagDelta(s, node.name));
560
+ close();
561
+ }));
558
562
  const names = (entry?.enumValues ?? []).filter((n) => !used.includes(n));
559
563
  if (names.length) {
560
564
  wrap.append(el("div", "exed-menu-head", ["Flag"]));
@@ -1145,27 +1149,46 @@ function mountExpressionEditor(host, opts) {
1145
1149
  }
1146
1150
  function flagValueBody(call) {
1147
1151
  const wrap = el("div", "exed-flat exed-flagvalue");
1152
+ const ctx = buildCtx(call ?? boolLit(true));
1148
1153
  if (call) {
1149
- const ctx = buildCtx(call);
1150
1154
  call.args.forEach((arg, i) => {
1151
1155
  if (arg.kind === "flagdelta") wrap.append(renderNode(arg, ["args", i], ctx));
1152
1156
  });
1153
- wrap.append(addFlagChip(call));
1154
- } else {
1155
- wrap.append(addFlagChip(null));
1156
1157
  }
1158
+ wrap.append(addFlagChip(ctx, call));
1157
1159
  return wrap;
1158
1160
  }
1159
- function addFlagChip(call) {
1160
- return button("exed-add exed-addflag", "+ flag", () => {
1161
- if (call) {
1162
- pendingFocus = ["args", call.args.length];
1163
- emit(toSrc({ ...call, args: [...call.args, flagDelta("+", "")] }));
1164
- } else {
1165
- const seeded = callNode("set_flags", [parseRef(opts.flagValue.target), flagDelta("+", "")]);
1166
- pendingFocus = ["args", 1];
1167
- emit(toSrc(seeded));
1168
- }
1161
+ function addFlagChip(ctx, call) {
1162
+ const flags = opts.flagValue?.flags ?? [];
1163
+ const used = call ? call.args.filter((a) => a.kind === "flagdelta").map((a) => a.name) : [];
1164
+ const available = flags.filter((f) => !used.includes(f));
1165
+ return button("exed-add exed-addflag", "+ flag", (e) => {
1166
+ const anchor = e.currentTarget;
1167
+ ctx.openPopover(anchor, (close) => {
1168
+ let sign = "+";
1169
+ const wrap = el("div", "exed-menu");
1170
+ wrap.append(el("div", "exed-menu-head", ["Add flag"]));
1171
+ const signHost = el("div");
1172
+ const drawSign = () => {
1173
+ signHost.replaceChildren(signToggle(sign, (s) => {
1174
+ sign = s;
1175
+ drawSign();
1176
+ }));
1177
+ };
1178
+ drawSign();
1179
+ wrap.append(signHost);
1180
+ const commit = (name) => {
1181
+ const delta = flagDelta(sign, name);
1182
+ emit(toSrc(call ? { ...call, args: [...call.args, delta] } : callNode("set_flags", [parseRef(opts.flagValue.target), delta])));
1183
+ close();
1184
+ };
1185
+ if (available.length) {
1186
+ for (const n of available) wrap.append(button("exed-opt", n, () => commit(n)));
1187
+ } else {
1188
+ wrap.append(el("div", "exed-hint", [flags.length === 0 ? "This flags property declares no flag values yet." : "All declared flags are already used."]));
1189
+ }
1190
+ return wrap;
1191
+ });
1169
1192
  }, "add a flag");
1170
1193
  }
1171
1194
  function addTermControl(ctx) {