@astroapps/forms-core 1.2.2 → 2.0.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.
@@ -0,0 +1,7 @@
1
+ import { Control } from "@astroapps/controls";
2
+ export declare function createOverrideProxy<A extends object, B extends Record<string, any>>(proxyFor: A, handlers: Control<B>): A;
3
+ declare class NoValue {
4
+ }
5
+ export declare const NoOverride: NoValue;
6
+ export type KeysOfUnion<T> = T extends T ? keyof T : never;
7
+ export {};
@@ -0,0 +1,19 @@
1
+ import { ChangeListenerFunc, CleanupScope, Control } from "@astroapps/controls";
2
+ import { ControlDefinition } from "./controlDefinition";
3
+ import { SchemaDataNode } from "./schemaDataNode";
4
+ import { FormNode } from "./formNode";
5
+ import { FormStateNode } from "./formStateNode";
6
+ export type ChildResolverFunc = (c: FormStateNode) => ChildNodeSpec[];
7
+ export interface ChildNodeSpec {
8
+ childKey: string | number;
9
+ create: (scope: CleanupScope, meta: Record<string, any>) => ChildNodeInit;
10
+ }
11
+ export interface ChildNodeInit {
12
+ definition?: ControlDefinition;
13
+ parent?: SchemaDataNode;
14
+ node?: FormNode | null;
15
+ variables?: (changes: ChangeListenerFunc<any>) => Record<string, any>;
16
+ resolveChildren?: ChildResolverFunc;
17
+ }
18
+ export declare function defaultResolveChildNodes(formStateNode: FormStateNode): ChildNodeSpec[];
19
+ export declare function resolveArrayChildren(data: SchemaDataNode, node: FormNode, adjustChild?: (elem: Control<any>, index: number) => Partial<ChildNodeInit>): ChildNodeSpec[];
@@ -34,3 +34,6 @@ export declare function schemaDataForFieldRef(fieldRef: string | undefined, sche
34
34
  export declare function schemaDataForFieldPath(fieldPath: string[], dataNode: SchemaDataNode): SchemaDataNode;
35
35
  export declare function validDataNode(context: SchemaDataNode): boolean;
36
36
  export declare function hideDisplayOnly(context: SchemaDataNode, schemaInterface: SchemaInterface, definition: ControlDefinition): boolean | undefined;
37
+ export declare function getLoadingControl(data: Control<any>): Control<boolean>;
38
+ export declare function getRefreshingControl(data: Control<any>): Control<boolean>;
39
+ export declare function getHasMoreControl(data: Control<any>): Control<boolean>;
@@ -15,7 +15,8 @@ export declare class SchemaNode {
15
15
  field: SchemaField;
16
16
  tree: SchemaTree;
17
17
  parent?: SchemaNode | undefined;
18
- constructor(id: string, field: SchemaField, tree: SchemaTree, parent?: SchemaNode | undefined);
18
+ private getChildFields?;
19
+ constructor(id: string, field: SchemaField, tree: SchemaTree, parent?: SchemaNode | undefined, getChildFields?: (() => SchemaField[]) | undefined);
19
20
  getSchema(schemaId: string): SchemaNode | undefined;
20
21
  getUnresolvedFields(): SchemaField[];
21
22
  getResolvedParent(noRecurse?: boolean): SchemaNode | undefined;
@@ -37,6 +38,8 @@ export declare function traverseData(fieldPath: string[], root: SchemaNode, data
37
38
  [k: string]: any;
38
39
  }): unknown;
39
40
  export declare function schemaForFieldPath(fieldPath: string[], schema: SchemaNode): SchemaNode;
41
+ export declare function schemaForDataPath(fieldPath: string[], schema: SchemaNode): DataPathNode;
42
+ export declare function getParentDataPath({ node, element, }: DataPathNode): DataPathNode | undefined;
40
43
  export declare function getSchemaNodePath(node: SchemaNode): string[];
41
44
  export declare function getSchemaNodePathString(node: SchemaNode): string;
42
45
  export declare function isCompoundNode(node: SchemaNode): boolean;
@@ -52,3 +55,7 @@ export declare function relativePath(parent: SchemaNode, child: SchemaNode): str
52
55
  * @param childPath
53
56
  */
54
57
  export declare function relativeSegmentPath(parentPath: string[], childPath: string[]): string;
58
+ export interface DataPathNode {
59
+ node: SchemaNode;
60
+ element: boolean;
61
+ }
@@ -25,3 +25,6 @@ export interface DateValidator extends SchemaValidator {
25
25
  fixedDate?: string | null;
26
26
  daysFromCurrent?: number | null;
27
27
  }
28
+ export declare function jsonataValidator(expr: string): JsonataValidator;
29
+ export declare function dateValidator(comparison: DateComparison, fixedDate?: string | null, daysFromCurrent?: number | null): DateValidator;
30
+ export declare function lengthValidator(min?: number | null, max?: number | null): LengthValidator;
@@ -1,10 +1,9 @@
1
1
  import { DateValidator, JsonataValidator, LengthValidator, SchemaValidator } from "./schemaValidator";
2
2
  import { ControlDefinition } from "./controlDefinition";
3
3
  import { SchemaDataNode } from "./schemaDataNode";
4
- import { Control } from "@astroapps/controls";
4
+ import { CleanupScope, Control } from "@astroapps/controls";
5
5
  import { SchemaInterface } from "./schemaInterface";
6
- import { FormContextOptions } from "./formState";
7
- import { FormNode } from "./formNode";
6
+ import { VariablesFunc } from "./formStateNode";
8
7
  export interface ValidationEvalContext {
9
8
  addSync(validate: (value: unknown) => string | undefined | null): void;
10
9
  addCleanup(cleanup: () => void): void;
@@ -12,7 +11,7 @@ export interface ValidationEvalContext {
12
11
  parentData: SchemaDataNode;
13
12
  data: SchemaDataNode;
14
13
  schemaInterface: SchemaInterface;
15
- formContext: Control<FormContextOptions>;
14
+ variables?: VariablesFunc;
16
15
  runAsync(af: () => void): void;
17
16
  }
18
17
  export type ValidatorEval<T extends SchemaValidator> = (validation: T, context: ValidationEvalContext) => void;
@@ -21,4 +20,4 @@ export declare const lengthValidator: ValidatorEval<LengthValidator>;
21
20
  export declare const dateValidator: ValidatorEval<DateValidator>;
22
21
  export declare const defaultValidators: Record<string, ValidatorEval<any>>;
23
22
  export declare function createValidators(def: ControlDefinition, context: ValidationEvalContext): void;
24
- export declare function setupValidation(controlImpl: Control<FormContextOptions>, definition: ControlDefinition, dataNode: Control<SchemaDataNode | undefined>, schemaInterface: SchemaInterface, parent: SchemaDataNode, formNode: FormNode, hidden: Control<boolean>, runAsync: (af: () => void) => void): void;
23
+ export declare function setupValidation(scope: CleanupScope, variables: VariablesFunc | undefined, definition: ControlDefinition, dataNode: Control<SchemaDataNode | undefined>, schemaInterface: SchemaInterface, parent: SchemaDataNode, visible: Control<boolean | null>, runAsync: (af: () => void) => void): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astroapps/forms-core",
3
- "version": "1.2.2",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -31,13 +31,13 @@
31
31
  "jsonata": "^2.0.4"
32
32
  },
33
33
  "peerDependencies": {
34
- "@astroapps/controls": "^1.3.0"
34
+ "@astroapps/controls": "^1.4.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@astroapps/controls": "^1.3.0",
37
+ "@astroapps/controls": "^1.4.0",
38
38
  "jest": "^29.7.0",
39
39
  "tsx": "^4.19.1",
40
- "fast-check": "^3.22.0",
40
+ "fast-check": "^3.23.2",
41
41
  "ts-jest": "^29.2.5",
42
42
  "@jest/globals": "^29.7.0",
43
43
  "typedoc": "^0.27.2",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "scripts": {
54
54
  "build": "rimraf ./lib/ node_modules/.cache && microbundle -f modern,cjs --no-compress",
55
- "watch": "microbundle -w -f modern,cjs --no-compress",
55
+ "watch": "microbundle -f modern,cjs --no-compress",
56
56
  "test": "jest --coverage",
57
57
  "play": "tsx test/play.ts",
58
58
  "update-readme": "md-magic --path README.md"
@@ -0,0 +1,218 @@
1
+ import {
2
+ AccordionAdornment,
3
+ ActionControlDefinition,
4
+ AutocompleteRenderOptions,
5
+ CheckListRenderOptions,
6
+ ControlAdornmentType,
7
+ ControlDefinition,
8
+ ControlDefinitionType,
9
+ DataControlDefinition,
10
+ DataRenderType,
11
+ DisplayControlDefinition,
12
+ DisplayDataType,
13
+ DisplayOnlyRenderOptions,
14
+ DynamicProperty,
15
+ DynamicPropertyType,
16
+ GroupedControlsDefinition,
17
+ GroupRenderType,
18
+ HtmlDisplay,
19
+ JsonataRenderOptions,
20
+ RadioButtonRenderOptions,
21
+ RenderOptions,
22
+ TextDisplay,
23
+ TextfieldRenderOptions,
24
+ } from "./controlDefinition";
25
+ import {
26
+ DateValidator,
27
+ JsonataValidator,
28
+ LengthValidator,
29
+ ValidatorType,
30
+ } from "./schemaValidator";
31
+ import {
32
+ DataExpression,
33
+ DataMatchExpression,
34
+ EntityExpression,
35
+ ExpressionType,
36
+ JsonataExpression,
37
+ NotEmptyExpression,
38
+ } from "./entityExpression";
39
+
40
+ export function dataControl(
41
+ field: string,
42
+ title?: string | null,
43
+ options?: Partial<DataControlDefinition>,
44
+ ): DataControlDefinition {
45
+ return { type: ControlDefinitionType.Data, field, title, ...options };
46
+ }
47
+
48
+ export function validatorOptions<A extends { type: string }>(
49
+ type: ValidatorType,
50
+ ): (options: Omit<A, "type">) => A {
51
+ return (o) => ({ type, ...o }) as A;
52
+ }
53
+
54
+ export function adornmentOptions<A extends { type: string }>(
55
+ type: ControlAdornmentType,
56
+ ): (options: Omit<A, "type">) => A {
57
+ return (o) => ({ type, ...o }) as A;
58
+ }
59
+
60
+ export function renderOptionsFor<A extends RenderOptions>(
61
+ type: DataRenderType,
62
+ ): (options: Omit<A, "type">) => { renderOptions: A } {
63
+ return (o) => ({ renderOptions: { type, ...o } as A });
64
+ }
65
+
66
+ export const autocompleteOptions = renderOptionsFor<AutocompleteRenderOptions>(
67
+ DataRenderType.Autocomplete,
68
+ );
69
+
70
+ export const checkListOptions = renderOptionsFor<CheckListRenderOptions>(
71
+ DataRenderType.CheckList,
72
+ );
73
+
74
+ export const radioButtonOptions = renderOptionsFor<RadioButtonRenderOptions>(
75
+ DataRenderType.Radio,
76
+ );
77
+
78
+ export const lengthValidatorOptions = validatorOptions<LengthValidator>(
79
+ ValidatorType.Length,
80
+ );
81
+
82
+ export const jsonataValidatorOptions = validatorOptions<JsonataValidator>(
83
+ ValidatorType.Jsonata,
84
+ );
85
+
86
+ export const dateValidatorOptions = validatorOptions<DateValidator>(
87
+ ValidatorType.Date,
88
+ );
89
+
90
+ export const accordionOptions = adornmentOptions<AccordionAdornment>(
91
+ ControlAdornmentType.Accordion,
92
+ );
93
+
94
+ export const textfieldOptions = renderOptionsFor<TextfieldRenderOptions>(
95
+ DataRenderType.Textfield,
96
+ );
97
+
98
+ export const displayOnlyOptions = renderOptionsFor<DisplayOnlyRenderOptions>(
99
+ DataRenderType.DisplayOnly,
100
+ );
101
+
102
+ export const jsonataOptions = renderOptionsFor<JsonataRenderOptions>(
103
+ DataRenderType.Jsonata,
104
+ );
105
+
106
+ export function textDisplayControl(
107
+ text: string,
108
+ options?: Partial<DisplayControlDefinition>,
109
+ ): DisplayControlDefinition {
110
+ return {
111
+ type: ControlDefinitionType.Display,
112
+ displayData: { type: DisplayDataType.Text, text } as TextDisplay,
113
+ ...options,
114
+ };
115
+ }
116
+
117
+ export function htmlDisplayControl(
118
+ html: string,
119
+ options?: Partial<DisplayControlDefinition>,
120
+ ): DisplayControlDefinition {
121
+ return {
122
+ type: ControlDefinitionType.Display,
123
+ displayData: { type: DisplayDataType.Html, html } as HtmlDisplay,
124
+ ...options,
125
+ };
126
+ }
127
+
128
+ export function dynamicDefaultValue(expr: EntityExpression): DynamicProperty {
129
+ return { type: DynamicPropertyType.DefaultValue, expr };
130
+ }
131
+
132
+ export function dynamicReadonly(expr: EntityExpression): DynamicProperty {
133
+ return { type: DynamicPropertyType.Readonly, expr };
134
+ }
135
+
136
+ export function dynamicVisibility(expr: EntityExpression): DynamicProperty {
137
+ return { type: DynamicPropertyType.Visible, expr };
138
+ }
139
+
140
+ export function dynamicDisabled(expr: EntityExpression): DynamicProperty {
141
+ return { type: DynamicPropertyType.Disabled, expr };
142
+ }
143
+
144
+ export function dataExpr(field: string): DataExpression {
145
+ return { type: ExpressionType.Data, field };
146
+ }
147
+
148
+ /**
149
+ * @deprecated Use dataExpr
150
+ */
151
+ export const fieldExpr = dataExpr;
152
+
153
+ /**
154
+ * @deprecated Use dataMatchExpr
155
+ */
156
+ export const fieldEqExpr = dataMatchExpr;
157
+
158
+ export const uuidExpr = { type: ExpressionType.UUID };
159
+ export function dataMatchExpr(field: string, value: any): DataMatchExpression {
160
+ return { type: ExpressionType.DataMatch, field, value };
161
+ }
162
+
163
+ export function notEmptyExpr(
164
+ field: string,
165
+ empty?: boolean,
166
+ ): NotEmptyExpression {
167
+ return { type: ExpressionType.NotEmpty, field, empty };
168
+ }
169
+ export function jsonataExpr(expression: string): JsonataExpression {
170
+ return { type: ExpressionType.Jsonata, expression };
171
+ }
172
+
173
+ export function groupedControl(
174
+ children: ControlDefinition[],
175
+ title?: string,
176
+ options?: Partial<GroupedControlsDefinition>,
177
+ ): GroupedControlsDefinition {
178
+ return {
179
+ type: ControlDefinitionType.Group,
180
+ children,
181
+ title,
182
+ groupOptions: { type: "Standard", hideTitle: !title },
183
+ ...options,
184
+ };
185
+ }
186
+ export function compoundControl(
187
+ field: string,
188
+ title: string | undefined | null,
189
+ children: ControlDefinition[],
190
+ options?: Partial<DataControlDefinition>,
191
+ ): DataControlDefinition {
192
+ return {
193
+ type: ControlDefinitionType.Data,
194
+ field,
195
+ children,
196
+ title,
197
+ renderOptions: { type: "Standard" },
198
+ ...options,
199
+ };
200
+ }
201
+
202
+ export function actionControl(
203
+ actionText: string,
204
+ actionId: string,
205
+ options?: Partial<ActionControlDefinition>,
206
+ ): ActionControlDefinition {
207
+ return {
208
+ type: ControlDefinitionType.Action,
209
+ title: actionText,
210
+ actionId,
211
+ ...options,
212
+ };
213
+ }
214
+ export const emptyGroupDefinition: GroupedControlsDefinition = {
215
+ type: ControlDefinitionType.Group,
216
+ children: [],
217
+ groupOptions: { type: GroupRenderType.Standard, hideTitle: true },
218
+ };
@@ -22,6 +22,8 @@ export interface ControlDefinition {
22
22
  childRefId?: string | null;
23
23
  title?: string | null;
24
24
  hidden?: boolean | null;
25
+ disabled?: boolean | null;
26
+ readonly?: boolean | null;
25
27
  styleClass?: string | null;
26
28
  textClass?: string | null;
27
29
  layoutClass?: string | null;
@@ -56,6 +58,7 @@ export enum DynamicPropertyType {
56
58
  AllowedOptions = "AllowedOptions",
57
59
  Label = "Label",
58
60
  ActionData = "ActionData",
61
+ GridColumns = "GridColumns",
59
62
  }
60
63
 
61
64
  export interface ControlAdornment {
@@ -133,11 +136,10 @@ export interface DataControlDefinition extends ControlDefinition {
133
136
  required?: boolean | null;
134
137
  renderOptions?: RenderOptions | null;
135
138
  defaultValue?: any;
136
- readonly?: boolean | null;
137
- disabled?: boolean | null;
138
139
  validators?: SchemaValidator[] | null;
139
140
  hideTitle?: boolean | null;
140
141
  dontClearHidden?: boolean | null;
142
+ requiredErrorText?: string | null;
141
143
  }
142
144
 
143
145
  export interface RenderOptions {
@@ -165,6 +167,7 @@ export enum DataRenderType {
165
167
  Array = "Array",
166
168
  ArrayElement = "ArrayElement",
167
169
  ElementSelected = "ElementSelected",
170
+ ScrollList = "ScrollList",
168
171
  }
169
172
 
170
173
  export interface TextfieldRenderOptions extends RenderOptions {
@@ -256,6 +259,7 @@ export interface ArrayRenderOptions extends RenderOptions {
256
259
  noRemove?: boolean | null;
257
260
  noReorder?: boolean | null;
258
261
  editExternal?: boolean | null;
262
+ childOverrideClass?: string | null;
259
263
  }
260
264
 
261
265
  export interface ArrayElementRenderOptions extends RenderOptions {
@@ -310,6 +314,12 @@ export interface IconSelectionRenderOptions extends RenderOptions {
310
314
  type: DataRenderType.IconSelector;
311
315
  }
312
316
 
317
+ export interface ScrollListRenderOptions extends RenderOptions {
318
+ type: DataRenderType.ScrollList;
319
+ bottomActionId?: string;
320
+ refreshActionId?: string;
321
+ }
322
+
313
323
  export interface GroupedControlsDefinition extends ControlDefinition {
314
324
  type: ControlDefinitionType.Group;
315
325
  compoundField?: string | null;
@@ -344,11 +354,19 @@ export enum GroupRenderType {
344
354
  Wizard = "Wizard",
345
355
  Dialog = "Dialog",
346
356
  Contents = "Contents",
357
+ Accordion = "Accordion",
358
+ }
359
+
360
+ export interface AccordionRenderer extends GroupRenderOptions {
361
+ type: GroupRenderType.Accordion;
362
+ defaultExpanded?: boolean | null;
363
+ expandStateField?: string | null;
347
364
  }
348
365
 
349
366
  export interface DialogRenderOptions extends GroupRenderOptions {
350
367
  type: GroupRenderType.Dialog;
351
368
  title?: string | null;
369
+ portalHost?: string | null;
352
370
  }
353
371
 
354
372
  export interface StandardGroupRenderer extends GroupRenderOptions {
@@ -366,10 +384,11 @@ export interface GroupElementRenderer extends GroupRenderOptions {
366
384
  value: any;
367
385
  }
368
386
 
369
- export interface GridRenderer extends GroupRenderOptions {
387
+ export interface GridRendererOptions extends GroupRenderOptions {
370
388
  type: GroupRenderType.Grid;
371
389
  columns?: number | null;
372
390
  rowClass?: string | null;
391
+ cellClass?: string | null;
373
392
  }
374
393
 
375
394
  export interface TabsRenderOptions extends GroupRenderOptions {
@@ -422,6 +441,12 @@ export interface CustomDisplay extends DisplayData {
422
441
  customId: string;
423
442
  }
424
443
 
444
+ export enum ControlDisableType {
445
+ None = "None",
446
+ Self = "Self",
447
+ Global = "Global",
448
+ }
449
+
425
450
  export interface ActionControlDefinition extends ControlDefinition {
426
451
  type: ControlDefinitionType.Action;
427
452
  actionId: string;
@@ -429,6 +454,7 @@ export interface ActionControlDefinition extends ControlDefinition {
429
454
  icon?: IconReference | null;
430
455
  actionStyle?: ActionStyle | null;
431
456
  iconPlacement?: IconPlacement | null;
457
+ disableType?: ControlDisableType | null;
432
458
  }
433
459
 
434
460
  export enum ActionStyle {
@@ -471,7 +497,7 @@ export function visitControlDefinition<A>(
471
497
  }
472
498
  export function isGridRenderer(
473
499
  options: GroupRenderOptions,
474
- ): options is GridRenderer {
500
+ ): options is GridRendererOptions {
475
501
  return options.type === GroupRenderType.Grid;
476
502
  }
477
503
 
@@ -487,9 +513,13 @@ export function isDialogRenderer(
487
513
  return options.type === GroupRenderType.Dialog;
488
514
  }
489
515
 
490
- export function isInlineRenderer(
516
+ export function isAccordionRenderer(
491
517
  options: GroupRenderOptions,
492
- ): options is GridRenderer {
518
+ ): options is AccordionRenderer {
519
+ return options.type === GroupRenderType.Accordion;
520
+ }
521
+
522
+ export function isInlineRenderer(options: GroupRenderOptions): boolean {
493
523
  return options.type === GroupRenderType.Inline;
494
524
  }
495
525
 
@@ -652,7 +682,7 @@ export function fontAwesomeIcon(icon: string) {
652
682
  * @returns True if the control definition is readonly, false otherwise.
653
683
  */
654
684
  export function isControlReadonly(c: ControlDefinition): boolean {
655
- return isDataControl(c) && !!c.readonly;
685
+ return !!c.readonly;
656
686
  }
657
687
 
658
688
  /**
@@ -661,7 +691,7 @@ export function isControlReadonly(c: ControlDefinition): boolean {
661
691
  * @returns True if the control definition is disabled, false otherwise.
662
692
  */
663
693
  export function isControlDisabled(c: ControlDefinition): boolean {
664
- return isDataControl(c) && !!c.disabled;
694
+ return !!c.disabled;
665
695
  }
666
696
 
667
697
  /**
@@ -109,7 +109,7 @@ export class DefaultSchemaInterface implements SchemaInterface {
109
109
  ? new Date("1970-01-01T" + value).toLocaleTimeString()
110
110
  : undefined;
111
111
  case FieldType.Bool:
112
- return this.boolStrings[value ? 1 : 0];
112
+ return value != null ? this.boolStrings[value ? 1 : 0] : undefined;
113
113
  default:
114
114
  return value != null ? value.toString() : undefined;
115
115
  }
@@ -122,7 +122,8 @@ export class DefaultSchemaInterface implements SchemaInterface {
122
122
  }
123
123
 
124
124
  valueLength(field: SchemaField, value: any): number {
125
- return (value && value?.length) ?? 0;
125
+ const len = value?.length;
126
+ return typeof len === "number" ? len : 0;
126
127
  }
127
128
 
128
129
  compareValue(field: SchemaField, v1: unknown, v2: unknown): number {
@@ -135,7 +136,7 @@ export class DefaultSchemaInterface implements SchemaInterface {
135
136
  case FieldType.String:
136
137
  return (v1 as string).localeCompare(v2 as string);
137
138
  case FieldType.Bool:
138
- return (v1 as boolean) ? ((v2 as boolean) ? 0 : 1) : -1;
139
+ return v1 === v2 ? 0 : !v1 ? -1 : 1;
139
140
  case FieldType.Int:
140
141
  case FieldType.Double:
141
142
  return (v1 as number) - (v2 as number);
@@ -8,12 +8,13 @@ import {
8
8
  } from "./entityExpression";
9
9
  import {
10
10
  AsyncEffect,
11
+ ChangeListenerFunc,
11
12
  CleanupScope,
12
13
  collectChanges,
14
+ Control,
13
15
  createAsyncEffect,
14
16
  createSyncEffect,
15
17
  trackedValue,
16
- Value,
17
18
  } from "@astroapps/controls";
18
19
  import { schemaDataForFieldRef, SchemaDataNode } from "./schemaDataNode";
19
20
  import { SchemaInterface } from "./schemaInterface";
@@ -27,7 +28,7 @@ export interface ExpressionEvalContext {
27
28
  returnResult: (k: unknown) => void;
28
29
  dataNode: SchemaDataNode;
29
30
  schemaInterface: SchemaInterface;
30
- variables?: Value<Record<string, any> | undefined>;
31
+ variables?: (changes: ChangeListenerFunc<any>) => Record<string, any>;
31
32
  runAsync(effect: () => void): void;
32
33
  }
33
34
 
@@ -91,15 +92,17 @@ export const jsonataEval: ExpressionEval<JsonataExpression> = (
91
92
  });
92
93
 
93
94
  async function runJsonata(effect: AsyncEffect<any>, signal: AbortSignal) {
94
- const bindings = collectChanges(
95
- effect.collectUsage,
96
- () => variables?.value,
97
- );
95
+ const trackedVars = variables?.(effect.collectUsage);
98
96
  const evalResult = await parsedJsonata.fields.expr.value.evaluate(
99
97
  trackedValue(rootData, effect.collectUsage),
100
- bindings,
98
+ trackedVars,
101
99
  );
102
- // console.log(parsedJsonata.fields.fullExpr.value, evalResult, bindings);
100
+ // console.log(
101
+ // rootData,
102
+ // parsedJsonata.fields.fullExpr.current.value,
103
+ // evalResult,
104
+ // trackedVars,
105
+ // );
103
106
  collectChanges(effect.collectUsage, () => returnResult(evalResult));
104
107
  }
105
108
 
@@ -118,3 +121,30 @@ export const defaultEvaluators: Record<string, ExpressionEval<any>> = {
118
121
  [ExpressionType.Jsonata]: jsonataEval,
119
122
  [ExpressionType.UUID]: uuidEval,
120
123
  };
124
+
125
+ export function createEvalExpr(
126
+ evalExpression: (e: EntityExpression, ctx: ExpressionEvalContext) => void,
127
+ context: Omit<ExpressionEvalContext, "returnResult" | "scope">,
128
+ ) {
129
+ function evalExpr<A>(
130
+ scope: CleanupScope,
131
+ init: A,
132
+ nk: Control<A>,
133
+ e: EntityExpression | undefined,
134
+ coerce: (t: unknown) => any,
135
+ ): boolean {
136
+ nk.value = init;
137
+ if (e?.type) {
138
+ evalExpression(e, {
139
+ returnResult: (r) => {
140
+ nk.value = coerce(r);
141
+ },
142
+ scope,
143
+ ...context,
144
+ });
145
+ return true;
146
+ }
147
+ return false;
148
+ }
149
+ return evalExpr;
150
+ }