@classytic/formkit 1.0.2 → 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 ADDED
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.3] - 2024-12-04
9
+
10
+ ### Changed
11
+
12
+ - **Complete TypeScript Migration**: Rewrote entire codebase with strict TypeScript
13
+ - **Improved Type Definitions**: Added comprehensive type exports for better DX
14
+ - **Modern Build System**: Updated tsup configuration for better tree-shaking
15
+ - **Enhanced Types**: Added `FieldOption`, `FieldOptionGroup`, `DefineField` utility types
16
+ - **Better Error Messages**: Improved development warnings with actionable messages
17
+
18
+ ### Added
19
+
20
+ - New type exports: `FieldOption`, `FieldOptionGroup`, `DefineField`, `SchemaFieldNames`, `InferSchemaValues`
21
+ - Subcomponent exports: `SectionRenderer`, `GridRenderer`, `FieldWrapper`
22
+ - Hook exports: `useFieldComponent`, `useLayoutComponent`
23
+ - Support for `gap` property in sections
24
+ - Support for `collapsible` and `defaultCollapsed` section properties
25
+ - `FormElement` type for consistent JSX return types
26
+ - `ClassValue` re-export from utils
27
+
28
+ ### Fixed
29
+
30
+ - Package exports configuration for proper ESM/CJS interop
31
+ - TypeScript strict mode compatibility
32
+ - Proper peer dependency declarations
33
+
34
+ ## [1.0.2] - 2024-12-03
35
+
36
+ ### Added
37
+
38
+ - Initial public release
39
+ - FormGenerator component
40
+ - FormSystemProvider context
41
+ - Schema-driven form generation
42
+ - Variant support for components
43
+ - Conditional field rendering
44
+
45
+ ## [1.0.1] - 2024-12-02
46
+
47
+ ### Fixed
48
+
49
+ - Package configuration fixes
50
+
51
+ ## [1.0.0] - 2024-12-01
52
+
53
+ ### Added
54
+
55
+ - Initial release
56
+
package/README.md CHANGED
@@ -4,41 +4,61 @@ Headless, type-safe form generation engine for React 18/19. Schema-driven with f
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/@classytic/formkit.svg)](https://www.npmjs.com/package/@classytic/formkit)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](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
7
19
 
8
20
  ## Installation
9
21
 
10
22
  ```bash
11
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
12
28
  ```
13
29
 
14
- ## Quick Start (Shadcn UI)
15
-
16
- **Important:** You must use `Controller` from react-hook-form. Raw Shadcn components won't work.
30
+ ## Quick Start
17
31
 
18
- ### 1. Install Shadcn Components
19
-
20
- ```bash
21
- npx shadcn@latest add input label select
22
- ```
32
+ ### 1. Create Field Components
23
33
 
24
- ### 2. Create Form Component
34
+ Each field component wraps your UI library with `react-hook-form`'s Controller:
25
35
 
26
36
  ```tsx
27
- // components/form-input.tsx
37
+ // components/form/form-input.tsx
38
+ "use client";
39
+
28
40
  import { Controller } from "react-hook-form";
41
+ import type { FieldComponentProps } from "@classytic/formkit";
29
42
  import { Input } from "@/components/ui/input";
30
43
  import { Label } from "@/components/ui/label";
31
44
 
32
- export function FormInput({ control, name, label, placeholder, required, ...props }) {
45
+ export function FormInput({ control, name, label, placeholder, required }: FieldComponentProps) {
33
46
  return (
34
47
  <Controller
35
48
  name={name}
36
49
  control={control}
37
50
  render={({ field, fieldState }) => (
38
51
  <div className="space-y-2">
39
- {label && <Label>{label}{required && '*'}</Label>}
40
- <Input {...field} placeholder={placeholder} />
41
- {fieldState.error && <p className="text-sm text-red-500">{fieldState.error.message}</p>}
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
+ )}
42
62
  </div>
43
63
  )}
44
64
  />
@@ -46,110 +66,362 @@ export function FormInput({ control, name, label, placeholder, required, ...prop
46
66
  }
47
67
  ```
48
68
 
49
- ### 3. Create Adapter
69
+ ### 2. Create Form Adapter
70
+
71
+ Register your components and layouts:
50
72
 
51
73
  ```tsx
52
74
  // lib/form-adapter.tsx
53
- import { FormSystemProvider } from "@classytic/formkit";
54
- import { FormInput } from "@/components/form-input";
75
+ "use client";
55
76
 
56
- const components = {
77
+ import { FormSystemProvider, type ComponentRegistry, type LayoutRegistry } from "@classytic/formkit";
78
+ import { FormInput } from "@/components/form/form-input";
79
+
80
+ const components: ComponentRegistry = {
57
81
  text: FormInput,
58
82
  email: FormInput,
83
+ password: FormInput,
84
+ // Add more field types...
59
85
  };
60
86
 
61
- const layouts = {
62
- section: ({ title, children }) => <div><h3>{title}</h3>{children}</div>,
63
- grid: ({ children, cols = 1 }) => <div className={`grid grid-cols-${cols} gap-4`}>{children}</div>,
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
+ ),
64
98
  };
65
99
 
66
- export function FormProvider({ children }) {
67
- return <FormSystemProvider components={components} layouts={layouts}>{children}</FormSystemProvider>;
100
+ export function FormProvider({ children }: { children: React.ReactNode }) {
101
+ return (
102
+ <FormSystemProvider components={components} layouts={layouts}>
103
+ {children}
104
+ </FormSystemProvider>
105
+ );
68
106
  }
69
107
  ```
70
108
 
71
- ### 4. Use in Your App
109
+ ### 3. Use FormGenerator
72
110
 
73
111
  ```tsx
112
+ // app/signup/page.tsx
113
+ "use client";
114
+
74
115
  import { useForm } from "react-hook-form";
75
- import { FormGenerator } from "@classytic/formkit";
76
- import { FormProvider } from "./form-adapter";
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
+ };
77
151
 
78
- export function MyForm() {
79
- const form = useForm();
152
+ export default function SignupPage() {
153
+ const form = useForm<SignupData>({
154
+ resolver: zodResolver(signupSchema),
155
+ });
80
156
 
81
157
  return (
82
158
  <FormProvider>
83
- <form onSubmit={form.handleSubmit(console.log)}>
84
- <FormGenerator
85
- schema={{
86
- sections: [{
87
- fields: [
88
- { name: "email", type: "email", label: "Email", required: true },
89
- { name: "password", type: "text", label: "Password" },
90
- ]
91
- }]
92
- }}
93
- control={form.control}
94
- />
95
- <button type="submit">Submit</button>
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>
96
162
  </form>
97
163
  </FormProvider>
98
164
  );
99
165
  }
100
166
  ```
101
167
 
102
- ## Complete Examples
168
+ ## API Reference
103
169
 
104
- See `example/shadcn/` directory for full working examples with:
105
- - FormInput, FormSelect, FormCheckbox
106
- - Validation with Zod
107
- - Conditional fields
108
- - Error handling
170
+ ### FormGenerator
109
171
 
110
- ## Key Points
172
+ The main component that renders forms from a schema.
111
173
 
112
- **You MUST use Controller:**
113
174
  ```tsx
114
- import { Controller } from "react-hook-form";
115
-
116
- <Controller
117
- name={name}
118
- control={control}
119
- render={({ field, fieldState }) => (
120
- <Input {...field} /> // ← Spread field
121
- )}
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
122
181
  />
123
182
  ```
124
183
 
125
- **Component receives these props:**
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
+
126
296
  ```tsx
127
297
  {
128
- field: {...}, // Config object
129
- control: {...}, // React Hook Form control
130
- name: "email", // Plus all field props spread
131
- label: "Email",
132
- // ...
298
+ name: "companyName",
299
+ type: "text",
300
+ label: "Company Name",
301
+ condition: (values) => values.accountType === "business",
133
302
  }
134
303
  ```
135
304
 
136
- ## API
305
+ ### Variants
137
306
 
138
- ### FormGenerator
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
139
335
 
140
- | Prop | Type | Required |
141
- |------|------|----------|
142
- | schema | FormSchema | Yes |
143
- | control | Control | Yes |
144
- | disabled | boolean | No |
145
- | variant | string | No |
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
+ ```
146
347
 
147
- ### Types
348
+ ### Grouped Select Options
148
349
 
149
350
  ```tsx
150
- import type { FormSchema, FieldComponentProps } from "@classytic/formkit";
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
+ }
151
371
  ```
152
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
+
153
425
  ## License
154
426
 
155
427
  MIT Š [Classytic](https://github.com/classytic)
@@ -157,5 +429,6 @@ MIT Š [Classytic](https://github.com/classytic)
157
429
  ## Links
158
430
 
159
431
  - [GitHub](https://github.com/classytic/formkit)
432
+ - [npm](https://www.npmjs.com/package/@classytic/formkit)
160
433
  - [Examples](https://github.com/classytic/formkit/tree/main/example/shadcn)
161
434
  - [Issues](https://github.com/classytic/formkit/issues)