@wildwinter/expr-editor 0.13.0 → 0.14.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.d.cts CHANGED
@@ -169,6 +169,16 @@ declare const choicesOf: (e: {
169
169
  enumValues?: string[];
170
170
  stages?: string[];
171
171
  } | null | undefined) => string[] | undefined;
172
+ /** What a property PILL says on rollover: the purpose, and for a quality its
173
+ * ladder (the one thing about a property whose order matters and which the
174
+ * use site cannot otherwise show). The picker already displays purposes; this
175
+ * is for the place an author actually meets a property, mid-expression.
176
+ * Pure, so hosts and tests share one answer; the pill wires it as its title. */
177
+ declare const propertyTip: (e: {
178
+ type: PropertyType;
179
+ purpose?: string;
180
+ stages?: string[];
181
+ } | null | undefined) => string | undefined;
172
182
  interface Filter {
173
183
  acceptTypes?: PropertyType[];
174
184
  acceptScopes?: string[];
@@ -426,6 +436,16 @@ interface EffectsEditorOptions {
426
436
  * inside a focus-trapping dialog so popovers do not dismiss it. Threaded to the
427
437
  * target/event pickers here and to every inline value editor. */
428
438
  popoverContainer?: HTMLElement;
439
+ /** Host actions for a property pill, shown on right-click (e.g. go to
440
+ * definition). Threaded to every inline value editor, and honoured by the
441
+ * set rows' own target pills (resolved via the catalogue). */
442
+ propertyActions?(ref: {
443
+ scope: string;
444
+ name: string;
445
+ }): Array<{
446
+ label: string;
447
+ run: () => void;
448
+ }>;
429
449
  /** Emitted on every structural / value edit with the whole new list. */
430
450
  onChange: (effects: EditorEffect[]) => void;
431
451
  }
@@ -517,4 +537,4 @@ interface ValueWizardOptions {
517
537
  /** Build the wizard UI into a fresh element (drive it via the host's popover). */
518
538
  declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
519
539
 
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 };
540
+ 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, propertyTip, 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
@@ -169,6 +169,16 @@ declare const choicesOf: (e: {
169
169
  enumValues?: string[];
170
170
  stages?: string[];
171
171
  } | null | undefined) => string[] | undefined;
172
+ /** What a property PILL says on rollover: the purpose, and for a quality its
173
+ * ladder (the one thing about a property whose order matters and which the
174
+ * use site cannot otherwise show). The picker already displays purposes; this
175
+ * is for the place an author actually meets a property, mid-expression.
176
+ * Pure, so hosts and tests share one answer; the pill wires it as its title. */
177
+ declare const propertyTip: (e: {
178
+ type: PropertyType;
179
+ purpose?: string;
180
+ stages?: string[];
181
+ } | null | undefined) => string | undefined;
172
182
  interface Filter {
173
183
  acceptTypes?: PropertyType[];
174
184
  acceptScopes?: string[];
@@ -426,6 +436,16 @@ interface EffectsEditorOptions {
426
436
  * inside a focus-trapping dialog so popovers do not dismiss it. Threaded to the
427
437
  * target/event pickers here and to every inline value editor. */
428
438
  popoverContainer?: HTMLElement;
439
+ /** Host actions for a property pill, shown on right-click (e.g. go to
440
+ * definition). Threaded to every inline value editor, and honoured by the
441
+ * set rows' own target pills (resolved via the catalogue). */
442
+ propertyActions?(ref: {
443
+ scope: string;
444
+ name: string;
445
+ }): Array<{
446
+ label: string;
447
+ run: () => void;
448
+ }>;
429
449
  /** Emitted on every structural / value edit with the whole new list. */
430
450
  onChange: (effects: EditorEffect[]) => void;
431
451
  }
@@ -517,4 +537,4 @@ interface ValueWizardOptions {
517
537
  /** Build the wizard UI into a fresh element (drive it via the host's popover). */
518
538
  declare function valueWizard(opts: ValueWizardOptions): HTMLElement;
519
539
 
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 };
540
+ 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, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
package/dist/index.js CHANGED
@@ -286,6 +286,13 @@ var choicesOf = (e) => {
286
286
  if (e.type === "enum") return e.enumValues;
287
287
  return void 0;
288
288
  };
289
+ var propertyTip = (e) => {
290
+ if (!e) return void 0;
291
+ const lines = [];
292
+ if (e.purpose) lines.push(e.purpose);
293
+ if (e.type === "quality" && e.stages?.length) lines.push(`Stages: ${e.stages.join(" \u2192 ")}`);
294
+ return lines.length ? lines.join("\n") : void 0;
295
+ };
289
296
  var refOf = (e, defaultScope) => e.scope === defaultScope ? `@${e.name}` : `@${e.scope}.${e.name}`;
290
297
  var displayName = (e, defaultScope) => e.scope === defaultScope ? e.name : `${e.scope}.${e.name}`;
291
298
  function filterCatalogue(entries, filter = {}) {
@@ -457,7 +464,12 @@ function renderNode(node, path, ctx, parentOp, side) {
457
464
  case "scopedvar": {
458
465
  const entry = lookup(ctx.catalogue, node.scope, node.name);
459
466
  const label = displayName({ scope: node.scope, name: node.name }, ctx.defaultScope);
460
- const varPill = pill("var", label, (b) => variableEditor(ctx, path, node, b), { issue: issue ?? (entry ? void 0 : `Unknown property ${label}`) });
467
+ const varPill = pill(
468
+ "var",
469
+ label,
470
+ (b) => variableEditor(ctx, path, node, b),
471
+ { issue: issue ?? (entry ? void 0 : `Unknown property ${label}`), title: propertyTip(entry) }
472
+ );
461
473
  attachPropertyMenu(ctx, node, varPill);
462
474
  return varPill;
463
475
  }
@@ -1543,8 +1555,14 @@ function mountEffectsEditor(host, opts) {
1543
1555
  scopeOrder: opts.scopeOrder,
1544
1556
  functions: opts.functions,
1545
1557
  mode: "flat",
1558
+ // Every editor here holds a VALUE (a set's value, an emit's argument), never a condition. Say
1559
+ // so, or emptying one falls back to the condition empty state and an outcome row reads
1560
+ // "always / + Add your first condition" - condition vocabulary, offering clauses where a value
1561
+ // belongs, and no way back to a value. `valueField` renders an editable empty literal instead.
1562
+ valueField: true,
1546
1563
  ...addTerm ? { addTerm } : {},
1547
1564
  ...selfAdvanceRef ? { selfAdvanceRef } : {},
1565
+ ...opts.propertyActions ? { propertyActions: opts.propertyActions } : {},
1548
1566
  ...opts.popoverContainer ? { popoverContainer: opts.popoverContainer } : {},
1549
1567
  text: textMode,
1550
1568
  onChange
@@ -1595,6 +1613,24 @@ function mountEffectsEditor(host, opts) {
1595
1613
  commit(updateAt(effects, i, { target: refOf(entry, defaultScope), value: initialValueFor(entry, defaultScope).src }));
1596
1614
  });
1597
1615
  }, "change the target property");
1616
+ const targetEntry = opts.catalogue.find((e) => refOf(e, defaultScope) === eff.target);
1617
+ const tip = propertyTip(targetEntry);
1618
+ if (tip) targetBtn.title = tip;
1619
+ if (targetEntry && opts.propertyActions) {
1620
+ const actions = opts.propertyActions({ scope: targetEntry.scope, name: targetEntry.name });
1621
+ if (actions.length) targetBtn.addEventListener("contextmenu", (e) => {
1622
+ e.preventDefault();
1623
+ closePopover();
1624
+ popover = openPopover(targetBtn, (close) => {
1625
+ const wrap = el("div", "exed-menu");
1626
+ for (const a of actions) wrap.append(button("exed-opt", a.label, () => {
1627
+ a.run();
1628
+ close();
1629
+ }));
1630
+ return wrap;
1631
+ }, { container: opts.popoverContainer });
1632
+ });
1633
+ }
1598
1634
  const advances = isSelfAdvance(eff, defaultScope, opts.dialect);
1599
1635
  row.append(targetBtn);
1600
1636
  if (!advances) row.append(el("span", "exed-effect-eq", ["="]));
@@ -1782,6 +1818,6 @@ function renderEffectsPreview(effects, o) {
1782
1818
  return wrap;
1783
1819
  }
1784
1820
 
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 };
1821
+ 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, propertyTip, redirectDeleteForPlaceholderSibling, refOf, removeArgAt, removeAt, renderConditionPreview, renderEffectsPreview, rhsTypesFor, scopedVar, searchCatalogue, seedValueSrc, setArgAt, setNodeAt, strLit, toggleContainerNot, toggleNotAt, updateAt, validateSource, valueWizard, wrapInNotAt };
1786
1822
  //# sourceMappingURL=index.js.map
1787
1823
  //# sourceMappingURL=index.js.map