@astroapps/forms-core 1.0.1 → 1.1.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.
@@ -13,6 +13,7 @@ export interface ValidationEvalContext {
13
13
  data: SchemaDataNode;
14
14
  schemaInterface: SchemaInterface;
15
15
  formContext: Control<FormContextOptions>;
16
+ runAsync(af: () => void): void;
16
17
  }
17
18
  export type ValidatorEval<T extends SchemaValidator> = (validation: T, context: ValidationEvalContext) => void;
18
19
  export declare const jsonataValidator: ValidatorEval<JsonataValidator>;
@@ -20,4 +21,4 @@ export declare const lengthValidator: ValidatorEval<LengthValidator>;
20
21
  export declare const dateValidator: ValidatorEval<DateValidator>;
21
22
  export declare const defaultValidators: Record<string, ValidatorEval<any>>;
22
23
  export declare function createValidators(def: ControlDefinition, context: ValidationEvalContext): void;
23
- export declare function setupValidation(controlImpl: Control<FormContextOptions>, definition: ControlDefinition, dataNode: Control<SchemaDataNode | undefined>, schemaInterface: SchemaInterface, parent: SchemaDataNode, formNode: FormNode): void;
24
+ export declare function setupValidation(controlImpl: Control<FormContextOptions>, definition: ControlDefinition, dataNode: Control<SchemaDataNode | undefined>, schemaInterface: SchemaInterface, parent: SchemaDataNode, formNode: FormNode, runAsync: (af: () => void) => void): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astroapps/forms-core",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -28,6 +28,7 @@ export interface ExpressionEvalContext {
28
28
  dataNode: SchemaDataNode;
29
29
  schemaInterface: SchemaInterface;
30
30
  variables?: Value<Record<string, any> | undefined>;
31
+ runAsync(effect: () => void): void;
31
32
  }
32
33
 
