@wildwinter/expr-editor 0.12.2 → 0.13.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/dist/index.cjs +75 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +74 -39
- package/dist/index.js.map +1 -1
- package/dist/styles.css +18 -0
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExprNode, BinaryOp, 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;
|
|
@@ -53,6 +53,16 @@ declare function findChoicePeer(ast: ExprNode, path: AstPath): {
|
|
|
53
53
|
} | null;
|
|
54
54
|
/** @deprecated Renamed to {@link findChoicePeer} - it serves qualities as well as enums now. */
|
|
55
55
|
declare const findEnumPeer: typeof findChoicePeer;
|
|
56
|
+
/** A name-form ref in ONE form, so two spellings of the same property compare equal: the default
|
|
57
|
+
* scope may be written or left off (`@patter.debt` and `@debt`), and refs are case-insensitive. */
|
|
58
|
+
declare const normaliseRef: (ref: string, defaultScope: string) => string;
|
|
59
|
+
/** The name-form ref a call ADVANCES, when the call is the plain `advance(@x)` shape: one scopedvar
|
|
60
|
+
* argument and nothing else. Null for any other call, and for an argument that is not a property
|
|
61
|
+
* (`advance(f(@x))` keeps its explicit form, since collapsing it would hide the middle).
|
|
62
|
+
*
|
|
63
|
+
* `advance` is the language's own built-in rather than a dialect function, so it is matched by name
|
|
64
|
+
* here the way the evaluator and validator match it. */
|
|
65
|
+
declare function advancedRef(node: ExprNode, defaultScope: string): string | null;
|
|
56
66
|
/**
|
|
57
67
|
* DFS for the first unfilled slot in a template-inserted clause: an empty string
|
|
58
68
|
* literal (a tag / value the author still has to set) or an unnamed flag delta.
|
|
@@ -108,6 +118,12 @@ declare function moveChildInContainer(ast: ExprNode, chainPath: AstPath, from: n
|
|
|
108
118
|
/** Human label for a binary operator (words for logical, glyphs for relational). */
|
|
109
119
|
declare const BINARY_LABEL: Record<BinaryOp, string>;
|
|
110
120
|
declare const UNARY_LABEL: Record<UnaryOp, string>;
|
|
121
|
+
/** Plain-language name for an operator. ONE table, because it is wanted in three places that must
|
|
122
|
+
* agree: a pill's accessible label, the clause wizard's operator step, and the swap menu. The swap
|
|
123
|
+
* menu once printed the label and the raw source instead ("> >"), which is what a second table
|
|
124
|
+
* buys you. Never the glyph twice: this reads NEXT to `BINARY_LABEL`, so it must say something the
|
|
125
|
+
* glyph does not. */
|
|
126
|
+
declare const OP_WORD: Record<BinaryOp | UnaryOp, string>;
|
|
111
127
|
declare const COMPARISON_OPS: BinaryOp[];
|
|
112
128
|
declare const ARITHMETIC_OPS: BinaryOp[];
|
|
113
129
|
/** Property types a comparison clause can be built on - what the clause wizard offers as a left-hand
|
|
@@ -274,6 +290,11 @@ interface EditCtx {
|
|
|
274
290
|
* (an enum-typed outcome target). Lets the string editor show an enum picker
|
|
275
291
|
* even without a comparison peer. No effect on non-root literals. */
|
|
276
292
|
valueEnumValues?: string[];
|
|
293
|
+
/** The name-form ref of the SET this value belongs to, when there is one. Its only use is the
|
|
294
|
+
* self-advance idiom: `@debt = advance(@debt)` names the property three times to say one thing,
|
|
295
|
+
* so when the value is exactly `advance(<this ref>)` the call collapses to a single "advances"
|
|
296
|
+
* word and the row reads `debt advances`. Any other target, or any other shape, renders in full. */
|
|
297
|
+
selfAdvanceRef?: string;
|
|
277
298
|
/** Host actions for a property pill (e.g. "Go to definition"). When set,
|
|
278
299
|
* right-clicking a property pill opens a menu of these actions. The host
|
|
279
300
|
* resolves what each does (navigation etc.); empty array = no menu. */
|
|
@@ -329,6 +350,10 @@ interface ExpressionEditorOptions {
|
|
|
329
350
|
/** Enum values for the single root literal of a value field (an enum-typed
|
|
330
351
|
* target): the literal editor then offers them as a picker. Flat mode only. */
|
|
331
352
|
valueEnumValues?: string[];
|
|
353
|
+
/** The ref of the SET this value belongs to (flat value fields only). Lets `advance(<that ref>)`
|
|
354
|
+
* render as a single "advances" word: the outcome row already names the property in its target
|
|
355
|
+
* column, so the call would say it twice more. */
|
|
356
|
+
selfAdvanceRef?: string;
|
|
332
357
|
/** Flat single-value field: an empty value renders one editable "set a value…"
|
|
333
358
|
* placeholder pill (a root string literal) rather than the condition
|
|
334
359
|
* clause-menu empty state. Pairs with mode:"flat". */
|
|
@@ -492,4 +517,4 @@ interface ValueWizardOptions {
|
|
|
492
517
|
/** Build the wizard UI into a fresh element (drive it via the host's popover). */
|
|
493
518
|
declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
|
|
494
519
|
|
|
495
|
-
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, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, 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 };
|
|
520
|
+
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, OP_WORD, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, 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 {
|
|
1
|
+
import { ExprNode, BinaryOp, 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;
|
|
@@ -53,6 +53,16 @@ declare function findChoicePeer(ast: ExprNode, path: AstPath): {
|
|
|
53
53
|
} | null;
|
|
54
54
|
/** @deprecated Renamed to {@link findChoicePeer} - it serves qualities as well as enums now. */
|
|
55
55
|
declare const findEnumPeer: typeof findChoicePeer;
|
|
56
|
+
/** A name-form ref in ONE form, so two spellings of the same property compare equal: the default
|
|
57
|
+
* scope may be written or left off (`@patter.debt` and `@debt`), and refs are case-insensitive. */
|
|
58
|
+
declare const normaliseRef: (ref: string, defaultScope: string) => string;
|
|
59
|
+
/** The name-form ref a call ADVANCES, when the call is the plain `advance(@x)` shape: one scopedvar
|
|
60
|
+
* argument and nothing else. Null for any other call, and for an argument that is not a property
|
|
61
|
+
* (`advance(f(@x))` keeps its explicit form, since collapsing it would hide the middle).
|
|
62
|
+
*
|
|
63
|
+
* `advance` is the language's own built-in rather than a dialect function, so it is matched by name
|
|
64
|
+
* here the way the evaluator and validator match it. */
|
|
65
|
+
declare function advancedRef(node: ExprNode, defaultScope: string): string | null;
|
|
56
66
|
/**
|
|
57
67
|
* DFS for the first unfilled slot in a template-inserted clause: an empty string
|
|
58
68
|
* literal (a tag / value the author still has to set) or an unnamed flag delta.
|
|
@@ -108,6 +118,12 @@ declare function moveChildInContainer(ast: ExprNode, chainPath: AstPath, from: n
|
|
|
108
118
|
/** Human label for a binary operator (words for logical, glyphs for relational). */
|
|
109
119
|
declare const BINARY_LABEL: Record<BinaryOp, string>;
|
|
110
120
|
declare const UNARY_LABEL: Record<UnaryOp, string>;
|
|
121
|
+
/** Plain-language name for an operator. ONE table, because it is wanted in three places that must
|
|
122
|
+
* agree: a pill's accessible label, the clause wizard's operator step, and the swap menu. The swap
|
|
123
|
+
* menu once printed the label and the raw source instead ("> >"), which is what a second table
|
|
124
|
+
* buys you. Never the glyph twice: this reads NEXT to `BINARY_LABEL`, so it must say something the
|
|
125
|
+
* glyph does not. */
|
|
126
|
+
declare const OP_WORD: Record<BinaryOp | UnaryOp, string>;
|
|
111
127
|
declare const COMPARISON_OPS: BinaryOp[];
|
|
112
128
|
declare const ARITHMETIC_OPS: BinaryOp[];
|
|
113
129
|
/** Property types a comparison clause can be built on - what the clause wizard offers as a left-hand
|
|
@@ -274,6 +290,11 @@ interface EditCtx {
|
|
|
274
290
|
* (an enum-typed outcome target). Lets the string editor show an enum picker
|
|
275
291
|
* even without a comparison peer. No effect on non-root literals. */
|
|
276
292
|
valueEnumValues?: string[];
|
|
293
|
+
/** The name-form ref of the SET this value belongs to, when there is one. Its only use is the
|
|
294
|
+
* self-advance idiom: `@debt = advance(@debt)` names the property three times to say one thing,
|
|
295
|
+
* so when the value is exactly `advance(<this ref>)` the call collapses to a single "advances"
|
|
296
|
+
* word and the row reads `debt advances`. Any other target, or any other shape, renders in full. */
|
|
297
|
+
selfAdvanceRef?: string;
|
|
277
298
|
/** Host actions for a property pill (e.g. "Go to definition"). When set,
|
|
278
299
|
* right-clicking a property pill opens a menu of these actions. The host
|
|
279
300
|
* resolves what each does (navigation etc.); empty array = no menu. */
|
|
@@ -329,6 +350,10 @@ interface ExpressionEditorOptions {
|
|
|
329
350
|
/** Enum values for the single root literal of a value field (an enum-typed
|
|
330
351
|
* target): the literal editor then offers them as a picker. Flat mode only. */
|
|
331
352
|
valueEnumValues?: string[];
|
|
353
|
+
/** The ref of the SET this value belongs to (flat value fields only). Lets `advance(<that ref>)`
|
|
354
|
+
* render as a single "advances" word: the outcome row already names the property in its target
|
|
355
|
+
* column, so the call would say it twice more. */
|
|
356
|
+
selfAdvanceRef?: string;
|
|
332
357
|
/** Flat single-value field: an empty value renders one editable "set a value…"
|
|
333
358
|
* placeholder pill (a root string literal) rather than the condition
|
|
334
359
|
* clause-menu empty state. Pairs with mode:"flat". */
|
|
@@ -492,4 +517,4 @@ interface ValueWizardOptions {
|
|
|
492
517
|
/** Build the wizard UI into a fresh element (drive it via the host's popover). */
|
|
493
518
|
declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
|
|
494
519
|
|
|
495
|
-
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, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, 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 };
|
|
520
|
+
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, OP_WORD, type PreviewOptions, type TreeRow, UNARY_LABEL, type Validation, type ValueWizardOptions, type WizardSpec, type WizardStepSpec, type WizardValue, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseAndValidate, unparse } from '@wildwinter/expr';
|
|
1
|
+
import { parseAndValidate, parse, unparse } from '@wildwinter/expr';
|
|
2
2
|
|
|
3
3
|
// src/ast.ts
|
|
4
4
|
var boolLit = (value) => ({ kind: "bool", value });
|
|
@@ -91,6 +91,16 @@ function findChoicePeer(ast, path) {
|
|
|
91
91
|
return other.kind === "scopedvar" ? { scope: other.scope, name: other.name } : null;
|
|
92
92
|
}
|
|
93
93
|
var findEnumPeer = findChoicePeer;
|
|
94
|
+
var normaliseRef = (ref, defaultScope) => {
|
|
95
|
+
const low = ref.toLowerCase();
|
|
96
|
+
const prefix = `@${defaultScope.toLowerCase()}.`;
|
|
97
|
+
return low.startsWith(prefix) ? `@${low.slice(prefix.length)}` : low;
|
|
98
|
+
};
|
|
99
|
+
function advancedRef(node, defaultScope) {
|
|
100
|
+
if (node.kind !== "call" || node.name !== "advance" || node.args.length !== 1) return null;
|
|
101
|
+
const arg = node.args[0];
|
|
102
|
+
return arg.kind === "scopedvar" ? normaliseRef(`@${arg.scope}.${arg.name}`, defaultScope) : null;
|
|
103
|
+
}
|
|
94
104
|
function firstEmptyLeafPath(node, base = []) {
|
|
95
105
|
switch (node.kind) {
|
|
96
106
|
case "string":
|
|
@@ -219,6 +229,22 @@ var BINARY_LABEL = {
|
|
|
219
229
|
"/": "\xF7"
|
|
220
230
|
};
|
|
221
231
|
var UNARY_LABEL = { not: "NOT", neg: "\u2212" };
|
|
232
|
+
var OP_WORD = {
|
|
233
|
+
"==": "equals",
|
|
234
|
+
"!=": "not equal to",
|
|
235
|
+
">": "greater than",
|
|
236
|
+
">=": "at least",
|
|
237
|
+
"<": "less than",
|
|
238
|
+
"<=": "at most",
|
|
239
|
+
and: "and",
|
|
240
|
+
or: "or",
|
|
241
|
+
"+": "plus",
|
|
242
|
+
"-": "minus",
|
|
243
|
+
"*": "times",
|
|
244
|
+
"/": "divided by",
|
|
245
|
+
not: "not",
|
|
246
|
+
neg: "negative"
|
|
247
|
+
};
|
|
222
248
|
var COMPARISON_OPS = ["==", "!=", ">", ">=", "<", "<="];
|
|
223
249
|
var ARITHMETIC_OPS = ["+", "-", "*", "/"];
|
|
224
250
|
var COMPARABLE_TYPES = ["boolean", "number", "string", "enum", "quality"];
|
|
@@ -404,20 +430,6 @@ function pill(kind, label, onClick, opts = {}) {
|
|
|
404
430
|
if (opts.path) b.dataset["exedPath"] = opts.path.join("/");
|
|
405
431
|
return b;
|
|
406
432
|
}
|
|
407
|
-
var OP_ARIA = {
|
|
408
|
-
"==": "equals",
|
|
409
|
-
"!=": "not equal to",
|
|
410
|
-
">": "greater than",
|
|
411
|
-
">=": "at least",
|
|
412
|
-
"<": "less than",
|
|
413
|
-
"<=": "at most",
|
|
414
|
-
"and": "and",
|
|
415
|
-
"or": "or",
|
|
416
|
-
"+": "plus",
|
|
417
|
-
"-": "minus",
|
|
418
|
-
"*": "times",
|
|
419
|
-
"/": "divided by"
|
|
420
|
-
};
|
|
421
433
|
var issueText = (ctx, path) => {
|
|
422
434
|
const list = issuesAt(ctx.byPath, path);
|
|
423
435
|
return list.length ? list.map((i) => i.message).join("; ") : void 0;
|
|
@@ -459,6 +471,16 @@ function renderNode(node, path, ctx, parentOp, side) {
|
|
|
459
471
|
for (let i = 1; i < node.args.length; i++) row2.append(renderNode(node.args[i], [...path, "args", i], ctx));
|
|
460
472
|
return row2;
|
|
461
473
|
}
|
|
474
|
+
if (ctx.selfAdvanceRef && advancedRef(node, ctx.defaultScope) === normaliseRef(ctx.selfAdvanceRef, ctx.defaultScope)) {
|
|
475
|
+
const row2 = el("span", "exed-call exed-call-advance");
|
|
476
|
+
row2.append(pill(
|
|
477
|
+
"advance",
|
|
478
|
+
"advances",
|
|
479
|
+
(b) => callEditor(ctx, path, node, b),
|
|
480
|
+
{ issue, title: "advance one stage", aria: "advances one stage" }
|
|
481
|
+
));
|
|
482
|
+
return row2;
|
|
483
|
+
}
|
|
462
484
|
const row = el("span", "exed-call");
|
|
463
485
|
row.append(pill("func", node.name, (b) => callEditor(ctx, path, node, b), { issue }));
|
|
464
486
|
row.append(el("span", "exed-paren", ["("]));
|
|
@@ -471,7 +493,7 @@ function renderNode(node, path, ctx, parentOp, side) {
|
|
|
471
493
|
}
|
|
472
494
|
case "unary": {
|
|
473
495
|
const row = el("span", "exed-unary");
|
|
474
|
-
row.append(pill("op", UNARY_LABEL[node.op], () => ctx.apply(deleteOrUnwrapNot(ctx, path, node)), { title: "remove", aria: `${
|
|
496
|
+
row.append(pill("op", UNARY_LABEL[node.op], () => ctx.apply(deleteOrUnwrapNot(ctx, path, node)), { title: "remove", aria: `${OP_WORD[node.op]} (click to remove)` }));
|
|
475
497
|
row.append(renderNode(node.operand, [...path, "operand"], ctx));
|
|
476
498
|
return row;
|
|
477
499
|
}
|
|
@@ -483,7 +505,7 @@ function renderNode(node, path, ctx, parentOp, side) {
|
|
|
483
505
|
const swap = opSwapGroup(node.op);
|
|
484
506
|
const opPill = pill("op", BINARY_LABEL[node.op], (b) => {
|
|
485
507
|
if (swap) operatorEditor(ctx, path, node.op, swap, b);
|
|
486
|
-
}, { title: swap ? "swap operator" : void 0, aria:
|
|
508
|
+
}, { title: swap ? "swap operator" : void 0, aria: OP_WORD[node.op] });
|
|
487
509
|
if (!swap) opPill.classList.add("exed-op-structural");
|
|
488
510
|
row.append(opPill);
|
|
489
511
|
row.append(renderNode(node.right, [...path, "right"], ctx, node.op, "right"));
|
|
@@ -594,12 +616,14 @@ function operatorEditor(ctx, path, current, group, anchor) {
|
|
|
594
616
|
const wrap = el("div", "exed-menu");
|
|
595
617
|
wrap.append(el("div", "exed-menu-head", ["Operator"]));
|
|
596
618
|
for (const op of group) {
|
|
597
|
-
|
|
619
|
+
const row = button(`exed-opt exed-opt-op${op === current ? " sel" : ""}`, "", () => {
|
|
598
620
|
const node = ctx.getAst();
|
|
599
621
|
const cur = setNodeAt(node, path, { ...getBinary(ctx, path), op });
|
|
600
622
|
ctx.apply(cur);
|
|
601
623
|
close();
|
|
602
|
-
})
|
|
624
|
+
});
|
|
625
|
+
row.append(el("span", "exed-opt-name", [BINARY_LABEL[op]]), el("span", "exed-opt-purpose", [OP_WORD[op]]));
|
|
626
|
+
wrap.append(row);
|
|
603
627
|
}
|
|
604
628
|
return wrap;
|
|
605
629
|
});
|
|
@@ -715,17 +739,9 @@ function propertyPicker(ctx, opts) {
|
|
|
715
739
|
}
|
|
716
740
|
|
|
717
741
|
// src/clausewizard.ts
|
|
718
|
-
var OP_WORD = {
|
|
719
|
-
"==": "equals",
|
|
720
|
-
"!=": "not equal to",
|
|
721
|
-
">": "greater than",
|
|
722
|
-
">=": "at least",
|
|
723
|
-
"<": "less than",
|
|
724
|
-
"<=": "at most"
|
|
725
|
-
};
|
|
726
742
|
var opButton = (o, onClick) => {
|
|
727
|
-
const b = button("exed-opt", "", onClick);
|
|
728
|
-
b.append(el("span", "exed-opt-name", [BINARY_LABEL[o]]), el("span", "exed-opt-purpose", [OP_WORD[o]
|
|
743
|
+
const b = button("exed-opt exed-opt-op", "", onClick);
|
|
744
|
+
b.append(el("span", "exed-opt-name", [BINARY_LABEL[o]]), el("span", "exed-opt-purpose", [OP_WORD[o]]));
|
|
729
745
|
return b;
|
|
730
746
|
};
|
|
731
747
|
function header(host, title, back, cancel) {
|
|
@@ -1148,6 +1164,7 @@ function mountExpressionEditor(host, opts) {
|
|
|
1148
1164
|
},
|
|
1149
1165
|
...opts.requireNonEmpty ? { requireNonEmpty: true } : {},
|
|
1150
1166
|
...opts.valueEnumValues ? { valueEnumValues: opts.valueEnumValues } : {},
|
|
1167
|
+
...opts.selfAdvanceRef ? { selfAdvanceRef: opts.selfAdvanceRef } : {},
|
|
1151
1168
|
...opts.propertyActions ? { propertyActions: opts.propertyActions } : {},
|
|
1152
1169
|
...opts.pickNode ? { pickNode: opts.pickNode } : {},
|
|
1153
1170
|
...opts.nodeLabel ? { nodeLabel: opts.nodeLabel } : {}
|
|
@@ -1411,6 +1428,14 @@ function valueWizard(opts) {
|
|
|
1411
1428
|
}
|
|
1412
1429
|
|
|
1413
1430
|
// src/effects.ts
|
|
1431
|
+
function isSelfAdvance(eff, defaultScope, dialect) {
|
|
1432
|
+
if (eff.kind !== "set" || !eff.target) return false;
|
|
1433
|
+
try {
|
|
1434
|
+
return advancedRef(parse(eff.value, dialect), defaultScope) === normaliseRef(eff.target, defaultScope);
|
|
1435
|
+
} catch {
|
|
1436
|
+
return false;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1414
1439
|
var clone = (list) => list.map((e) => e.kind === "set" ? { ...e } : { ...e, args: [...e.args] });
|
|
1415
1440
|
var addSet = (list, target, value) => [...clone(list), { kind: "set", target, value }];
|
|
1416
1441
|
var addEmit = (list, event) => [...clone(list), { kind: "emit", event, args: [] }];
|
|
@@ -1507,7 +1532,7 @@ function mountEffectsEditor(host, opts) {
|
|
|
1507
1532
|
}), { container: opts.popoverContainer });
|
|
1508
1533
|
};
|
|
1509
1534
|
const targetType = (target) => opts.catalogue.find((e) => refOf(e, defaultScope) === target)?.type;
|
|
1510
|
-
const valueDisplay = (value, onChange, type) => {
|
|
1535
|
+
const valueDisplay = (value, onChange, type, selfAdvanceRef) => {
|
|
1511
1536
|
const sub = el("div", "exed-effect-value");
|
|
1512
1537
|
const addTerm = type === "number" ? "arithmetic" : type === "boolean" ? "boolean" : void 0;
|
|
1513
1538
|
inner.push(mountExpressionEditor(sub, {
|
|
@@ -1519,6 +1544,7 @@ function mountEffectsEditor(host, opts) {
|
|
|
1519
1544
|
functions: opts.functions,
|
|
1520
1545
|
mode: "flat",
|
|
1521
1546
|
...addTerm ? { addTerm } : {},
|
|
1547
|
+
...selfAdvanceRef ? { selfAdvanceRef } : {},
|
|
1522
1548
|
...opts.popoverContainer ? { popoverContainer: opts.popoverContainer } : {},
|
|
1523
1549
|
text: textMode,
|
|
1524
1550
|
onChange
|
|
@@ -1569,11 +1595,18 @@ function mountEffectsEditor(host, opts) {
|
|
|
1569
1595
|
commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value: initialValueFor(entry, defaultScope).src }));
|
|
1570
1596
|
});
|
|
1571
1597
|
}, "change the target property");
|
|
1572
|
-
|
|
1598
|
+
const advances = isSelfAdvance(eff, defaultScope, opts.dialect);
|
|
1599
|
+
row.append(targetBtn);
|
|
1600
|
+
if (!advances) row.append(el("span", "exed-effect-eq", ["="]));
|
|
1573
1601
|
row.append(valueDisplay(eff.value, (src) => {
|
|
1574
|
-
|
|
1602
|
+
const next = updateAt(effects, i, { value: src });
|
|
1603
|
+
if (isSelfAdvance(next[i], defaultScope, opts.dialect) !== advances) {
|
|
1604
|
+
commit(next);
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
effects = next;
|
|
1575
1608
|
opts.onChange(effects);
|
|
1576
|
-
}, targetType(eff.target)));
|
|
1609
|
+
}, targetType(eff.target), advances ? eff.target : void 0));
|
|
1577
1610
|
row.append(rowActions2(i));
|
|
1578
1611
|
return row;
|
|
1579
1612
|
}
|
|
@@ -1687,7 +1720,7 @@ function mountEffectsEditor(host, opts) {
|
|
|
1687
1720
|
}
|
|
1688
1721
|
|
|
1689
1722
|
// src/preview.ts
|
|
1690
|
-
function frozenCtx(src, o) {
|
|
1723
|
+
function frozenCtx(src, o, selfAdvanceRef) {
|
|
1691
1724
|
const v = validateSource(src, o.schema, o.dialect);
|
|
1692
1725
|
const ctx = {
|
|
1693
1726
|
schema: o.schema,
|
|
@@ -1706,6 +1739,7 @@ function frozenCtx(src, o) {
|
|
|
1706
1739
|
pickNode: () => {
|
|
1707
1740
|
},
|
|
1708
1741
|
// enables labelled node pills; never fires (preview is read-only)
|
|
1742
|
+
...selfAdvanceRef ? { selfAdvanceRef } : {},
|
|
1709
1743
|
...o.nodeLabel ? { nodeLabel: o.nodeLabel } : {},
|
|
1710
1744
|
...o.propertyActions ? {
|
|
1711
1745
|
propertyActions: o.propertyActions,
|
|
@@ -1716,8 +1750,8 @@ function frozenCtx(src, o) {
|
|
|
1716
1750
|
};
|
|
1717
1751
|
return { ctx, ast: v.ast };
|
|
1718
1752
|
}
|
|
1719
|
-
function exprPills(src, o) {
|
|
1720
|
-
const { ctx, ast } = frozenCtx(src, o);
|
|
1753
|
+
function exprPills(src, o, selfAdvanceRef) {
|
|
1754
|
+
const { ctx, ast } = frozenCtx(src, o, selfAdvanceRef);
|
|
1721
1755
|
return ast ? renderNode(ast, [], ctx) : el("span", "exed-preview-raw", [src]);
|
|
1722
1756
|
}
|
|
1723
1757
|
function renderConditionPreview(src, o) {
|
|
@@ -1729,9 +1763,10 @@ function renderEffectsPreview(effects, o) {
|
|
|
1729
1763
|
for (const eff of effects) {
|
|
1730
1764
|
const row = el("div", "exed-preview-eff");
|
|
1731
1765
|
if (eff.kind === "set") {
|
|
1766
|
+
const advances = isSelfAdvance(eff, o.dialect.defaultScope, o.dialect);
|
|
1732
1767
|
row.append(el("span", "exed-pill exed-pill-prop", [eff.target || "(property)"]));
|
|
1733
|
-
row.append(el("span", "exed-effect-eq", ["="]));
|
|
1734
|
-
row.append(exprPills(eff.value, o));
|
|
1768
|
+
if (!advances) row.append(el("span", "exed-effect-eq", ["="]));
|
|
1769
|
+
row.append(exprPills(eff.value, o, advances ? eff.target : void 0));
|
|
1735
1770
|
} else {
|
|
1736
1771
|
row.append(el("span", "exed-effect-kw", ["emit"]));
|
|
1737
1772
|
row.append(el("span", "exed-pill exed-pill-event", [eff.event ? `"${eff.event}"` : "(name)"]));
|
|
@@ -1747,6 +1782,6 @@ function renderEffectsPreview(effects, o) {
|
|
|
1747
1782
|
return wrap;
|
|
1748
1783
|
}
|
|
1749
1784
|
|
|
1750
|
-
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, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, 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 };
|
|
1785
|
+
export { ARITHMETIC_OPS, BINARY_LABEL, COMPARABLE_TYPES, COMPARISON_OPS, EQUALITY_OPS, OP_WORD, UNARY_LABEL, addArg, addChildToContainer, addEmit, addSet, advancedRef, astToTree, binary, boolLit, buildSubGroupClause, callNode, choicesOf, comparisonOpsFor, deleteAt, displayName, filterCatalogue, findChoicePeer, findEnumPeer, firstEmptyLeafPath, flagDelta, flipContainerOp, formatNumber, getNodeAt, groupByScope, initialValueFor, insertSiblingClauseAt, isComparisonOp, isPlaceholderForOp, isWrappedInNot, issuesAt, lookup, mountEffectsEditor, mountExpressionEditor, moveAt, moveChildInContainer, needsParens, normaliseRef, notNode, numLit, opSwapGroup, pathKey, placeholderForOp, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
|
|
1751
1786
|
//# sourceMappingURL=index.js.map
|
|
1752
1787
|
//# sourceMappingURL=index.js.map
|