@react-typed-forms/schemas 7.1.0 → 7.3.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/lib/internal.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import { Control } from "@react-typed-forms/core";
1
+ import { ChangeListenerFunc, Control } from "@react-typed-forms/core";
2
2
  export declare function useCalculatedControl<V>(calculate: () => V): Control<V>;
3
3
  export declare function cc(n: string | null | undefined): string | undefined;
4
+ export declare function trackedStructure<A>(c: Control<A>, tracker: ChangeListenerFunc<any>): A;
@@ -41,7 +41,7 @@ export declare function stringOptionsField(displayName: string, ...options: Fiel
41
41
  displayName: string;
42
42
  options: FieldOption[];
43
43
  };
44
- export declare function withScalarOptions<S extends SchemaField>(options: Partial<SchemaField>, v: (name: string) => S): (name: string) => S;
44
+ export declare function withScalarOptions<S extends SchemaField, S2 extends Partial<SchemaField>>(options: S2, v: (name: string) => S): (name: string) => S & S2;
45
45
  export declare function makeScalarField<S extends Partial<SchemaField>>(options: S): (name: string) => SchemaField & S;
46
46
  export declare function makeCompoundField<S extends Partial<CompoundField>>(options: S): (name: string) => CompoundField & {
47
47
  type: FieldType.Compound;
package/lib/util.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { CompoundField, ControlDefinition, DataControlDefinition, DisplayOnlyRenderOptions, FieldOption, GroupedControlsDefinition, SchemaField, SchemaInterface } from "./types";
2
2
  import { MutableRefObject } from "react";
3
3
  import { Control } from "@react-typed-forms/core";
4
- export interface ControlDataContext {
5
- groupControl: Control<any>;
4
+ import { DataContext, JsonPath } from "./controlRender";
5
+ export interface ControlDataContext extends DataContext {
6
6
  fields: SchemaField[];
7
7
  schemaInterface: SchemaInterface;
8
8
  }
@@ -33,7 +33,9 @@ export declare function getDisplayOnlyOptions(d: ControlDefinition): DisplayOnly
33
33
  export declare function getTypeField(context: ControlDataContext): Control<string> | undefined;
34
34
  export declare function visitControlDataArray<A>(controls: ControlDefinition[] | undefined | null, context: ControlDataContext, cb: (definition: DataControlDefinition, field: SchemaField, control: Control<any>, element: boolean) => A | undefined): A | undefined;
35
35
  export declare function visitControlData<A>(definition: ControlDefinition, ctx: ControlDataContext, cb: (definition: DataControlDefinition, field: SchemaField, control: Control<any>, element: boolean) => A | undefined): A | undefined;
36
+ export declare function lookupChildControl(data: DataContext, child: JsonPath): Control<any> | undefined;
36
37
  export declare function cleanDataForSchema(v: {
37
38
  [k: string]: any;
38
39
  } | undefined, fields: SchemaField[]): any;
39
40
  export declare function getAllReferencedClasses(c: ControlDefinition): string[];
41
+ export declare function jsonPathString(jsonPath: JsonPath[]): string;
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
1
  {
2
- "name": "@react-typed-forms/schemas",
3
- "version": "7.1.0",
4
- "description": "",
5
- "main": "lib/index.js",
6
- "types": "lib/index.d.ts",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/doolse/react-typed-forms.git"
10
- },
11
- "author": "Jolse Maginnis",
12
- "license": "ISC",
13
- "bugs": {
14
- "url": "https://github.com/doolse/react-typed-forms/issues"
15
- },
16
- "homepage": "https://github.com/doolse/react-typed-forms#readme",
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "keywords": [
21
- "react",
22
- "typescript",
23
- "forms",
24
- "material-ui"
25
- ],
26
- "dependencies": {
27
- "@react-typed-forms/core": "^3.0.0",
28
- "clsx": "^1 || ^2",
29
- "jsonata": "^2.0.4",
30
- "react": "^18.2.0"
31
- },
32
- "devDependencies": {
33
- "@react-typed-forms/transform": "^0.2.0",
34
- "@types/react": "^18.2.28",
35
- "markdown-magic": "^2.6.1",
36
- "microbundle": "^0.15.1",
37
- "nswag": "^13.18.2",
38
- "prettier": "^3.0.3",
39
- "rimraf": "^3.0.2",
40
- "typescript": "^5.2.2"
41
- },
42
- "gitHead": "698e16cd3ab31b7dd0528fc76536f4d3205ce8c6",
43
- "scripts": {
44
- "build": "rimraf ./lib/ && microbundle -f cjs --no-compress --jsx React.createElement --jsxFragment React.Fragment",
45
- "watch": "microbundle -w -f cjs --no-compress --jsx React.createElement --jsxFragment React.Fragment",
46
- "update-readme": "md-magic --path README.md",
47
- "gencode": "nswag swagger2tsclient /input:http://localhost:5216/swagger/v1/swagger.json /runtime:Net60 /output:src/types.ts /GenerateClientClasses:false /MarkOptionalProperties:false /Template:Fetch /TypeStyle:Interface /DateTimeType:string"
48
- }
49
- }
2
+ "name": "@react-typed-forms/schemas",
3
+ "version": "7.3.0",
4
+ "description": "",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/doolse/react-typed-forms.git"
10
+ },
11
+ "author": "Jolse Maginnis",
12
+ "license": "ISC",
13
+ "bugs": {
14
+ "url": "https://github.com/doolse/react-typed-forms/issues"
15
+ },
16
+ "homepage": "https://github.com/doolse/react-typed-forms#readme",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "keywords": [
21
+ "react",
22
+ "typescript",
23
+ "forms",
24
+ "material-ui"
25
+ ],
26
+ "dependencies": {
27
+ "@react-typed-forms/core": "^3.1.0",
28
+ "clsx": "^1 || ^2",
29
+ "jsonata": "^2.0.4",
30
+ "react": "^18.2.0"
31
+ },
32
+ "devDependencies": {
33
+ "@react-typed-forms/transform": "^0.2.0",
34
+ "@types/react": "^18.2.28",
35
+ "markdown-magic": "^2.6.1",
36
+ "microbundle": "^0.15.1",
37
+ "nswag": "^13.18.2",
38
+ "prettier": "^3.0.3",
39
+ "rimraf": "^3.0.2",
40
+ "typescript": "^5.2.2"
41
+ },
42
+ "gitHead": "698e16cd3ab31b7dd0528fc76536f4d3205ce8c6",
43
+ "scripts": {
44
+ "build": "rimraf ./lib/ && microbundle -f cjs --no-compress --jsx React.createElement --jsxFragment React.Fragment",
45
+ "watch": "microbundle -w -f cjs --no-compress --jsx React.createElement --jsxFragment React.Fragment",
46
+ "update-readme": "md-magic --path README.md",
47
+ "gencode": "nswag swagger2tsclient /input:http://localhost:5216/swagger/v1/swagger.json /runtime:Net60 /output:src/types.ts /GenerateClientClasses:false /MarkOptionalProperties:false /Template:Fetch /TypeStyle:Interface /DateTimeType:string"
48
+ }
49
+ }
@@ -10,8 +10,10 @@ import React, {
10
10
  import {
11
11
  addElement,
12
12
  Control,
13
+ ControlChange,
13
14
  newControl,
14
15
  removeElement,
16
+ trackControlChange,
15
17
  useComponentTracking,
16
18
  useControl,
17
19
  useControlEffect,
@@ -166,6 +168,7 @@ export interface GroupRendererProps {
166
168
  }
167
169
 
168
170
  export interface DataRendererProps {
171
+ definition: DataControlDefinition;
169
172
  renderOptions: RenderOptions;
170
173
  field: SchemaField;
171
174
  id: string;
@@ -192,6 +195,7 @@ export interface ActionRendererProps {
192
195
 
193
196
  export interface ControlRenderProps {
194
197
  control: Control<any>;
198
+ parentPath?: JsonPath[];
195
199
  }
196
200
 
197
201
  export interface FormContextOptions {
@@ -210,12 +214,18 @@ export interface DataControlProps {
210
214
  childCount: number;
211
215
  renderChild: ChildRenderer;
212
216
  allowedOptions?: Control<any[] | undefined>;
213
- elementRenderer?: (elemProps: Control<any>) => ReactNode;
217
+ elementRenderer?: (elemIndex: number) => ReactNode;
214
218
  }
215
219
  export type CreateDataProps = (
216
220
  controlProps: DataControlProps,
217
221
  ) => DataRendererProps;
218
222
 
223
+ export type JsonPath = string | number;
224
+
225
+ export interface DataContext {
226
+ data: Control<any>;
227
+ path: JsonPath[];
228
+ }
219
229
  export interface ControlRenderOptions extends FormContextOptions {
220
230
  useDataHook?: (c: ControlDefinition) => CreateDataProps;
221
231
  useEvalExpressionHook?: UseEvalExpressionHook;
@@ -257,21 +267,22 @@ export function useControlRenderer(
257
267
  const r = useUpdatedRef({ options, definition, fields, schemaField });
258
268
 
259
269
  const Component = useCallback(
260
- ({ control: parentControl }: ControlRenderProps) => {
270
+ ({ control: rootControl, parentPath = [] }: ControlRenderProps) => {
261
271
  const stopTracking = useComponentTracking();
262
272
  try {
263
273
  const { definition: c, options, fields, schemaField } = r.current;
264
- const dataContext: ControlDataContext = {
265
- groupControl: parentControl,
274
+ const parentDataContext: ControlDataContext = {
266
275
  fields,
267
276
  schemaInterface,
277
+ data: rootControl,
278
+ path: parentPath,
268
279
  };
269
- const readonlyControl = useIsReadonly(dataContext);
270
- const disabledControl = useIsDisabled(dataContext);
271
- const visibleControl = useIsVisible(dataContext);
272
- const displayControl = useDynamicDisplay(dataContext);
273
- const customStyle = useCustomStyle(dataContext).value;
274
- const layoutStyle = useLayoutStyle(dataContext).value;
280
+ const readonlyControl = useIsReadonly(parentDataContext);
281
+ const disabledControl = useIsDisabled(parentDataContext);
282
+ const visibleControl = useIsVisible(parentDataContext);
283
+ const displayControl = useDynamicDisplay(parentDataContext);
284
+ const customStyle = useCustomStyle(parentDataContext).value;
285
+ const layoutStyle = useLayoutStyle(parentDataContext).value;
275
286
  const visible = visibleControl.current.value;
276
287
  const visibility = useControl<Visibility | undefined>(() =>
277
288
  visible != null
@@ -292,31 +303,35 @@ export function useControlRenderer(
292
303
  },
293
304
  );
294
305
 
295
- const allowedOptions = useAllowedOptions(dataContext);
296
- const defaultValueControl = useDefaultValue(dataContext);
297
- const [control, childContext] = getControlData(
306
+ const allowedOptions = useAllowedOptions(parentDataContext);
307
+ const defaultValueControl = useDefaultValue(parentDataContext);
308
+ const [parentControl, control, controlDataContext] = getControlData(
298
309
  schemaField,
299
- dataContext,
310
+ parentDataContext,
300
311
  );
301
312
  useControlEffect(
302
313
  () => [
303
314
  visibility.value,
304
315
  defaultValueControl.value,
305
316
  control,
306
- parentControl.isNull,
307
317
  isDataControlDefinition(definition) && definition.dontClearHidden,
318
+ parentControl?.isNull,
308
319
  ],
309
- ([vc, dv, cd, pn, dontClear]) => {
310
- if (pn) {
311
- parentControl.value = {};
312
- }
320
+ ([vc, dv, cd, dontClear, parentNull]) => {
313
321
  if (vc && cd && vc.visible === vc.showing) {
314
322
  if (!vc.visible) {
315
- if (options.clearHidden && !dontClear) cd.value = undefined;
323
+ if (options.clearHidden && !dontClear) {
324
+ console.log("Clearing ", schemaField?.field);
325
+ cd.value = undefined;
326
+ }
316
327
  } else if (cd.value == null) {
328
+ console.log("Defaulting ", schemaField?.field, dv);
317
329
  cd.value = dv;
318
330
  }
319
331
  }
332
+ if (parentNull && parentControl?.isNull) {
333
+ parentControl.value = {};
334
+ }
320
335
  },
321
336
  true,
322
337
  );
@@ -325,19 +340,25 @@ export function useControlRenderer(
325
340
  readonly: options.readonly || readonlyControl.value,
326
341
  disabled: options.disabled || disabledControl.value,
327
342
  })).value;
328
- useValidation(control!, !!myOptions.hidden, dataContext);
343
+ useValidation(
344
+ control ?? newControl(null),
345
+ !!myOptions.hidden,
346
+ parentDataContext,
347
+ );
329
348
  const childRenderers: FC<ControlRenderProps>[] =
330
349
  c.children?.map((cd) =>
331
- useControlRenderer(cd, childContext.fields, renderer, {
350
+ useControlRenderer(cd, controlDataContext.fields, renderer, {
332
351
  ...options,
333
352
  ...myOptions,
334
353
  }),
335
354
  ) ?? [];
355
+
336
356
  useEffect(() => {
337
357
  if (control && typeof myOptions.disabled === "boolean")
338
358
  control.disabled = myOptions.disabled;
339
359
  }, [control, myOptions.disabled]);
340
- if (parentControl.isNull) return <></>;
360
+ if (parentControl?.isNull) return <></>;
361
+
341
362
  const adornments =
342
363
  definition.adornments?.map((x) =>
343
364
  renderer.renderAdornment({ adornment: x }),
@@ -352,7 +373,7 @@ export function useControlRenderer(
352
373
  },
353
374
  createDataProps: dataProps,
354
375
  formOptions: myOptions,
355
- dataContext,
376
+ dataContext: controlDataContext,
356
377
  control: displayControl ?? control,
357
378
  schemaField,
358
379
  displayControl,
@@ -396,24 +417,31 @@ export function lookupSchemaField(
396
417
  const fieldName = isGroupControlsDefinition(c)
397
418
  ? c.compoundField
398
419
  : isDataControlDefinition(c)
399
- ? c.field
400
- : undefined;
420
+ ? c.field
421
+ : undefined;
401
422
  return fieldName ? findField(fields, fieldName) : undefined;
402
423
  }
403
424
  export function getControlData(
404
425
  schemaField: SchemaField | undefined,
405
426
  parentContext: ControlDataContext,
406
- ): [Control<any> | undefined, ControlDataContext] {
407
- const childControl: Control<any> | undefined = schemaField
408
- ? parentContext.groupControl.fields?.[schemaField.field] ?? newControl({})
409
- : undefined;
427
+ ): [Control<any> | undefined, Control<any> | undefined, ControlDataContext] {
428
+ const { data, path } = parentContext;
429
+ const parentControl = data.lookupControl(path);
430
+ const childPath = schemaField ? [...path, schemaField.field] : path;
431
+ const childControl =
432
+ schemaField && parentControl
433
+ ? parentControl.fields[schemaField.field]
434
+ : undefined;
410
435
  return [
436
+ parentControl,
411
437
  childControl,
412
- schemaField && isCompoundField(schemaField)
438
+ schemaField
413
439
  ? {
414
- groupControl: childControl!,
415
- fields: schemaField.children,
416
- schemaInterface: parentContext.schemaInterface,
440
+ ...parentContext,
441
+ path: childPath,
442
+ fields: isCompoundField(schemaField)
443
+ ? schemaField.children
444
+ : parentContext.fields,
417
445
  }
418
446
  : parentContext,
419
447
  ];
@@ -423,13 +451,14 @@ function groupProps(
423
451
  renderOptions: GroupRenderOptions = { type: "Standard" },
424
452
  childCount: number,
425
453
  renderChild: ChildRenderer,
426
- control: Control<any>,
454
+ data: DataContext,
427
455
  className: string | null | undefined,
428
456
  style: React.CSSProperties | undefined,
429
457
  ): GroupRendererProps {
430
458
  return {
431
459
  childCount,
432
- renderChild: (i) => renderChild(i, i, { control }),
460
+ renderChild: (i) =>
461
+ renderChild(i, i, { control: data.data, parentPath: data.path }),
433
462
  renderOptions,
434
463
  className: cc(className),
435
464
  style,
@@ -452,6 +481,7 @@ export function defaultDataProps({
452
481
  (field.options?.length ?? 0) === 0 ? null : field.options;
453
482
  const allowed = allowedOptions?.value ?? [];
454
483
  return {
484
+ definition,
455
485
  control,
456
486
  field,
457
487
  id: "c" + control.uniqueId,
@@ -486,7 +516,7 @@ export function defaultArrayProps(
486
516
  required: boolean,
487
517
  style: CSSProperties | undefined,
488
518
  className: string | undefined,
489
- renderElement: (elemProps: Control<any>) => ReactNode,
519
+ renderElement: (elemIndex: number) => ReactNode,
490
520
  ): ArrayRendererProps {
491
521
  const noun = field.displayName ?? field.field;
492
522
  const elems = arrayControl.elements ?? [];
@@ -505,7 +535,7 @@ export function defaultArrayProps(
505
535
  actionText: "Remove",
506
536
  onClick: () => removeElement(arrayControl, i),
507
537
  }),
508
- renderElement: (i) => renderElement(elems[i]),
538
+ renderElement: (i) => renderElement(i),
509
539
  className: cc(className),
510
540
  style,
511
541
  };
@@ -563,7 +593,7 @@ export function renderControlLayout({
563
593
  c.groupOptions,
564
594
  childCount,
565
595
  childRenderer,
566
- dataContext.groupControl,
596
+ dataContext,
567
597
  c.styleClass,
568
598
  style,
569
599
  ),
@@ -598,22 +628,26 @@ export function renderControlLayout({
598
628
  }
599
629
  return {};
600
630
 
601
- function renderData(c: DataControlDefinition, elementControl?: Control<any>) {
631
+ function renderData(c: DataControlDefinition, elemIndex?: number) {
602
632
  if (!schemaField) return { children: "No schema field for: " + c.field };
633
+ if (!childControl) return { children: "No control for: " + c.field };
603
634
  const props = dataProps({
604
635
  definition: c,
605
636
  field: schemaField,
606
- dataContext,
607
- control: elementControl ?? childControl!,
637
+ dataContext:
638
+ elemIndex != null
639
+ ? { ...dataContext, path: [...dataContext.path, elemIndex] }
640
+ : dataContext,
641
+ control:
642
+ elemIndex != null ? childControl!.elements[elemIndex] : childControl,
608
643
  options: dataOptions,
609
644
  style,
610
645
  childCount,
611
646
  allowedOptions,
612
647
  renderChild: childRenderer,
613
648
  elementRenderer:
614
- elementControl == null && schemaField.collection
615
- ? (element) =>
616
- renderLayoutParts(renderData(c, element), renderer).children
649
+ elemIndex == null && schemaField.collection
650
+ ? (ei) => renderLayoutParts(renderData(c, ei), renderer).children
617
651
  : undefined,
618
652
  });
619
653
 
@@ -632,32 +666,6 @@ export function renderControlLayout({
632
666
  errorControl: childControl,
633
667
  };
634
668
  }
635
-
636
- function compoundRenderer(i: number, control: Control<any>): ReactNode {
637
- const { className, style, children } = renderer.renderLayout({
638
- processLayout: renderer.renderGroup({
639
- renderOptions: { type: "Standard", hideTitle: true },
640
- childCount,
641
- renderChild: (ci) => childRenderer(ci, ci, { control }),
642
- }),
643
- });
644
- return (
645
- <div key={control.uniqueId} style={style} className={cc(className)}>
646
- {children}
647
- </div>
648
- );
649
- }
650
- function scalarRenderer(
651
- dataProps: DataRendererProps,
652
- ): (i: number, control: Control<any>) => ReactNode {
653
- return (i, control) => {
654
- return (
655
- <Fragment key={control.uniqueId}>
656
- {renderer.renderData({ ...dataProps, control })({}).children}
657
- </Fragment>
658
- );
659
- };
660
- }
661
669
  }
662
670
 
663
671
  export function appendMarkup(
@@ -737,3 +745,23 @@ export function controlTitle(
737
745
  ) {
738
746
  return title ? title : fieldDisplayName(field);
739
747
  }
748
+
749
+ function lookupControl(
750
+ base: Control<any> | undefined,
751
+ path: (string | number)[],
752
+ ): Control<any> | undefined {
753
+ let index = 0;
754
+ while (index < path.length && base) {
755
+ const childId = path[index];
756
+ const c = base.current;
757
+ if (typeof childId === "string") {
758
+ const next = c.fields?.[childId];
759
+ if (!next) trackControlChange(base, ControlChange.Structure);
760
+ base = next;
761
+ } else {
762
+ base = c.elements?.[childId];
763
+ }
764
+ index++;
765
+ }
766
+ return base;
767
+ }
package/src/hooks.tsx CHANGED
@@ -10,12 +10,15 @@ import {
10
10
  SchemaField,
11
11
  SchemaInterface,
12
12
  } from "./types";
13
- import { useCallback, useMemo } from "react";
13
+ import { useCallback, useEffect, useMemo, useRef } from "react";
14
14
  import {
15
+ addAfterChangesCallback,
16
+ collectChanges,
15
17
  Control,
18
+ makeChangeTracker,
16
19
  useComputed,
17
20
  useControl,
18
- useControlEffect,
21
+ useRefState,
19
22
  } from "@react-typed-forms/core";
20
23
 
21
24
  import {
@@ -25,10 +28,13 @@ import {
25
28
  getDisplayOnlyOptions,
26
29
  getTypeField,
27
30
  isControlReadonly,
31
+ jsonPathString,
32
+ lookupChildControl,
28
33
  useUpdatedRef,
29
34
  } from "./util";
30
35
  import jsonata from "jsonata";
31
- import { useCalculatedControl } from "./internal";
36
+ import { trackedStructure, useCalculatedControl } from "./internal";
37
+ import { DataContext } from "./controlRender";
32
38
 
33
39
  export type UseEvalExpressionHook = (
34
40
  expr: EntityExpression | undefined,
@@ -106,20 +112,20 @@ export function useEvalStyleHook(
106
112
  }
107
113
 
108
114
  export function useEvalAllowedOptionsHook(
109
- useEvalExpressionHook: UseEvalExpressionHook,
110
- definition: ControlDefinition,
115
+ useEvalExpressionHook: UseEvalExpressionHook,
116
+ definition: ControlDefinition,
111
117
  ): EvalExpressionHook<any[]> {
112
118
  const dynamicAllowed = useEvalDynamicHook(
113
- definition,
114
- DynamicPropertyType.AllowedOptions,
115
- useEvalExpressionHook,
119
+ definition,
120
+ DynamicPropertyType.AllowedOptions,
121
+ useEvalExpressionHook,
116
122
  );
117
123
  return useCallback(
118
- (ctx) => {
119
- if (dynamicAllowed) return dynamicAllowed(ctx);
120
- return useControl([]);
121
- },
122
- [dynamicAllowed],
124
+ (ctx) => {
125
+ if (dynamicAllowed) return dynamicAllowed(ctx);
126
+ return useControl([]);
127
+ },
128
+ [dynamicAllowed],
123
129
  );
124
130
  }
125
131
 
@@ -192,20 +198,24 @@ export type EvalExpressionHook<A = any> = (
192
198
  function useDataExpression(
193
199
  fvExpr: DataExpression,
194
200
  fields: SchemaField[],
195
- data: Control<any>,
201
+ data: DataContext,
196
202
  ) {
197
203
  const refField = findField(fields, fvExpr.field);
198
- const otherField = refField ? data.fields[refField.field] : undefined;
204
+ const otherField = refField
205
+ ? lookupChildControl(data, refField.field)
206
+ : undefined;
199
207
  return useCalculatedControl(() => otherField?.value);
200
208
  }
201
209
 
202
210
  function useDataMatchExpression(
203
211
  fvExpr: DataMatchExpression,
204
212
  fields: SchemaField[],
205
- data: Control<any>,
213
+ data: DataContext,
206
214
  ) {
207
215
  const refField = findField(fields, fvExpr.field);
208
- const otherField = refField ? data.fields[refField.field] : undefined;
216
+ const otherField = refField
217
+ ? lookupChildControl(data, refField.field)
218
+ : undefined;
209
219
  return useComputed(() => {
210
220
  const fv = otherField?.value;
211
221
  return Array.isArray(fv) ? fv.includes(fvExpr.value) : fv === fvExpr.value;
@@ -220,19 +230,15 @@ export function defaultEvalHooks(
220
230
  case ExpressionType.Jsonata:
221
231
  return useJsonataExpression(
222
232
  (expr as JsonataExpression).expression,
223
- context.groupControl,
233
+ context,
224
234
  );
225
235
  case ExpressionType.Data:
226
- return useDataExpression(
227
- expr as DataExpression,
228
- context.fields,
229
- context.groupControl,
230
- );
236
+ return useDataExpression(expr as DataExpression, context.fields, context);
231
237
  case ExpressionType.DataMatch:
232
238
  return useDataMatchExpression(
233
239
  expr as DataMatchExpression,
234
240
  context.fields,
235
- context.groupControl,
241
+ context,
236
242
  );
237
243
  default:
238
244
  return useControl(undefined);
@@ -290,30 +296,57 @@ export function hideDisplayOnly(
290
296
  !displayOptions.emptyText &&
291
297
  schemaInterface.isEmptyValue(
292
298
  field,
293
- context.groupControl.fields[field.field].value,
299
+ lookupChildControl(context, field.field)?.value,
294
300
  )
295
301
  );
296
302
  }
297
303
 
298
304
  export function useJsonataExpression(
299
305
  jExpr: string,
300
- data: Control<any>,
306
+ dataContext: DataContext,
307
+ bindings?: () => Record<string, any>,
301
308
  ): Control<any> {
309
+ const pathString = jsonPathString(dataContext.path);
302
310
  const compiledExpr = useMemo(() => {
303
311
  try {
304
- return jsonata(jExpr);
312
+ return jsonata(pathString ? pathString + ".(" + jExpr + ")" : jExpr);
305
313
  } catch (e) {
306
314
  console.error(e);
307
- return jsonata("");
315
+ return jsonata("null");
308
316
  }
309
- }, [jExpr]);
317
+ }, [jExpr, pathString]);
310
318
  const control = useControl();
311
- useControlEffect(
312
- () => data.value,
313
- async (v) => {
314
- control.value = await compiledExpr.evaluate(v);
315
- },
316
- true,
319
+ const listenerRef = useRef<() => void>();
320
+ const [ref] = useRefState(() =>
321
+ makeChangeTracker(() => {
322
+ const l = listenerRef.current;
323
+ if (l) {
324
+ listenerRef.current = undefined;
325
+ addAfterChangesCallback(() => {
326
+ l();
327
+ listenerRef.current = l;
328
+ });
329
+ }
330
+ }),
317
331
  );
332
+ useEffect(() => {
333
+ listenerRef.current = apply;
334
+ apply();
335
+ async function apply() {
336
+ const [collect, updateSubscriptions] = ref.current;
337
+ try {
338
+ const bindingData = bindings
339
+ ? collectChanges(collect, bindings)
340
+ : undefined;
341
+ control.value = await compiledExpr.evaluate(
342
+ trackedStructure(dataContext.data, collect),
343
+ bindingData,
344
+ );
345
+ } finally {
346
+ updateSubscriptions();
347
+ }
348
+ }
349
+ return () => ref.current[1](true);
350
+ }, [compiledExpr]);
318
351
  return control;
319
352
  }