33
34
  export type ExpressionEval<T extends EntityExpression> = (
@@ -72,7 +73,7 @@ const notEmptyEval: ExpressionEval<NotEmptyExpression> = (
72
73
 
73
74
  export const jsonataEval: ExpressionEval<JsonataExpression> = (
74
75
  expr,
75
- { scope, returnResult, dataNode, variables },
76
+ { scope, returnResult, dataNode, variables, runAsync },
76
77
  ) => {
77
78
  const path = getJsonPath(dataNode);
78
79
  const pathString = jsonPathString(path, (x) => `#$i[${x}]`);
@@ -102,7 +103,8 @@ export const jsonataEval: ExpressionEval<JsonataExpression> = (
102
103
  collectChanges(effect.collectUsage, () => returnResult(evalResult));
103
104
  }
104
105
 
105
- createAsyncEffect(runJsonata, scope);
106
+ const asyncEffect = createAsyncEffect(runJsonata, scope);
107
+ runAsync(() => asyncEffect.start());
106
108
  };
107
109
 
108
110
  export const uuidEval: ExpressionEval<EntityExpression> = (_, ctx) => {
package/src/formNode.ts CHANGED
@@ -247,3 +247,48 @@ export function visitFormDataInContext<A>(
247
247
  const dataNode = lookupDataNode(node.definition, parentContext);
248
248
  return visitFormData(node, dataNode ?? parentContext, cb, !dataNode);
249
249
  }
250
+
251
+ export interface FormDataNode {
252
+ parent?: FormDataNode;
253
+ formNode: FormNode;
254
+ parentData: SchemaDataNode;
255
+ childIndex?: number;
256
+ }
257
+
258
+ export function visitFormDataNode<A>(
259
+ node: FormDataNode,
260
+ visitFn: (node: FormDataNode, data?: SchemaDataNode) => A | undefined,
261
+ ): A | undefined {
262
+ const dataNode = lookupDataNode(node.formNode.definition, node.parentData);
263
+ const v = visitFn(node, dataNode);
264
+ if (v !== undefined) return v;
265
+
266
+ const parentData = dataNode ?? node.parentData;
267
+ if (parentData.schema.field.collection && parentData.elementIndex == null) {
268
+ const elemCount = parentData.control.elements.length;
269
+ for (let i = 0; i < elemCount; i++) {
270
+ const v = visitChildren(parentData.getChildElement(i));
271
+ if (v !== undefined) return v;
272
+ }
273
+ return undefined;
274
+ } else {
275
+ return visitChildren(parentData);
276
+ }
277
+
278
+ function visitChildren(parentData: SchemaDataNode) {
279
+ const children = node.formNode.getChildNodes();
280
+ for (let i = 0; i < children.length; i++) {
281
+ const child = children[i];
282
+ const res = visitFormDataNode(
283
+ {
284
+ formNode: child,
285
+ parent: node,
286
+ parentData,
287
+ childIndex: i,
288
+ },
289
+ visitFn,
290
+ );
291
+ if (res !== undefined) return res;
292
+ }
293
+ }
294
+ }
package/src/formState.ts CHANGED
@@ -24,15 +24,14 @@ import { SchemaInterface } from "./schemaInterface";
24
24
  import { FieldOption } from "./schemaField";
25
25
  import {
26
26
  CleanupScope,
27
- clearMetaValue,
28
27
  Control,
29
28
  createScopedEffect,
30
29
  createSyncEffect,
31
30
  ensureMetaValue,
32
31
  getControlPath,
33
32
  getCurrentFields,
33
+ getMetaValue,
34
34
  newControl,
35
- trackedValue,
36
35
  unsafeRestoreControl,
37
36
  updateComputedValue,
38
37
  } from "@astroapps/controls";
@@ -58,6 +57,7 @@ export interface ControlState {
58
57
  disabled: boolean;
59
58
  clearHidden: boolean;
60
59
  variables: Record<string, any>;
60
+ meta: Control<Record<string, any>>;
61
61
  }
62
62
 
63
63
  export interface FormContextOptions {
@@ -82,15 +82,30 @@ export interface FormState {
82
82
  parent: SchemaDataNode,
83
83
  formNode: FormNode,
84
84
  context: FormContextOptions,
85
+ runAsync: (af: () => void) => void,
85
86
  ): ControlState;
86
87
 
87
88
  cleanup(): void;
88
89
 
89
90
  evalExpression(expr: EntityExpression, context: ExpressionEvalContext): void;
91
+
92
+ getExistingControlState(
93
+ parent: SchemaDataNode,
94
+ formNode: FormNode,
95
+ stateKey?: string,
96
+ ): ControlState | undefined;
90
97
  }
91
98
 
92
99
  const formStates: FormState[] = [];
93
100
 
101
+ export function getControlStateId(
102
+ parent: SchemaDataNode,
103
+ formNode: FormNode,
104
+ stateKey?: string,
105
+ ): string {
106
+ return parent.id + "$" + formNode.id + (stateKey ?? "");
107
+ }
108
+
94
109
  export function createFormState(
95
110
  schemaInterface: SchemaInterface,
96
111
  evaluators: Record<string, ExpressionEval<any>> = defaultEvaluators,
@@ -112,12 +127,25 @@ export function createFormState(
112
127
  // console.log("Cleanup form state");
113
128
  controlStates.cleanup();
114
129
  },
130
+ getExistingControlState(
131
+ parent: SchemaDataNode,
132
+ formNode: FormNode,
133
+ stateKey?: string,
134
+ ): ControlState | undefined {
135
+ const stateId = getControlStateId(parent, formNode, stateKey);
136
+ const control = getCurrentFields(controlStates)[stateId];
137
+ if (control) {
138
+ return getMetaValue<Control<ControlState>>(control, "impl")?.value;
139
+ }
140
+ return undefined;
141
+ },
115
142
  getControlState(
116
143
  parent: SchemaDataNode,
117
144
  formNode: FormNode,
118
145
  context: FormContextOptions,
146
+ runAsync: (af: () => void) => void,
119
147
  ): ControlState {
120
- const stateId = parent.id + "$" + formNode.id + (context.stateKey ?? "");
148
+ const stateId = getControlStateId(parent, formNode, context.stateKey);
121
149
  const controlImpl = controlStates.fields[stateId];
122
150
  controlImpl.value = context;
123
151
  function evalExpr<A>(
@@ -137,6 +165,7 @@ export function createFormState(
137
165
  dataNode: parent,
138
166
  variables: controlImpl.fields.variables,
139
167
  schemaInterface,
168
+ runAsync,
140
169
  });
141
170
  return true;
142
171
  }
@@ -278,6 +307,7 @@ export function createFormState(
278
307
  hidden: false,
279
308
  variables: controlImpl.fields.variables.current.value ?? {},
280
309
  stateId,
310
+ meta: newControl({}),
281
311
  });
282
312
 
283
313
  const {
@@ -365,6 +395,7 @@ export function createFormState(
365
395
  schemaInterface,
366
396
  parent,
367
397
  formNode,
398
+ runAsync,
368
399
  );
369
400
 
370
401
  createSyncEffect(() => {
package/src/validators.ts CHANGED
@@ -32,6 +32,7 @@ export interface ValidationEvalContext {
32
32
  data: SchemaDataNode;
33
33
  schemaInterface: SchemaInterface;
34
34
  formContext: Control<FormContextOptions>;
35
+ runAsync(af: () => void): void;
35
36
  }
36
37
 
37
38
  export type ValidatorEval<T extends SchemaValidator> = (
@@ -51,11 +52,12 @@ export const jsonataValidator: ValidatorEval<JsonataValidator> = (
51
52
  dataNode: context.parentData,
52
53
  returnResult: (v) => {
53
54
  trackControlChange(context.data.control, ControlChange.Validate);
54
- console.log("Setting jsonata error", v);
55
+ // console.log("Setting jsonata error", v);
55
56
  context.data.control.setError("jsonata", v?.toString());
56
57
  },
57
58
  schemaInterface: context.schemaInterface,
58
59
  variables: context.formContext.fields.variables,
60
+ runAsync: context.runAsync,
59
61
  },
60
62
  );
61
63
  };
@@ -169,6 +171,7 @@ export function setupValidation(
169
171
  schemaInterface: SchemaInterface,
170
172
  parent: SchemaDataNode,
171
173
  formNode: FormNode,
174
+ runAsync: (af: () => void) => void,
172
175
  ) {
173
176
  const validationEnabled = createScopedComputed(
174
177
  controlImpl,
@@ -193,6 +196,7 @@ export function setupValidation(
193
196
  validatorsScope.addCleanup(cleanup);
194
197
  },
195
198
  formContext: controlImpl,
199
+ runAsync,
196
200
  });
197
201
 
198
202
  createEffect(