@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/.rush/temp/operation/build/state.json +1 -1
- package/.rush/temp/shrinkwrap-deps.json +220 -235
- package/lib/controlRender.d.ts +10 -3
- package/lib/hooks.d.ts +2 -1
- package/lib/index.js +179 -73
- package/lib/index.js.map +1 -1
- package/lib/internal.d.ts +2 -1
- package/lib/schemaBuilder.d.ts +1 -1
- package/lib/util.d.ts +4 -2
- package/package.json +48 -48
- package/src/controlRender.tsx +99 -71
- package/src/hooks.tsx +68 -35
- package/src/internal.ts +38 -1
- package/src/renderers.tsx +7 -3
- package/src/schemaBuilder.ts +20 -20
- package/src/util.ts +32 -7
- package/src/validators.ts +1 -1
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/schemaBuilder.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
5
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
+
}
|
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,12 +214,18 @@ 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;
|
|
@@ -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:
|
|
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
|
|
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(
|
|
270
|
-
const disabledControl = useIsDisabled(
|
|
271
|
-
const visibleControl = useIsVisible(
|
|
272
|
-
const displayControl = useDynamicDisplay(
|
|
273
|
-
const customStyle = useCustomStyle(
|
|
274
|
-
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;
|
|
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(
|
|
296
|
-
const defaultValueControl = useDefaultValue(
|
|
297
|
-
const [control,
|
|
306
|
+
const allowedOptions = useAllowedOptions(parentDataContext);
|
|
307
|
+
const defaultValueControl = useDefaultValue(parentDataContext);
|
|
308
|
+
const [parentControl, control, controlDataContext] = getControlData(
|
|
298
309
|
schemaField,
|
|
299
|
-
|
|
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,
|
|
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)
|
|
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(
|
|
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,
|
|
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
|
|
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
|
-
|
|
400
|
-
|
|
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
|
|
408
|
-
|
|
409
|
-
|
|
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
|
|
438
|
+
schemaField
|
|
413
439
|
? {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
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) =>
|
|
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: (
|
|
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(
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
615
|
-
? (
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
115
|
+
useEvalExpressionHook: UseEvalExpressionHook,
|
|
116
|
+
definition: ControlDefinition,
|
|
111
117
|
): EvalExpressionHook<any[]> {
|
|
112
118
|
const dynamicAllowed = useEvalDynamicHook(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
definition,
|
|
120
|
+
DynamicPropertyType.AllowedOptions,
|
|
121
|
+
useEvalExpressionHook,
|
|
116
122
|
);
|
|
117
123
|
return useCallback(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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:
|
|
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,19 +230,15 @@ export function defaultEvalHooks(
|
|
|
220
230
|
case ExpressionType.Jsonata:
|
|
221
231
|
return useJsonataExpression(
|
|
222
232
|
(expr as JsonataExpression).expression,
|
|
223
|
-
context
|
|
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
|
|
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
|
|
299
|
+
lookupChildControl(context, field.field)?.value,
|
|
294
300
|
)
|
|
295
301
|
);
|
|
296
302
|
}
|
|
297
303
|
|
|
298
304
|
export function useJsonataExpression(
|
|
299
305
|
jExpr: string,
|
|
300
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
}
|