@react-typed-forms/schemas 7.2.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/.rush/temp/operation/build/state.json +1 -1
- package/.rush/temp/shrinkwrap-deps.json +2 -31
- package/lib/controlRender.d.ts +10 -4
- package/lib/hooks.d.ts +2 -1
- package/lib/index.js +174 -72
- package/lib/index.js.map +1 -1
- package/lib/internal.d.ts +2 -1
- package/lib/util.d.ts +4 -3
- package/package.json +2 -2
- package/src/controlRender.tsx +98 -73
- package/src/hooks.tsx +57 -26
- package/src/internal.ts +38 -1
- package/src/renderers.tsx +7 -3
- package/src/util.ts +31 -7
- package/src/validators.ts +1 -5
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;
|
package/lib/util.d.ts
CHANGED
|
@@ -1,9 +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
|
-
|
|
5
|
-
|
|
6
|
-
root: Control<any>;
|
|
4
|
+
import { DataContext, JsonPath } from "./controlRender";
|
|
5
|
+
export interface ControlDataContext extends DataContext {
|
|
7
6
|
fields: SchemaField[];
|
|
8
7
|
schemaInterface: SchemaInterface;
|
|
9
8
|
}
|
|
@@ -34,7 +33,9 @@ export declare function getDisplayOnlyOptions(d: ControlDefinition): DisplayOnly
|
|
|
34
33
|
export declare function getTypeField(context: ControlDataContext): Control<string> | undefined;
|
|
35
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;
|
|
36
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;
|
|
37
37
|
export declare function cleanDataForSchema(v: {
|
|
38
38
|
[k: string]: any;
|
|
39
39
|
} | undefined, fields: SchemaField[]): any;
|
|
40
40
|
export declare function getAllReferencedClasses(c: ControlDefinition): string[];
|
|
41
|
+
export declare function jsonPathString(jsonPath: JsonPath[]): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-typed-forms/schemas",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"material-ui"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@react-typed-forms/core": "^3.
|
|
27
|
+
"@react-typed-forms/core": "^3.1.0",
|
|
28
28
|
"clsx": "^1 || ^2",
|
|
29
29
|
"jsonata": "^2.0.4",
|
|
30
30
|
"react": "^18.2.0"
|
package/src/controlRender.tsx
CHANGED
|
@@ -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,18 +214,23 @@ export interface DataControlProps {
|
|
|
210
214
|
childCount: number;
|
|
211
215
|
renderChild: ChildRenderer;
|
|
212
216
|
allowedOptions?: Control<any[] | undefined>;
|
|
213
|
-
elementRenderer?: (
|
|
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;
|
|
222
232
|
clearHidden?: boolean;
|
|
223
233
|
schemaInterface?: SchemaInterface;
|
|
224
|
-
dataRoot?: Control<any>;
|
|
225
234
|
}
|
|
226
235
|
export function useControlRenderer(
|
|
227
236
|
definition: ControlDefinition,
|
|
@@ -258,22 +267,22 @@ export function useControlRenderer(
|
|
|
258
267
|
const r = useUpdatedRef({ options, definition, fields, schemaField });
|
|
259
268
|
|
|
260
269
|
const Component = useCallback(
|
|
261
|
-
({ control:
|
|
270
|
+
({ control: rootControl, parentPath = [] }: ControlRenderProps) => {
|
|
262
271
|
const stopTracking = useComponentTracking();
|
|
263
272
|
try {
|
|
264
273
|
const { definition: c, options, fields, schemaField } = r.current;
|
|
265
|
-
const
|
|
266
|
-
groupControl: parentControl,
|
|
274
|
+
const parentDataContext: ControlDataContext = {
|
|
267
275
|
fields,
|
|
268
276
|
schemaInterface,
|
|
269
|
-
|
|
277
|
+
data: rootControl,
|
|
278
|
+
path: parentPath,
|
|
270
279
|
};
|
|
271
|
-
const readonlyControl = useIsReadonly(
|
|
272
|
-
const disabledControl = useIsDisabled(
|
|
273
|
-
const visibleControl = useIsVisible(
|
|
274
|
-
const displayControl = useDynamicDisplay(
|
|
275
|
-
const customStyle = useCustomStyle(
|
|
276
|
-
const layoutStyle = useLayoutStyle(
|
|
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;
|
|
277
286
|
const visible = visibleControl.current.value;
|
|
278
287
|
const visibility = useControl<Visibility | undefined>(() =>
|
|
279
288
|
visible != null
|
|
@@ -294,31 +303,35 @@ export function useControlRenderer(
|
|
|
294
303
|
},
|
|
295
304
|
);
|
|
296
305
|
|
|
297
|
-
const allowedOptions = useAllowedOptions(
|
|
298
|
-
const defaultValueControl = useDefaultValue(
|
|
299
|
-
const [control,
|
|
306
|
+
const allowedOptions = useAllowedOptions(parentDataContext);
|
|
307
|
+
const defaultValueControl = useDefaultValue(parentDataContext);
|
|
308
|
+
const [parentControl, control, controlDataContext] = getControlData(
|
|
300
309
|
schemaField,
|
|
301
|
-
|
|
310
|
+
parentDataContext,
|
|
302
311
|
);
|
|
303
312
|
useControlEffect(
|
|
304
313
|
() => [
|
|
305
314
|
visibility.value,
|
|
306
315
|
defaultValueControl.value,
|
|
307
316
|
control,
|
|
308
|
-
parentControl.isNull,
|
|
309
317
|
isDataControlDefinition(definition) && definition.dontClearHidden,
|
|
318
|
+
parentControl?.isNull,
|
|
310
319
|
],
|
|
311
|
-
([vc, dv, cd,
|
|
312
|
-
if (pn) {
|
|
313
|
-
parentControl.value = {};
|
|
314
|
-
}
|
|
320
|
+
([vc, dv, cd, dontClear, parentNull]) => {
|
|
315
321
|
if (vc && cd && vc.visible === vc.showing) {
|
|
316
322
|
if (!vc.visible) {
|
|
317
|
-
if (options.clearHidden && !dontClear)
|
|
323
|
+
if (options.clearHidden && !dontClear) {
|
|
324
|
+
console.log("Clearing ", schemaField?.field);
|
|
325
|
+
cd.value = undefined;
|
|
326
|
+
}
|
|
318
327
|
} else if (cd.value == null) {
|
|
328
|
+
console.log("Defaulting ", schemaField?.field, dv);
|
|
319
329
|
cd.value = dv;
|
|
320
330
|
}
|
|
321
331
|
}
|
|
332
|
+
if (parentNull && parentControl?.isNull) {
|
|
333
|
+
parentControl.value = {};
|
|
334
|
+
}
|
|
322
335
|
},
|
|
323
336
|
true,
|
|
324
337
|
);
|
|
@@ -327,20 +340,25 @@ export function useControlRenderer(
|
|
|
327
340
|
readonly: options.readonly || readonlyControl.value,
|
|
328
341
|
disabled: options.disabled || disabledControl.value,
|
|
329
342
|
})).value;
|
|
330
|
-
useValidation(
|
|
343
|
+
useValidation(
|
|
344
|
+
control ?? newControl(null),
|
|
345
|
+
!!myOptions.hidden,
|
|
346
|
+
parentDataContext,
|
|
347
|
+
);
|
|
331
348
|
const childRenderers: FC<ControlRenderProps>[] =
|
|
332
349
|
c.children?.map((cd) =>
|
|
333
|
-
useControlRenderer(cd,
|
|
350
|
+
useControlRenderer(cd, controlDataContext.fields, renderer, {
|
|
334
351
|
...options,
|
|
335
352
|
...myOptions,
|
|
336
|
-
dataRoot: dataContext.root,
|
|
337
353
|
}),
|
|
338
354
|
) ?? [];
|
|
355
|
+
|
|
339
356
|
useEffect(() => {
|
|
340
357
|
if (control && typeof myOptions.disabled === "boolean")
|
|
341
358
|
control.disabled = myOptions.disabled;
|
|
342
359
|
}, [control, myOptions.disabled]);
|
|
343
|
-
if (parentControl
|
|
360
|
+
if (parentControl?.isNull) return <></>;
|
|
361
|
+
|
|
344
362
|
const adornments =
|
|
345
363
|
definition.adornments?.map((x) =>
|
|
346
364
|
renderer.renderAdornment({ adornment: x }),
|
|
@@ -355,7 +373,7 @@ export function useControlRenderer(
|
|
|
355
373
|
},
|
|
356
374
|
createDataProps: dataProps,
|
|
357
375
|
formOptions: myOptions,
|
|
358
|
-
dataContext,
|
|
376
|
+
dataContext: controlDataContext,
|
|
359
377
|
control: displayControl ?? control,
|
|
360
378
|
schemaField,
|
|
361
379
|
displayControl,
|
|
@@ -399,24 +417,31 @@ export function lookupSchemaField(
|
|
|
399
417
|
const fieldName = isGroupControlsDefinition(c)
|
|
400
418
|
? c.compoundField
|
|
401
419
|
: isDataControlDefinition(c)
|
|
402
|
-
|
|
403
|
-
|
|
420
|
+
? c.field
|
|
421
|
+
: undefined;
|
|
404
422
|
return fieldName ? findField(fields, fieldName) : undefined;
|
|
405
423
|
}
|
|
406
424
|
export function getControlData(
|
|
407
425
|
schemaField: SchemaField | undefined,
|
|
408
426
|
parentContext: ControlDataContext,
|
|
409
|
-
): [Control<any> | undefined, ControlDataContext] {
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
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;
|
|
413
435
|
return [
|
|
436
|
+
parentControl,
|
|
414
437
|
childControl,
|
|
415
|
-
schemaField
|
|
438
|
+
schemaField
|
|
416
439
|
? {
|
|
417
440
|
...parentContext,
|
|
418
|
-
|
|
419
|
-
fields: schemaField
|
|
441
|
+
path: childPath,
|
|
442
|
+
fields: isCompoundField(schemaField)
|
|
443
|
+
? schemaField.children
|
|
444
|
+
: parentContext.fields,
|
|
420
445
|
}
|
|
421
446
|
: parentContext,
|
|
422
447
|
];
|
|
@@ -426,13 +451,14 @@ function groupProps(
|
|
|
426
451
|
renderOptions: GroupRenderOptions = { type: "Standard" },
|
|
427
452
|
childCount: number,
|
|
428
453
|
renderChild: ChildRenderer,
|
|
429
|
-
|
|
454
|
+
data: DataContext,
|
|
430
455
|
className: string | null | undefined,
|
|
431
456
|
style: React.CSSProperties | undefined,
|
|
432
457
|
): GroupRendererProps {
|
|
433
458
|
return {
|
|
434
459
|
childCount,
|
|
435
|
-
renderChild: (i) =>
|
|
460
|
+
renderChild: (i) =>
|
|
461
|
+
renderChild(i, i, { control: data.data, parentPath: data.path }),
|
|
436
462
|
renderOptions,
|
|
437
463
|
className: cc(className),
|
|
438
464
|
style,
|
|
@@ -455,6 +481,7 @@ export function defaultDataProps({
|
|
|
455
481
|
(field.options?.length ?? 0) === 0 ? null : field.options;
|
|
456
482
|
const allowed = allowedOptions?.value ?? [];
|
|
457
483
|
return {
|
|
484
|
+
definition,
|
|
458
485
|
control,
|
|
459
486
|
field,
|
|
460
487
|
id: "c" + control.uniqueId,
|
|
@@ -489,7 +516,7 @@ export function defaultArrayProps(
|
|
|
489
516
|
required: boolean,
|
|
490
517
|
style: CSSProperties | undefined,
|
|
491
518
|
className: string | undefined,
|
|
492
|
-
renderElement: (
|
|
519
|
+
renderElement: (elemIndex: number) => ReactNode,
|
|
493
520
|
): ArrayRendererProps {
|
|
494
521
|
const noun = field.displayName ?? field.field;
|
|
495
522
|
const elems = arrayControl.elements ?? [];
|
|
@@ -508,7 +535,7 @@ export function defaultArrayProps(
|
|
|
508
535
|
actionText: "Remove",
|
|
509
536
|
onClick: () => removeElement(arrayControl, i),
|
|
510
537
|
}),
|
|
511
|
-
renderElement: (i) => renderElement(
|
|
538
|
+
renderElement: (i) => renderElement(i),
|
|
512
539
|
className: cc(className),
|
|
513
540
|
style,
|
|
514
541
|
};
|
|
@@ -566,7 +593,7 @@ export function renderControlLayout({
|
|
|
566
593
|
c.groupOptions,
|
|
567
594
|
childCount,
|
|
568
595
|
childRenderer,
|
|
569
|
-
dataContext
|
|
596
|
+
dataContext,
|
|
570
597
|
c.styleClass,
|
|
571
598
|
style,
|
|
572
599
|
),
|
|
@@ -601,22 +628,26 @@ export function renderControlLayout({
|
|
|
601
628
|
}
|
|
602
629
|
return {};
|
|
603
630
|
|
|
604
|
-
function renderData(c: DataControlDefinition,
|
|
631
|
+
function renderData(c: DataControlDefinition, elemIndex?: number) {
|
|
605
632
|
if (!schemaField) return { children: "No schema field for: " + c.field };
|
|
633
|
+
if (!childControl) return { children: "No control for: " + c.field };
|
|
606
634
|
const props = dataProps({
|
|
607
635
|
definition: c,
|
|
608
636
|
field: schemaField,
|
|
609
|
-
dataContext
|
|
610
|
-
|
|
637
|
+
dataContext:
|
|
638
|
+
elemIndex != null
|
|
639
|
+
? { ...dataContext, path: [...dataContext.path, elemIndex] }
|
|
640
|
+
: dataContext,
|
|
641
|
+
control:
|
|
642
|
+
elemIndex != null ? childControl!.elements[elemIndex] : childControl,
|
|
611
643
|
options: dataOptions,
|
|
612
644
|
style,
|
|
613
645
|
childCount,
|
|
614
646
|
allowedOptions,
|
|
615
647
|
renderChild: childRenderer,
|
|
616
648
|
elementRenderer:
|
|
617
|
-
|
|
618
|
-
? (
|
|
619
|
-
renderLayoutParts(renderData(c, element), renderer).children
|
|
649
|
+
elemIndex == null && schemaField.collection
|
|
650
|
+
? (ei) => renderLayoutParts(renderData(c, ei), renderer).children
|
|
620
651
|
: undefined,
|
|
621
652
|
});
|
|
622
653
|
|
|
@@ -635,32 +666,6 @@ export function renderControlLayout({
|
|
|
635
666
|
errorControl: childControl,
|
|
636
667
|
};
|
|
637
668
|
}
|
|
638
|
-
|
|
639
|
-
function compoundRenderer(i: number, control: Control<any>): ReactNode {
|
|
640
|
-
const { className, style, children } = renderer.renderLayout({
|
|
641
|
-
processLayout: renderer.renderGroup({
|
|
642
|
-
renderOptions: { type: "Standard", hideTitle: true },
|
|
643
|
-
childCount,
|
|
644
|
-
renderChild: (ci) => childRenderer(ci, ci, { control }),
|
|
645
|
-
}),
|
|
646
|
-
});
|
|
647
|
-
return (
|
|
648
|
-
<div key={control.uniqueId} style={style} className={cc(className)}>
|
|
649
|
-
{children}
|
|
650
|
-
</div>
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
function scalarRenderer(
|
|
654
|
-
dataProps: DataRendererProps,
|
|
655
|
-
): (i: number, control: Control<any>) => ReactNode {
|
|
656
|
-
return (i, control) => {
|
|
657
|
-
return (
|
|
658
|
-
<Fragment key={control.uniqueId}>
|
|
659
|
-
{renderer.renderData({ ...dataProps, control })({}).children}
|
|
660
|
-
</Fragment>
|
|
661
|
-
);
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
669
|
}
|
|
665
670
|
|
|
666
671
|
export function appendMarkup(
|
|
@@ -740,3 +745,23 @@ export function controlTitle(
|
|
|
740
745
|
) {
|
|
741
746
|
return title ? title : fieldDisplayName(field);
|
|
742
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
|
-
|
|
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,
|
|
@@ -192,20 +198,24 @@ export type EvalExpressionHook<A = any> = (
|
|
|
192
198
|
function useDataExpression(
|
|
193
199
|
fvExpr: DataExpression,
|
|
194
200
|
fields: SchemaField[],
|
|
195
|
-
data:
|
|
201
|
+
data: DataContext,
|
|
196
202
|
) {
|
|
197
203
|
const refField = findField(fields, fvExpr.field);
|
|
198
|
-
const otherField = refField
|
|
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:
|
|
213
|
+
data: DataContext,
|
|
206
214
|
) {
|
|
207
215
|
const refField = findField(fields, fvExpr.field);
|
|
208
|
-
const otherField = refField
|
|
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,20 +230,15 @@ export function defaultEvalHooks(
|
|
|
220
230
|
case ExpressionType.Jsonata:
|
|
221
231
|
return useJsonataExpression(
|
|
222
232
|
(expr as JsonataExpression).expression,
|
|
223
|
-
context
|
|
224
|
-
context.root,
|
|
233
|
+
context,
|
|
225
234
|
);
|
|
226
235
|
case ExpressionType.Data:
|
|
227
|
-
return useDataExpression(
|
|
228
|
-
expr as DataExpression,
|
|
229
|
-
context.fields,
|
|
230
|
-
context.groupControl,
|
|
231
|
-
);
|
|
236
|
+
return useDataExpression(expr as DataExpression, context.fields, context);
|
|
232
237
|
case ExpressionType.DataMatch:
|
|
233
238
|
return useDataMatchExpression(
|
|
234
239
|
expr as DataMatchExpression,
|
|
235
240
|
context.fields,
|
|
236
|
-
context
|
|
241
|
+
context,
|
|
237
242
|
);
|
|
238
243
|
default:
|
|
239
244
|
return useControl(undefined);
|
|
@@ -291,31 +296,57 @@ export function hideDisplayOnly(
|
|
|
291
296
|
!displayOptions.emptyText &&
|
|
292
297
|
schemaInterface.isEmptyValue(
|
|
293
298
|
field,
|
|
294
|
-
context
|
|
299
|
+
lookupChildControl(context, field.field)?.value,
|
|
295
300
|
)
|
|
296
301
|
);
|
|
297
302
|
}
|
|
298
303
|
|
|
299
304
|
export function useJsonataExpression(
|
|
300
305
|
jExpr: string,
|
|
301
|
-
|
|
302
|
-
|
|
306
|
+
dataContext: DataContext,
|
|
307
|
+
bindings?: () => Record<string, any>,
|
|
303
308
|
): Control<any> {
|
|
309
|
+
const pathString = jsonPathString(dataContext.path);
|
|
304
310
|
const compiledExpr = useMemo(() => {
|
|
305
311
|
try {
|
|
306
|
-
return jsonata(jExpr);
|
|
312
|
+
return jsonata(pathString ? pathString + ".(" + jExpr + ")" : jExpr);
|
|
307
313
|
} catch (e) {
|
|
308
314
|
console.error(e);
|
|
309
315
|
return jsonata("null");
|
|
310
316
|
}
|
|
311
|
-
}, [jExpr]);
|
|
317
|
+
}, [jExpr, pathString]);
|
|
312
318
|
const control = useControl();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
+
}),
|
|
319
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]);
|
|
320
351
|
return control;
|
|
321
352
|
}
|
package/src/internal.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ChangeListenerFunc,
|
|
3
|
+
Control,
|
|
4
|
+
ControlChange,
|
|
5
|
+
useControl,
|
|
6
|
+
useControlEffect,
|
|
7
|
+
} from "@react-typed-forms/core";
|
|
2
8
|
|
|
3
9
|
export function useCalculatedControl<V>(calculate: () => V): Control<V> {
|
|
4
10
|
const c = useControl(calculate);
|
|
@@ -9,3 +15,34 @@ export function useCalculatedControl<V>(calculate: () => V): Control<V> {
|
|
|
9
15
|
export function cc(n: string | null | undefined): string | undefined {
|
|
10
16
|
return n ? n : undefined;
|
|
11
17
|
}
|
|
18
|
+
|
|
19
|
+
export function trackedStructure<A>(
|
|
20
|
+
c: Control<A>,
|
|
21
|
+
tracker: ChangeListenerFunc<any>,
|
|
22
|
+
): A {
|
|
23
|
+
const cc = c.current;
|
|
24
|
+
const cv = cc.value;
|
|
25
|
+
if (cv == null) {
|
|
26
|
+
tracker(c, ControlChange.Structure);
|
|
27
|
+
return cv;
|
|
28
|
+
}
|
|
29
|
+
if (typeof cv !== "object") {
|
|
30
|
+
tracker(c, ControlChange.Value);
|
|
31
|
+
return cv;
|
|
32
|
+
}
|
|
33
|
+
return new Proxy(cv, {
|
|
34
|
+
get(target: object, p: string | symbol, receiver: any): any {
|
|
35
|
+
if (Array.isArray(cv)) {
|
|
36
|
+
tracker(c, ControlChange.Structure);
|
|
37
|
+
if (typeof p === "symbol" || p[0] >= "9" || p[0] < "0")
|
|
38
|
+
return Reflect.get(cv, p);
|
|
39
|
+
const nc = (cc.elements as any)[p];
|
|
40
|
+
if (typeof nc === "function") return nc;
|
|
41
|
+
if (nc == null) return null;
|
|
42
|
+
return trackedStructure(nc, tracker);
|
|
43
|
+
}
|
|
44
|
+
if (p in cv) return trackedStructure((cc.fields as any)[p], tracker);
|
|
45
|
+
return undefined;
|
|
46
|
+
},
|
|
47
|
+
}) as A;
|
|
48
|
+
}
|
package/src/renderers.tsx
CHANGED
|
@@ -428,8 +428,8 @@ export function createDefaultGroupRenderer(
|
|
|
428
428
|
const { style, className: gcn } = isGridRenderer(renderOptions)
|
|
429
429
|
? gridStyles(renderOptions)
|
|
430
430
|
: isFlexRenderer(renderOptions)
|
|
431
|
-
|
|
432
|
-
|
|
431
|
+
? flexStyles(renderOptions)
|
|
432
|
+
: ({ className: standardClassName } as StyleProps);
|
|
433
433
|
|
|
434
434
|
return (cp: ControlLayoutProps) => {
|
|
435
435
|
return {
|
|
@@ -531,7 +531,11 @@ export function createDefaultDataRenderer(
|
|
|
531
531
|
style: props.style,
|
|
532
532
|
className: props.className,
|
|
533
533
|
renderOptions: { type: "Standard", hideTitle: true },
|
|
534
|
-
renderChild: (i) =>
|
|
534
|
+
renderChild: (i) =>
|
|
535
|
+
props.renderChild(i, i, {
|
|
536
|
+
control: props.dataContext.data,
|
|
537
|
+
parentPath: props.dataContext.path,
|
|
538
|
+
}),
|
|
535
539
|
childCount: props.childCount,
|
|
536
540
|
});
|
|
537
541
|
}
|