@wildwinter/expr-editor 0.10.3 → 0.12.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 +14 -0
- package/dist/index.cjs +57 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +53 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BinaryOp, ExprNode, AstPath,
|
|
1
|
+
import { BinaryOp, ExprNode, AstPath, PropertyType, UnaryOp, ExpressionValidationResult, ExpressionValidationIssue, ExpressionSchema, Dialect } from '@wildwinter/expr';
|
|
2
2
|
export { ExpressionValidationIssue, ExpressionValidationResult, PropertyType } from '@wildwinter/expr';
|
|
3
3
|
|
|
4
4
|
declare const boolLit: (value: boolean) => ExprNode;
|
|
@@ -99,6 +99,20 @@ declare const BINARY_LABEL: Record<BinaryOp, string>;
|
|
|
99
99
|
declare const UNARY_LABEL: Record<UnaryOp, string>;
|
|
100
100
|
declare const COMPARISON_OPS: BinaryOp[];
|
|
101
101
|
declare const ARITHMETIC_OPS: BinaryOp[];
|
|
102
|
+
/** Property types a comparison clause can be built on - what the clause wizard offers as a left-hand
|
|
103
|
+
* side. `flags` is absent because its clause is the flag-check wizard, not a comparison. */
|
|
104
|
+
declare const COMPARABLE_TYPES: PropertyType[];
|
|
105
|
+
/** The operators offered for a comparison on a property of this type.
|
|
106
|
+
*
|
|
107
|
+
* Ordered types get the full set; the rest get equality only. A QUALITY is ordered: its stages are a
|
|
108
|
+
* ladder, and "at or past a stage" is the gate the type exists for, so offering it `==` alone (as the
|
|
109
|
+
* editor did before 0.11.0) hides the feature behind hand-written source. */
|
|
110
|
+
declare const comparisonOpsFor: (t: PropertyType) => BinaryOp[];
|
|
111
|
+
declare const EQUALITY_OPS: BinaryOp[];
|
|
112
|
+
/** The property types a comparison's RIGHT-hand side may be picked from, given the left's type.
|
|
113
|
+
* A quality accepts only another quality: a plain string would name a stage nothing validates, and
|
|
114
|
+
* comparing across two ladders is an error the engine raises by design. */
|
|
115
|
+
declare const rhsTypesFor: (t: PropertyType) => PropertyType[];
|
|
102
116
|
/** The set of operators a given operator can be swapped to inline, or null if structural (and/or/not). */
|
|
103
117
|
declare function opSwapGroup(op: BinaryOp): BinaryOp[] | null;
|
|
104
118
|
/** Whether a child binary needs parentheses inside a parent binary on the given side. */
|
|
@@ -112,9 +126,22 @@ interface CatalogueEntry {
|
|
|
112
126
|
name: string;
|
|
113
127
|
type: PropertyType;
|
|
114
128
|
enumValues?: string[];
|
|
129
|
+
/** A quality's ORDERED stage ladder. Separate from `enumValues` because the order carries meaning
|
|
130
|
+
* here (comparisons are positional, `advance()` walks it) - a host that smuggled stages through
|
|
131
|
+
* `enumValues` got a value picker but no ordering affordances. */
|
|
132
|
+
stages?: string[];
|
|
115
133
|
/** Free-text description; the picker search matches it alongside the name. */
|
|
116
134
|
purpose?: string;
|
|
117
135
|
}
|
|
136
|
+
/** The CLOSED set of values a property can hold, when it has one: a quality's stages (in ladder
|
|
137
|
+
* order) or an enum's values. Everything that offers "pick a value" for a property goes through
|
|
138
|
+
* this, so a quality is never mistaken for free text. `stages` falls back to `enumValues` for a
|
|
139
|
+
* host still passing a quality's ladder the old way. */
|
|
140
|
+
declare const choicesOf: (e: {
|
|
141
|
+
type: PropertyType;
|
|
142
|
+
enumValues?: string[];
|
|
143
|
+
stages?: string[];
|
|
144
|
+
} | null | undefined) => string[] | undefined;
|
|
118
145
|
interface Filter {
|
|
119
146
|
acceptTypes?: PropertyType[];
|
|
120
147
|
acceptScopes?: string[];
|
|
@@ -381,8 +408,13 @@ declare function setArgAt(list: EditorEffect[], i: number, argIdx: number, value
|
|
|
381
408
|
declare function addArg(list: EditorEffect[], i: number, value?: string): EditorEffect[];
|
|
382
409
|
declare function removeArgAt(list: EditorEffect[], i: number, argIdx: number): EditorEffect[];
|
|
383
410
|
/** A sensible starting value-expression for a freshly targeted property. Literals
|
|
384
|
-
* are seeded so the value editor opens on an editable pill, never the empty state.
|
|
385
|
-
|
|
411
|
+
* are seeded so the value editor opens on an editable pill, never the empty state.
|
|
412
|
+
*
|
|
413
|
+
* A QUALITY seeds to `advance(<ref>)` when the target's ref is known: moving to the next stage is
|
|
414
|
+
* what outcomes on a ladder overwhelmingly do, and it is the form that keeps a story insertion-safe
|
|
415
|
+
* (it names no destination, so a stage added later routes through it). Pass no ref and it falls back
|
|
416
|
+
* to the first stage as a literal, which the author can still swap to `advance(...)`. */
|
|
417
|
+
declare function seedValueSrc(type: PropertyType, values?: string[], ref?: string): string;
|
|
386
418
|
declare function mountEffectsEditor(host: HTMLElement, opts: EffectsEditorOptions): EffectsEditorHandle;
|
|
387
419
|
|
|
388
420
|
interface PreviewOptions {
|
|
@@ -418,6 +450,11 @@ interface ValueWizardOptions {
|
|
|
418
450
|
defaultScope: string;
|
|
419
451
|
/** When known (a `set` target's declared type), the picker leads straight to that input. */
|
|
420
452
|
expectedType?: PropertyType;
|
|
453
|
+
/** The target's closed set of values, when it has one: an enum's values, or a QUALITY's stages in
|
|
454
|
+
* ladder order. Callers should fill this with `choicesOf(entry)` rather than reading a single
|
|
455
|
+
* field, so a quality is never handed a wizard that knows its type but not its ladder. */
|
|
456
|
+
expectedChoices?: string[];
|
|
457
|
+
/** @deprecated Use `expectedChoices`. Kept so a 0.11.x caller keeps working. */
|
|
421
458
|
expectedEnumValues?: string[];
|
|
422
459
|
/** Receives the chosen value as name-form source. */
|
|
423
460
|
onCommit: (src: string) => void;
|
|
@@ -427,4 +464,4 @@ interface ValueWizardOptions {
|
|
|
427
464
|
/** Build the wizard UI into a fresh element (drive it via the host's popover). */
|
|
428
465
|
declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
|
|
429
466
|
|
|
430
|
-
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARISON_OPS, type CatalogueEntry, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, astToTree, binary, boolLit, buildSubGroupClause, callNode, deleteAt, displayName, filterCatalogue, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
|
467
|
+
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, type CatalogueEntry, EQUALITY_OPS, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BinaryOp, ExprNode, AstPath,
|
|
1
|
+
import { BinaryOp, ExprNode, AstPath, PropertyType, UnaryOp, ExpressionValidationResult, ExpressionValidationIssue, ExpressionSchema, Dialect } from '@wildwinter/expr';
|
|
2
2
|
export { ExpressionValidationIssue, ExpressionValidationResult, PropertyType } from '@wildwinter/expr';
|
|
3
3
|
|
|
4
4
|
declare const boolLit: (value: boolean) => ExprNode;
|
|
@@ -99,6 +99,20 @@ declare const BINARY_LABEL: Record<BinaryOp, string>;
|
|
|
99
99
|
declare const UNARY_LABEL: Record<UnaryOp, string>;
|
|
100
100
|
declare const COMPARISON_OPS: BinaryOp[];
|
|
101
101
|
declare const ARITHMETIC_OPS: BinaryOp[];
|
|
102
|
+
/** Property types a comparison clause can be built on - what the clause wizard offers as a left-hand
|
|
103
|
+
* side. `flags` is absent because its clause is the flag-check wizard, not a comparison. */
|
|
104
|
+
declare const COMPARABLE_TYPES: PropertyType[];
|
|
105
|
+
/** The operators offered for a comparison on a property of this type.
|
|
106
|
+
*
|
|
107
|
+
* Ordered types get the full set; the rest get equality only. A QUALITY is ordered: its stages are a
|
|
108
|
+
* ladder, and "at or past a stage" is the gate the type exists for, so offering it `==` alone (as the
|
|
109
|
+
* editor did before 0.11.0) hides the feature behind hand-written source. */
|
|
110
|
+
declare const comparisonOpsFor: (t: PropertyType) => BinaryOp[];
|
|
111
|
+
declare const EQUALITY_OPS: BinaryOp[];
|
|
112
|
+
/** The property types a comparison's RIGHT-hand side may be picked from, given the left's type.
|
|
113
|
+
* A quality accepts only another quality: a plain string would name a stage nothing validates, and
|
|
114
|
+
* comparing across two ladders is an error the engine raises by design. */
|
|
115
|
+
declare const rhsTypesFor: (t: PropertyType) => PropertyType[];
|
|
102
116
|
/** The set of operators a given operator can be swapped to inline, or null if structural (and/or/not). */
|
|
103
117
|
declare function opSwapGroup(op: BinaryOp): BinaryOp[] | null;
|
|
104
118
|
/** Whether a child binary needs parentheses inside a parent binary on the given side. */
|
|
@@ -112,9 +126,22 @@ interface CatalogueEntry {
|
|
|
112
126
|
name: string;
|
|
113
127
|
type: PropertyType;
|
|
114
128
|
enumValues?: string[];
|
|
129
|
+
/** A quality's ORDERED stage ladder. Separate from `enumValues` because the order carries meaning
|
|
130
|
+
* here (comparisons are positional, `advance()` walks it) - a host that smuggled stages through
|
|
131
|
+
* `enumValues` got a value picker but no ordering affordances. */
|
|
132
|
+
stages?: string[];
|
|
115
133
|
/** Free-text description; the picker search matches it alongside the name. */
|
|
116
134
|
purpose?: string;
|
|
117
135
|
}
|
|
136
|
+
/** The CLOSED set of values a property can hold, when it has one: a quality's stages (in ladder
|
|
137
|
+
* order) or an enum's values. Everything that offers "pick a value" for a property goes through
|
|
138
|
+
* this, so a quality is never mistaken for free text. `stages` falls back to `enumValues` for a
|
|
139
|
+
* host still passing a quality's ladder the old way. */
|
|
140
|
+
declare const choicesOf: (e: {
|
|
141
|
+
type: PropertyType;
|
|
142
|
+
enumValues?: string[];
|
|
143
|
+
stages?: string[];
|
|
144
|
+
} | null | undefined) => string[] | undefined;
|
|
118
145
|
interface Filter {
|
|
119
146
|
acceptTypes?: PropertyType[];
|
|
120
147
|
acceptScopes?: string[];
|
|
@@ -381,8 +408,13 @@ declare function setArgAt(list: EditorEffect[], i: number, argIdx: number, value
|
|
|
381
408
|
declare function addArg(list: EditorEffect[], i: number, value?: string): EditorEffect[];
|
|
382
409
|
declare function removeArgAt(list: EditorEffect[], i: number, argIdx: number): EditorEffect[];
|
|
383
410
|
/** A sensible starting value-expression for a freshly targeted property. Literals
|
|
384
|
-
* are seeded so the value editor opens on an editable pill, never the empty state.
|
|
385
|
-
|
|
411
|
+
* are seeded so the value editor opens on an editable pill, never the empty state.
|
|
412
|
+
*
|
|
413
|
+
* A QUALITY seeds to `advance(<ref>)` when the target's ref is known: moving to the next stage is
|
|
414
|
+
* what outcomes on a ladder overwhelmingly do, and it is the form that keeps a story insertion-safe
|
|
415
|
+
* (it names no destination, so a stage added later routes through it). Pass no ref and it falls back
|
|
416
|
+
* to the first stage as a literal, which the author can still swap to `advance(...)`. */
|
|
417
|
+
declare function seedValueSrc(type: PropertyType, values?: string[], ref?: string): string;
|
|
386
418
|
declare function mountEffectsEditor(host: HTMLElement, opts: EffectsEditorOptions): EffectsEditorHandle;
|
|
387
419
|
|
|
388
420
|
interface PreviewOptions {
|
|
@@ -418,6 +450,11 @@ interface ValueWizardOptions {
|
|
|
418
450
|
defaultScope: string;
|
|
419
451
|
/** When known (a `set` target's declared type), the picker leads straight to that input. */
|
|
420
452
|
expectedType?: PropertyType;
|
|
453
|
+
/** The target's closed set of values, when it has one: an enum's values, or a QUALITY's stages in
|
|
454
|
+
* ladder order. Callers should fill this with `choicesOf(entry)` rather than reading a single
|
|
455
|
+
* field, so a quality is never handed a wizard that knows its type but not its ladder. */
|
|
456
|
+
expectedChoices?: string[];
|
|
457
|
+
/** @deprecated Use `expectedChoices`. Kept so a 0.11.x caller keeps working. */
|
|
421
458
|
expectedEnumValues?: string[];
|
|
422
459
|
/** Receives the chosen value as name-form source. */
|
|
423
460
|
onCommit: (src: string) => void;
|
|
@@ -427,4 +464,4 @@ interface ValueWizardOptions {
|
|
|
427
464
|
/** Build the wizard UI into a fresh element (drive it via the host's popover). */
|
|
428
465
|
declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
|
|
429
466
|
|
|
430
|
-
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARISON_OPS, type CatalogueEntry, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, astToTree, binary, boolLit, buildSubGroupClause, callNode, deleteAt, displayName, filterCatalogue, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
|
467
|
+
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, type CatalogueEntry, EQUALITY_OPS, type EditCtx, type EditorEffect, type EffectsEditorHandle, type EffectsEditorOptions, type ExpressionEditorHandle, type ExpressionEditorOptions, type Filter, type FunctionTemplateSpec, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
package/dist/index.js
CHANGED
|
@@ -220,6 +220,10 @@ var BINARY_LABEL = {
|
|
|
220
220
|
var UNARY_LABEL = { not: "NOT", neg: "\u2212" };
|
|
221
221
|
var COMPARISON_OPS = ["==", "!=", ">", ">=", "<", "<="];
|
|
222
222
|
var ARITHMETIC_OPS = ["+", "-", "*", "/"];
|
|
223
|
+
var COMPARABLE_TYPES = ["boolean", "number", "string", "enum", "quality"];
|
|
224
|
+
var comparisonOpsFor = (t) => t === "number" || t === "quality" ? COMPARISON_OPS : EQUALITY_OPS;
|
|
225
|
+
var EQUALITY_OPS = ["==", "!="];
|
|
226
|
+
var rhsTypesFor = (t) => t === "number" ? ["number"] : t === "boolean" ? ["boolean"] : t === "quality" ? ["quality"] : t === "enum" ? ["enum", "string"] : ["string", "enum"];
|
|
223
227
|
function opSwapGroup(op) {
|
|
224
228
|
if (COMPARISON_OPS.includes(op)) return COMPARISON_OPS;
|
|
225
229
|
if (ARITHMETIC_OPS.includes(op)) return ARITHMETIC_OPS;
|
|
@@ -249,6 +253,12 @@ function formatNumber(n) {
|
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
// src/schema.ts
|
|
256
|
+
var choicesOf = (e) => {
|
|
257
|
+
if (!e) return void 0;
|
|
258
|
+
if (e.type === "quality") return e.stages?.length ? e.stages : e.enumValues;
|
|
259
|
+
if (e.type === "enum") return e.enumValues;
|
|
260
|
+
return void 0;
|
|
261
|
+
};
|
|
252
262
|
var refOf = (e, defaultScope) => e.scope === defaultScope ? `@${e.name}` : `@${e.scope}.${e.name}`;
|
|
253
263
|
var displayName = (e, defaultScope) => e.scope === defaultScope ? e.name : `${e.scope}.${e.name}`;
|
|
254
264
|
function filterCatalogue(entries, filter = {}) {
|
|
@@ -552,8 +562,9 @@ function stringEditor(ctx, path, node, anchor) {
|
|
|
552
562
|
const peer = findEnumPeer(ctx.getAst(), path);
|
|
553
563
|
const enumEntry = peer ? lookup(ctx.catalogue, peer.scope, peer.name) : null;
|
|
554
564
|
const rootValueEnums = path.length === 0 && ctx.valueEnumValues?.length ? ctx.valueEnumValues : null;
|
|
555
|
-
const
|
|
556
|
-
const
|
|
565
|
+
const peerChoices = choicesOf(enumEntry);
|
|
566
|
+
const enumValues = peerChoices?.length ? peerChoices : rootValueEnums;
|
|
567
|
+
const enumHeadLabel = enumEntry ? `${displayName(enumEntry, ctx.defaultScope)} ${enumEntry.type === "quality" ? "stage" : "value"}` : "Value";
|
|
557
568
|
ctx.openPopover(anchor, (close) => {
|
|
558
569
|
if (enumValues?.length) {
|
|
559
570
|
const wrap = el("div", "exed-menu");
|
|
@@ -703,7 +714,6 @@ function propertyPicker(ctx, opts) {
|
|
|
703
714
|
}
|
|
704
715
|
|
|
705
716
|
// src/clausewizard.ts
|
|
706
|
-
var EQUALITY = ["==", "!="];
|
|
707
717
|
var OP_WORD = {
|
|
708
718
|
"==": "equals",
|
|
709
719
|
"!=": "not equal to",
|
|
@@ -717,8 +727,6 @@ var opButton = (o, onClick) => {
|
|
|
717
727
|
b.append(el("span", "exed-opt-name", [BINARY_LABEL[o]]), el("span", "exed-opt-purpose", [OP_WORD[o] ?? ""]));
|
|
718
728
|
return b;
|
|
719
729
|
};
|
|
720
|
-
var opsForType = (t) => t === "number" ? COMPARISON_OPS : EQUALITY;
|
|
721
|
-
var rhsTypesFor = (t) => t === "number" ? ["number"] : t === "boolean" ? ["boolean"] : t === "enum" ? ["enum", "string"] : ["string", "enum"];
|
|
722
730
|
function header(host, title, back, cancel) {
|
|
723
731
|
const h = el("div", "exed-vwiz-head");
|
|
724
732
|
if (back) h.append(button("exed-vwiz-back", "\u2190", back, "Back"));
|
|
@@ -749,8 +757,8 @@ function valueStep(host, w, lhs, op, back, cancel, commit) {
|
|
|
749
757
|
const row = el("div", "exed-field-row");
|
|
750
758
|
row.append(button("exed-opt", "true", () => done(boolLit(true))), button("exed-opt", "false", () => done(boolLit(false))));
|
|
751
759
|
body.append(row);
|
|
752
|
-
} else if (lhs
|
|
753
|
-
for (const v of lhs
|
|
760
|
+
} else if (choicesOf(lhs)?.length) {
|
|
761
|
+
for (const v of choicesOf(lhs)) body.append(button("exed-opt", v, () => done(strLit(v))));
|
|
754
762
|
} else if (lhs.type === "number") {
|
|
755
763
|
body.append(textField({ caption: "Number", placeholder: "e.g. 3", validate: (v) => v.trim() !== "" && Number.isFinite(Number(v)), onCommit: (v) => done(numLit(Number(v))) }));
|
|
756
764
|
} else {
|
|
@@ -772,8 +780,13 @@ function comparisonWizard(host, w, commit, cancel) {
|
|
|
772
780
|
const body = el("div", "exed-vwiz-body");
|
|
773
781
|
host.append(body);
|
|
774
782
|
body.append(propertyPicker(pickCtxOf(w), {
|
|
775
|
-
accept:
|
|
776
|
-
onPick: (e) => pickOp({
|
|
783
|
+
accept: COMPARABLE_TYPES,
|
|
784
|
+
onPick: (e) => pickOp({
|
|
785
|
+
ref: refDisplay(w, e),
|
|
786
|
+
type: e.type,
|
|
787
|
+
...e.enumValues ? { enumValues: e.enumValues } : {},
|
|
788
|
+
...e.stages ? { stages: e.stages } : {}
|
|
789
|
+
})
|
|
777
790
|
}));
|
|
778
791
|
};
|
|
779
792
|
const pickOp = (lhs) => {
|
|
@@ -781,7 +794,7 @@ function comparisonWizard(host, w, commit, cancel) {
|
|
|
781
794
|
header(host, el("span", void 0, ["Operator for ", mono(lhs.ref)]), pickLhs, cancel);
|
|
782
795
|
const body = el("div", "exed-vwiz-body");
|
|
783
796
|
host.append(body);
|
|
784
|
-
for (const o of
|
|
797
|
+
for (const o of comparisonOpsFor(lhs.type)) {
|
|
785
798
|
body.append(opButton(o, () => valueStep(host, w, lhs, o, () => pickOp(lhs), cancel, commit)));
|
|
786
799
|
}
|
|
787
800
|
};
|
|
@@ -1335,7 +1348,9 @@ function valueWizard(opts) {
|
|
|
1335
1348
|
host.append(head("Pick a value"));
|
|
1336
1349
|
const body = el("div", "exed-vwiz-body");
|
|
1337
1350
|
host.append(body);
|
|
1338
|
-
|
|
1351
|
+
const choices = opts.expectedChoices?.length ? opts.expectedChoices : opts.expectedEnumValues;
|
|
1352
|
+
const isStage = opts.expectedType === "quality";
|
|
1353
|
+
let kind = opts.expectedType === "number" ? "number" : opts.expectedType === "string" ? "text" : opts.expectedType === "boolean" ? "bool" : (opts.expectedType === "enum" || opts.expectedType === "quality") && choices?.length ? "enum" : "menu";
|
|
1339
1354
|
const draw = () => {
|
|
1340
1355
|
body.replaceChildren();
|
|
1341
1356
|
const other = () => button("exed-vwiz-other", "\u21A9 a different kind", () => {
|
|
@@ -1360,7 +1375,7 @@ function valueWizard(opts) {
|
|
|
1360
1375
|
kind = "bool";
|
|
1361
1376
|
draw();
|
|
1362
1377
|
}));
|
|
1363
|
-
if (
|
|
1378
|
+
if (choices?.length) body.append(optBtn(isStage ? "A stage" : "A listed value", () => {
|
|
1364
1379
|
kind = "enum";
|
|
1365
1380
|
draw();
|
|
1366
1381
|
}));
|
|
@@ -1384,7 +1399,8 @@ function valueWizard(opts) {
|
|
|
1384
1399
|
break;
|
|
1385
1400
|
}
|
|
1386
1401
|
case "enum":
|
|
1387
|
-
|
|
1402
|
+
body.append(el("div", "exed-vwiz-note", [isStage ? "Stages, in ladder order:" : ""]));
|
|
1403
|
+
for (const v of choices ?? []) body.append(optBtn(v, () => opts.onCommit(enumSrc(v))));
|
|
1388
1404
|
body.append(other());
|
|
1389
1405
|
break;
|
|
1390
1406
|
}
|
|
@@ -1433,14 +1449,23 @@ function removeArgAt(list, i, argIdx) {
|
|
|
1433
1449
|
cur.args.splice(argIdx, 1);
|
|
1434
1450
|
return next;
|
|
1435
1451
|
}
|
|
1436
|
-
function
|
|
1452
|
+
function initialValueFor(entry, defaultScope) {
|
|
1453
|
+
const ref = refOf(entry, defaultScope);
|
|
1454
|
+
return {
|
|
1455
|
+
src: seedValueSrc(entry.type, choicesOf(entry), ref),
|
|
1456
|
+
ask: entry.type !== "quality"
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
function seedValueSrc(type, values, ref) {
|
|
1437
1460
|
switch (type) {
|
|
1438
1461
|
case "boolean":
|
|
1439
1462
|
return "true";
|
|
1440
1463
|
case "number":
|
|
1441
1464
|
return "0";
|
|
1442
1465
|
case "enum":
|
|
1443
|
-
return JSON.stringify(
|
|
1466
|
+
return JSON.stringify(values?.[0] ?? "");
|
|
1467
|
+
case "quality":
|
|
1468
|
+
return ref ? `advance(${ref})` : JSON.stringify(values?.[0] ?? "");
|
|
1444
1469
|
case "string":
|
|
1445
1470
|
return '""';
|
|
1446
1471
|
default:
|
|
@@ -1470,7 +1495,9 @@ function mountEffectsEditor(host, opts) {
|
|
|
1470
1495
|
scopeOrder: opts.scopeOrder ?? [],
|
|
1471
1496
|
defaultScope,
|
|
1472
1497
|
...expected?.type ? { expectedType: expected.type } : {},
|
|
1473
|
-
|
|
1498
|
+
// choicesOf, never a raw field: a quality's ladder lives in `stages`, and reading `enumValues`
|
|
1499
|
+
// here told the wizard the type and withheld the values.
|
|
1500
|
+
...choicesOf(expected) ? { expectedChoices: choicesOf(expected) } : {},
|
|
1474
1501
|
onCommit: (src) => {
|
|
1475
1502
|
onCommit(src);
|
|
1476
1503
|
close();
|
|
@@ -1538,7 +1565,7 @@ function mountEffectsEditor(host, opts) {
|
|
|
1538
1565
|
const row = el("div", "exed-effect exed-effect-set");
|
|
1539
1566
|
const targetBtn = button("exed-pill exed-pill-prop", eff.target || "(pick property)", (e) => {
|
|
1540
1567
|
pickTarget(e.currentTarget, void 0, (entry) => {
|
|
1541
|
-
commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value:
|
|
1568
|
+
commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value: initialValueFor(entry, defaultScope).src }));
|
|
1542
1569
|
});
|
|
1543
1570
|
}, "change the target property");
|
|
1544
1571
|
row.append(targetBtn, el("span", "exed-effect-eq", ["="]));
|
|
@@ -1624,7 +1651,14 @@ function mountEffectsEditor(host, opts) {
|
|
|
1624
1651
|
const bar = el("div", "exed-effect-addbar");
|
|
1625
1652
|
bar.append(button("exed-eff-add", "+ set property", (e) => {
|
|
1626
1653
|
const anchor = e.currentTarget;
|
|
1627
|
-
pickTarget(anchor, void 0, (entry) =>
|
|
1654
|
+
pickTarget(anchor, void 0, (entry) => {
|
|
1655
|
+
const { src, ask } = initialValueFor(entry, defaultScope);
|
|
1656
|
+
if (!ask) {
|
|
1657
|
+
commit(addSet(effects, refOf(entry, defaultScope), src));
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
openValueWizard(anchor, entry, (chosen) => commit(addSet(effects, refOf(entry, defaultScope), chosen)));
|
|
1661
|
+
});
|
|
1628
1662
|
}));
|
|
1629
1663
|
if (opts.allowEmit !== false) {
|
|
1630
1664
|
bar.append(button("exed-eff-add", "+ emit event", (e) => editEvent(e.currentTarget, "", (name) => commit(addEmit(effects, name)))));
|
|
@@ -1712,6 +1746,6 @@ function renderEffectsPreview(effects, o) {
|
|
|
1712
1746
|
return wrap;
|
|
1713
1747
|
}
|
|
1714
1748
|
|
|
1715
|
-
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARISON_OPS, UNARY_LABEL, addArg, addChildToContainer, addEmit, addSet, astToTree, binary, boolLit, buildSubGroupClause, callNode, deleteAt, displayName, filterCatalogue, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
|
1749
|
+
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, EQUALITY_OPS, UNARY_LABEL, addArg, addChildToContainer, addEmit, addSet, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
|
1716
1750
|
//# sourceMappingURL=index.js.map
|
|
1717
1751
|
//# sourceMappingURL=index.js.map
|