@wildwinter/expr-editor 0.4.0 → 0.6.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 +5 -0
- package/dist/index.cjs +68 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +68 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -276,6 +276,16 @@ interface ExpressionEditorOptions {
|
|
|
276
276
|
* placeholder pill (a root string literal) rather than the condition
|
|
277
277
|
* clause-menu empty state. Pairs with mode:"flat". */
|
|
278
278
|
valueField?: boolean;
|
|
279
|
+
/** Flat compact editor for a `set_flags(@target, +a, -b)` value whose target is
|
|
280
|
+
* implied by context (an outcome row's target column). Renders only the
|
|
281
|
+
* flag-delta pills plus an "+ flag" chip, hiding the function name and target
|
|
282
|
+
* arg. `target` is the name-form ref of the flags property (e.g. "@quests"),
|
|
283
|
+
* `flags` its declared flag names (offered by the "+ flag" picker). Pairs
|
|
284
|
+
* with mode:"flat"; set requireNonEmpty so the last flag can't be removed. */
|
|
285
|
+
flagValue?: {
|
|
286
|
+
target: string;
|
|
287
|
+
flags: string[];
|
|
288
|
+
};
|
|
279
289
|
/** Emitted on every edit (name-form; "" when cleared). */
|
|
280
290
|
onChange: (src: string) => void;
|
|
281
291
|
/** Notified when the author starts (true) / stops (false) editing inside a popover
|
package/dist/index.d.ts
CHANGED
|
@@ -276,6 +276,16 @@ interface ExpressionEditorOptions {
|
|
|
276
276
|
* placeholder pill (a root string literal) rather than the condition
|
|
277
277
|
* clause-menu empty state. Pairs with mode:"flat". */
|
|
278
278
|
valueField?: boolean;
|
|
279
|
+
/** Flat compact editor for a `set_flags(@target, +a, -b)` value whose target is
|
|
280
|
+
* implied by context (an outcome row's target column). Renders only the
|
|
281
|
+
* flag-delta pills plus an "+ flag" chip, hiding the function name and target
|
|
282
|
+
* arg. `target` is the name-form ref of the flags property (e.g. "@quests"),
|
|
283
|
+
* `flags` its declared flag names (offered by the "+ flag" picker). Pairs
|
|
284
|
+
* with mode:"flat"; set requireNonEmpty so the last flag can't be removed. */
|
|
285
|
+
flagValue?: {
|
|
286
|
+
target: string;
|
|
287
|
+
flags: string[];
|
|
288
|
+
};
|
|
279
289
|
/** Emitted on every edit (name-form; "" when cleared). */
|
|
280
290
|
onChange: (src: string) => void;
|
|
281
291
|
/** Notified when the author starts (true) / stops (false) editing inside a popover
|
package/dist/index.js
CHANGED
|
@@ -539,7 +539,9 @@ function flagEditor(ctx, path, node, anchor) {
|
|
|
539
539
|
const call = getNodeAt(ctx.getAst(), callPath);
|
|
540
540
|
const flagsVar = call?.kind === "call" ? call.args[0] : void 0;
|
|
541
541
|
const entry = flagsVar?.kind === "scopedvar" ? lookup(ctx.catalogue, flagsVar.scope, flagsVar.name) : null;
|
|
542
|
+
const flagCount = call?.kind === "call" ? call.args.filter((a) => a.kind === "flagdelta").length : 0;
|
|
542
543
|
const used = call?.kind === "call" ? call.args.filter((a, i) => a.kind === "flagdelta" && i !== path[path.length - 1]).map((a) => a.name) : [];
|
|
544
|
+
const canRemove = !(ctx.requireNonEmpty && flagCount <= 1);
|
|
543
545
|
ctx.openPopover(anchor, (close) => {
|
|
544
546
|
const wrap = el("div", "exed-menu");
|
|
545
547
|
wrap.append(el("div", "exed-menu-head", ["Flag set?"]));
|
|
@@ -564,6 +566,12 @@ function flagEditor(ctx, path, node, anchor) {
|
|
|
564
566
|
close();
|
|
565
567
|
} }));
|
|
566
568
|
}
|
|
569
|
+
if (canRemove) {
|
|
570
|
+
wrap.append(button("exed-opt danger", "Remove flag", () => {
|
|
571
|
+
ctx.apply(deleteAt(ctx.getAst(), path));
|
|
572
|
+
close();
|
|
573
|
+
}));
|
|
574
|
+
}
|
|
567
575
|
return wrap;
|
|
568
576
|
});
|
|
569
577
|
}
|
|
@@ -1073,8 +1081,12 @@ function mountExpressionEditor(host, opts) {
|
|
|
1073
1081
|
closePopover();
|
|
1074
1082
|
host.replaceChildren();
|
|
1075
1083
|
const v = validateSource(src, opts.schema, opts.dialect);
|
|
1076
|
-
|
|
1084
|
+
const flagCompact = !!opts.flagValue && (opts.mode ?? "tree") === "flat";
|
|
1085
|
+
const astIsFlagCall = v.ast?.kind === "call" && (v.ast.name === "set_flags" || v.ast.name === "check_flags");
|
|
1086
|
+
if (raw || v.unparseable || flagCompact && !!v.ast && !astIsFlagCall) {
|
|
1077
1087
|
host.append(rawArea());
|
|
1088
|
+
} else if (flagCompact) {
|
|
1089
|
+
host.append(flagValueBody(astIsFlagCall ? v.ast : null));
|
|
1078
1090
|
} else if (!src.trim() || !v.ast) {
|
|
1079
1091
|
host.append(opts.valueField ? valueEmptyState() : emptyState());
|
|
1080
1092
|
} else {
|
|
@@ -1124,6 +1136,61 @@ function mountExpressionEditor(host, opts) {
|
|
|
1124
1136
|
const ctx = buildCtx(strLit(""));
|
|
1125
1137
|
return el("div", "exed-flat", [renderNode(strLit(""), [], ctx)]);
|
|
1126
1138
|
}
|
|
1139
|
+
function parseRef(ref) {
|
|
1140
|
+
const bare = ref.replace(/^@/, "");
|
|
1141
|
+
const dot = bare.indexOf(".");
|
|
1142
|
+
return dot >= 0 ? scopedVar(bare.slice(0, dot), bare.slice(dot + 1)) : scopedVar(defaultScope, bare);
|
|
1143
|
+
}
|
|
1144
|
+
function flagValueBody(call) {
|
|
1145
|
+
const wrap = el("div", "exed-flat exed-flagvalue");
|
|
1146
|
+
const ctx = buildCtx(call ?? boolLit(true));
|
|
1147
|
+
if (call) {
|
|
1148
|
+
call.args.forEach((arg, i) => {
|
|
1149
|
+
if (arg.kind === "flagdelta") wrap.append(renderNode(arg, ["args", i], ctx));
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
wrap.append(addFlagChip(ctx, call));
|
|
1153
|
+
return wrap;
|
|
1154
|
+
}
|
|
1155
|
+
function addFlagChip(ctx, call) {
|
|
1156
|
+
const flags = opts.flagValue?.flags ?? [];
|
|
1157
|
+
const used = call ? call.args.filter((a) => a.kind === "flagdelta").map((a) => a.name) : [];
|
|
1158
|
+
const available = flags.filter((f) => !used.includes(f));
|
|
1159
|
+
return button("exed-add exed-addflag", "+ flag", (e) => {
|
|
1160
|
+
const anchor = e.currentTarget;
|
|
1161
|
+
ctx.openPopover(anchor, (close) => {
|
|
1162
|
+
let sign = "+";
|
|
1163
|
+
const wrap = el("div", "exed-menu");
|
|
1164
|
+
wrap.append(el("div", "exed-menu-head", ["Add flag"]));
|
|
1165
|
+
const signRow = el("div", "exed-field-row");
|
|
1166
|
+
const drawSign = () => {
|
|
1167
|
+
signRow.replaceChildren(
|
|
1168
|
+
button(`exed-opt${sign === "+" ? " sel" : ""}`, "+ set", () => {
|
|
1169
|
+
sign = "+";
|
|
1170
|
+
drawSign();
|
|
1171
|
+
}),
|
|
1172
|
+
button(`exed-opt${sign === "-" ? " sel" : ""}`, "\u2212 unset", () => {
|
|
1173
|
+
sign = "-";
|
|
1174
|
+
drawSign();
|
|
1175
|
+
})
|
|
1176
|
+
);
|
|
1177
|
+
};
|
|
1178
|
+
drawSign();
|
|
1179
|
+
wrap.append(signRow);
|
|
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
|
+
});
|
|
1192
|
+
}, "add a flag");
|
|
1193
|
+
}
|
|
1127
1194
|
function addTermControl(ctx) {
|
|
1128
1195
|
const boolean = opts.addTerm === "boolean";
|
|
1129
1196
|
const ops = boolean ? ["and", "or"] : ARITHMETIC_OPS;
|