@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/dist/index.d.cts CHANGED
@@ -1,56 +1,118 @@
1
- import { FieldValues, Control } from 'react-hook-form';
2
- import { ReactNode, ComponentType } from 'react';
1
+ import { FieldValues, Path, Control } from 'react-hook-form';
2
+ import { ReactNode, JSX, ComponentType } from 'react';
3
3
  import { ClassValue } from 'clsx';
4
+ export { ClassValue } from 'clsx';
4
5
 
5
6
  /**
6
- * Field type identifier
7
+ * Field type identifier.
8
+ * Can be built-in types or custom string identifiers.
7
9
  */
8
- type FieldType = string;
10
+ type FieldType = "text" | "email" | "password" | "number" | "tel" | "url" | "textarea" | "select" | "checkbox" | "radio" | "switch" | "date" | "time" | "datetime" | "file" | "hidden" | "custom" | (string & {});
9
11
  /**
10
- * Layout type identifier
12
+ * Layout type identifier.
11
13
  */
12
- type LayoutType = "section" | "grid" | "default" | string;
14
+ type LayoutType = "section" | "grid" | "default" | (string & {});
13
15
  /**
14
- * Variant identifier for component styling
16
+ * Variant identifier for component styling.
15
17
  */
16
- type Variant = string | undefined;
18
+ type Variant = "default" | "compact" | "inline" | (string & {}) | undefined;
17
19
  /**
18
- * Base field configuration
20
+ * Single option for select/radio/checkbox fields.
19
21
  */
