formcn 1.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/LICENSE +21 -0
- package/README.md +294 -0
- package/bin/index.js +71 -0
- package/generators/form-generator.js +152 -0
- package/generators/form-ui-templates.js +183 -0
- package/generators/multi-form-generator.js +257 -0
- package/generators/schema-generator.js +89 -0
- package/package.json +46 -0
- package/test/README.md +73 -0
- package/test/components.json +22 -0
- package/test/eslint.config.js +23 -0
- package/test/index.html +13 -0
- package/test/package-lock.json +4759 -0
- package/test/package.json +46 -0
- package/test/public/vite.svg +1 -0
- package/test/src/App.css +42 -0
- package/test/src/App.tsx +7 -0
- package/test/src/assets/react.svg +1 -0
- package/test/src/components/ui/button.tsx +62 -0
- package/test/src/components/ui/checkbox.tsx +32 -0
- package/test/src/components/ui/field.tsx +246 -0
- package/test/src/components/ui/input-group.tsx +170 -0
- package/test/src/components/ui/input.tsx +21 -0
- package/test/src/components/ui/label.tsx +22 -0
- package/test/src/components/ui/radio-group.tsx +43 -0
- package/test/src/components/ui/select.tsx +188 -0
- package/test/src/components/ui/separator.tsx +28 -0
- package/test/src/components/ui/textarea.tsx +18 -0
- package/test/src/index.css +123 -0
- package/test/src/lib/utils.ts +6 -0
- package/test/src/main.tsx +10 -0
- package/test/tsconfig.app.json +33 -0
- package/test/tsconfig.json +13 -0
- package/test/tsconfig.node.json +26 -0
- package/test/vite.config.ts +14 -0
- package/utils/ensurePackages.js +62 -0
- package/utils/lib.js +22 -0
- package/utils/prompts.js +200 -0
- package/utils/tailwind-presets.js +132 -0
- package/utils/templates.js +103 -0
- package/utils/test.js +136 -0
package/utils/prompts.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { select, text, confirm, isCancel } from "@clack/prompts";
|
|
4
|
+
|
|
5
|
+
export async function askFormName() {
|
|
6
|
+
const baseDir = fs.existsSync(path.resolve("src"))
|
|
7
|
+
? path.resolve("src/components/forms")
|
|
8
|
+
: path.resolve("components/forms");
|
|
9
|
+
|
|
10
|
+
const formName = await text({
|
|
11
|
+
message: "Form name (the component will be suffixed with 'Form')",
|
|
12
|
+
placeholder: "register → RegisterForm",
|
|
13
|
+
validate(value) {
|
|
14
|
+
if (!value) return "Form name is required";
|
|
15
|
+
|
|
16
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(value)) {
|
|
17
|
+
return "Use only letters, numbers, dashes or underscores";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const formDir = path.join(baseDir, value);
|
|
21
|
+
|
|
22
|
+
if (fs.pathExistsSync(formDir)) {
|
|
23
|
+
return `A form named "${value}" already exists`;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (isCancel(formName)) {
|
|
29
|
+
console.log("❌ Operation cancelled.");
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return formName;
|
|
34
|
+
}
|
|
35
|
+
export async function askFormType() {
|
|
36
|
+
const result = await select({
|
|
37
|
+
message: "Form type?",
|
|
38
|
+
options: [
|
|
39
|
+
{ value: "single", label: "Single step" },
|
|
40
|
+
{ value: "multi", label: "Multi step" },
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (isCancel(result)) {
|
|
45
|
+
console.log("❌ Operation cancelled.");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function askFormTemplate(formType) {
|
|
52
|
+
const choice = await select({
|
|
53
|
+
message: `Do you want a ready template for the ${formType} form, or create manually?`,
|
|
54
|
+
options: [
|
|
55
|
+
{ value: "template", label: "Use ready template" },
|
|
56
|
+
{ value: "manual", label: "Create manually" },
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (isCancel(choice)) {
|
|
61
|
+
console.log("❌ Operation cancelled.");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return choice;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const FIELD_TYPES = [
|
|
69
|
+
{ value: "text", label: "Text" },
|
|
70
|
+
{ value: "email", label: "Email" },
|
|
71
|
+
{ value: "password", label: "Password" },
|
|
72
|
+
{ value: "number", label: "Number" },
|
|
73
|
+
{ value: "url", label: "URL" },
|
|
74
|
+
{ value: "textarea", label: "Textarea" },
|
|
75
|
+
{ value: "select", label: "Select" },
|
|
76
|
+
{ value: "checkbox", label: "Checkbox" },
|
|
77
|
+
{ value: "radio", label: "Radio" },
|
|
78
|
+
{ value: "date", label: "Date" },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
export async function askFields() {
|
|
82
|
+
const fields = [];
|
|
83
|
+
|
|
84
|
+
while (true) {
|
|
85
|
+
const type = await select({
|
|
86
|
+
message: "Field type?",
|
|
87
|
+
options: FIELD_TYPES,
|
|
88
|
+
});
|
|
89
|
+
if (isCancel(type)) {
|
|
90
|
+
console.log("❌ Operation cancelled.");
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const label = await text({
|
|
95
|
+
message: 'Label (e.g. "First Name" → "first_name" as field name)',
|
|
96
|
+
validate(value) {
|
|
97
|
+
if (!value.trim()) return "Label cannot be empty.";
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
if (isCancel(label)) {
|
|
101
|
+
console.log("❌ Operation cancelled.");
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const name = label.trim().toLowerCase().replace(/\s+/g, "_");
|
|
106
|
+
|
|
107
|
+
const required = await confirm({ message: "Required?" });
|
|
108
|
+
if (isCancel(required)) {
|
|
109
|
+
console.log("❌ Operation cancelled.");
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let options;
|
|
114
|
+
|
|
115
|
+
if (type === "select" || type === "radio") {
|
|
116
|
+
options = [];
|
|
117
|
+
|
|
118
|
+
while (true) {
|
|
119
|
+
const labelInput = await text({
|
|
120
|
+
message: 'Option label? (e.g. "Active User" → "active_user")',
|
|
121
|
+
validate(value) {
|
|
122
|
+
const transformed = value.trim().toLowerCase().replace(/\s+/g, "_");
|
|
123
|
+
if (!value.trim()) return "Option label cannot be empty.";
|
|
124
|
+
if (options.some((o) => o.value === transformed))
|
|
125
|
+
return `Option "${transformed}" already exists.`;
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
if (isCancel(labelInput)) {
|
|
129
|
+
console.log("❌ Operation cancelled.");
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const label = labelInput
|
|
134
|
+
.split(" ")
|
|
135
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
136
|
+
.join(" ");
|
|
137
|
+
|
|
138
|
+
const value = label.trim().toLowerCase().replace(/\s+/g, "_");
|
|
139
|
+
|
|
140
|
+
options.push({ label, value });
|
|
141
|
+
|
|
142
|
+
const more = await confirm({ message: "Add another option?" });
|
|
143
|
+
if (isCancel(more) || !more) break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fields.push({
|
|
148
|
+
type,
|
|
149
|
+
name,
|
|
150
|
+
label,
|
|
151
|
+
required,
|
|
152
|
+
options,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const addMore = await confirm({ message: "Add another field?" });
|
|
156
|
+
if (isCancel(addMore) || !addMore) break;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return fields;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function askSteps() {
|
|
163
|
+
const steps = [];
|
|
164
|
+
|
|
165
|
+
while (true) {
|
|
166
|
+
const stepName = await text({
|
|
167
|
+
message: "Step name?",
|
|
168
|
+
validate(value) {
|
|
169
|
+
if (!value.trim()) return "Step Name cannot be empty.";
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
if (isCancel(stepName)) {
|
|
173
|
+
console.log("❌ Operation cancelled.");
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log(`\nAdd fields for step: ${stepName}\n`);
|
|
178
|
+
const fields = await askFields();
|
|
179
|
+
|
|
180
|
+
steps.push({ stepName, fields });
|
|
181
|
+
|
|
182
|
+
const addMoreSteps = await confirm({ message: "Add another step?" });
|
|
183
|
+
if (isCancel(addMoreSteps) || !addMoreSteps) break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return steps;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function askFormPreset(presets) {
|
|
190
|
+
const choices = Object.keys(presets).map((key) => ({
|
|
191
|
+
value: key,
|
|
192
|
+
label: key,
|
|
193
|
+
}));
|
|
194
|
+
const preset = await select({
|
|
195
|
+
message: "Choose a form preset:",
|
|
196
|
+
options: choices,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return preset;
|
|
200
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export const singleFormPresets = {
|
|
2
|
+
default: {
|
|
3
|
+
form: "space-y-6 max-w-3xl mx-auto bg-white p-6 rounded-lg border border-slate-200 shadow-sm",
|
|
4
|
+
buttonsWrapper: "flex justify-end gap-2 pt-4 border-t border-slate-100",
|
|
5
|
+
},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const multiFormPresets = {
|
|
9
|
+
minimal: {
|
|
10
|
+
form: "w-full max-w-4xl mx-auto bg-white text-zinc-900 p-8 rounded-2xl border border-zinc-200 shadow-xl",
|
|
11
|
+
buttonsWrapper: "flex justify-between mt-10",
|
|
12
|
+
stepper: `<div className="mb-4 space-y-2">
|
|
13
|
+
<div className="flex gap-1 mb-4">
|
|
14
|
+
{steps.map((_, i) => (
|
|
15
|
+
<div
|
|
16
|
+
key={i}
|
|
17
|
+
className={\`h-1 flex-1 rounded-full transition-all duration-500 \${
|
|
18
|
+
i <= currentStep ? "bg-zinc-900" : "bg-zinc-100"
|
|
19
|
+
}\`}
|
|
20
|
+
/>
|
|
21
|
+
))}
|
|
22
|
+
</div>
|
|
23
|
+
<h2 className="text-2xl font-bold tracking-tight capitalize text-start">
|
|
24
|
+
{currentStepData.title}
|
|
25
|
+
</h2>
|
|
26
|
+
</div>`,
|
|
27
|
+
},
|
|
28
|
+
sidebarStepper: {
|
|
29
|
+
form: "w-full max-w-4xl mx-auto flex bg-white rounded-2xl border border-slate-200 shadow-xl overflow-hidden min-h-[500px]",
|
|
30
|
+
buttonsWrapper:
|
|
31
|
+
"flex justify-between items-center mt-10 pt-8 border-t border-slate-100",
|
|
32
|
+
step: "flex-1 p-8",
|
|
33
|
+
stepper: `
|
|
34
|
+
<div className="w-72 bg-slate-900 p-8 text-white space-y-8">
|
|
35
|
+
<div className="space-y-1 flex flex-col items-start">
|
|
36
|
+
<h3 className="text-lg font-bold">Registration</h3>
|
|
37
|
+
<p className="text-slate-400 text-xs">Complete all steps to join us.</p>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div className="space-y-6">
|
|
41
|
+
{steps.map((step, i) => {
|
|
42
|
+
const isCompleted = i < currentStep
|
|
43
|
+
const isActive = i === currentStep
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div key={i} className="flex gap-4 items-center">
|
|
47
|
+
<div className="mt-1">
|
|
48
|
+
{isCompleted ? (
|
|
49
|
+
<div className="w-5 h-5 rounded-full bg-emerald-400 flex items-center justify-center text-[10px] font-bold text-slate-900">
|
|
50
|
+
✓
|
|
51
|
+
</div>
|
|
52
|
+
) : (
|
|
53
|
+
<div
|
|
54
|
+
className={
|
|
55
|
+
"w-5 h-5 rounded-full border-2 flex items-center justify-center text-[10px] font-bold " +
|
|
56
|
+
(isActive
|
|
57
|
+
? "border-white bg-white text-slate-900"
|
|
58
|
+
: "border-slate-700 text-slate-500")
|
|
59
|
+
}
|
|
60
|
+
>
|
|
61
|
+
{i + 1}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<p
|
|
67
|
+
className={
|
|
68
|
+
"text-xs font-bold uppercase tracking-wider " +
|
|
69
|
+
(i <= currentStep ? "text-white" : "text-slate-600")
|
|
70
|
+
}
|
|
71
|
+
>
|
|
72
|
+
{step.title}
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
})}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
},
|
|
81
|
+
softType: {
|
|
82
|
+
form: "w-full max-w-2xl mx-auto bg-white p-12 rounded-[2.5rem] shadow-[0_20px_50px_rgba(0,0,0,0.05)] border border-slate-50/50",
|
|
83
|
+
buttonsWrapper: "flex gap-4 mt-10",
|
|
84
|
+
stepper: ` <div className="flex flex-col items-start justify-start mb-4">
|
|
85
|
+
<h2 className="text-xl font-bold text-slate-800">
|
|
86
|
+
Step {currentStep + 1} of {steps.length}
|
|
87
|
+
</h2>
|
|
88
|
+
<p className="text-slate-400 text-sm capitalize">{currentStepData.title}</p>
|
|
89
|
+
</div>`,
|
|
90
|
+
},
|
|
91
|
+
stepperTop: {
|
|
92
|
+
form: "w-full max-w-2xl mx-auto bg-white rounded-2xl border p-8 border-slate-200 shadow-sm overflow-hidden",
|
|
93
|
+
buttonsWrapper: "flex justify-end gap-3 mt-10",
|
|
94
|
+
stepper: `
|
|
95
|
+
<div className="py-6">
|
|
96
|
+
<div className="relative flex justify-between">
|
|
97
|
+
<div className="absolute top-4 left-0 w-full h-0.5 bg-slate-200 z-0">
|
|
98
|
+
<div
|
|
99
|
+
className="h-full bg-slate-600 transition-all duration-500"
|
|
100
|
+
style={{ width: \`\${(currentStep / (steps.length - 1)) * 100}%\` }}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
{steps.map((step, i) => (
|
|
105
|
+
<div key={i} className="relative z-10 flex flex-col items-center group">
|
|
106
|
+
<div
|
|
107
|
+
className={
|
|
108
|
+
"w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold transition-all duration-300 border-2 " +
|
|
109
|
+
(i < currentStep
|
|
110
|
+
? "bg-slate-600 border-slate-600 text-white"
|
|
111
|
+
: i === currentStep
|
|
112
|
+
? "bg-white border-slate-600 text-slate-600"
|
|
113
|
+
: "bg-white border-slate-300 text-slate-400")
|
|
114
|
+
}
|
|
115
|
+
>
|
|
116
|
+
{i + 1}
|
|
117
|
+
</div>
|
|
118
|
+
<span
|
|
119
|
+
className={
|
|
120
|
+
"mt-2 text-[10px] font-bold uppercase tracking-wider transition-colors " +
|
|
121
|
+
(i <= currentStep ? "text-slate-600" : "text-slate-400")
|
|
122
|
+
}
|
|
123
|
+
>
|
|
124
|
+
{step.title.split(" ")[0]}
|
|
125
|
+
</span>
|
|
126
|
+
</div>
|
|
127
|
+
))}
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
`,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export const SINGLE_FIELD_TEMPLATES = {
|
|
2
|
+
registration: [
|
|
3
|
+
{ name: "first_name", type: "text", label: "First Name", required: true },
|
|
4
|
+
{ name: "last_name", type: "text", label: "Last Name", required: true },
|
|
5
|
+
{ name: "email", type: "email", label: "Email", required: true },
|
|
6
|
+
{
|
|
7
|
+
name: "password",
|
|
8
|
+
type: "password",
|
|
9
|
+
label: "Password",
|
|
10
|
+
required: true,
|
|
11
|
+
confirm: true,
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
login: [
|
|
15
|
+
{ name: "email", type: "email", label: "Email", required: true },
|
|
16
|
+
{
|
|
17
|
+
name: "password",
|
|
18
|
+
type: "password",
|
|
19
|
+
label: "Password",
|
|
20
|
+
required: true,
|
|
21
|
+
confirm: false,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
contact: [
|
|
25
|
+
{ name: "full_name", type: "text", label: "Full Name", required: true },
|
|
26
|
+
{ name: "email", type: "email", label: "Email", required: true },
|
|
27
|
+
{ name: "subject", type: "text", label: "Subject", required: true },
|
|
28
|
+
{ name: "message", type: "textarea", label: "Message", required: true },
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const MULTI_STEP_TEMPLATES = {
|
|
33
|
+
registration: [
|
|
34
|
+
{
|
|
35
|
+
stepName: "Personal Info",
|
|
36
|
+
fields: [
|
|
37
|
+
{
|
|
38
|
+
name: "first_name",
|
|
39
|
+
type: "text",
|
|
40
|
+
label: "First Name",
|
|
41
|
+
required: true,
|
|
42
|
+
},
|
|
43
|
+
{ name: "last_name", type: "text", label: "Last Name", required: true },
|
|
44
|
+
{
|
|
45
|
+
name: "gender",
|
|
46
|
+
type: "radio",
|
|
47
|
+
label: "Gender",
|
|
48
|
+
required: true,
|
|
49
|
+
options: [
|
|
50
|
+
{ label: "Male", value: "male" },
|
|
51
|
+
{ label: "Female", value: "female" },
|
|
52
|
+
{ label: "Other", value: "other" },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{ name: "birthdate", type: "date", label: "Birthdate", required: true },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
stepName: "Account Details",
|
|
60
|
+
fields: [
|
|
61
|
+
{ name: "email", type: "email", label: "Email", required: true },
|
|
62
|
+
{
|
|
63
|
+
name: "password",
|
|
64
|
+
type: "password",
|
|
65
|
+
label: "Password",
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: "password_confirmation",
|
|
70
|
+
type: "password",
|
|
71
|
+
label: "Confirm Password",
|
|
72
|
+
required: true,
|
|
73
|
+
isConfirmation: true,
|
|
74
|
+
confirmationFor: "password",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
stepName: "Contact Info",
|
|
80
|
+
fields: [
|
|
81
|
+
{
|
|
82
|
+
name: "phone",
|
|
83
|
+
type: "number",
|
|
84
|
+
label: "Phone Number",
|
|
85
|
+
required: true,
|
|
86
|
+
},
|
|
87
|
+
{ name: "address", type: "text", label: "Address", required: true },
|
|
88
|
+
{ name: "city", type: "text", label: "City", required: true },
|
|
89
|
+
{
|
|
90
|
+
name: "country",
|
|
91
|
+
type: "select",
|
|
92
|
+
label: "Country",
|
|
93
|
+
required: true,
|
|
94
|
+
options: [
|
|
95
|
+
{ label: "United States", value: "us" },
|
|
96
|
+
{ label: "Canada", value: "ca" },
|
|
97
|
+
{ label: "United Kingdom", value: "uk" },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
package/utils/test.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export const FAST_MODE = false;
|
|
2
|
+
|
|
3
|
+
export const FAST_FORMS = [
|
|
4
|
+
{
|
|
5
|
+
formName: "testSingleAllTypes",
|
|
6
|
+
type: "single",
|
|
7
|
+
presetKey: "default",
|
|
8
|
+
steps: [
|
|
9
|
+
{
|
|
10
|
+
stepName: "main",
|
|
11
|
+
fields: [
|
|
12
|
+
{
|
|
13
|
+
name: "first_name",
|
|
14
|
+
label: "First Name",
|
|
15
|
+
type: "text",
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
{ name: "email", label: "Email", type: "email", required: true },
|
|
19
|
+
{
|
|
20
|
+
name: "password",
|
|
21
|
+
label: "Password",
|
|
22
|
+
type: "password",
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
{ name: "age", label: "Age", type: "number", required: false },
|
|
26
|
+
{
|
|
27
|
+
name: "birthdate",
|
|
28
|
+
label: "Birthdate",
|
|
29
|
+
type: "date",
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
{ name: "website", label: "Website", type: "url", required: false },
|
|
33
|
+
{
|
|
34
|
+
name: "subscribe",
|
|
35
|
+
label: "Subscribe",
|
|
36
|
+
type: "checkbox",
|
|
37
|
+
required: false,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "gender",
|
|
41
|
+
label: "Gender",
|
|
42
|
+
type: "radio",
|
|
43
|
+
required: true,
|
|
44
|
+
options: [
|
|
45
|
+
{ label: "Male", value: "male" },
|
|
46
|
+
{ label: "Female", value: "female" },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "country",
|
|
51
|
+
label: "Country",
|
|
52
|
+
type: "select",
|
|
53
|
+
required: true,
|
|
54
|
+
options: [
|
|
55
|
+
{ label: "USA", value: "usa" },
|
|
56
|
+
{ label: "UK", value: "uk" },
|
|
57
|
+
{ label: "UAE", value: "uae" },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Multi-step form with all types split across steps
|
|
66
|
+
{
|
|
67
|
+
formName: "test",
|
|
68
|
+
type: "multi",
|
|
69
|
+
presetKey: "minimal", // stepperTop | minimal | sidebarStepper | softType
|
|
70
|
+
steps: [
|
|
71
|
+
{
|
|
72
|
+
stepName: "personal",
|
|
73
|
+
fields: [
|
|
74
|
+
{
|
|
75
|
+
name: "first_name",
|
|
76
|
+
label: "First Name",
|
|
77
|
+
type: "text",
|
|
78
|
+
required: true,
|
|
79
|
+
},
|
|
80
|
+
{ name: "email", label: "Email", type: "email", required: true },
|
|
81
|
+
{
|
|
82
|
+
name: "password",
|
|
83
|
+
label: "Password",
|
|
84
|
+
type: "password",
|
|
85
|
+
required: true,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
stepName: "details",
|
|
91
|
+
fields: [
|
|
92
|
+
{ name: "age", label: "Age", type: "number", required: false },
|
|
93
|
+
{
|
|
94
|
+
name: "birthdate",
|
|
95
|
+
label: "Birthdate",
|
|
96
|
+
type: "date",
|
|
97
|
+
required: true,
|
|
98
|
+
},
|
|
99
|
+
{ name: "website", label: "Website", type: "url", required: false },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
stepName: "preferences",
|
|
104
|
+
fields: [
|
|
105
|
+
{
|
|
106
|
+
name: "subscribe",
|
|
107
|
+
label: "Subscribe",
|
|
108
|
+
type: "checkbox",
|
|
109
|
+
required: false,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "gender",
|
|
113
|
+
label: "Gender",
|
|
114
|
+
type: "radio",
|
|
115
|
+
required: true,
|
|
116
|
+
options: [
|
|
117
|
+
{ label: "Male", value: "male" },
|
|
118
|
+
{ label: "Female", value: "female" },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "country",
|
|
123
|
+
label: "Country",
|
|
124
|
+
type: "select",
|
|
125
|
+
required: true,
|
|
126
|
+
options: [
|
|
127
|
+
{ label: "USA", value: "usa" },
|
|
128
|
+
{ label: "UK", value: "uk" },
|
|
129
|
+
{ label: "UAE", value: "uae" },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
];
|