@classytic/formkit 1.0.0 → 1.0.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/README.md CHANGED
@@ -1,273 +1,161 @@
1
- # @classytic/formkit
2
-
3
- A headless, type-safe form generation engine for React 18+ and React 19. Build dynamic forms from schemas with full TypeScript support and zero UI opinions.
4
-
5
- [![npm version](https://img.shields.io/npm/v/@classytic/formkit.svg)](https://www.npmjs.com/package/@classytic/formkit)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
8
- ## ✨ Features
9
-
10
- - 🎯 **Headless & UI-Agnostic**: Bring your own components (Shadcn, Material UI, Ant Design, or custom)
11
- - 📝 **Schema-Driven**: Generate complex forms from simple JSON schemas
12
- - 🔒 **Type-Safe**: Built with TypeScript, full type inference and autocomplete
13
- - ⚡ **Performance**: Optimized rendering with React 18/19 concurrent features
14
- - 🎨 **Variants**: Support multiple component styles (compact, default, custom)
15
- - 🔀 **Conditional Fields**: Show/hide fields based on form values
16
- - 🧩 **Composable**: Modular architecture with pluggable components
17
- - 🪝 **React Hook Form**: Built on top of the industry-standard form library
18
- - 🌲 **Tree-Shakeable**: Only bundle what you use
19
-
20
- ## 📦 Installation
21
-
22
- ```bash
23
- npm install @classytic/formkit react-hook-form
24
- # or
25
- yarn add @classytic/formkit react-hook-form
26
- # or
27
- pnpm add @classytic/formkit react-hook-form
28
- ```
29
-
30
- ## 🚀 Quick Start
31
-
32
- ### 1. Create a Component Adapter
33
-
34
- Map your UI components to form field types:
35
-
36
- ```tsx
37
- // form-adapter.tsx
38
- import { FormSystemProvider } from "@classytic/formkit";
39
- import { Input } from "@/components/ui/input";
40
- import { Select } from "@/components/ui/select";
41
- import { Checkbox } from "@/components/ui/checkbox";
42
-
43
- const components = {
44
- text: Input,
45
- email: Input,
46
- select: Select,
47
- checkbox: Checkbox,
48
- // Add more field types as needed
49
- };
50
-
51
- const layouts = {
52
- grid: ({ children, cols = 1 }) => (
53
- <div className={`grid grid-cols-${cols} gap-4`}>{children}</div>
54
- ),
55
- section: ({ title, description, children }) => (
56
- <div className="space-y-4">
57
- {title && <h3 className="text-lg font-semibold">{title}</h3>}
58
- {description && <p className="text-sm text-muted-foreground">{description}</p>}
59
- {children}
60
- </div>
61
- ),
62
- };
63
-
64
- export function FormProvider({ children }: { children: React.ReactNode }) {
65
- return (
66
- <FormSystemProvider components={components} layouts={layouts}>
67
- {children}
68
- </FormSystemProvider>
69
- );
70
- }
71
- ```
72
-
73
- ### 2. Define Your Schema
74
-
75
- ```tsx
76
- import type { FormSchema } from "@classytic/formkit";
77
-
78
- const userFormSchema: FormSchema = {
79
- sections: [
80
- {
81
- title: "Personal Information",
82
- cols: 2,
83
- fields: [
84
- {
85
- name: "firstName",
86
- type: "text",
87
- label: "First Name",
88
- required: true,
89
- },
90
- {
91
- name: "lastName",
92
- type: "text",
93
- label: "Last Name",
94
- required: true,
95
- },
96
- {
97
- name: "email",
98
- type: "email",
99
- label: "Email",
100
- fullWidth: true,
101
- },
102
- ],
103
- },
104
- {
105
- title: "Account Settings",
106
- fields: [
107
- {
108
- name: "role",
109
- type: "select",
110
- label: "Role",
111
- options: [
112
- { value: "user", label: "User" },
113
- { value: "admin", label: "Admin" },
114
- ],
115
- },
116
- {
117
- name: "notifications",
118
- type: "checkbox",
119
- label: "Enable notifications",
120
- // Conditional field - only show for admins
121
- condition: (values) => values.role === "admin",
122
- },
123
- ],
124
- },
125
- ],
126
- };
127
- ```
128
-
129
- ### 3. Render Your Form
130
-
131
- ```tsx
132
- import { useForm } from "react-hook-form";
133
- import { FormGenerator } from "@classytic/formkit";
134
- import { FormProvider } from "./form-adapter";
135
-
136
- function UserForm() {
137
- const form = useForm();
138
-
139
- const onSubmit = (data: any) => {
140
- console.log("Form data:", data);
141
- };
142
-
143
- return (
144
- <FormProvider>
145
- <form onSubmit={form.handleSubmit(onSubmit)}>
146
- <FormGenerator schema={userFormSchema} control={form.control} />
147
- <button type="submit">Submit</button>
148
- </form>
149
- </FormProvider>
150
- );
151
- }
152
- ```
153
-
154
- ## 📖 API Reference
155
-
156
- ### `<FormSystemProvider>`
157
-
158
- The root provider that registers your components.
159
-
160
- ```tsx
161
- <FormSystemProvider components={componentsMap} layouts={layoutsMap}>
162
- {children}
163
- </FormSystemProvider>
164
- ```
165
-
166
- **Props:**
167
- - `components`: Object mapping field types to React components
168
- - `layouts`: Object mapping layout types to React components
169
- - `children`: React children
170
-
171
- ### `<FormGenerator>`
172
-
173
- Renders forms from schemas.
174
-
175
- ```tsx
176
- <FormGenerator schema={schema} control={control} disabled={false} variant="default" />
177
- ```
178
-
179
- **Props:**
180
- - `schema`: Form schema object
181
- - `control?`: React Hook Form control (optional if inside FormProvider)
182
- - `disabled?`: Global disabled state
183
- - `variant?`: Component variant to use
184
-
185
- ### Type Exports
186
-
187
- ```tsx
188
- import type {
189
- FormSchema,
190
- BaseField,
191
- Section,
192
- FieldComponentProps,
193
- ComponentRegistry,
194
- } from "@classytic/formkit";
195
- ```
196
-
197
- ## 🎨 Advanced Usage
198
-
199
- ### Variants
200
-
201
- Support multiple UI styles:
202
-
203
- ```tsx
204
- const components = {
205
- text: DefaultInput,
206
- compact: {
207
- text: CompactInput,
208
- select: CompactSelect,
209
- },
210
- };
211
-
212
- // Use compact variant
213
- <FormGenerator schema={schema} variant="compact" />
214
- ```
215
-
216
- ### Conditional Fields
217
-
218
- ```tsx
219
- {
220
- name: "vatNumber",
221
- type: "text",
222
- label: "VAT Number",
223
- condition: (values) => values.country === "EU",
224
- }
225
- ```
226
-
227
- ### Custom Renderers
228
-
229
- ```tsx
230
- {
231
- title: "Custom Section",
232
- render: ({ control, disabled }) => (
233
- <div>Your custom JSX here</div>
234
- ),
235
- }
236
- ```
237
-
238
- ### Full Width Fields
239
-
240
- ```tsx
241
- {
242
- name: "description",
243
- type: "textarea",
244
- label: "Description",
245
- fullWidth: true, // Spans all columns
246
- }
247
- ```
248
-
249
- ## 🧪 Testing
250
-
251
- ```bash
252
- npm test
253
- ```
254
-
255
- ## 📄 License
256
-
257
- MIT © [Classytic](https://github.com/classytic)
258
-
259
- ## 🤝 Contributing
260
-
261
- Contributions are welcome! Please feel free to submit a Pull Request.
262
-
263
- ## 🔗 Links
264
-
265
- - [GitHub Repository](https://github.com/classytic/formkit)
266
- - [Issue Tracker](https://github.com/classytic/formkit/issues)
267
- - [npm Package](https://www.npmjs.com/package/@classytic/formkit)
268
-
269
- ## 🌟 Related Packages
270
-
271
- - [@classytic/notifications](https://www.npmjs.com/package/@classytic/notifications) - Multi-channel notification system
272
- - [@classytic/mongokit](https://www.npmjs.com/package/@classytic/mongokit) - Event-driven MongoDB repositories
273
-
1
+ # @classytic/formkit
2
+
3
+ Headless, type-safe form generation engine for React 18/19. Schema-driven with full TypeScript support.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@classytic/formkit.svg)](https://www.npmjs.com/package/@classytic/formkit)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install @classytic/formkit react-hook-form
12
+ ```
13
+
14
+ ## Quick Start (Shadcn UI)
15
+
16
+ **Important:** You must use `Controller` from react-hook-form. Raw Shadcn components won't work.
17
+
18
+ ### 1. Install Shadcn Components
19
+
20
+ ```bash
21
+ npx shadcn@latest add input label select
22
+ ```
23
+
24
+ ### 2. Create Form Component
25
+
26
+ ```tsx
27
+ // components/form-input.tsx
28
+ import { Controller } from "react-hook-form";
29
+ import { Input } from "@/components/ui/input";
30
+ import { Label } from "@/components/ui/label";
31
+
32
+ export function FormInput({ control, name, label, placeholder, required, ...props }) {
33
+ return (
34
+ <Controller
35
+ name={name}
36
+ control={control}
37
+ render={({ field, fieldState }) => (
38
+ <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>}
42
+ </div>
43
+ )}
44
+ />
45
+ );
46
+ }
47
+ ```
48
+
49
+ ### 3. Create Adapter
50
+
51
+ ```tsx
52
+ // lib/form-adapter.tsx
53
+ import { FormSystemProvider } from "@classytic/formkit";
54
+ import { FormInput } from "@/components/form-input";
55
+
56
+ const components = {
57
+ text: FormInput,
58
+ email: FormInput,
59
+ };
60
+
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>,
64
+ };
65
+
66
+ export function FormProvider({ children }) {
67
+ return <FormSystemProvider components={components} layouts={layouts}>{children}</FormSystemProvider>;
68
+ }
69
+ ```
70
+
71
+ ### 4. Use in Your App
72
+
73
+ ```tsx
74
+ import { useForm } from "react-hook-form";
75
+ import { FormGenerator } from "@classytic/formkit";
76
+ import { FormProvider } from "./form-adapter";
77
+
78
+ export function MyForm() {
79
+ const form = useForm();
80
+
81
+ return (
82
+ <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>
96
+ </form>
97
+ </FormProvider>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ## Complete Examples
103
+
104
+ See `example/shadcn/` directory for full working examples with:
105
+ - FormInput, FormSelect, FormCheckbox
106
+ - Validation with Zod
107
+ - Conditional fields
108
+ - Error handling
109
+
110
+ ## Key Points
111
+
112
+ **You MUST use Controller:**
113
+ ```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
+ )}
122
+ />
123
+ ```
124
+
125
+ **Component receives these props:**
126
+ ```tsx
127
+ {
128
+ field: {...}, // Config object
129
+ control: {...}, // React Hook Form control
130
+ name: "email", // Plus all field props spread
131
+ label: "Email",
132
+ // ...
133
+ }
134
+ ```
135
+
136
+ ## API
137
+
138
+ ### FormGenerator
139
+
140
+ | Prop | Type | Required |
141
+ |------|------|----------|
142
+ | schema | FormSchema | Yes |
143
+ | control | Control | Yes |
144
+ | disabled | boolean | No |
145
+ | variant | string | No |
146
+
147
+ ### Types
148
+
149
+ ```tsx
150
+ import type { FormSchema, FieldComponentProps } from "@classytic/formkit";
151
+ ```
152
+
153
+ ## License
154
+
155
+ MIT © [Classytic](https://github.com/classytic)
156
+
157
+ ## Links
158
+
159
+ - [GitHub](https://github.com/classytic/formkit)
160
+ - [Examples](https://github.com/classytic/formkit/tree/main/example/shadcn)
161
+ - [Issues](https://github.com/classytic/formkit/issues)
package/dist/index.d.cts CHANGED
@@ -45,15 +45,38 @@ interface BaseField {
45
45
  }
46
46
  /**
47
47
  * Field component props
48
+ *
49
+ * IMPORTANT: Components receive both:
50
+ * 1. A `field` object with the complete field configuration
51
+ * 2. All field properties spread at the top level (via ...field)
52
+ *
53
+ * Example:
54
+ * ```tsx
55
+ * // Schema
56
+ * { name: "email", type: "email", label: "Email", placeholder: "user@example.com" }
57
+ *
58
+ * // Your component receives:
59
+ * {
60
+ * field: { name: "email", type: "email", label: "Email", placeholder: "..." },
61
+ * control: {...},
62
+ * disabled: false,
63
+ * variant: undefined,
64
+ * // PLUS all field props at top level:
65
+ * name: "email",
66
+ * type: "email",
67
+ * label: "Email",
68
+ * placeholder: "user@example.com"
69
+ * }
70
+ * ```
48
71
  */
49
- interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> {
50
- /** Field configuration */
72
+ interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> extends BaseField {
73
+ /** Field configuration object (contains all field props) */
51
74
  field: BaseField;
52
- /** React Hook Form control */
53
- control?: Control<TFieldValues>;
54
- /** Whether field is disabled */
75
+ /** React Hook Form control (for Controller integration) */
76
+ control?: Control<TFieldValues> | Control<any>;
77
+ /** Whether field is globally disabled */
55
78
  disabled?: boolean;
56
- /** Field variant */
79
+ /** Component variant (e.g., "compact", "default") */
57
80
  variant?: Variant;
58
81
  }
59
82
  /**
package/dist/index.d.ts CHANGED
@@ -45,15 +45,38 @@ interface BaseField {
45
45
  }
46
46
  /**
47
47
  * Field component props
48
+ *
49
+ * IMPORTANT: Components receive both:
50
+ * 1. A `field` object with the complete field configuration
51
+ * 2. All field properties spread at the top level (via ...field)
52
+ *
53
+ * Example:
54
+ * ```tsx
55
+ * // Schema
56
+ * { name: "email", type: "email", label: "Email", placeholder: "user@example.com" }
57
+ *
58
+ * // Your component receives:
59
+ * {
60
+ * field: { name: "email", type: "email", label: "Email", placeholder: "..." },
61
+ * control: {...},
62
+ * disabled: false,
63
+ * variant: undefined,
64
+ * // PLUS all field props at top level:
65
+ * name: "email",
66
+ * type: "email",
67
+ * label: "Email",
68
+ * placeholder: "user@example.com"
69
+ * }
70
+ * ```
48
71
  */
49
- interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> {
50
- /** Field configuration */
72
+ interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> extends BaseField {
73
+ /** Field configuration object (contains all field props) */
51
74
  field: BaseField;
52
- /** React Hook Form control */
53
- control?: Control<TFieldValues>;
54
- /** Whether field is disabled */
75
+ /** React Hook Form control (for Controller integration) */
76
+ control?: Control<TFieldValues> | Control<any>;
77
+ /** Whether field is globally disabled */
55
78
  disabled?: boolean;
56
- /** Field variant */
79
+ /** Component variant (e.g., "compact", "default") */
57
80
  variant?: Variant;
58
81
  }
59
82
  /**
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@classytic/formkit",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Headless, type-safe form generation engine for React. Schema-driven with full TypeScript support.",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
13
- "require": "./dist/index.js"
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
16
  "files": [