20
- interface BaseField {
21
- /** Field name (maps to form field) */
22
- name: string;
23
- /** Field type (text, select, checkbox, etc.) */
22
+ interface FieldOption<TValue = string> {
23
+ /** Display label */
24
+ label: string;
25
+ /** Option value */
26
+ value: TValue;
27
+ /** Whether option is disabled */
28
+ disabled?: boolean;
29
+ /** Optional description */
30
+ description?: string;
31
+ /** Optional icon */
32
+ icon?: ReactNode;
33
+ }
34
+ /**
35
+ * Option group for select fields.
36
+ */
37
+ interface FieldOptionGroup<TValue = string> {
38
+ /** Group label */
39
+ label: string;
40
+ /** Group options */
41
+ options: FieldOption<TValue>[];
42
+ /** Whether group is disabled */
43
+ disabled?: boolean;
44
+ }
45
+ /**
46
+ * Base field configuration shared by all field types.
47
+ * @template TFieldValues - Form field values type for type-safe field names
48
+ */
49
+ interface BaseField<TFieldValues extends FieldValues = FieldValues> {
50
+ /** Field name (must be a valid path in form values) */
51
+ name: Path<TFieldValues> | (string & {});
52
+ /** Field type identifier */
24
53
  type: FieldType;
25
54
  /** Field label */
26
55
  label?: string;
27
56
  /** Placeholder text */
28
57
  placeholder?: string;
58
+ /** Helper text shown below the field */
59
+ helperText?: string;
29
60
  /** Whether field is disabled */
30
61
  disabled?: boolean;
31
62
  /** Whether field is required */
32
63
  required?: boolean;
64
+ /** Whether field is read-only */
65
+ readOnly?: boolean;
33
66
  /** Field variant */
34
67
  variant?: Variant;
35
- /** Whether field should span full width */
68
+ /** Whether field should span full width in grid */
36
69
  fullWidth?: boolean;
37
70
  /** Custom CSS class name */
38
71
  className?: string;
39
- /** Conditional rendering function */
40
- condition?: (formValues: FieldValues) => boolean;
72
+ /**
73
+ * Conditional rendering function.
74
+ * Return true to show the field, false to hide.
75
+ */
76
+ condition?: (formValues: TFieldValues) => boolean;
41
77
  /** Default value */
42
- defaultValue?: any;
43
- /** Additional field-specific props */
44
- [key: string]: any;
78
+ defaultValue?: unknown;
79
+ /** Options for select/radio/checkbox fields */
80
+ options?: (FieldOption | FieldOptionGroup)[];
81
+ /** Minimum value (for number/date fields) */
82
+ min?: number | string;
83
+ /** Maximum value (for number/date fields) */
84
+ max?: number | string;
85
+ /** Step value (for number fields) */
86
+ step?: number;
87
+ /** Pattern for validation (regex string) */
88
+ pattern?: string;
89
+ /** Minimum length */
90
+ minLength?: number;
91
+ /** Maximum length */
92
+ maxLength?: number;
93
+ /** Number of rows (for textarea) */
94
+ rows?: number;
95
+ /** Multiple selection (for select/file) */
96
+ multiple?: boolean;
97
+ /** Accepted file types (for file input) */
98
+ accept?: string;
99
+ /** Auto-complete attribute */
100
+ autoComplete?: string;
101
+ /** Auto-focus on mount */
102
+ autoFocus?: boolean;
103
+ /** Additional field-specific props (for custom components) */
104
+ [key: string]: unknown;
45
105
  }
46
106
  /**
47
- * Field component props
107
+ * Props passed to field components.
48
108
  *
49
- * IMPORTANT: Components receive both:
109
+ * Components receive both:
50
110
  * 1. A `field` object with the complete field configuration
51
- * 2. All field properties spread at the top level (via ...field)
111
+ * 2. All field properties spread at the top level
112
+ *
113
+ * @template TFieldValues - Form field values type
52
114
  *
53
- * Example:
115
+ * @example
54
116
  * ```tsx
55
117
  * // Schema
56
118
  * { name: "email", type: "email", label: "Email", placeholder: "user@example.com" }
@@ -69,21 +131,27 @@ interface BaseField {
69
131
  * }
70
132
  * ```
71
133
  */
72
- interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> extends BaseField {
134
+ interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> extends BaseField<TFieldValues> {
73
135
  /** Field configuration object (contains all field props) */
74
- field: BaseField;
75
- /** React Hook Form control (for Controller integration) */
76
- control?: Control<TFieldValues> | Control<any>;
136
+ field: BaseField<TFieldValues>;
137
+ /** React Hook Form control for Controller integration */
138
+ control: Control<TFieldValues>;
77
139
  /** Whether field is globally disabled */
78
140
  disabled?: boolean;
79
- /** Component variant (e.g., "compact", "default") */
141
+ /** Component variant */
80
142
  variant?: Variant;
81
143
  }
82
144
  /**
83
- * Section configuration
145
+ * Field component type.
146
+ * A React component that renders a form field.
147
+ */
148
+ type FieldComponent<TFieldValues extends FieldValues = FieldValues> = ComponentType<FieldComponentProps<TFieldValues>>;
149
+ /**
150
+ * Section configuration for grouping fields.
151
+ * @template TFieldValues - Form field values type
84
152
  */
85
- interface Section {
86
- /** Section ID (optional) */
153
+ interface Section<TFieldValues extends FieldValues = FieldValues> {
154
+ /** Unique section identifier */
87
155
  id?: string;
88
156
  /** Section title */
89
157
  title?: string;
@@ -91,32 +159,41 @@ interface Section {
91
159
  description?: string;
92
160
  /** Section icon */
93
161
  icon?: ReactNode;
94
- /** Section fields */
95
- fields?: BaseField[];
96
- /** Number of columns in grid */
97
- cols?: number;
98
- /** Custom render function */
99
- render?: (props: {
100
- control?: Control<any>;
101
- disabled?: boolean;
102
- section: Section;
103
- }) => ReactNode;
162
+ /** Fields in this section */
163
+ fields?: BaseField<TFieldValues>[];
164
+ /** Number of columns in grid layout */
165
+ cols?: 1 | 2 | 3 | 4 | 5 | 6 | number;
166
+ /** Gap between grid items (Tailwind spacing scale) */
167
+ gap?: number;
168
+ /**
169
+ * Custom render function for complete control.
170
+ * When provided, fields array is ignored.
171
+ */
172
+ render?: (props: SectionRenderProps<TFieldValues>) => ReactNode;
104
173
  /** Section variant */
105
174
  variant?: Variant;
106
175
  /** Custom CSS class name */
107
176
  className?: string;
108
- /** Conditional rendering function */
109
- condition?: (control?: Control<any>) => boolean;
177
+ /**
178
+ * Conditional rendering function.
179
+ * Return true to show the section, false to hide.
180
+ */
181
+ condition?: (control?: Control<TFieldValues>) => boolean;
182
+ /** Whether section is collapsible */
183
+ collapsible?: boolean;
184
+ /** Default collapsed state */
185
+ defaultCollapsed?: boolean;
110
186
  }
111
187
  /**
112
- * Form schema configuration
188
+ * Props passed to section render function.
113
189
  */
114
- interface FormSchema {
115
- /** Form sections */
116
- sections: Section[];
190
+ interface SectionRenderProps<TFieldValues extends FieldValues = FieldValues> {
191
+ control?: Control<TFieldValues>;
192
+ disabled?: boolean;
193
+ section: Section<TFieldValues>;
117
194
  }
118
195
  /**
119
- * Section layout component props
196
+ * Section layout component props.
120
197
  */
121
198
  interface SectionLayoutProps {
122
199
  /** Section title */
@@ -129,44 +206,79 @@ interface SectionLayoutProps {
129
206
  variant?: Variant;
130
207
  /** Custom CSS class name */
131
208
  className?: string;
209
+ /** Whether section is collapsible */
210
+ collapsible?: boolean;
211
+ /** Default collapsed state */
212
+ defaultCollapsed?: boolean;
132
213
  /** Children content */
133
214
  children: ReactNode;
134
215
  }
135
216
  /**
136
- * Grid layout component props
217
+ * Grid layout component props.
137
218
  */
138
219
  interface GridLayoutProps {
139
220
  /** Number of columns */
140
221
  cols?: number;
222
+ /** Gap between items */
223
+ gap?: number;
224
+ /** Custom CSS class name */
225
+ className?: string;
141
226
  /** Children content */
142
227
  children: ReactNode;
143
228
  }
144
229
  /**
145
- * Layout component props union
230
+ * Default layout component props.
146
231
  */
147
- type LayoutComponentProps = SectionLayoutProps | GridLayoutProps;
232
+ interface DefaultLayoutProps {
233
+ /** Custom CSS class name */
234
+ className?: string;
235
+ /** Children content */
236
+ children: ReactNode;
237
+ }
148
238
  /**
149
- * Field component type
239
+ * Union of all layout component props.
150
240
  */
151
- type FieldComponent<TFieldValues extends FieldValues = FieldValues> = ComponentType<FieldComponentProps<TFieldValues>>;
241
+ type LayoutComponentProps = SectionLayoutProps | GridLayoutProps | DefaultLayoutProps;
152
242
  /**
153
- * Layout component type
243
+ * Layout component type.
154
244
  */
155
- type LayoutComponent = ComponentType<any>;
245
+ type LayoutComponent<TProps extends LayoutComponentProps = LayoutComponentProps> = ComponentType<TProps>;
156
246
  /**
157
- * Component registry mapping field types to components
247
+ * Complete form schema configuration.
248
+ * @template TFieldValues - Form field values type for type-safe schemas
249
+ */
250
+ interface FormSchema<TFieldValues extends FieldValues = FieldValues> {
251
+ /** Form sections */
252
+ sections: Section<TFieldValues>[];
253
+ }
254
+ /**
255
+ * Component registry mapping field types to components.
256
+ * Supports variant-specific components via nested objects.
257
+ *
258
+ * @example
259
+ * ```tsx
260
+ * const components: ComponentRegistry = {
261
+ * text: TextInput,
262
+ * select: SelectInput,
263
+ * // Variant-specific
264
+ * compact: {
265
+ * text: CompactTextInput,
266
+ * },
267
+ * };
268
+ * ```
158
269
  */
159
270
  interface ComponentRegistry {
160
- [key: string]: FieldComponent | ComponentRegistry;
271
+ [key: string]: FieldComponent<FieldValues> | ComponentRegistry;
161
272
  }
162
273
  /**
163
- * Layout registry mapping layout types to components
274
+ * Layout registry mapping layout types to components.
275
+ * Supports variant-specific layouts via nested objects.
164
276
  */
165
277
  interface LayoutRegistry {
166
- [key: string]: LayoutComponent | LayoutRegistry;
278
+ [key: string]: LayoutComponent<SectionLayoutProps | GridLayoutProps | DefaultLayoutProps> | LayoutRegistry;
167
279
  }
168
280
  /**
169
- * Form system context value
281
+ * Form system context value.
170
282
  */
171
283
  interface FormSystemContextValue {
172
284
  /** Registered field components */
@@ -175,7 +287,7 @@ interface FormSystemContextValue {
175
287
  layouts: LayoutRegistry;
176
288
  }
177
289
  /**
178
- * Form system provider props
290
+ * Form system provider props.
179
291
  */
180
292
  interface FormSystemProviderProps {
181
293
  /** Field component registry */
@@ -186,34 +298,61 @@ interface FormSystemProviderProps {
186
298
  children: ReactNode;
187
299
  }
188
300
  /**
189
- * Form generator props
301
+ * FormGenerator component props.
302
+ * @template TFieldValues - Form field values type
190
303
  */
191
304
  interface FormGeneratorProps<TFieldValues extends FieldValues = FieldValues> {
192
- /** Form schema */
193
- schema: FormSchema;
194
- /** React Hook Form control */
305
+ /** Form schema defining sections and fields */
306
+ schema: FormSchema<TFieldValues>;
307
+ /** React Hook Form control object */
195
308
  control?: Control<TFieldValues>;
196
- /** Global disabled state */
309
+ /** Global disabled state for all fields */
197
310
  disabled?: boolean;
198
- /** Global variant */
311
+ /** Global variant for all components */
199
312
  variant?: Variant;
313
+ /** Additional CSS class for the root element */
314
+ className?: string;
200
315
  }
316
+ /**
317
+ * Extract field names from a schema.
318
+ */
319
+ type SchemaFieldNames<TSchema extends FormSchema> = TSchema extends FormSchema<infer T> ? keyof T : never;
320
+ /**
321
+ * Infer field values type from a schema.
322
+ */
323
+ type InferSchemaValues<TSchema extends FormSchema> = TSchema extends FormSchema<infer T> ? T : FieldValues;
324
+ /**
325
+ * Helper type for creating type-safe field definitions.
326
+ */
327
+ type DefineField<TFieldValues extends FieldValues, TType extends FieldType = FieldType> = BaseField<TFieldValues> & {
328
+ type: TType;
329
+ };
330
+ /**
331
+ * JSX Element return type for components.
332
+ */
333
+ type FormElement = JSX.Element | null;
201
334
 
202
335
  /**
203
336
  * FormGenerator - Headless Form Generator Component
204
337
  *
205
- * Renders a form based on a schema, using components injected via FormSystemProvider.
206
- * Supports conditional fields, dynamic layouts, and variants.
338
+ * Renders a form based on a schema definition, using components registered
339
+ * via FormSystemProvider. Supports conditional fields, dynamic layouts,
340
+ * and component variants.
207
341
  *
208
- * @template TFieldValues - Form field values type
342
+ * @template TFieldValues - Form field values type for type safety
209
343
  *
210
344
  * @example
211
345
  * ```tsx
212
346
  * import { useForm } from 'react-hook-form';
213
347
  * import { FormGenerator } from '@classytic/formkit';
214
348
  *
349
+ * interface FormData {
350
+ * firstName: string;
351
+ * email: string;
352
+ * }
353
+ *
215
354
  * function MyForm() {
216
- * const { control } = useForm();
355
+ * const { control } = useForm<FormData>();
217
356
  *
218
357
  * const schema = {
219
358
  * sections: [
@@ -231,13 +370,47 @@ interface FormGeneratorProps<TFieldValues extends FieldValues = FieldValues> {
231
370
  * }
232
371
  * ```
233
372
  */
234
- declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({ schema, control, disabled, variant, }: FormGeneratorProps<TFieldValues>): JSX.Element | null;
373
+ declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({ schema, control, disabled, variant, className, }: FormGeneratorProps<TFieldValues>): FormElement;
374
+ interface SectionRendererProps<TFieldValues extends FieldValues = FieldValues> {
375
+ section: Section<TFieldValues>;
376
+ control?: Control<TFieldValues>;
377
+ disabled?: boolean;
378
+ variant?: Variant;
379
+ }
380
+ /**
381
+ * Renders a single section with its fields.
382
+ * Handles conditional rendering and variant resolution.
383
+ */
384
+ declare function SectionRenderer<TFieldValues extends FieldValues = FieldValues>({ section, control, disabled, variant, }: SectionRendererProps<TFieldValues>): FormElement;
385
+ interface GridRendererProps<TFieldValues extends FieldValues = FieldValues> {
386
+ fields?: BaseField<TFieldValues>[];
387
+ cols?: number;
388
+ gap?: number;
389
+ control?: Control<TFieldValues>;
390
+ disabled?: boolean;
391
+ variant?: Variant;
392
+ }
393
+ /**
394
+ * Renders a grid of fields with specified column layout.
395
+ */
396
+ declare function GridRenderer<TFieldValues extends FieldValues = FieldValues>({ fields, cols, gap, control, disabled, variant, }: GridRendererProps<TFieldValues>): FormElement;
397
+ interface FieldWrapperProps<TFieldValues extends FieldValues = FieldValues> {
398
+ field: BaseField<TFieldValues>;
399
+ control?: Control<TFieldValues>;
400
+ disabled?: boolean;
401
+ variant?: Variant;
402
+ }
403
+ /**
404
+ * Wraps individual fields with conditional rendering logic.
405
+ * Handles field visibility and variant resolution.
406
+ */
407
+ declare function FieldWrapper<TFieldValues extends FieldValues = FieldValues>({ field, control, disabled, variant, }: FieldWrapperProps<TFieldValues>): FormElement;
235
408
 
236
409
  /**
237
410
  * FormSystemProvider
238
411
  *
239
- * Provides the component registry to the form system.
240
- * This is the root provider that enables FormGenerator to work.
412
+ * Root provider that enables the form system. Provides component and layout
413
+ * registries to FormGenerator and its descendants.
241
414
  *
242
415
  * @example
243
416
  * ```tsx
@@ -246,6 +419,10 @@ declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({
246
419
  * const components = {
247
420
  * text: TextInput,
248
421
  * select: SelectInput,
422
+ * // Variant-specific components
423
+ * compact: {
424
+ * text: CompactTextInput,
425
+ * },
249
426
  * };
250
427
  *
251
428
  * const layouts = {
@@ -262,9 +439,9 @@ declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({
262
439
  * }
263
440
  * ```
264
441
  */
265
- declare function FormSystemProvider({ components, layouts, children, }: FormSystemProviderProps): JSX.Element;
442
+ declare function FormSystemProvider({ components, layouts, children, }: FormSystemProviderProps): FormElement;
266
443
  /**
267
- * Hook to access the form system context
444
+ * Hook to access the form system context.
268
445
  *
269
446
  * @throws {Error} If used outside FormSystemProvider
270
447
  * @returns Form system context value
@@ -275,14 +452,60 @@ declare function FormSystemProvider({ components, layouts, children, }: FormSyst
275
452
  * ```
276
453
  */
277
454
  declare function useFormSystem(): FormSystemContextValue;
455
+ /**
456
+ * Hook to get a field component by type and optional variant.
457
+ *
458
+ * Resolution order:
459
+ * 1. Variant-specific component: `components[variant][type]`
460
+ * 2. Type-specific component: `components[type]`
461
+ * 3. Default component: `components["default"]`
462
+ * 4. Text fallback: `components["text"]`
463
+ *
464
+ * @param type - Field type identifier
465
+ * @param variant - Optional variant name
466
+ * @returns Field component or fallback
467
+ *
468
+ * @internal
469
+ */
470
+ declare function useFieldComponent(type: FieldType, variant?: Variant): FieldComponent;
471
+ /**
472
+ * Hook to get a layout component by type and optional variant.
473
+ *
474
+ * Resolution order:
475
+ * 1. Variant-specific layout: `layouts[variant][type]`
476
+ * 2. Type-specific layout: `layouts[type]`
477
+ * 3. Default layout: `layouts["default"]`
478
+ * 4. Built-in default layout
479
+ *
480
+ * @param type - Layout type identifier
481
+ * @param variant - Optional variant name
482
+ * @returns Layout component or fallback
483
+ *
484
+ * @internal
485
+ */
486
+ declare function useLayoutComponent(type: LayoutType, variant?: Variant): LayoutComponent<SectionLayoutProps | GridLayoutProps | DefaultLayoutProps>;
278
487
 
279
488
  /**
280
- * Utility function to merge Tailwind CSS classes
281
- * Combines clsx and tailwind-merge for conflict-free class merging
489
+ * Utility function to merge CSS classes with Tailwind CSS conflict resolution.
490
+ *
491
+ * Combines `clsx` for conditional class handling with `tailwind-merge`
492
+ * for proper Tailwind CSS class conflict resolution.
493
+ *
494
+ * @param inputs - Class values to merge (strings, arrays, objects, or conditionals)
495
+ * @returns Merged and deduplicated class string
282
496
  *
283
- * @param inputs - Class values to merge
284
- * @returns Merged class string
497
+ * @example
498
+ * ```tsx
499
+ * // Basic usage
500
+ * cn("px-2 py-1", "px-4") // "py-1 px-4"
501
+ *
502
+ * // Conditional classes
503
+ * cn("base", isActive && "active", { "disabled": isDisabled })
504
+ *
505
+ * // Arrays
506
+ * cn(["flex", "items-center"], "gap-2")
507
+ * ```
285
508
  */
286
509
  declare function cn(...inputs: ClassValue[]): string;
287
510
 
288
- export { type BaseField, type ComponentRegistry, type FieldComponent, type FieldComponentProps, type FieldType, FormGenerator, type FormGeneratorProps, type FormSchema, type FormSystemContextValue, FormSystemProvider, type FormSystemProviderProps, type GridLayoutProps, type LayoutComponent, type LayoutComponentProps, type LayoutRegistry, type LayoutType, type Section, type SectionLayoutProps, type Variant, cn, useFormSystem };
511
+ export { type BaseField, type ComponentRegistry, type DefaultLayoutProps, type DefineField, type FieldComponent, type FieldComponentProps, type FieldOption, type FieldOptionGroup, type FieldType, FieldWrapper, type FormElement, FormGenerator, type FormGeneratorProps, type FormSchema, type FormSystemContextValue, FormSystemProvider, type FormSystemProviderProps, type GridLayoutProps, GridRenderer, type InferSchemaValues, type LayoutComponent, type LayoutComponentProps, type LayoutRegistry, type LayoutType, type SchemaFieldNames, type Section, type SectionLayoutProps, type SectionRenderProps, SectionRenderer, type Variant, cn, useFieldComponent, useFormSystem, useLayoutComponent };