@react-typed-forms/schemas 14.2.0 → 14.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.
@@ -0,0 +1,792 @@
1
+ import { SchemaValidator } from "./schemaValidator";
2
+ import {
3
+ FieldOption,
4
+ schemaDataForFieldPath,
5
+ SchemaDataNode,
6
+ SchemaField,
7
+ SchemaInterface,
8
+ SchemaNode,
9
+ } from "./schemaField";
10
+ import { EntityExpression } from "./entityExpression";
11
+
12
+ /**
13
+ * Interface representing the form context data.
14
+ */
15
+ export interface FormContextData {
16
+ option?: FieldOption;
17
+ optionSelected?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Interface representing the control data context.
22
+ */
23
+ export interface ControlDataContext {
24
+ schemaInterface: SchemaInterface;
25
+ dataNode: SchemaDataNode | undefined;
26
+ parentNode: SchemaDataNode;
27
+ formData: FormContextData;
28
+ }
29
+
30
+ /**
31
+ * Represents any control definition.
32
+ */
33
+ export type AnyControlDefinition =
34
+ | DataControlDefinition
35
+ | GroupedControlsDefinition
36
+ | ActionControlDefinition
37
+ | DisplayControlDefinition;
38
+
39
+ /**
40
+ * Represents a control definition.
41
+ */
42
+ export interface ControlDefinition {
43
+ type: string;
44
+ id?: string | null;
45
+ childRefId?: string | null;
46
+ title?: string | null;
47
+ styleClass?: string | null;
48
+ layoutClass?: string | null;
49
+ labelClass?: string | null;
50
+ dynamic?: DynamicProperty[] | null;
51
+ adornments?: ControlAdornment[] | null;
52
+ children?: ControlDefinition[] | null;
53
+ }
54
+
55
+ export enum ControlDefinitionType {
56
+ Data = "Data",
57
+ Group = "Group",
58
+ Display = "Display",
59
+ Action = "Action",
60
+ }
61
+
62
+ export interface DynamicProperty {
63
+ type: string;
64
+ expr: EntityExpression;
65
+ }
66
+
67
+ export enum DynamicPropertyType {
68
+ Visible = "Visible",
69
+ DefaultValue = "DefaultValue",
70
+ Readonly = "Readonly",
71
+ Disabled = "Disabled",
72
+ Display = "Display",
73
+ Style = "Style",
74
+ LayoutStyle = "LayoutStyle",
75
+ AllowedOptions = "AllowedOptions",
76
+ Label = "Label",
77
+ ActionData = "ActionData",
78
+ }
79
+
80
+ export interface ControlAdornment {
81
+ type: string;
82
+ }
83
+
84
+ export enum AdornmentPlacement {
85
+ ControlStart = "ControlStart",
86
+ ControlEnd = "ControlEnd",
87
+ LabelStart = "LabelStart",
88
+ LabelEnd = "LabelEnd",
89
+ }
90
+
91
+ export enum ControlAdornmentType {
92
+ Tooltip = "Tooltip",
93
+ Accordion = "Accordion",
94
+ HelpText = "HelpText",
95
+ Icon = "Icon",
96
+ SetField = "SetField",
97
+ Optional = "Optional",
98
+ }
99
+
100
+ export interface IconAdornment extends ControlAdornment {
101
+ type: ControlAdornmentType.Icon;
102
+ iconClass: string;
103
+ placement?: AdornmentPlacement | null;
104
+ }
105
+
106
+ export interface TooltipAdornment extends ControlAdornment {
107
+ type: ControlAdornmentType.Tooltip;
108
+ tooltip: string;
109
+ }
110
+
111
+ export interface AccordionAdornment extends ControlAdornment {
112
+ type: ControlAdornmentType.Accordion;
113
+ title: string;
114
+ defaultExpanded?: boolean | null;
115
+ }
116
+
117
+ export interface HelpTextAdornment extends ControlAdornment {
118
+ type: ControlAdornmentType.HelpText;
119
+ helpText: string;
120
+ placement?: AdornmentPlacement | null;
121
+ }
122
+
123
+ export interface SetFieldAdornment extends ControlAdornment {
124
+ type: ControlAdornmentType.SetField;
125
+ field: string;
126
+ defaultOnly?: boolean | null;
127
+ expression?: EntityExpression;
128
+ }
129
+
130
+ export interface OptionalAdornment extends ControlAdornment {
131
+ type: ControlAdornmentType.Optional;
132
+ placement?: AdornmentPlacement | null;
133
+ allowNull?: boolean;
134
+ editSelectable?: boolean;
135
+ }
136
+
137
+ export interface DataControlDefinition extends ControlDefinition {
138
+ type: ControlDefinitionType.Data;
139
+ field: string;
140
+ required?: boolean | null;
141
+ renderOptions?: RenderOptions | null;
142
+ defaultValue?: any;
143
+ readonly?: boolean | null;
144
+ disabled?: boolean | null;
145
+ validators?: SchemaValidator[] | null;
146
+ hideTitle?: boolean | null;
147
+ dontClearHidden?: boolean | null;
148
+ }
149
+
150
+ export interface RenderOptions {
151
+ type: string;
152
+ }
153
+
154
+ export enum DataRenderType {
155
+ Standard = "Standard",
156
+ Textfield = "Textfield",
157
+ Radio = "Radio",
158
+ HtmlEditor = "HtmlEditor",
159
+ IconList = "IconList",
160
+ CheckList = "CheckList",
161
+ UserSelection = "UserSelection",
162
+ Synchronised = "Synchronised",
163
+ IconSelector = "IconSelector",
164
+ DateTime = "DateTime",
165
+ Checkbox = "Checkbox",
166
+ Dropdown = "Dropdown",
167
+ DisplayOnly = "DisplayOnly",
168
+ Group = "Group",
169
+ NullToggle = "NullToggle",
170
+ Autocomplete = "Autocomplete",
171
+ Jsonata = "Jsonata",
172
+ Array = "Array",
173
+ ArrayElement = "ArrayElement",
174
+ }
175
+
176
+ export interface TextfieldRenderOptions extends RenderOptions {
177
+ type: DataRenderType.Textfield;
178
+ placeholder?: string | null;
179
+ multiline?: boolean | null;
180
+ }
181
+
182
+ export interface AutocompleteRenderOptions
183
+ extends RenderOptions,
184
+ AutocompleteClasses {
185
+ type: DataRenderType.Autocomplete;
186
+ }
187
+
188
+ export interface AutocompleteClasses {
189
+ listContainerClass?: string | null;
190
+ listEntryClass?: string | null;
191
+ chipContainerClass?: string | null;
192
+ chipCloseButtonClass?: string | null;
193
+ placeholder?: string | null;
194
+ }
195
+
196
+ export interface CheckEntryClasses {
197
+ entryWrapperClass?: string | null;
198
+ selectedClass?: string | null;
199
+ notSelectedClass?: string | null;
200
+ }
201
+ export interface RadioButtonRenderOptions
202
+ extends RenderOptions,
203
+ CheckEntryClasses {
204
+ type: DataRenderType.Radio;
205
+ }
206
+
207
+ export interface StandardRenderer extends RenderOptions {
208
+ type: DataRenderType.Standard;
209
+ }
210
+
211
+ export interface DataGroupRenderOptions extends RenderOptions {
212
+ type: DataRenderType.Group;
213
+ groupOptions?: GroupRenderOptions;
214
+ }
215
+
216
+ export interface HtmlEditorRenderOptions extends RenderOptions {
217
+ type: DataRenderType.HtmlEditor;
218
+ allowImages: boolean;
219
+ }
220
+
221
+ export interface DateTimeRenderOptions extends RenderOptions {
222
+ type: DataRenderType.DateTime;
223
+ format?: string | null;
224
+ forceMidnight?: boolean;
225
+ forceStandard?: boolean;
226
+ }
227
+
228
+ export interface IconListRenderOptions extends RenderOptions {
229
+ type: DataRenderType.IconList;
230
+ iconMappings: IconMapping[];
231
+ }
232
+
233
+ export interface DisplayOnlyRenderOptions extends RenderOptions {
234
+ type: DataRenderType.DisplayOnly;
235
+ emptyText?: string | null;
236
+ sampleText?: string | null;
237
+ }
238
+ export interface IconMapping {
239
+ value: string;
240
+ materialIcon?: string | null;
241
+ }
242
+
243
+ export interface JsonataRenderOptions extends RenderOptions {
244
+ type: DataRenderType.Jsonata;
245
+ expression: string;
246
+ }
247
+
248
+ export interface JsonataRenderOptions extends RenderOptions {
249
+ type: DataRenderType.Jsonata;
250
+ expression: string;
251
+ }
252
+
253
+ export interface ArrayRenderOptions extends RenderOptions {
254
+ type: DataRenderType.Array;
255
+ addText?: string | null;
256
+ addActionId?: string | null;
257
+ removeText?: string | null;
258
+ removeActionId?: string | null;
259
+ editText?: string | null;
260
+ editActionId?: string | null;
261
+ noAdd?: boolean | null;
262
+ noRemove?: boolean | null;
263
+ noReorder?: boolean | null;
264
+ childOptions?: RenderOptions | null;
265
+ editExternal?: boolean | null;
266
+ }
267
+
268
+ export interface ArrayElementRenderOptions extends RenderOptions {
269
+ type: DataRenderType.ArrayElement;
270
+ showInline?: boolean | null;
271
+ }
272
+
273
+ export type ArrayActionOptions = Pick<
274
+ ArrayRenderOptions,
275
+ | "addText"
276
+ | "addActionId"
277
+ | "removeText"
278
+ | "removeActionId"
279
+ | "noAdd"
280
+ | "noRemove"
281
+ | "noReorder"
282
+ | "editExternal"
283
+ | "editActionId"
284
+ | "editText"
285
+ > & { readonly?: boolean; disabled?: boolean; designMode?: boolean };
286
+
287
+ export interface CheckListRenderOptions
288
+ extends RenderOptions,
289
+ CheckEntryClasses {
290
+ type: DataRenderType.CheckList;
291
+ }
292
+
293
+ export interface SynchronisedRenderOptions extends RenderOptions {
294
+ type: DataRenderType.Synchronised;
295
+ fieldToSync: string;
296
+ syncType: SyncTextType;
297
+ }
298
+
299
+ export enum SyncTextType {
300
+ Camel = "Camel",
301
+ Snake = "Snake",
302
+ Pascal = "Pascal",
303
+ }
304
+
305
+ export interface UserSelectionRenderOptions extends RenderOptions {
306
+ type: DataRenderType.UserSelection;
307
+ noGroups: boolean;
308
+ noUsers: boolean;
309
+ }
310
+
311
+ export interface IconSelectionRenderOptions extends RenderOptions {
312
+ type: DataRenderType.IconSelector;
313
+ }
314
+
315
+ export interface GroupedControlsDefinition extends ControlDefinition {
316
+ type: ControlDefinitionType.Group;
317
+ compoundField?: string | null;
318
+ groupOptions?: GroupRenderOptions;
319
+ }
320
+
321
+ export interface GroupRenderOptions {
322
+ type: string;
323
+ hideTitle?: boolean | null;
324
+ childStyleClass?: string | null;
325
+ childLayoutClass?: string | null;
326
+ childLabelClass?: string | null;
327
+ displayOnly?: boolean | null;
328
+ }
329
+
330
+ export enum GroupRenderType {
331
+ Standard = "Standard",
332
+ Grid = "Grid",
333
+ Flex = "Flex",
334
+ Tabs = "Tabs",
335
+ GroupElement = "GroupElement",
336
+ SelectChild = "SelectChild",
337
+ }
338
+
339
+ export interface StandardGroupRenderer extends GroupRenderOptions {
340
+ type: GroupRenderType.Standard;
341
+ }
342
+
343
+ export interface FlexRenderer extends GroupRenderOptions {
344
+ type: GroupRenderType.Flex;
345
+ direction?: string | null;
346
+ gap?: string | null;
347
+ }
348
+
349
+ export interface GroupElementRenderer extends GroupRenderOptions {
350
+ type: GroupRenderType.GroupElement;
351
+ value: any;
352
+ }
353
+
354
+ export interface GridRenderer extends GroupRenderOptions {
355
+ type: GroupRenderType.Grid;
356
+ columns?: number | null;
357
+ }
358
+
359
+ export interface TabsGroupRenderer extends GroupRenderOptions {
360
+ type: GroupRenderType.Tabs;
361
+ }
362
+
363
+ export interface SelectChildRenderer extends GroupRenderOptions {
364
+ type: GroupRenderType.SelectChild;
365
+ childIndexExpression?: EntityExpression | null;
366
+ }
367
+
368
+ export interface DisplayControlDefinition extends ControlDefinition {
369
+ type: ControlDefinitionType.Display;
370
+ displayData: DisplayData;
371
+ }
372
+
373
+ export interface DisplayData {
374
+ type: string;
375
+ }
376
+
377
+ export enum DisplayDataType {
378
+ Text = "Text",
379
+ Html = "Html",
380
+ Icon = "Icon",
381
+ Custom = "Custom",
382
+ }
383
+ export interface TextDisplay extends DisplayData {
384
+ type: DisplayDataType.Text;
385
+ text: string;
386
+ }
387
+
388
+ export interface IconDisplay extends DisplayData {
389
+ type: DisplayDataType.Icon;
390
+ iconClass: string;
391
+ }
392
+
393
+ export interface HtmlDisplay extends DisplayData {
394
+ type: DisplayDataType.Html;
395
+ html: string;
396
+ }
397
+
398
+ export interface CustomDisplay extends DisplayData {
399
+ type: DisplayDataType.Custom;
400
+ customId: string;
401
+ }
402
+
403
+ export interface ActionControlDefinition extends ControlDefinition {
404
+ type: ControlDefinitionType.Action;
405
+ actionId: string;
406
+ actionData?: string | null;
407
+ }
408
+ export interface ControlVisitor<A> {
409
+ data(d: DataControlDefinition): A;
410
+ group(d: GroupedControlsDefinition): A;
411
+ display(d: DisplayControlDefinition): A;
412
+ action(d: ActionControlDefinition): A;
413
+ }
414
+
415
+ export function visitControlDefinition<A>(
416
+ x: ControlDefinition,
417
+ visitor: ControlVisitor<A>,
418
+ defaultValue: (c: ControlDefinition) => A,
419
+ ): A {
420
+ switch (x.type) {
421
+ case ControlDefinitionType.Action:
422
+ return visitor.action(x as ActionControlDefinition);
423
+ case ControlDefinitionType.Data:
424
+ return visitor.data(x as DataControlDefinition);
425
+ case ControlDefinitionType.Display:
426
+ return visitor.display(x as DisplayControlDefinition);
427
+ case ControlDefinitionType.Group:
428
+ return visitor.group(x as GroupedControlsDefinition);
429
+ default:
430
+ return defaultValue(x);
431
+ }
432
+ }
433
+ export function isGridRenderer(
434
+ options: GroupRenderOptions,
435
+ ): options is GridRenderer {
436
+ return options.type === GroupRenderType.Grid;
437
+ }
438
+
439
+ export function isSelectChildRenderer(
440
+ options: GroupRenderOptions,
441
+ ): options is SelectChildRenderer {
442
+ return options.type === GroupRenderType.SelectChild;
443
+ }
444
+
445
+ export function isTabsRenderer(
446
+ options: GroupRenderOptions,
447
+ ): options is TabsGroupRenderer {
448
+ return options.type === GroupRenderType.Tabs;
449
+ }
450
+
451
+ export function isFlexRenderer(
452
+ options: GroupRenderOptions,
453
+ ): options is FlexRenderer {
454
+ return options.type === GroupRenderType.Flex;
455
+ }
456
+
457
+ export function isDisplayOnlyRenderer(
458
+ options: RenderOptions,
459
+ ): options is DisplayOnlyRenderOptions {
460
+ return options.type === DataRenderType.DisplayOnly;
461
+ }
462
+
463
+ export function isTextfieldRenderer(
464
+ options: RenderOptions,
465
+ ): options is TextfieldRenderOptions {
466
+ return options.type === DataRenderType.Textfield;
467
+ }
468
+
469
+ export function isDateTimeRenderer(
470
+ options: RenderOptions,
471
+ ): options is DateTimeRenderOptions {
472
+ return options.type === DataRenderType.DateTime;
473
+ }
474
+
475
+ export function isAutocompleteRenderer(
476
+ options: RenderOptions,
477
+ ): options is AutocompleteRenderOptions {
478
+ return options.type === DataRenderType.Autocomplete;
479
+ }
480
+
481
+ export function isAutoCompleteClasses(
482
+ options?: RenderOptions | null,
483
+ ): options is AutocompleteClasses & RenderOptions {
484
+ switch (options?.type) {
485
+ case DataRenderType.Autocomplete:
486
+ return true;
487
+ default:
488
+ return false;
489
+ }
490
+ }
491
+
492
+ export function isDataGroupRenderer(
493
+ options?: RenderOptions | null,
494
+ ): options is DataGroupRenderOptions {
495
+ return options?.type === DataRenderType.Group;
496
+ }
497
+
498
+ export function isArrayRenderer(
499
+ options: RenderOptions,
500
+ ): options is ArrayRenderOptions {
501
+ return options.type === DataRenderType.Array;
502
+ }
503
+
504
+ export function isDataControl(
505
+ c: ControlDefinition,
506
+ ): c is DataControlDefinition {
507
+ return c.type === ControlDefinitionType.Data;
508
+ }
509
+
510
+ export function isGroupControl(
511
+ c: ControlDefinition,
512
+ ): c is GroupedControlsDefinition {
513
+ return c.type === ControlDefinitionType.Group;
514
+ }
515
+
516
+ export function isActionControl(
517
+ c: ControlDefinition,
518
+ ): c is ActionControlDefinition {
519
+ return c.type === ControlDefinitionType.Action;
520
+ }
521
+
522
+ export function isDisplayControl(
523
+ c: ControlDefinition,
524
+ ): c is DisplayControlDefinition {
525
+ return c.type === ControlDefinitionType.Display;
526
+ }
527
+
528
+ export type ControlActionHandler = (
529
+ actionId: string,
530
+ actionData: any,
531
+ ctx: ControlDataContext,
532
+ ) => (() => void) | undefined;
533
+
534
+ export function isCheckEntryClasses(
535
+ options?: RenderOptions | null,
536
+ ): options is CheckEntryClasses & RenderOptions {
537
+ switch (options?.type) {
538
+ case DataRenderType.Radio:
539
+ case DataRenderType.CheckList:
540
+ return true;
541
+ default:
542
+ return false;
543
+ }
544
+ }
545
+
546
+ export type ControlMap = { [k: string]: ControlDefinition };
547
+
548
+ export class FormNode {
549
+ constructor(
550
+ public id: string,
551
+ public definition: ControlDefinition,
552
+ public tree: FormTree,
553
+ public parent?: FormNode,
554
+ public overrideChildren?: FormNode[],
555
+ ) {
556
+ this.id = id;
557
+ this.definition = definition;
558
+ }
559
+
560
+ getChildNodes(): FormNode[] {
561
+ if (this.overrideChildren) return this.overrideChildren;
562
+ let children = this.definition.children;
563
+ if (this.definition.childRefId) {
564
+ const ref = this.tree.controlMap[this.definition.childRefId];
565
+ children = ref?.children;
566
+ }
567
+ return (
568
+ children?.map(
569
+ (x, i) => new FormNode(this.id + "/" + i, x, this.tree, this),
570
+ ) ?? []
571
+ );
572
+ }
573
+
574
+ withOverrideChildren(children: ControlDefinition[]) {
575
+ return new FormNode(
576
+ this.id,
577
+ this.definition,
578
+ this.tree,
579
+ this.parent,
580
+ children.map((x) => nodeForControl(x, this.tree)),
581
+ );
582
+ }
583
+ }
584
+
585
+ export interface FormTreeLookup<A = string> {
586
+ getForm(formId: A): FormTree | undefined;
587
+ }
588
+ export interface FormTree extends FormTreeLookup {
589
+ rootNode: FormNode;
590
+ controlMap: ControlMap;
591
+ }
592
+
593
+ export function nodeForControl(
594
+ definition: ControlDefinition,
595
+ tree: FormTree,
596
+ indexOrId?: number | string,
597
+ parent?: FormNode,
598
+ ): FormNode {
599
+ return new FormNode(
600
+ parent ? parent.id + "/" + indexOrId : "",
601
+ definition,
602
+ tree,
603
+ parent,
604
+ );
605
+ }
606
+
607
+ export function legacyFormNode(definition: ControlDefinition) {
608
+ return createFormLookup({ $legacy: [definition] })
609
+ .getForm("$legacy")!
610
+ .rootNode.getChildNodes()[0];
611
+ }
612
+
613
+ function getControlIds(
614
+ definition: ControlDefinition,
615
+ ): [string, ControlDefinition][] {
616
+ const childEntries = definition.children?.flatMap(getControlIds) ?? [];
617
+ return !definition.id
618
+ ? childEntries
619
+ : [[definition.id, definition], ...childEntries];
620
+ }
621
+
622
+ function makeFormTree(
623
+ controls: ControlDefinition[],
624
+ getForm: FormTreeLookup,
625
+ ): FormTree {
626
+ const tree = {
627
+ getForm,
628
+ controlMap: Object.fromEntries(controls.flatMap(getControlIds)),
629
+ } as unknown as FormTree;
630
+ tree.rootNode = nodeForControl(
631
+ {
632
+ children: controls,
633
+ type: ControlDefinitionType.Group,
634
+ },
635
+ tree,
636
+ );
637
+ return tree;
638
+ }
639
+
640
+ export function createFormLookup<A extends Record<string, ControlDefinition[]>>(
641
+ formMap: A,
642
+ ): FormTreeLookup<keyof A> {
643
+ const lookup = {
644
+ getForm,
645
+ };
646
+ const forms = Object.fromEntries(
647
+ Object.entries(formMap).map(([k, v]) => [k, makeFormTree(v, lookup)]),
648
+ );
649
+ return lookup;
650
+
651
+ function getForm(formId: keyof A): FormTree | undefined {
652
+ return forms[formId as string];
653
+ }
654
+ }
655
+
656
+ export function fieldPathForDefinition(
657
+ c: ControlDefinition,
658
+ ): string[] | undefined {
659
+ const fieldName = isGroupControl(c)
660
+ ? c.compoundField
661
+ : isDataControl(c)
662
+ ? c.field
663
+ : undefined;
664
+ return fieldName?.split("/");
665
+ }
666
+
667
+ export function lookupDataNode(
668
+ c: ControlDefinition,
669
+ parentNode: SchemaDataNode,
670
+ ) {
671
+ const fieldNamePath = fieldPathForDefinition(c);
672
+ return fieldNamePath
673
+ ? schemaDataForFieldPath(fieldNamePath, parentNode)
674
+ : undefined;
675
+ }
676
+
677
+ export function traverseParents<A, B extends { parent?: B | undefined }>(
678
+ current: B | undefined,
679
+ get: (b: B) => A,
680
+ until?: (b: B) => boolean,
681
+ ): A[] {
682
+ let outArray: A[] = [];
683
+ while (current && !until?.(current)) {
684
+ outArray.push(get(current));
685
+ current = current.parent;
686
+ }
687
+ return outArray.reverse();
688
+ }
689
+
690
+ export function getRootDataNode(dataNode: SchemaDataNode) {
691
+ while (dataNode.parent) {
692
+ dataNode = dataNode.parent;
693
+ }
694
+ return dataNode;
695
+ }
696
+
697
+ export function getJsonPath(dataNode: SchemaDataNode) {
698
+ return traverseParents(
699
+ dataNode,
700
+ (d) => (d.elementIndex == null ? d.schema.field.field : d.elementIndex),
701
+ (x) => !x.parent,
702
+ );
703
+ }
704
+
705
+ export function getSchemaPath(schemaNode: SchemaNode): SchemaField[] {
706
+ return traverseParents(
707
+ schemaNode,
708
+ (d) => d.field,
709
+ (x) => !x.parent,
710
+ );
711
+ }
712
+
713
+ export function getSchemaFieldList(schema: SchemaNode): SchemaField[] {
714
+ return schema.getChildNodes().map((x) => x.field);
715
+ }
716
+
717
+ /**
718
+ * @deprecated use visitFormNodeData instead
719
+ */
720
+ export function visitControlDataArray<A>(
721
+ controls: ControlDefinition[] | undefined | null,
722
+ context: SchemaDataNode,
723
+ cb: (
724
+ definition: DataControlDefinition,
725
+ node: SchemaDataNode,
726
+ ) => A | undefined,
727
+ ): A | undefined {
728
+ if (!controls) return undefined;
729
+ for (const c of controls) {
730
+ const r = visitControlData(c, context, cb);
731
+ if (r !== undefined) return r;
732
+ }
733
+ return undefined;
734
+ }
735
+
736
+ /**
737
+ * @deprecated use visitFormDataInContext instead
738
+ */
739
+ export function visitControlData<A>(
740
+ definition: ControlDefinition,
741
+ ctx: SchemaDataNode,
742
+ cb: (
743
+ definition: DataControlDefinition,
744
+ field: SchemaDataNode,
745
+ ) => A | undefined,
746
+ ): A | undefined {
747
+ return visitFormDataInContext(ctx, legacyFormNode(definition), (n, d) =>
748
+ cb(d, n),
749
+ );
750
+ }
751
+
752
+ export type ControlDataVisitor<A> = (
753
+ dataNode: SchemaDataNode,
754
+ definition: DataControlDefinition,
755
+ ) => A | undefined;
756
+
757
+ export function visitFormData<A>(
758
+ node: FormNode,
759
+ dataNode: SchemaDataNode,
760
+ cb: ControlDataVisitor<A>,
761
+ notSelf?: boolean,
762
+ ): A | undefined {
763
+ const def = node.definition;
764
+ const result = !notSelf && isDataControl(def) ? cb(dataNode, def) : undefined;
765
+ if (result !== undefined) return result;
766
+ if (dataNode.elementIndex == null && dataNode.schema.field.collection) {
767
+ const l = dataNode.control.elements.length;
768
+ for (let i = 0; i < l; i++) {
769
+ const elemChild = dataNode.getChildElement(i);
770
+ const elemResult = visitFormData(node, elemChild, cb);
771
+ if (elemResult !== undefined) return elemResult;
772
+ }
773
+ return undefined;
774
+ }
775
+ if (dataNode.control.isNull) return undefined;
776
+ const children = node.getChildNodes();
777
+ const l = children.length;
778
+ for (let i = 0; i < l; i++) {
779
+ const elemResult = visitFormDataInContext(dataNode, children[i], cb);
780
+ if (elemResult !== undefined) return elemResult;
781
+ }
782
+ return undefined;
783
+ }
784
+
785
+ export function visitFormDataInContext<A>(
786
+ parentContext: SchemaDataNode,
787
+ node: FormNode,
788
+ cb: ControlDataVisitor<A>,
789
+ ): A | undefined {
790
+ const dataNode = lookupDataNode(node.definition, parentContext);
791
+ return visitFormData(node, dataNode ?? parentContext, cb, !dataNode);
792
+ }