@classytic/formkit 1.0.1 → 1.0.3
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/CHANGELOG.md +56 -0
- package/README.md +434 -273
- package/dist/index.cjs +122 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +326 -80
- package/dist/index.d.ts +326 -80
- package/dist/index.js +119 -60
- package/dist/index.js.map +1 -1
- package/package.json +49 -16
package/README.md
CHANGED
|
@@ -1,273 +1,434 @@
|
|
|
1
|
-
# @classytic/formkit
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
npm install @classytic/formkit react-hook-form
|
|
24
|
-
# or
|
|
25
|
-
|
|
26
|
-
# or
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
### 1. Create
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
// form-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
</
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
1
|
+
# @classytic/formkit
|
|
2
|
+
|
|
3
|
+
Headless, type-safe form generation engine for React 18/19. Schema-driven with full TypeScript support.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@classytic/formkit)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🎯 **Headless** - Bring your own UI components (Shadcn, MUI, Chakra, etc.)
|
|
12
|
+
- 📝 **Schema-driven** - Define forms with JSON/TypeScript schemas
|
|
13
|
+
- 🔒 **Type-safe** - Full TypeScript support with generics
|
|
14
|
+
- ⚡ **React Hook Form** - Built on top of the best form library
|
|
15
|
+
- 🎨 **Variants** - Support for multiple component variants
|
|
16
|
+
- 🔀 **Conditional fields** - Show/hide fields based on form values
|
|
17
|
+
- 📱 **Responsive layouts** - Multi-column grid layouts
|
|
18
|
+
- 🪶 **Lightweight** - ~6KB minified, tree-shakeable
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @classytic/formkit react-hook-form
|
|
24
|
+
# or
|
|
25
|
+
pnpm add @classytic/formkit react-hook-form
|
|
26
|
+
# or
|
|
27
|
+
yarn add @classytic/formkit react-hook-form
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Create Field Components
|
|
33
|
+
|
|
34
|
+
Each field component wraps your UI library with `react-hook-form`'s Controller:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// components/form/form-input.tsx
|
|
38
|
+
"use client";
|
|
39
|
+
|
|
40
|
+
import { Controller } from "react-hook-form";
|
|
41
|
+
import type { FieldComponentProps } from "@classytic/formkit";
|
|
42
|
+
import { Input } from "@/components/ui/input";
|
|
43
|
+
import { Label } from "@/components/ui/label";
|
|
44
|
+
|
|
45
|
+
export function FormInput({ control, name, label, placeholder, required }: FieldComponentProps) {
|
|
46
|
+
return (
|
|
47
|
+
<Controller
|
|
48
|
+
name={name}
|
|
49
|
+
control={control}
|
|
50
|
+
render={({ field, fieldState }) => (
|
|
51
|
+
<div className="space-y-2">
|
|
52
|
+
{label && (
|
|
53
|
+
<Label htmlFor={name}>
|
|
54
|
+
{label}
|
|
55
|
+
{required && <span className="text-red-500 ml-1">*</span>}
|
|
56
|
+
</Label>
|
|
57
|
+
)}
|
|
58
|
+
<Input {...field} id={name} placeholder={placeholder} />
|
|
59
|
+
{fieldState.error && (
|
|
60
|
+
<p className="text-sm text-red-500">{fieldState.error.message}</p>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Create Form Adapter
|
|
70
|
+
|
|
71
|
+
Register your components and layouts:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// lib/form-adapter.tsx
|
|
75
|
+
"use client";
|
|
76
|
+
|
|
77
|
+
import { FormSystemProvider, type ComponentRegistry, type LayoutRegistry } from "@classytic/formkit";
|
|
78
|
+
import { FormInput } from "@/components/form/form-input";
|
|
79
|
+
|
|
80
|
+
const components: ComponentRegistry = {
|
|
81
|
+
text: FormInput,
|
|
82
|
+
email: FormInput,
|
|
83
|
+
password: FormInput,
|
|
84
|
+
// Add more field types...
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const layouts: LayoutRegistry = {
|
|
88
|
+
section: ({ title, description, children }) => (
|
|
89
|
+
<div className="space-y-4">
|
|
90
|
+
{title && <h3 className="text-lg font-semibold">{title}</h3>}
|
|
91
|
+
{description && <p className="text-muted-foreground">{description}</p>}
|
|
92
|
+
{children}
|
|
93
|
+
</div>
|
|
94
|
+
),
|
|
95
|
+
grid: ({ children, cols = 1 }) => (
|
|
96
|
+
<div className={`grid grid-cols-${cols} gap-4`}>{children}</div>
|
|
97
|
+
),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export function FormProvider({ children }: { children: React.ReactNode }) {
|
|
101
|
+
return (
|
|
102
|
+
<FormSystemProvider components={components} layouts={layouts}>
|
|
103
|
+
{children}
|
|
104
|
+
</FormSystemProvider>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3. Use FormGenerator
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
// app/signup/page.tsx
|
|
113
|
+
"use client";
|
|
114
|
+
|
|
115
|
+
import { useForm } from "react-hook-form";
|
|
116
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
117
|
+
import { z } from "zod";
|
|
118
|
+
import { FormGenerator, type FormSchema } from "@classytic/formkit";
|
|
119
|
+
import { FormProvider } from "@/lib/form-adapter";
|
|
120
|
+
|
|
121
|
+
// Validation schema
|
|
122
|
+
const signupSchema = z.object({
|
|
123
|
+
firstName: z.string().min(2),
|
|
124
|
+
lastName: z.string().min(2),
|
|
125
|
+
email: z.string().email(),
|
|
126
|
+
password: z.string().min(8),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
type SignupData = z.infer<typeof signupSchema>;
|
|
130
|
+
|
|
131
|
+
// Form schema (type-safe!)
|
|
132
|
+
const formSchema: FormSchema<SignupData> = {
|
|
133
|
+
sections: [
|
|
134
|
+
{
|
|
135
|
+
title: "Personal Information",
|
|
136
|
+
cols: 2,
|
|
137
|
+
fields: [
|
|
138
|
+
{ name: "firstName", type: "text", label: "First Name", required: true },
|
|
139
|
+
{ name: "lastName", type: "text", label: "Last Name", required: true },
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
title: "Account",
|
|
144
|
+
fields: [
|
|
145
|
+
{ name: "email", type: "email", label: "Email", required: true },
|
|
146
|
+
{ name: "password", type: "password", label: "Password", required: true },
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export default function SignupPage() {
|
|
153
|
+
const form = useForm<SignupData>({
|
|
154
|
+
resolver: zodResolver(signupSchema),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<FormProvider>
|
|
159
|
+
<form onSubmit={form.handleSubmit(console.log)} className="space-y-8">
|
|
160
|
+
<FormGenerator schema={formSchema} control={form.control} />
|
|
161
|
+
<button type="submit">Sign Up</button>
|
|
162
|
+
</form>
|
|
163
|
+
</FormProvider>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## API Reference
|
|
169
|
+
|
|
170
|
+
### FormGenerator
|
|
171
|
+
|
|
172
|
+
The main component that renders forms from a schema.
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
<FormGenerator
|
|
176
|
+
schema={formSchema} // Required: Form schema
|
|
177
|
+
control={form.control} // Required: React Hook Form control
|
|
178
|
+
disabled={false} // Optional: Disable all fields
|
|
179
|
+
variant="default" // Optional: Global variant
|
|
180
|
+
className="my-form" // Optional: Root element class
|
|
181
|
+
/>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### FormSchema
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
interface FormSchema<T extends FieldValues = FieldValues> {
|
|
188
|
+
sections: Section<T>[];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
interface Section<T> {
|
|
192
|
+
id?: string; // Unique identifier
|
|
193
|
+
title?: string; // Section title
|
|
194
|
+
description?: string; // Section description
|
|
195
|
+
icon?: ReactNode; // Section icon
|
|
196
|
+
fields?: BaseField<T>[]; // Fields in this section
|
|
197
|
+
cols?: number; // Grid columns (1-6)
|
|
198
|
+
gap?: number; // Grid gap
|
|
199
|
+
variant?: string; // Section variant
|
|
200
|
+
className?: string; // Custom class
|
|
201
|
+
collapsible?: boolean; // Make section collapsible
|
|
202
|
+
defaultCollapsed?: boolean;
|
|
203
|
+
condition?: (control) => boolean; // Conditional rendering
|
|
204
|
+
render?: (props) => ReactNode; // Custom render function
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### BaseField
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
interface BaseField<T> {
|
|
212
|
+
name: string; // Field name (required)
|
|
213
|
+
type: FieldType; // Field type (required)
|
|
214
|
+
label?: string; // Field label
|
|
215
|
+
placeholder?: string; // Placeholder text
|
|
216
|
+
helperText?: string; // Helper text below field
|
|
217
|
+
disabled?: boolean; // Disable field
|
|
218
|
+
required?: boolean; // Mark as required
|
|
219
|
+
readOnly?: boolean; // Read-only field
|
|
220
|
+
variant?: string; // Field variant
|
|
221
|
+
fullWidth?: boolean; // Span full grid width
|
|
222
|
+
className?: string; // Custom class
|
|
223
|
+
defaultValue?: unknown; // Default value
|
|
224
|
+
|
|
225
|
+
// Conditional rendering
|
|
226
|
+
condition?: (formValues: T) => boolean;
|
|
227
|
+
|
|
228
|
+
// For select/radio/checkbox
|
|
229
|
+
options?: FieldOption[];
|
|
230
|
+
|
|
231
|
+
// HTML input attributes
|
|
232
|
+
min?: number | string;
|
|
233
|
+
max?: number | string;
|
|
234
|
+
step?: number;
|
|
235
|
+
pattern?: string;
|
|
236
|
+
minLength?: number;
|
|
237
|
+
maxLength?: number;
|
|
238
|
+
rows?: number; // For textarea
|
|
239
|
+
multiple?: boolean; // For select/file
|
|
240
|
+
accept?: string; // For file input
|
|
241
|
+
autoComplete?: string;
|
|
242
|
+
autoFocus?: boolean;
|
|
243
|
+
|
|
244
|
+
// Custom props
|
|
245
|
+
[key: string]: unknown;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### FieldComponentProps
|
|
250
|
+
|
|
251
|
+
Props passed to your field components:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
interface FieldComponentProps<T extends FieldValues = FieldValues> extends BaseField<T> {
|
|
255
|
+
field: BaseField<T>; // Full field config
|
|
256
|
+
control: Control<T>; // React Hook Form control
|
|
257
|
+
disabled?: boolean; // Merged disabled state
|
|
258
|
+
variant?: string; // Active variant
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### ComponentRegistry
|
|
263
|
+
|
|
264
|
+
```tsx
|
|
265
|
+
const components: ComponentRegistry = {
|
|
266
|
+
// Simple mapping
|
|
267
|
+
text: FormInput,
|
|
268
|
+
select: FormSelect,
|
|
269
|
+
|
|
270
|
+
// Variant-specific components
|
|
271
|
+
compact: {
|
|
272
|
+
text: CompactInput,
|
|
273
|
+
select: CompactSelect,
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### LayoutRegistry
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
const layouts: LayoutRegistry = {
|
|
282
|
+
section: SectionLayout,
|
|
283
|
+
grid: GridLayout,
|
|
284
|
+
|
|
285
|
+
// Variant-specific layouts
|
|
286
|
+
compact: {
|
|
287
|
+
section: CompactSection,
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Advanced Features
|
|
293
|
+
|
|
294
|
+
### Conditional Fields
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
{
|
|
298
|
+
name: "companyName",
|
|
299
|
+
type: "text",
|
|
300
|
+
label: "Company Name",
|
|
301
|
+
condition: (values) => values.accountType === "business",
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Variants
|
|
306
|
+
|
|
307
|
+
Apply different styles based on context:
|
|
308
|
+
|
|
309
|
+
```tsx
|
|
310
|
+
// Register variant-specific components
|
|
311
|
+
const components = {
|
|
312
|
+
text: DefaultInput,
|
|
313
|
+
compact: {
|
|
314
|
+
text: CompactInput,
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// Use variant in schema
|
|
319
|
+
const schema = {
|
|
320
|
+
sections: [{
|
|
321
|
+
variant: "compact", // All fields use compact variant
|
|
322
|
+
fields: [...]
|
|
323
|
+
}]
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Or per-field
|
|
327
|
+
{
|
|
328
|
+
name: "notes",
|
|
329
|
+
type: "text",
|
|
330
|
+
variant: "compact",
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Custom Section Render
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
{
|
|
338
|
+
title: "Payment",
|
|
339
|
+
render: ({ control, disabled }) => (
|
|
340
|
+
<StripeElements>
|
|
341
|
+
<CardElement />
|
|
342
|
+
<FormInput name="billingName" control={control} />
|
|
343
|
+
</StripeElements>
|
|
344
|
+
),
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Grouped Select Options
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
{
|
|
352
|
+
name: "country",
|
|
353
|
+
type: "select",
|
|
354
|
+
options: [
|
|
355
|
+
{
|
|
356
|
+
label: "North America",
|
|
357
|
+
options: [
|
|
358
|
+
{ value: "us", label: "United States" },
|
|
359
|
+
{ value: "ca", label: "Canada" },
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
label: "Europe",
|
|
364
|
+
options: [
|
|
365
|
+
{ value: "uk", label: "United Kingdom" },
|
|
366
|
+
{ value: "de", label: "Germany" },
|
|
367
|
+
],
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Type Exports
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
import type {
|
|
377
|
+
// Core
|
|
378
|
+
FormSchema,
|
|
379
|
+
FormGeneratorProps,
|
|
380
|
+
BaseField,
|
|
381
|
+
Section,
|
|
382
|
+
|
|
383
|
+
// Components
|
|
384
|
+
FieldComponentProps,
|
|
385
|
+
FieldComponent,
|
|
386
|
+
ComponentRegistry,
|
|
387
|
+
|
|
388
|
+
// Layouts
|
|
389
|
+
SectionLayoutProps,
|
|
390
|
+
GridLayoutProps,
|
|
391
|
+
LayoutComponent,
|
|
392
|
+
LayoutRegistry,
|
|
393
|
+
|
|
394
|
+
// Options
|
|
395
|
+
FieldOption,
|
|
396
|
+
FieldOptionGroup,
|
|
397
|
+
|
|
398
|
+
// Utility types
|
|
399
|
+
FieldType,
|
|
400
|
+
LayoutType,
|
|
401
|
+
Variant,
|
|
402
|
+
DefineField,
|
|
403
|
+
InferSchemaValues,
|
|
404
|
+
SchemaFieldNames,
|
|
405
|
+
} from "@classytic/formkit";
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Examples
|
|
409
|
+
|
|
410
|
+
See the [`example/shadcn`](./example/shadcn) directory for complete working examples with:
|
|
411
|
+
|
|
412
|
+
- Form components (Input, Select, Checkbox)
|
|
413
|
+
- Full adapter configuration
|
|
414
|
+
- Zod validation
|
|
415
|
+
- Conditional fields
|
|
416
|
+
- Multi-column layouts
|
|
417
|
+
- TypeScript integration
|
|
418
|
+
|
|
419
|
+
## Browser Support
|
|
420
|
+
|
|
421
|
+
- React 18.0+
|
|
422
|
+
- React 19.0+
|
|
423
|
+
- All modern browsers
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT © [Classytic](https://github.com/classytic)
|
|
428
|
+
|
|
429
|
+
## Links
|
|
430
|
+
|
|
431
|
+
- [GitHub](https://github.com/classytic/formkit)
|
|
432
|
+
- [npm](https://www.npmjs.com/package/@classytic/formkit)
|
|
433
|
+
- [Examples](https://github.com/classytic/formkit/tree/main/example/shadcn)
|
|
434
|
+
- [Issues](https://github.com/classytic/formkit/issues)
|