@react-typed-forms/schemas 16.2.2 → 17.0.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/FORM_EXTENSIONS_GUIDE.md +781 -0
- package/lib/RenderForm.d.ts +22 -5
- package/lib/controlBuilder.d.ts +4 -47
- package/lib/controlRender.d.ts +49 -24
- package/lib/index.cjs +310 -332
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +270 -272
- package/lib/index.js.map +1 -1
- package/lib/renderer/elementSelected.d.ts +8 -0
- package/lib/renderers.d.ts +6 -2
- package/lib/types.d.ts +10 -5
- package/lib/util.d.ts +3 -2
- package/package.json +5 -5
- package/src/RenderForm.tsx +130 -64
- package/src/controlBuilder.ts +6 -193
- package/src/controlRender.tsx +127 -81
- package/src/createFormRenderer.tsx +52 -19
- package/src/index.ts +1 -0
- package/src/renderer/elementSelected.ts +48 -0
- package/src/renderers.tsx +8 -1
- package/src/types.ts +15 -5
- package/src/util.ts +13 -1
package/src/controlRender.tsx
CHANGED
|
@@ -9,29 +9,29 @@ import React, {
|
|
|
9
9
|
} from "react";
|
|
10
10
|
import {
|
|
11
11
|
addElement,
|
|
12
|
+
ChangeListenerFunc,
|
|
12
13
|
Control,
|
|
14
|
+
newControl,
|
|
13
15
|
removeElement,
|
|
14
|
-
RenderArrayElements,
|
|
15
16
|
} from "@react-typed-forms/core";
|
|
16
17
|
import {
|
|
17
18
|
ActionStyle,
|
|
18
19
|
AdornmentPlacement,
|
|
19
20
|
ArrayActionOptions,
|
|
21
|
+
CheckEntryClasses,
|
|
22
|
+
ChildNodeSpec,
|
|
20
23
|
ControlAdornment,
|
|
21
24
|
ControlDefinition,
|
|
22
|
-
|
|
23
|
-
ControlState,
|
|
25
|
+
ControlDisableType,
|
|
24
26
|
CustomDisplay,
|
|
27
|
+
dataControl,
|
|
25
28
|
DataControlDefinition,
|
|
26
29
|
defaultSchemaInterface,
|
|
27
30
|
DisplayData,
|
|
28
31
|
DisplayDataType,
|
|
29
32
|
FieldOption,
|
|
30
|
-
|
|
31
|
-
FormNode,
|
|
32
|
-
FormState,
|
|
33
|
+
FormStateNode,
|
|
33
34
|
GroupRenderOptions,
|
|
34
|
-
GroupRenderType,
|
|
35
35
|
isActionControl,
|
|
36
36
|
isDataControl,
|
|
37
37
|
isDisplayControl,
|
|
@@ -54,9 +54,10 @@ import {
|
|
|
54
54
|
getExternalEditData,
|
|
55
55
|
rendererClass,
|
|
56
56
|
} from "./util";
|
|
57
|
-
import { createAction
|
|
57
|
+
import { createAction } from "./controlBuilder";
|
|
58
58
|
import {
|
|
59
59
|
ActionRendererProps,
|
|
60
|
+
ControlActionContext,
|
|
60
61
|
ControlActionHandler,
|
|
61
62
|
ControlDataContext,
|
|
62
63
|
RunExpression,
|
|
@@ -86,6 +87,7 @@ export interface HtmlDivProperties {
|
|
|
86
87
|
html?: string;
|
|
87
88
|
nativeRef?: (e: HTMLElement | null) => void;
|
|
88
89
|
inline?: boolean;
|
|
90
|
+
role?: string;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
export interface HtmlInputProperties {
|
|
@@ -104,6 +106,8 @@ export interface HtmlInputProperties {
|
|
|
104
106
|
inputRef?: (e: HTMLElement | null) => void;
|
|
105
107
|
onChangeValue?: (value: string) => void;
|
|
106
108
|
onChangeChecked?: (checked: boolean) => void;
|
|
109
|
+
"aria-describedby"?: string;
|
|
110
|
+
"aria-invalid"?: boolean;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
export interface HtmlButtonProperties {
|
|
@@ -118,7 +122,35 @@ export interface HtmlButtonProperties {
|
|
|
118
122
|
notWrapInText?: boolean;
|
|
119
123
|
androidRippleColor?: string;
|
|
120
124
|
nonTextContent?: boolean;
|
|
125
|
+
"aria-expanded"?: boolean;
|
|
126
|
+
"aria-controls"?: string;
|
|
121
127
|
}
|
|
128
|
+
|
|
129
|
+
export interface CheckRendererOptions {
|
|
130
|
+
className?: string;
|
|
131
|
+
entryClass?: string;
|
|
132
|
+
checkClass?: string;
|
|
133
|
+
labelClass?: string;
|
|
134
|
+
entryWrapperClass?: string;
|
|
135
|
+
selectedClass?: string;
|
|
136
|
+
notSelectedClass?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface CheckButtonsProps {
|
|
140
|
+
id?: string;
|
|
141
|
+
className?: string;
|
|
142
|
+
options?: FieldOption[] | null;
|
|
143
|
+
control: Control<any>;
|
|
144
|
+
classes: CheckRendererOptions;
|
|
145
|
+
controlClasses?: CheckEntryClasses;
|
|
146
|
+
readonly?: boolean;
|
|
147
|
+
type: "checkbox" | "radio";
|
|
148
|
+
isChecked: (c: Control<any>, o: FieldOption) => boolean;
|
|
149
|
+
setChecked: (c: Control<any>, o: FieldOption, checked: boolean) => void;
|
|
150
|
+
entryAdornment?: (c: FieldOption, i: number, selected: boolean) => ReactNode;
|
|
151
|
+
renderer: FormRenderer;
|
|
152
|
+
}
|
|
153
|
+
|
|
122
154
|
export interface HtmlComponents {
|
|
123
155
|
Div: ComponentType<HtmlDivProperties>;
|
|
124
156
|
Span: ElementType<HTMLAttributes<HTMLSpanElement>>;
|
|
@@ -128,6 +160,7 @@ export interface HtmlComponents {
|
|
|
128
160
|
B: ElementType<HTMLAttributes<HTMLElement>>;
|
|
129
161
|
H1: ElementType<HTMLAttributes<HTMLElement>>;
|
|
130
162
|
Input: ComponentType<HtmlInputProperties>;
|
|
163
|
+
CheckButtons: ComponentType<CheckButtonsProps>;
|
|
131
164
|
}
|
|
132
165
|
/**
|
|
133
166
|
* Interface for rendering different types of form controls.
|
|
@@ -214,6 +247,8 @@ export interface FormRenderer {
|
|
|
214
247
|
renderLabelText: (props: ReactNode) => ReactNode;
|
|
215
248
|
|
|
216
249
|
html: HtmlComponents;
|
|
250
|
+
|
|
251
|
+
resolveChildren(c: FormStateNode): ChildNodeSpec[];
|
|
217
252
|
}
|
|
218
253
|
|
|
219
254
|
export interface AdornmentProps {
|
|
@@ -221,7 +256,7 @@ export interface AdornmentProps {
|
|
|
221
256
|
dataContext: ControlDataContext;
|
|
222
257
|
runExpression?: RunExpression;
|
|
223
258
|
designMode?: boolean;
|
|
224
|
-
|
|
259
|
+
formNode: FormStateNode;
|
|
225
260
|
}
|
|
226
261
|
|
|
227
262
|
export const AppendAdornmentPriority = 0;
|
|
@@ -240,14 +275,16 @@ export interface ArrayRendererProps {
|
|
|
240
275
|
editAction?: (elemIndex: number) => ActionRendererProps;
|
|
241
276
|
renderElement: (
|
|
242
277
|
elemIndex: number,
|
|
243
|
-
wrapEntry: (children: ReactNode) => ReactNode,
|
|
278
|
+
wrapEntry: (key: Key, children: ReactNode) => ReactNode,
|
|
244
279
|
) => ReactNode;
|
|
245
|
-
arrayControl
|
|
280
|
+
arrayControl?: Control<any[] | undefined | null>;
|
|
281
|
+
getElementCount(): number;
|
|
246
282
|
className?: string;
|
|
247
283
|
style?: React.CSSProperties;
|
|
248
284
|
min?: number | null;
|
|
249
285
|
max?: number | null;
|
|
250
286
|
disabled?: boolean;
|
|
287
|
+
childOverrideClass?: string | null;
|
|
251
288
|
}
|
|
252
289
|
export interface Visibility {
|
|
253
290
|
visible: boolean;
|
|
@@ -262,6 +299,7 @@ export interface RenderedLayout {
|
|
|
262
299
|
label?: ReactNode;
|
|
263
300
|
children?: ReactNode;
|
|
264
301
|
errorControl?: Control<any>;
|
|
302
|
+
errorId?: string;
|
|
265
303
|
className?: string;
|
|
266
304
|
style?: React.CSSProperties;
|
|
267
305
|
wrapLayout: (layout: ReactElement) => ReactElement;
|
|
@@ -283,6 +321,7 @@ export interface VisibilityRendererProps extends RenderedControl {
|
|
|
283
321
|
export interface ControlLayoutProps {
|
|
284
322
|
label?: LabelRendererProps;
|
|
285
323
|
errorControl?: Control<any>;
|
|
324
|
+
errorId?: string;
|
|
286
325
|
adornments?: AdornmentRenderer[];
|
|
287
326
|
children?: ReactNode;
|
|
288
327
|
processLayout?: (props: ControlLayoutProps) => ControlLayoutProps;
|
|
@@ -386,14 +425,12 @@ export interface DisplayRendererProps {
|
|
|
386
425
|
}
|
|
387
426
|
|
|
388
427
|
export interface ParentRendererProps {
|
|
389
|
-
formNode:
|
|
390
|
-
state: ControlState;
|
|
428
|
+
formNode: FormStateNode;
|
|
391
429
|
renderChild: ChildRenderer;
|
|
392
430
|
className?: string;
|
|
393
431
|
textClass?: string;
|
|
394
432
|
style?: React.CSSProperties;
|
|
395
433
|
dataContext: ControlDataContext;
|
|
396
|
-
getChildState(node: FormNode, parent?: SchemaDataNode): ControlState;
|
|
397
434
|
runExpression: RunExpression;
|
|
398
435
|
designMode?: boolean;
|
|
399
436
|
actionOnClick?: ControlActionHandler;
|
|
@@ -417,6 +454,7 @@ export interface DataRendererProps extends ParentRendererProps {
|
|
|
417
454
|
dataNode: SchemaDataNode;
|
|
418
455
|
displayOnly: boolean;
|
|
419
456
|
inline: boolean;
|
|
457
|
+
errorId: string;
|
|
420
458
|
}
|
|
421
459
|
|
|
422
460
|
export interface ControlRenderProps {
|
|
@@ -431,7 +469,6 @@ export type CreateDataProps = (
|
|
|
431
469
|
) => DataRendererProps;
|
|
432
470
|
|
|
433
471
|
export interface ControlRenderOptions extends ControlClasses {
|
|
434
|
-
formState?: FormState;
|
|
435
472
|
useDataHook?: (c: ControlDefinition) => CreateDataProps;
|
|
436
473
|
actionOnClick?: ControlActionHandler;
|
|
437
474
|
customDisplay?: (
|
|
@@ -450,14 +487,13 @@ export interface ControlRenderOptions extends ControlClasses {
|
|
|
450
487
|
clearHidden?: boolean;
|
|
451
488
|
stateKey?: string;
|
|
452
489
|
schemaInterface?: SchemaInterface;
|
|
453
|
-
variables?: Record<string, any>;
|
|
490
|
+
variables?: (changes: ChangeListenerFunc<any>) => Record<string, any>;
|
|
454
491
|
}
|
|
455
492
|
|
|
456
493
|
export function defaultDataProps(
|
|
457
494
|
{
|
|
458
|
-
|
|
495
|
+
formNode,
|
|
459
496
|
style,
|
|
460
|
-
allowedOptions,
|
|
461
497
|
schemaInterface = defaultSchemaInterface,
|
|
462
498
|
styleClass,
|
|
463
499
|
textClass: tc,
|
|
@@ -473,34 +509,22 @@ export function defaultDataProps(
|
|
|
473
509
|
const className = rendererClass(styleClass, definition.styleClass);
|
|
474
510
|
const textClass = rendererClass(tc, definition.textClass);
|
|
475
511
|
const required = !!definition.required && !displayOnly;
|
|
476
|
-
const
|
|
477
|
-
const _allowed = allowedOptions ?? [];
|
|
478
|
-
const allowed = Array.isArray(_allowed) ? _allowed : [_allowed];
|
|
512
|
+
const id = "c" + control.uniqueId;
|
|
479
513
|
return {
|
|
480
514
|
dataNode,
|
|
515
|
+
formNode,
|
|
481
516
|
definition,
|
|
482
517
|
control,
|
|
483
518
|
field,
|
|
484
|
-
id
|
|
519
|
+
id,
|
|
520
|
+
errorId: "err_" + id,
|
|
485
521
|
inline: !!inline,
|
|
486
|
-
options:
|
|
487
|
-
|
|
488
|
-
? allowed
|
|
489
|
-
.map((x) =>
|
|
490
|
-
typeof x === "object"
|
|
491
|
-
? x
|
|
492
|
-
: fieldOptions?.find((y) => y.value == x) ?? {
|
|
493
|
-
name: x.toString(),
|
|
494
|
-
value: x,
|
|
495
|
-
},
|
|
496
|
-
)
|
|
497
|
-
.filter((x) => x != null)
|
|
498
|
-
: fieldOptions,
|
|
499
|
-
readonly: !!formOptions.readonly,
|
|
522
|
+
options: formNode.resolved.fieldOptions,
|
|
523
|
+
readonly: formNode.readonly,
|
|
500
524
|
displayOnly: !!displayOnly,
|
|
501
525
|
renderOptions: definition.renderOptions ?? { type: "Standard" },
|
|
502
526
|
required,
|
|
503
|
-
hidden:
|
|
527
|
+
hidden: !formNode.visible,
|
|
504
528
|
className,
|
|
505
529
|
textClass,
|
|
506
530
|
style,
|
|
@@ -509,7 +533,6 @@ export function defaultDataProps(
|
|
|
509
533
|
}
|
|
510
534
|
|
|
511
535
|
export interface ChildRendererOptions {
|
|
512
|
-
parentDataNode?: SchemaDataNode;
|
|
513
536
|
inline?: boolean;
|
|
514
537
|
displayOnly?: boolean;
|
|
515
538
|
styleClass?: string;
|
|
@@ -517,28 +540,21 @@ export interface ChildRendererOptions {
|
|
|
517
540
|
labelClass?: string;
|
|
518
541
|
labelTextClass?: string;
|
|
519
542
|
actionOnClick?: ControlActionHandler;
|
|
520
|
-
stateKey?: string;
|
|
521
|
-
variables?: Record<string, any>;
|
|
522
543
|
}
|
|
523
544
|
|
|
524
545
|
export type ChildRenderer = (
|
|
525
|
-
|
|
526
|
-
child: FormNode,
|
|
546
|
+
child: FormStateNode,
|
|
527
547
|
options?: ChildRendererOptions,
|
|
528
548
|
) => ReactNode;
|
|
529
549
|
|
|
530
550
|
export interface RenderLayoutProps {
|
|
531
|
-
formNode:
|
|
551
|
+
formNode: FormStateNode;
|
|
532
552
|
renderer: FormRenderer;
|
|
533
|
-
state: ControlState;
|
|
534
553
|
renderChild: ChildRenderer;
|
|
535
554
|
createDataProps: CreateDataProps;
|
|
536
|
-
formOptions: FormContextOptions;
|
|
537
555
|
dataContext: ControlDataContext;
|
|
538
556
|
control?: Control<any>;
|
|
539
557
|
style?: React.CSSProperties;
|
|
540
|
-
allowedOptions?: any[];
|
|
541
|
-
getChildState(node: FormNode, parent?: SchemaDataNode): ControlState;
|
|
542
558
|
runExpression: RunExpression;
|
|
543
559
|
|
|
544
560
|
actionOnClick?: ControlActionHandler;
|
|
@@ -573,14 +589,11 @@ export function renderControlLayout(
|
|
|
573
589
|
styleClass,
|
|
574
590
|
textClass,
|
|
575
591
|
formNode,
|
|
576
|
-
formOptions,
|
|
577
592
|
actionOnClick,
|
|
578
|
-
state,
|
|
579
|
-
getChildState,
|
|
580
593
|
inline,
|
|
581
594
|
displayOnly,
|
|
582
595
|
} = props;
|
|
583
|
-
const c =
|
|
596
|
+
const c = formNode.definition;
|
|
584
597
|
if (isDataControl(c)) {
|
|
585
598
|
return renderData(c);
|
|
586
599
|
}
|
|
@@ -598,7 +611,6 @@ export function renderControlLayout(
|
|
|
598
611
|
inline,
|
|
599
612
|
processLayout: renderer.renderGroup({
|
|
600
613
|
formNode,
|
|
601
|
-
state,
|
|
602
614
|
definition: c,
|
|
603
615
|
renderChild,
|
|
604
616
|
runExpression,
|
|
@@ -609,7 +621,6 @@ export function renderControlLayout(
|
|
|
609
621
|
style,
|
|
610
622
|
designMode,
|
|
611
623
|
actionOnClick,
|
|
612
|
-
getChildState,
|
|
613
624
|
}),
|
|
614
625
|
label: {
|
|
615
626
|
label: c.title,
|
|
@@ -625,6 +636,7 @@ export function renderControlLayout(
|
|
|
625
636
|
const actionStyle = c.actionStyle ?? ActionStyle.Button;
|
|
626
637
|
const actionContent =
|
|
627
638
|
actionStyle == ActionStyle.Group ? renderActionGroup() : undefined;
|
|
639
|
+
const handler = props.actionOnClick?.(c.actionId, actionData, dataContext);
|
|
628
640
|
return {
|
|
629
641
|
inline,
|
|
630
642
|
children: renderer.renderAction({
|
|
@@ -637,24 +649,49 @@ export function renderControlLayout(
|
|
|
637
649
|
iconPlacement: c.iconPlacement,
|
|
638
650
|
icon: c.icon,
|
|
639
651
|
inline,
|
|
640
|
-
disabled:
|
|
641
|
-
onClick:
|
|
642
|
-
|
|
643
|
-
|
|
652
|
+
disabled: formNode.disabled,
|
|
653
|
+
onClick: handler
|
|
654
|
+
? () => {
|
|
655
|
+
let disableType: ControlDisableType =
|
|
656
|
+
c.disableType ?? ControlDisableType.Self;
|
|
657
|
+
const actionContext: ControlActionContext = {
|
|
658
|
+
disableForm(type: ControlDisableType) {
|
|
659
|
+
disableType = type;
|
|
660
|
+
},
|
|
661
|
+
runAction(
|
|
662
|
+
actionId: string,
|
|
663
|
+
actionData?: any,
|
|
664
|
+
): void | Promise<any> {
|
|
665
|
+
const h = props.actionOnClick?.(
|
|
666
|
+
actionId,
|
|
667
|
+
actionData,
|
|
668
|
+
dataContext,
|
|
669
|
+
);
|
|
670
|
+
if (h) {
|
|
671
|
+
h(actionContext);
|
|
672
|
+
}
|
|
673
|
+
return;
|
|
674
|
+
},
|
|
675
|
+
};
|
|
676
|
+
const r = handler(actionContext);
|
|
677
|
+
if (r instanceof Promise) {
|
|
678
|
+
const cleanup = formNode.ui.getDisabler(disableType)();
|
|
679
|
+
formNode.setBusy(true);
|
|
680
|
+
r.then(() => {
|
|
681
|
+
cleanup();
|
|
682
|
+
formNode.setBusy(false);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
: () => {},
|
|
644
687
|
className: rendererClass(styleClass, c.styleClass),
|
|
645
688
|
style,
|
|
689
|
+
busy: formNode.busy,
|
|
646
690
|
}),
|
|
647
691
|
};
|
|
648
692
|
|
|
649
693
|
function renderActionGroup() {
|
|
650
|
-
|
|
651
|
-
const childDef = {
|
|
652
|
-
type: ControlDefinitionType.Group,
|
|
653
|
-
groupOptions: { type: GroupRenderType.Contents, hideTitle: true },
|
|
654
|
-
children: childDefs,
|
|
655
|
-
};
|
|
656
|
-
const childNode: FormNode = formNode.createChildNode("child", childDef);
|
|
657
|
-
return renderChild("child", childNode, {});
|
|
694
|
+
return <>{formNode.children.map((x) => renderChild(x))}</>;
|
|
658
695
|
}
|
|
659
696
|
}
|
|
660
697
|
if (isDisplayControl(c)) {
|
|
@@ -700,6 +737,7 @@ export function renderControlLayout(
|
|
|
700
737
|
textClass: rendererClass(labelTextClass, c.labelTextClass),
|
|
701
738
|
},
|
|
702
739
|
errorControl: control,
|
|
740
|
+
errorId: rendererProps.errorId,
|
|
703
741
|
};
|
|
704
742
|
}
|
|
705
743
|
}
|
|
@@ -713,6 +751,7 @@ type MarkupKeys = keyof Omit<
|
|
|
713
751
|
| "readonly"
|
|
714
752
|
| "disabled"
|
|
715
753
|
| "inline"
|
|
754
|
+
| "errorId"
|
|
716
755
|
>;
|
|
717
756
|
export function appendMarkup(
|
|
718
757
|
k: MarkupKeys,
|
|
@@ -782,6 +821,7 @@ export function renderLayoutParts(
|
|
|
782
821
|
label,
|
|
783
822
|
adornments,
|
|
784
823
|
inline,
|
|
824
|
+
errorId,
|
|
785
825
|
} = props.processLayout?.(props) ?? props;
|
|
786
826
|
const layout: RenderedLayout = {
|
|
787
827
|
children,
|
|
@@ -789,6 +829,7 @@ export function renderLayoutParts(
|
|
|
789
829
|
style,
|
|
790
830
|
className: className!,
|
|
791
831
|
inline,
|
|
832
|
+
errorId,
|
|
792
833
|
wrapLayout: (x) => x,
|
|
793
834
|
};
|
|
794
835
|
(adornments ?? [])
|
|
@@ -818,11 +859,16 @@ export function getLengthRestrictions(definition: DataControlDefinition) {
|
|
|
818
859
|
|
|
819
860
|
export function createArrayActions(
|
|
820
861
|
control: Control<any[]>,
|
|
862
|
+
getElementCount: () => number,
|
|
821
863
|
field: SchemaField,
|
|
822
864
|
options?: ArrayActionOptions,
|
|
823
865
|
): Pick<
|
|
824
866
|
ArrayRendererProps,
|
|
825
|
-
|
|
867
|
+
| "addAction"
|
|
868
|
+
| "removeAction"
|
|
869
|
+
| "editAction"
|
|
870
|
+
| "arrayControl"
|
|
871
|
+
| "getElementCount"
|
|
826
872
|
> {
|
|
827
873
|
const noun = field.displayName ?? field.field;
|
|
828
874
|
const {
|
|
@@ -841,6 +887,7 @@ export function createArrayActions(
|
|
|
841
887
|
} = options ?? {};
|
|
842
888
|
return {
|
|
843
889
|
arrayControl: control,
|
|
890
|
+
getElementCount,
|
|
844
891
|
addAction:
|
|
845
892
|
!readonly && !noAdd
|
|
846
893
|
? makeAdd(() => {
|
|
@@ -941,7 +988,7 @@ export function createArrayActions(
|
|
|
941
988
|
|
|
942
989
|
export function applyArrayLengthRestrictions(
|
|
943
990
|
{
|
|
944
|
-
|
|
991
|
+
getElementCount,
|
|
945
992
|
min,
|
|
946
993
|
max,
|
|
947
994
|
editAction,
|
|
@@ -953,7 +1000,7 @@ export function applyArrayLengthRestrictions(
|
|
|
953
1000
|
| "addAction"
|
|
954
1001
|
| "removeAction"
|
|
955
1002
|
| "editAction"
|
|
956
|
-
| "
|
|
1003
|
+
| "getElementCount"
|
|
957
1004
|
| "min"
|
|
958
1005
|
| "max"
|
|
959
1006
|
| "required"
|
|
@@ -964,7 +1011,7 @@ export function applyArrayLengthRestrictions(
|
|
|
964
1011
|
removeDisabled: boolean;
|
|
965
1012
|
} {
|
|
966
1013
|
const [removeAllowed, addAllowed] = applyLengthRestrictions(
|
|
967
|
-
|
|
1014
|
+
getElementCount(),
|
|
968
1015
|
min == null && required ? 1 : min,
|
|
969
1016
|
max,
|
|
970
1017
|
true,
|
|
@@ -980,18 +1027,13 @@ export function applyArrayLengthRestrictions(
|
|
|
980
1027
|
}
|
|
981
1028
|
|
|
982
1029
|
export function fieldOptionAdornment(p: DataRendererProps) {
|
|
983
|
-
return (o: FieldOption, fieldIndex: number, selected: boolean) =>
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
variables: { formData: { option: o, optionSelected: selected } },
|
|
991
|
-
})
|
|
992
|
-
}
|
|
993
|
-
/>
|
|
994
|
-
);
|
|
1030
|
+
return (o: FieldOption, fieldIndex: number, selected: boolean) => {
|
|
1031
|
+
const fieldChild = p.formNode.children.find(
|
|
1032
|
+
(x) => x.meta["fieldOptionValue"] === o.value,
|
|
1033
|
+
);
|
|
1034
|
+
if (fieldChild) return p.renderChild(fieldChild);
|
|
1035
|
+
return undefined;
|
|
1036
|
+
};
|
|
995
1037
|
}
|
|
996
1038
|
|
|
997
1039
|
export function lookupChildDataContext(
|
|
@@ -1002,3 +1044,7 @@ export function lookupChildDataContext(
|
|
|
1002
1044
|
const dataNode = lookupDataNode(c, parentNode);
|
|
1003
1045
|
return { ...dataContext, parentNode, dataNode };
|
|
1004
1046
|
}
|
|
1047
|
+
|
|
1048
|
+
function getBusyControl(formNode: FormStateNode) {
|
|
1049
|
+
return formNode.ensureMeta("$busy", () => newControl(false));
|
|
1050
|
+
}
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
LabelType,
|
|
13
13
|
VisibilityRendererProps,
|
|
14
14
|
} from "./controlRender";
|
|
15
|
-
import { hasOptions } from "./util";
|
|
16
15
|
import {
|
|
17
16
|
ActionRendererRegistration,
|
|
18
17
|
AdornmentRendererRegistration,
|
|
@@ -26,7 +25,15 @@ import {
|
|
|
26
25
|
RendererRegistration,
|
|
27
26
|
VisibilityRendererRegistration,
|
|
28
27
|
} from "./renderers";
|
|
29
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
ChildNodeSpec,
|
|
30
|
+
DataRenderType,
|
|
31
|
+
defaultResolveChildNodes,
|
|
32
|
+
FormStateNode,
|
|
33
|
+
isDataControl,
|
|
34
|
+
RenderOptions,
|
|
35
|
+
SchemaDataNode,
|
|
36
|
+
} from "@astroapps/forms-core";
|
|
30
37
|
import { ActionRendererProps } from "./types";
|
|
31
38
|
|
|
32
39
|
export function createFormRenderer(
|
|
@@ -60,6 +67,19 @@ export function createFormRenderer(
|
|
|
60
67
|
renderVisibility,
|
|
61
68
|
renderLabelText,
|
|
62
69
|
html: defaultRenderers.html,
|
|
70
|
+
resolveChildren(c: FormStateNode): ChildNodeSpec[] {
|
|
71
|
+
const def = c.definition;
|
|
72
|
+
if (isDataControl(def)) {
|
|
73
|
+
if (!c.dataNode) return [];
|
|
74
|
+
const matching = matchData(
|
|
75
|
+
c,
|
|
76
|
+
def.renderOptions ?? { type: DataRenderType.Standard },
|
|
77
|
+
c.dataNode!,
|
|
78
|
+
);
|
|
79
|
+
if (matching?.resolveChildren) return matching.resolveChildren(c);
|
|
80
|
+
}
|
|
81
|
+
return defaultResolveChildNodes(c);
|
|
82
|
+
},
|
|
63
83
|
};
|
|
64
84
|
|
|
65
85
|
function renderVisibility(props: VisibilityRendererProps) {
|
|
@@ -103,37 +123,33 @@ export function createFormRenderer(
|
|
|
103
123
|
return renderer.render(props, labelStart, labelEnd, formRenderers);
|
|
104
124
|
}
|
|
105
125
|
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
126
|
+
function matchData(
|
|
127
|
+
formState: FormStateNode,
|
|
128
|
+
renderOptions: RenderOptions,
|
|
129
|
+
dataNode: SchemaDataNode,
|
|
130
|
+
): DataRendererRegistration | undefined {
|
|
131
|
+
const field = dataNode.schema.field;
|
|
132
|
+
const options = (formState.resolved.fieldOptions?.length ?? 0) > 0;
|
|
112
133
|
const renderType = renderOptions.type;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const result = (renderer ?? defaultRenderers.data).render(
|
|
116
|
-
props,
|
|
117
|
-
formRenderers,
|
|
118
|
-
);
|
|
119
|
-
if (typeof result === "function") return result;
|
|
120
|
-
return (l) => ({ ...l, children: result });
|
|
134
|
+
return dataRegistrations.find(matchesRenderer);
|
|
121
135
|
|
|
122
136
|
function matchesRenderer(x: DataRendererRegistration) {
|
|
123
|
-
const noMatch = x.match ? !x.match(
|
|
137
|
+
const noMatch = x.match ? !x.match(formState, renderOptions) : undefined;
|
|
124
138
|
if (noMatch === true) return false;
|
|
125
139
|
const matchCollection =
|
|
126
140
|
(x.collection ?? false) ===
|
|
127
|
-
(
|
|
141
|
+
(dataNode.elementIndex == null && (field.collection ?? false));
|
|
128
142
|
const isSchemaAllowed =
|
|
129
143
|
!!x.schemaType && renderType == DataRenderType.Standard
|
|
130
144
|
? isOneOf(x.schemaType, field.type)
|
|
131
145
|
: undefined;
|
|
132
146
|
const isRendererAllowed =
|
|
133
147
|
!!x.renderType && isOneOf(x.renderType, renderType);
|
|
148
|
+
const optionsMatch =
|
|
149
|
+
isRendererAllowed || (x.options ?? false) === options;
|
|
134
150
|
return (
|
|
135
151
|
matchCollection &&
|
|
136
|
-
|
|
152
|
+
optionsMatch &&
|
|
137
153
|
(isSchemaAllowed ||
|
|
138
154
|
isRendererAllowed ||
|
|
139
155
|
(!x.renderType && !x.schemaType && noMatch === false))
|
|
@@ -141,6 +157,23 @@ export function createFormRenderer(
|
|
|
141
157
|
}
|
|
142
158
|
}
|
|
143
159
|
|
|
160
|
+
function renderData(
|
|
161
|
+
props: DataRendererProps,
|
|
162
|
+
): (layout: ControlLayoutProps) => ControlLayoutProps {
|
|
163
|
+
const renderer = matchData(
|
|
164
|
+
props.formNode,
|
|
165
|
+
props.renderOptions,
|
|
166
|
+
props.dataNode,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const result = (renderer ?? defaultRenderers.data).render(
|
|
170
|
+
props,
|
|
171
|
+
formRenderers,
|
|
172
|
+
);
|
|
173
|
+
if (typeof result === "function") return result;
|
|
174
|
+
return (l) => ({ ...l, children: result });
|
|
175
|
+
}
|
|
176
|
+
|
|
144
177
|
function renderGroup(
|
|
145
178
|
props: GroupRendererProps,
|
|
146
179
|
): (layout: ControlLayoutProps) => ControlLayoutProps {
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Control,
|
|
3
|
+
useComputed,
|
|
4
|
+
useControl,
|
|
5
|
+
useControlEffect,
|
|
6
|
+
} from "@react-typed-forms/core";
|
|
7
|
+
import { useEffect } from "react";
|
|
8
|
+
import {
|
|
9
|
+
ElementSelectedRenderOptions,
|
|
10
|
+
RenderOptions,
|
|
11
|
+
} from "@astroapps/forms-core";
|
|
12
|
+
import { RunExpression } from "../types";
|
|
13
|
+
import { setIncluded } from "../util";
|
|
14
|
+
|
|
15
|
+
export function useElementSelectedRenderer({
|
|
16
|
+
runExpression,
|
|
17
|
+
renderOptions,
|
|
18
|
+
control,
|
|
19
|
+
}: {
|
|
20
|
+
runExpression: RunExpression;
|
|
21
|
+
renderOptions: RenderOptions;
|
|
22
|
+
control: Control<any>;
|
|
23
|
+
}) {
|
|
24
|
+
const elementValue = useControl();
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
runExpression(
|
|
27
|
+
elementValue,
|
|
28
|
+
(renderOptions as ElementSelectedRenderOptions).elementExpression,
|
|
29
|
+
(v) => (elementValue.value = v as any),
|
|
30
|
+
);
|
|
31
|
+
}, []);
|
|
32
|
+
const isSelected = useComputed(
|
|
33
|
+
() =>
|
|
34
|
+
control.as<any[] | undefined>().value?.includes(elementValue.value) ??
|
|
35
|
+
false,
|
|
36
|
+
);
|
|
37
|
+
const selControl = useControl(() => isSelected.current.value);
|
|
38
|
+
selControl.value = isSelected.value;
|
|
39
|
+
useControlEffect(
|
|
40
|
+
() => selControl.value,
|
|
41
|
+
(v) => {
|
|
42
|
+
control
|
|
43
|
+
.as<any[] | undefined>()
|
|
44
|
+
.setValue((x) => setIncluded(x ?? [], elementValue.value, v));
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
return selControl;
|
|
48
|
+
}
|