@developer_tribe/react-builder 1.2.43 → 1.2.44-test.2
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/dist/attributes-editor/Field.d.ts +3 -1
- package/dist/attributes-editor/attributesEditorModelTypes.d.ts +3 -0
- package/dist/attributes-editor/useAttributesEditorModel.d.ts +1 -1
- package/dist/build-components/Checkbox/Checkbox.d.ts +6 -0
- package/dist/build-components/Checkbox/CheckboxProps.generated.d.ts +67 -0
- package/dist/build-components/FormCheckbox/FormCheckbox.d.ts +3 -0
- package/dist/build-components/FormCheckbox/FormCheckboxProps.generated.d.ts +69 -0
- package/dist/build-components/FormErrorText/FormErrorText.d.ts +3 -0
- package/dist/build-components/FormErrorText/FormErrorTextProps.generated.d.ts +61 -0
- package/dist/build-components/FormProvider/FormProvider.d.ts +11 -0
- package/dist/build-components/FormProvider/FormProviderProps.generated.d.ts +55 -0
- package/dist/build-components/FormSubmitButton/FormSubmitButton.d.ts +2 -0
- package/dist/build-components/FormSubmitButton/FormSubmitButtonProps.generated.d.ts +78 -0
- package/dist/build-components/GlobalProvider/GlobalContext.d.ts +28 -0
- package/dist/build-components/GlobalProvider/GlobalProvider.d.ts +5 -0
- package/dist/build-components/GlobalProvider/GlobalProviderProps.generated.d.ts +60 -0
- package/dist/build-components/GlobalProvider/globalProviderUtils.d.ts +28 -0
- package/dist/build-components/GlobalProvider/useGlobalNavigation.d.ts +19 -0
- package/dist/build-components/GlobalProvider/useGlobalProviderLogic.d.ts +15 -0
- package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +22 -10
- package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +2 -0
- package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +2 -0
- package/dist/build-components/SystemButton/SystemButton.d.ts +7 -0
- package/dist/build-components/SystemButton/SystemButtonProps.generated.d.ts +76 -0
- package/dist/build-components/SystemButton/usePlacementButtonEvents.d.ts +35 -0
- package/dist/build-components/TermsProvider/TermsProvider.d.ts +5 -0
- package/dist/build-components/TermsProvider/TermsProviderProps.generated.d.ts +55 -0
- package/dist/build-components/WebView/WebView.d.ts +2 -0
- package/dist/build-components/WebView/WebViewProps.generated.d.ts +59 -0
- package/dist/build-components/index.d.ts +10 -1
- package/dist/build-components/patterns.generated.d.ts +5645 -1686
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.web.cjs.js +5 -5
- package/dist/index.web.cjs.js.map +1 -1
- package/dist/index.web.d.ts +1 -0
- package/dist/index.web.esm.js +4 -4
- package/dist/index.web.esm.js.map +1 -1
- package/dist/mockOS/context/MockOSContextBase.d.ts +3 -1
- package/dist/styles.css +1 -1
- package/dist/types/PreviewConfig.d.ts +1 -1
- package/dist/utils/nodeTree.d.ts +18 -0
- package/package.json +2 -1
- package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +68 -4
- package/src/assets/meta.json +1 -1
- package/src/assets/prompt-scheme-onboard.generated.ts +1 -1
- package/src/assets/prompt-scheme-paywall.generated.ts +1 -1
- package/src/assets/samples/getSamples.ts +7 -0
- package/src/assets/samples/global-onboard-flow.json +735 -0
- package/src/assets/samples/terms-and-privacy-no-form.json +108 -0
- package/src/assets/samples/terms-and-privacy.json +130 -0
- package/src/attributes-editor/AttributesEditorView.tsx +3 -0
- package/src/attributes-editor/Field.tsx +91 -2
- package/src/attributes-editor/attributesEditorModelTypes.ts +3 -0
- package/src/attributes-editor/useAttributesEditorModel.ts +8 -0
- package/src/build-components/Checkbox/Checkbox.tsx +165 -0
- package/src/build-components/Checkbox/CheckboxProps.generated.ts +84 -0
- package/src/build-components/Checkbox/pattern.json +83 -0
- package/src/build-components/FormCheckbox/FormCheckbox.tsx +108 -0
- package/src/build-components/FormCheckbox/FormCheckboxProps.generated.ts +86 -0
- package/src/build-components/FormCheckbox/pattern.json +39 -0
- package/src/build-components/FormErrorText/FormErrorText.tsx +34 -0
- package/src/build-components/FormErrorText/FormErrorTextProps.generated.ts +78 -0
- package/src/build-components/FormErrorText/pattern.json +21 -0
- package/src/build-components/FormProvider/FormProvider.tsx +131 -0
- package/src/build-components/FormProvider/FormProviderProps.generated.ts +72 -0
- package/src/build-components/FormProvider/pattern.json +33 -0
- package/src/build-components/FormSubmitButton/FormSubmitButton.tsx +52 -0
- package/src/build-components/FormSubmitButton/FormSubmitButtonProps.generated.ts +114 -0
- package/src/build-components/FormSubmitButton/pattern.json +33 -0
- package/src/build-components/GlobalProvider/GlobalContext.ts +48 -0
- package/src/build-components/GlobalProvider/GlobalProvider.tsx +51 -0
- package/src/build-components/GlobalProvider/GlobalProviderProps.generated.ts +78 -0
- package/src/build-components/GlobalProvider/globalProviderUtils.ts +204 -0
- package/src/build-components/GlobalProvider/pattern.json +55 -0
- package/src/build-components/GlobalProvider/useGlobalNavigation.ts +65 -0
- package/src/build-components/GlobalProvider/useGlobalProviderLogic.ts +172 -0
- package/src/build-components/OnboardButton/OnboardButton.tsx +44 -36
- package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +40 -10
- package/src/build-components/OnboardButton/pattern.json +7 -4
- package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +12 -0
- package/src/build-components/OnboardProvider/pattern.json +9 -1
- package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +12 -0
- package/src/build-components/PaywallProvider/pattern.json +9 -1
- package/src/build-components/RenderNode.generated.tsx +46 -1
- package/src/build-components/SystemButton/SystemButton.tsx +74 -0
- package/src/build-components/SystemButton/SystemButtonProps.generated.ts +112 -0
- package/src/build-components/SystemButton/pattern.json +63 -0
- package/src/build-components/SystemButton/usePlacementButtonEvents.ts +114 -0
- package/src/build-components/TermsProvider/TermsProvider.tsx +45 -0
- package/src/build-components/TermsProvider/TermsProviderProps.generated.ts +82 -0
- package/src/build-components/TermsProvider/pattern.json +35 -0
- package/src/build-components/WebView/WebView.tsx +149 -0
- package/src/build-components/WebView/WebViewProps.generated.ts +76 -0
- package/src/build-components/WebView/pattern.json +71 -0
- package/src/build-components/index.ts +45 -0
- package/src/build-components/patterns.generated.ts +5735 -1557
- package/src/components/AttributesEditorPanel.tsx +1 -0
- package/src/index.web.ts +3 -0
- package/src/mockOS/components/MockOSRouter.tsx +21 -0
- package/src/mockOS/context/MockOSContext.tsx +7 -0
- package/src/mockOS/context/MockOSContextBase.ts +4 -0
- package/src/patterns/event-constants.json +19 -0
- package/src/styles/components/_checkbox.scss +19 -0
- package/src/styles/components/_global-provider.scss +131 -0
- package/src/styles/components/_webview.scss +52 -0
- package/src/styles/index.scss +4 -0
- package/src/types/PreviewConfig.ts +19 -0
- package/src/utils/analyseNodeByPatterns.ts +5 -2
- package/src/utils/nodeTree.ts +115 -0
- package/src/utils/projectColors.ts +4 -0
- package/src/.DS_Store +0 -0
- package/src/assets/.DS_Store +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* AUTO-GENERATED FILE - DO NOT EDIT */
|
|
2
|
+
|
|
3
|
+
import type { NodeData } from '../../types/Node';
|
|
4
|
+
|
|
5
|
+
export type FlexDirectionOptionType = 'row' | 'column';
|
|
6
|
+
export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
|
|
7
|
+
export type AlignItemsOptionType =
|
|
8
|
+
| 'flex-start'
|
|
9
|
+
| 'center'
|
|
10
|
+
| 'flex-end'
|
|
11
|
+
| 'stretch'
|
|
12
|
+
| 'baseline';
|
|
13
|
+
export type JustifyContentOptionType =
|
|
14
|
+
| 'flex-start'
|
|
15
|
+
| 'center'
|
|
16
|
+
| 'flex-end'
|
|
17
|
+
| 'space-between'
|
|
18
|
+
| 'space-around'
|
|
19
|
+
| 'space-evenly';
|
|
20
|
+
export type PositionOptionType = 'relative' | 'absolute';
|
|
21
|
+
|
|
22
|
+
export interface CheckboxStyleGenerated {
|
|
23
|
+
color?: string;
|
|
24
|
+
fontSize?: string;
|
|
25
|
+
fontFamily?: string;
|
|
26
|
+
fontWeight?: string;
|
|
27
|
+
textAlign?: string;
|
|
28
|
+
flexDirection?: FlexDirectionOptionType;
|
|
29
|
+
flexWrap?: FlexWrapOptionType;
|
|
30
|
+
alignItems?: AlignItemsOptionType;
|
|
31
|
+
justifyContent?: JustifyContentOptionType;
|
|
32
|
+
gap?: string;
|
|
33
|
+
padding?: string;
|
|
34
|
+
paddingHorizontal?: string;
|
|
35
|
+
paddingVertical?: string;
|
|
36
|
+
paddingTop?: string;
|
|
37
|
+
paddingBottom?: string;
|
|
38
|
+
paddingLeft?: string;
|
|
39
|
+
paddingRight?: string;
|
|
40
|
+
margin?: string;
|
|
41
|
+
marginHorizontal?: string;
|
|
42
|
+
marginVertical?: string;
|
|
43
|
+
marginTop?: string;
|
|
44
|
+
marginBottom?: string;
|
|
45
|
+
marginLeft?: string;
|
|
46
|
+
marginRight?: string;
|
|
47
|
+
backgroundColor?: string;
|
|
48
|
+
borderRadius?: number;
|
|
49
|
+
width?: string;
|
|
50
|
+
minWidth?: string;
|
|
51
|
+
maxWidth?: string;
|
|
52
|
+
height?: string;
|
|
53
|
+
minHeight?: string;
|
|
54
|
+
maxHeight?: string;
|
|
55
|
+
flex?: number;
|
|
56
|
+
position?: PositionOptionType;
|
|
57
|
+
top?: string;
|
|
58
|
+
bottom?: string;
|
|
59
|
+
left?: string;
|
|
60
|
+
right?: string;
|
|
61
|
+
zIndex?: number;
|
|
62
|
+
checkIconSize?: number;
|
|
63
|
+
checkIconStrokeWidth?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CheckboxPropsGenerated {
|
|
67
|
+
child: string;
|
|
68
|
+
attributes: {
|
|
69
|
+
styles?: CheckboxStyleGenerated;
|
|
70
|
+
adjustsFontSizeToFit?: boolean;
|
|
71
|
+
numberOfLines?: number;
|
|
72
|
+
translateCounter?: number;
|
|
73
|
+
scrollable?: boolean;
|
|
74
|
+
testID?: string;
|
|
75
|
+
checked?: boolean;
|
|
76
|
+
label?: string;
|
|
77
|
+
checkedColor?: string;
|
|
78
|
+
syncTermsAccepted?: boolean;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface CheckboxComponentProps {
|
|
83
|
+
node: NodeData<CheckboxPropsGenerated['attributes']>;
|
|
84
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"pattern": {
|
|
4
|
+
"type": "Checkbox",
|
|
5
|
+
"title": "Checkbox",
|
|
6
|
+
"description": "A standard checkbox component.",
|
|
7
|
+
"children": "never",
|
|
8
|
+
"extends": "Text",
|
|
9
|
+
"attributes": {
|
|
10
|
+
"checked": "boolean",
|
|
11
|
+
"label": "string",
|
|
12
|
+
"checkedColor": "color",
|
|
13
|
+
"syncTermsAccepted": "boolean",
|
|
14
|
+
"styles": {
|
|
15
|
+
"borderRadius": "number",
|
|
16
|
+
"checkIconSize": "number",
|
|
17
|
+
"checkIconStrokeWidth": "number"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"defaults": {
|
|
21
|
+
"checked": false,
|
|
22
|
+
"label": "view.terms.checkbox.label",
|
|
23
|
+
"styles": {
|
|
24
|
+
"borderRadius": 4,
|
|
25
|
+
"checkIconSize": 12,
|
|
26
|
+
"checkIconStrokeWidth": 1.7,
|
|
27
|
+
"flexDirection": "row"
|
|
28
|
+
},
|
|
29
|
+
"checkedColor": "THEME_COLORS.CHECKBOX_ACTIVE_COLOR"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"meta": {
|
|
33
|
+
"label": "Checkbox",
|
|
34
|
+
"description": "A checkbox with an optional label.",
|
|
35
|
+
"attributes": {
|
|
36
|
+
"checked": {
|
|
37
|
+
"label": "Checked",
|
|
38
|
+
"description": "Whether the checkbox is checked by default.",
|
|
39
|
+
"category": "other",
|
|
40
|
+
"sort": 1
|
|
41
|
+
},
|
|
42
|
+
"label": {
|
|
43
|
+
"label": "Label",
|
|
44
|
+
"description": "The text label next to the checkbox.",
|
|
45
|
+
"category": "other",
|
|
46
|
+
"sort": 2
|
|
47
|
+
},
|
|
48
|
+
"checkedColor": {
|
|
49
|
+
"label": "Checked color",
|
|
50
|
+
"description": "The color of the checkbox when checked.",
|
|
51
|
+
"category": "style",
|
|
52
|
+
"sort": 3
|
|
53
|
+
},
|
|
54
|
+
"styles": {
|
|
55
|
+
"width": {
|
|
56
|
+
"label": "Box Width",
|
|
57
|
+
"description": "Width of the checkbox box (px).",
|
|
58
|
+
"category": "style"
|
|
59
|
+
},
|
|
60
|
+
"height": {
|
|
61
|
+
"label": "Box Height",
|
|
62
|
+
"description": "Height of the checkbox box (px).",
|
|
63
|
+
"category": "style"
|
|
64
|
+
},
|
|
65
|
+
"borderRadius": {
|
|
66
|
+
"label": "Box Border Radius",
|
|
67
|
+
"description": "Border radius of the checkbox box (px).",
|
|
68
|
+
"category": "style"
|
|
69
|
+
},
|
|
70
|
+
"checkIconSize": {
|
|
71
|
+
"label": "Check Icon Size",
|
|
72
|
+
"description": "Size of the check icon inside the box (px).",
|
|
73
|
+
"category": "style"
|
|
74
|
+
},
|
|
75
|
+
"checkIconStrokeWidth": {
|
|
76
|
+
"label": "Check Icon Stroke Width",
|
|
77
|
+
"description": "Stroke width of the check icon inside the box.",
|
|
78
|
+
"category": "style"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { Controller } from 'react-hook-form';
|
|
3
|
+
import type {
|
|
4
|
+
FieldValues,
|
|
5
|
+
Control,
|
|
6
|
+
ControllerRenderProps,
|
|
7
|
+
} from 'react-hook-form';
|
|
8
|
+
import type { FormCheckboxComponentProps } from './FormCheckboxProps.generated';
|
|
9
|
+
import { formContext } from '../FormProvider/FormProvider';
|
|
10
|
+
import { Checkbox } from '../Checkbox/Checkbox';
|
|
11
|
+
import useNode from '../useNode';
|
|
12
|
+
import { useLogRender } from '../../utils/useLogRender';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Converts builder validation array to react-hook-form rules.
|
|
16
|
+
* Builder must send validation as key-value pairs; e.g. ['required'] or ['min', 2], ['max', 5].
|
|
17
|
+
* If 'min' or 'max' is the last element (no value following), that rule is skipped (no crash).
|
|
18
|
+
*/
|
|
19
|
+
function validationArrayToRules(validation: string[] | undefined): {
|
|
20
|
+
required?: boolean;
|
|
21
|
+
min?: number;
|
|
22
|
+
max?: number;
|
|
23
|
+
} {
|
|
24
|
+
const rules: { required?: boolean; min?: number; max?: number } = {};
|
|
25
|
+
if (!Array.isArray(validation)) return rules;
|
|
26
|
+
if (validation.includes('required')) rules.required = true;
|
|
27
|
+
const minIdx = validation.indexOf('min');
|
|
28
|
+
if (minIdx >= 0) {
|
|
29
|
+
if (validation[minIdx + 1] != null) {
|
|
30
|
+
const minVal = Number(validation[minIdx + 1]);
|
|
31
|
+
if (Number.isFinite(minVal)) rules.min = minVal;
|
|
32
|
+
else {
|
|
33
|
+
console.warn(
|
|
34
|
+
'[FormCheckbox] validation "min" rule skipped: value must be a number.',
|
|
35
|
+
'Received:',
|
|
36
|
+
validation[minIdx + 1],
|
|
37
|
+
'— Expected e.g. ["min", 2].',
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
console.warn(
|
|
42
|
+
'[FormCheckbox] validation "min" rule skipped: no value after "min".',
|
|
43
|
+
'Expected e.g. ["min", 2], got:',
|
|
44
|
+
validation,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const maxIdx = validation.indexOf('max');
|
|
49
|
+
if (maxIdx >= 0) {
|
|
50
|
+
if (validation[maxIdx + 1] != null) {
|
|
51
|
+
const maxVal = Number(validation[maxIdx + 1]);
|
|
52
|
+
if (Number.isFinite(maxVal)) rules.max = maxVal;
|
|
53
|
+
else {
|
|
54
|
+
console.warn(
|
|
55
|
+
'[FormCheckbox] validation "max" rule skipped: value must be a number.',
|
|
56
|
+
'Received:',
|
|
57
|
+
validation[maxIdx + 1],
|
|
58
|
+
'— Expected e.g. ["max", 5].',
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
console.warn(
|
|
63
|
+
'[FormCheckbox] validation "max" rule skipped: no value after "max".',
|
|
64
|
+
'Expected e.g. ["max", 5], got:',
|
|
65
|
+
validation,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return rules;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** FormCheckbox wraps Checkbox with RHF; UI and styling come from Checkbox. */
|
|
73
|
+
// eslint-disable-next-line react/prop-types
|
|
74
|
+
export function FormCheckbox({ node }: FormCheckboxComponentProps) {
|
|
75
|
+
useLogRender('FormCheckbox');
|
|
76
|
+
node = useNode(node);
|
|
77
|
+
const ctx = useContext(formContext);
|
|
78
|
+
// eslint-disable-next-line react/prop-types
|
|
79
|
+
const attrs = node?.attributes;
|
|
80
|
+
const name = attrs?.name as string | undefined;
|
|
81
|
+
const validation = attrs?.validation as string[] | undefined;
|
|
82
|
+
|
|
83
|
+
if (!ctx || !name) {
|
|
84
|
+
return <Checkbox node={node as Parameters<typeof Checkbox>[0]['node']} />;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const control = ctx.control as Control<FieldValues>;
|
|
88
|
+
const rules = validationArrayToRules(validation);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Controller
|
|
92
|
+
name={name}
|
|
93
|
+
control={control}
|
|
94
|
+
rules={rules}
|
|
95
|
+
render={({
|
|
96
|
+
field,
|
|
97
|
+
}: {
|
|
98
|
+
field: ControllerRenderProps<FieldValues, string>;
|
|
99
|
+
}) => (
|
|
100
|
+
<Checkbox
|
|
101
|
+
node={node as Parameters<typeof Checkbox>[0]['node']}
|
|
102
|
+
checked={Boolean(field.value)}
|
|
103
|
+
onCheckedChange={(value) => field.onChange(value)}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* AUTO-GENERATED FILE - DO NOT EDIT */
|
|
2
|
+
|
|
3
|
+
import type { NodeData } from '../../types/Node';
|
|
4
|
+
|
|
5
|
+
export type FlexDirectionOptionType = 'row' | 'column';
|
|
6
|
+
export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
|
|
7
|
+
export type AlignItemsOptionType =
|
|
8
|
+
| 'flex-start'
|
|
9
|
+
| 'center'
|
|
10
|
+
| 'flex-end'
|
|
11
|
+
| 'stretch'
|
|
12
|
+
| 'baseline';
|
|
13
|
+
export type JustifyContentOptionType =
|
|
14
|
+
| 'flex-start'
|
|
15
|
+
| 'center'
|
|
16
|
+
| 'flex-end'
|
|
17
|
+
| 'space-between'
|
|
18
|
+
| 'space-around'
|
|
19
|
+
| 'space-evenly';
|
|
20
|
+
export type PositionOptionType = 'relative' | 'absolute';
|
|
21
|
+
|
|
22
|
+
export interface FormCheckboxStyleGenerated {
|
|
23
|
+
borderRadius?: string;
|
|
24
|
+
checkIconSize?: number;
|
|
25
|
+
checkIconStrokeWidth?: number;
|
|
26
|
+
color?: string;
|
|
27
|
+
fontSize?: string;
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
fontWeight?: string;
|
|
30
|
+
textAlign?: string;
|
|
31
|
+
flexDirection?: FlexDirectionOptionType;
|
|
32
|
+
flexWrap?: FlexWrapOptionType;
|
|
33
|
+
alignItems?: AlignItemsOptionType;
|
|
34
|
+
justifyContent?: JustifyContentOptionType;
|
|
35
|
+
gap?: string;
|
|
36
|
+
padding?: string;
|
|
37
|
+
paddingHorizontal?: string;
|
|
38
|
+
paddingVertical?: string;
|
|
39
|
+
paddingTop?: string;
|
|
40
|
+
paddingBottom?: string;
|
|
41
|
+
paddingLeft?: string;
|
|
42
|
+
paddingRight?: string;
|
|
43
|
+
margin?: string;
|
|
44
|
+
marginHorizontal?: string;
|
|
45
|
+
marginVertical?: string;
|
|
46
|
+
marginTop?: string;
|
|
47
|
+
marginBottom?: string;
|
|
48
|
+
marginLeft?: string;
|
|
49
|
+
marginRight?: string;
|
|
50
|
+
backgroundColor?: string;
|
|
51
|
+
width?: string;
|
|
52
|
+
minWidth?: string;
|
|
53
|
+
maxWidth?: string;
|
|
54
|
+
height?: string;
|
|
55
|
+
minHeight?: string;
|
|
56
|
+
maxHeight?: string;
|
|
57
|
+
flex?: number;
|
|
58
|
+
position?: PositionOptionType;
|
|
59
|
+
top?: string;
|
|
60
|
+
bottom?: string;
|
|
61
|
+
left?: string;
|
|
62
|
+
right?: string;
|
|
63
|
+
zIndex?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface FormCheckboxPropsGenerated {
|
|
67
|
+
child: string;
|
|
68
|
+
attributes: {
|
|
69
|
+
styles?: FormCheckboxStyleGenerated;
|
|
70
|
+
checked?: boolean;
|
|
71
|
+
label?: string;
|
|
72
|
+
checkedColor?: string;
|
|
73
|
+
syncTermsAccepted?: boolean;
|
|
74
|
+
adjustsFontSizeToFit?: boolean;
|
|
75
|
+
numberOfLines?: number;
|
|
76
|
+
translateCounter?: number;
|
|
77
|
+
scrollable?: boolean;
|
|
78
|
+
testID?: string;
|
|
79
|
+
name?: string;
|
|
80
|
+
validation?: string[];
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface FormCheckboxComponentProps {
|
|
85
|
+
node: NodeData<FormCheckboxPropsGenerated['attributes']>;
|
|
86
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"pattern": {
|
|
4
|
+
"type": "FormCheckbox",
|
|
5
|
+
"title": "FormCheckbox",
|
|
6
|
+
"description": "Checkbox bound to FormProvider; supports validation (required, min, max).",
|
|
7
|
+
"children": "never",
|
|
8
|
+
"extends": "Checkbox",
|
|
9
|
+
"attributes": {
|
|
10
|
+
"name": "string",
|
|
11
|
+
"validation": "string[]"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"defaults": {
|
|
15
|
+
"styles": {
|
|
16
|
+
"flexDirection": "row",
|
|
17
|
+
"alignItems": "center"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"meta": {
|
|
21
|
+
"desiredParent": [">FormProvider"],
|
|
22
|
+
"label": "Form Checkbox",
|
|
23
|
+
"description": "Checkbox registered with form context; use name for form field (e.g. termsAccepted).",
|
|
24
|
+
"attributes": {
|
|
25
|
+
"name": {
|
|
26
|
+
"label": "Field name",
|
|
27
|
+
"description": "Form field name for react-hook-form (e.g. termsAccepted).",
|
|
28
|
+
"category": "other",
|
|
29
|
+
"sort": 5
|
|
30
|
+
},
|
|
31
|
+
"validation": {
|
|
32
|
+
"label": "Validation",
|
|
33
|
+
"description": "Validation rules as key-value pairs: e.g. ['required'] or ['min', 2], ['max', 5]. Builder must send min/max with their value in the next array slot.",
|
|
34
|
+
"category": "other",
|
|
35
|
+
"sort": 6
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { useContext, useMemo } from 'react';
|
|
2
|
+
import type { FormErrorTextComponentProps } from './FormErrorTextProps.generated';
|
|
3
|
+
import { formContext } from '../FormProvider/FormProvider';
|
|
4
|
+
import { Text } from '../Text/Text';
|
|
5
|
+
import useNode from '../useNode';
|
|
6
|
+
import { useLogRender } from '../../utils/useLogRender';
|
|
7
|
+
import { useLocalize } from '../../hooks/useLocalize';
|
|
8
|
+
|
|
9
|
+
/** Renders FormProvider globalError as Text when present; only valid inside FormProvider. */
|
|
10
|
+
export function FormErrorText({ node }: FormErrorTextComponentProps) {
|
|
11
|
+
useLogRender('FormErrorText');
|
|
12
|
+
node = useNode(node);
|
|
13
|
+
const ctx = useContext(formContext);
|
|
14
|
+
const localize = useLocalize();
|
|
15
|
+
|
|
16
|
+
const displayMessage = useMemo(
|
|
17
|
+
() => (ctx?.globalError ? localize(ctx.globalError) : ''),
|
|
18
|
+
[ctx?.globalError, localize],
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
if (!ctx?.globalError || !displayMessage) return null;
|
|
22
|
+
|
|
23
|
+
const textNode = {
|
|
24
|
+
...node,
|
|
25
|
+
type: 'Text',
|
|
26
|
+
children: displayMessage,
|
|
27
|
+
} as Parameters<typeof Text>[0]['node'];
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div role="alert" aria-live="polite">
|
|
31
|
+
<Text node={textNode} />
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* AUTO-GENERATED FILE - DO NOT EDIT */
|
|
2
|
+
|
|
3
|
+
import type { NodeData } from '../../types/Node';
|
|
4
|
+
|
|
5
|
+
export type FlexDirectionOptionType = 'row' | 'column';
|
|
6
|
+
export type FlexWrapOptionType = 'nowrap' | 'wrap' | 'wrap-reverse';
|
|
7
|
+
export type AlignItemsOptionType =
|
|
8
|
+
| 'flex-start'
|
|
9
|
+
| 'center'
|
|
10
|
+
| 'flex-end'
|
|
11
|
+
| 'stretch'
|
|
12
|
+
| 'baseline';
|
|
13
|
+
export type JustifyContentOptionType =
|
|
14
|
+
| 'flex-start'
|
|
15
|
+
| 'center'
|
|
16
|
+
| 'flex-end'
|
|
17
|
+
| 'space-between'
|
|
18
|
+
| 'space-around'
|
|
19
|
+
| 'space-evenly';
|
|
20
|
+
export type PositionOptionType = 'relative' | 'absolute';
|
|
21
|
+
|
|
22
|
+
export interface FormErrorTextStyleGenerated {
|
|
23
|
+
color?: string;
|
|
24
|
+
fontSize?: string;
|
|
25
|
+
fontFamily?: string;
|
|
26
|
+
fontWeight?: string;
|
|
27
|
+
textAlign?: string;
|
|
28
|
+
flexDirection?: FlexDirectionOptionType;
|
|
29
|
+
flexWrap?: FlexWrapOptionType;
|
|
30
|
+
alignItems?: AlignItemsOptionType;
|
|
31
|
+
justifyContent?: JustifyContentOptionType;
|
|
32
|
+
gap?: string;
|
|
33
|
+
padding?: string;
|
|
34
|
+
paddingHorizontal?: string;
|
|
35
|
+
paddingVertical?: string;
|
|
36
|
+
paddingTop?: string;
|
|
37
|
+
paddingBottom?: string;
|
|
38
|
+
paddingLeft?: string;
|
|
39
|
+
paddingRight?: string;
|
|
40
|
+
margin?: string;
|
|
41
|
+
marginHorizontal?: string;
|
|
42
|
+
marginVertical?: string;
|
|
43
|
+
marginTop?: string;
|
|
44
|
+
marginBottom?: string;
|
|
45
|
+
marginLeft?: string;
|
|
46
|
+
marginRight?: string;
|
|
47
|
+
backgroundColor?: string;
|
|
48
|
+
borderRadius?: string;
|
|
49
|
+
width?: string;
|
|
50
|
+
minWidth?: string;
|
|
51
|
+
maxWidth?: string;
|
|
52
|
+
height?: string;
|
|
53
|
+
minHeight?: string;
|
|
54
|
+
maxHeight?: string;
|
|
55
|
+
flex?: number;
|
|
56
|
+
position?: PositionOptionType;
|
|
57
|
+
top?: string;
|
|
58
|
+
bottom?: string;
|
|
59
|
+
left?: string;
|
|
60
|
+
right?: string;
|
|
61
|
+
zIndex?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface FormErrorTextPropsGenerated {
|
|
65
|
+
child: string;
|
|
66
|
+
attributes: {
|
|
67
|
+
styles?: FormErrorTextStyleGenerated;
|
|
68
|
+
adjustsFontSizeToFit?: boolean;
|
|
69
|
+
numberOfLines?: number;
|
|
70
|
+
translateCounter?: number;
|
|
71
|
+
scrollable?: boolean;
|
|
72
|
+
testID?: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface FormErrorTextComponentProps {
|
|
77
|
+
node: NodeData<FormErrorTextPropsGenerated['attributes']>;
|
|
78
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"pattern": {
|
|
4
|
+
"type": "FormErrorText",
|
|
5
|
+
"title": "FormErrorText",
|
|
6
|
+
"description": "Displays FormProvider global submit/async error as text. Only valid as child of FormProvider.",
|
|
7
|
+
"children": "never",
|
|
8
|
+
"extends": "Text"
|
|
9
|
+
},
|
|
10
|
+
"defaults": {
|
|
11
|
+
"styles": {
|
|
12
|
+
"color": "THEME_COLORS.FORM_ERROR_TEXT_COLOR",
|
|
13
|
+
"fontSize": "14@fs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"meta": {
|
|
17
|
+
"desiredParent": [">FormProvider"],
|
|
18
|
+
"label": "Form Error Text",
|
|
19
|
+
"description": "Shows form global error (e.g. submit failure). Place inside FormProvider where the error should appear."
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { createContext, useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { FormProvider as RHFFormProvider, useForm } from 'react-hook-form';
|
|
3
|
+
import type { UseFormReturn } from 'react-hook-form';
|
|
4
|
+
import type { FormProviderComponentProps } from './FormProviderProps.generated';
|
|
5
|
+
import RenderNode from '../RenderNode.generated';
|
|
6
|
+
import useNode from '../useNode';
|
|
7
|
+
import { useLogRender } from '../../utils/useLogRender';
|
|
8
|
+
import { useExtractViewStyle } from '../../attribute-analyser/style/web/useExtractViewStyle';
|
|
9
|
+
|
|
10
|
+
export type FormContextValue = {
|
|
11
|
+
handleSubmit: (
|
|
12
|
+
callback: (data: Record<string, unknown>) => void | Promise<unknown>,
|
|
13
|
+
) => (e?: React.BaseSyntheticEvent) => void;
|
|
14
|
+
isValid: boolean;
|
|
15
|
+
globalError: string | null;
|
|
16
|
+
control: UseFormReturn<Record<string, unknown>>['control'];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const formContext = createContext<FormContextValue | null>(null);
|
|
20
|
+
|
|
21
|
+
function parseDefaultValues(raw: string | undefined): Record<string, unknown> {
|
|
22
|
+
if (raw == null || typeof raw !== 'string' || raw.trim() === '') {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
27
|
+
return typeof parsed === 'object' &&
|
|
28
|
+
parsed !== null &&
|
|
29
|
+
!Array.isArray(parsed)
|
|
30
|
+
? (parsed as Record<string, unknown>)
|
|
31
|
+
: {};
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.warn(
|
|
34
|
+
'[FormProvider] parseDefaultValues: invalid defaultValues JSON',
|
|
35
|
+
raw,
|
|
36
|
+
err,
|
|
37
|
+
);
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function FormProviderInner({ node }: FormProviderComponentProps) {
|
|
43
|
+
useLogRender('FormProvider');
|
|
44
|
+
node = useNode(node);
|
|
45
|
+
const attrs = node.attributes;
|
|
46
|
+
const defaultValues = useMemo(
|
|
47
|
+
() => parseDefaultValues(attrs?.defaultValues as string | undefined),
|
|
48
|
+
[attrs?.defaultValues],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const form = useForm<Record<string, unknown>>({
|
|
52
|
+
defaultValues,
|
|
53
|
+
mode: 'onChange',
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const [globalError, setGlobalError] = useState<string | null>(null);
|
|
57
|
+
|
|
58
|
+
const validationErrorKey =
|
|
59
|
+
(attrs?.validationErrorMessageKey as string | undefined) ??
|
|
60
|
+
'form.error.validation_required';
|
|
61
|
+
|
|
62
|
+
const handleSubmit = useCallback(
|
|
63
|
+
(callback: (data: Record<string, unknown>) => void | Promise<unknown>) => {
|
|
64
|
+
const onValid = (data: Record<string, unknown>) => {
|
|
65
|
+
setGlobalError(null);
|
|
66
|
+
try {
|
|
67
|
+
const result = callback(data);
|
|
68
|
+
const promise =
|
|
69
|
+
result !== undefined &&
|
|
70
|
+
result !== null &&
|
|
71
|
+
typeof (result as Promise<unknown>).then === 'function'
|
|
72
|
+
? (result as Promise<unknown>)
|
|
73
|
+
: null;
|
|
74
|
+
if (promise) {
|
|
75
|
+
promise.catch((e: unknown) => {
|
|
76
|
+
setGlobalError(e instanceof Error ? e.message : String(e));
|
|
77
|
+
throw e;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
setGlobalError(e instanceof Error ? e.message : String(e));
|
|
82
|
+
throw e;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const onInvalid = () => {
|
|
86
|
+
setGlobalError(validationErrorKey);
|
|
87
|
+
};
|
|
88
|
+
return form.handleSubmit(onValid, onInvalid);
|
|
89
|
+
},
|
|
90
|
+
[form, validationErrorKey],
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const contextValue = useMemo<FormContextValue>(
|
|
94
|
+
() => ({
|
|
95
|
+
handleSubmit,
|
|
96
|
+
isValid: form.formState.isValid,
|
|
97
|
+
globalError,
|
|
98
|
+
control: form.control,
|
|
99
|
+
}),
|
|
100
|
+
[handleSubmit, form.formState.isValid, form.control, globalError],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const viewStyle = useExtractViewStyle(node);
|
|
104
|
+
|
|
105
|
+
const onSubmit = (e: React.FormEvent) => {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<formContext.Provider value={contextValue}>
|
|
111
|
+
<RHFFormProvider {...form}>
|
|
112
|
+
<form
|
|
113
|
+
onSubmit={onSubmit}
|
|
114
|
+
style={viewStyle}
|
|
115
|
+
attribute-name={node.sourceType ?? node.type ?? 'FormProvider'}
|
|
116
|
+
attribute-key={node.key}
|
|
117
|
+
>
|
|
118
|
+
{node.children ? (
|
|
119
|
+
<RenderNode
|
|
120
|
+
node={node.children as Parameters<typeof RenderNode>[0]['node']}
|
|
121
|
+
/>
|
|
122
|
+
) : null}
|
|
123
|
+
</form>
|
|
124
|
+
</RHFFormProvider>
|
|
125
|
+
</formContext.Provider>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function FormProvider(props: FormProviderComponentProps) {
|
|
130
|
+
return <FormProviderInner node={props.node} />;
|
|
131
|
+
}
|