@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/dist/index.d.ts CHANGED
@@ -1,66 +1,157 @@
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.
108
+ *
109
+ * Components receive both:
110
+ * 1. A `field` object with the complete field configuration
111
+ * 2. All field properties spread at the top level
112
+ *
113
+ * @template TFieldValues - Form field values type
114
+ *
115
+ * @example
116
+ * ```tsx
117
+ * // Schema
118
+ * { name: "email", type: "email", label: "Email", placeholder: "user@example.com" }
119
+ *
120
+ * // Your component receives:
121
+ * {
122
+ * field: { name: "email", type: "email", label: "Email", placeholder: "..." },
123
+ * control: {...},
124
+ * disabled: false,
125
+ * variant: undefined,
126
+ * // PLUS all field props at top level:
127
+ * name: "email",
128
+ * type: "email",
129
+ * label: "Email",
130
+ * placeholder: "user@example.com"
131
+ * }
132
+ * ```
48
133
  */
49
- interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> {
50
- /** Field configuration */
51
- field: BaseField;
52
- /** React Hook Form control */
53
- control?: Control<TFieldValues>;
54
- /** Whether field is disabled */
134
+ interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> extends BaseField<TFieldValues> {
135
+ /** Field configuration object (contains all field props) */
136
+ field: BaseField<TFieldValues>;
137
+ /** React Hook Form control for Controller integration */
138
+ control: Control<TFieldValues>;
139
+ /** Whether field is globally disabled */
55
140
  disabled?: boolean;
56
- /** Field variant */
141
+ /** Component variant */
57
142
  variant?: Variant;
58
143
  }
59
144
  /**
60
- * Section configuration
145
+ * Field component type.
146
+ * A React component that renders a form field.
61
147
  */
62
- interface Section {
63
- /** Section ID (optional) */
148
+ type FieldComponent<TFieldValues extends FieldValues = FieldValues> = ComponentType<FieldComponentProps<TFieldValues>>;
149
+ /**
150
+ * Section configuration for grouping fields.
151
+ * @template TFieldValues - Form field values type
152
+ */
153
+ interface Section<TFieldValues extends FieldValues = FieldValues> {
154
+ /** Unique section identifier */
64
155
  id?: string;
65
156
  /** Section title */
66
157
  title?: string;
@@ -68,32 +159,41 @@ interface Section {
68
159
  description?: string;
69
160
  /** Section icon */
70
161
  icon?: ReactNode;
71
- /** Section fields */
72
- fields?: BaseField[];
73
- /** Number of columns in grid */
74
- cols?: number;
75
- /** Custom render function */
76
- render?: (props: {
77
- control?: Control<any>;
78
- disabled?: boolean;
79
- section: Section;
80
- }) => 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;
81
173
  /** Section variant */
82
174
  variant?: Variant;
83
175
  /** Custom CSS class name */
84
176
  className?: string;
85
- /** Conditional rendering function */
86
- 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;
87
186
  }
88
187
  /**
89
- * Form schema configuration
188
+ * Props passed to section render function.
90
189
  */
91
- interface FormSchema {
92
- /** Form sections */
93
- sections: Section[];
190
+ interface SectionRenderProps<TFieldValues extends FieldValues = FieldValues> {
191
+ control?: Control<TFieldValues>;
192
+ disabled?: boolean;
193
+ section: Section<TFieldValues>;
94
194
  }
95
195
  /**
96
- * Section layout component props
196
+ * Section layout component props.
97
197
  */
98
198
  interface SectionLayoutProps {
99
199
  /** Section title */
@@ -106,44 +206,79 @@ interface SectionLayoutProps {
106
206
  variant?: Variant;
107
207
  /** Custom CSS class name */
108
208
  className?: string;
209
+ /** Whether section is collapsible */
210
+ collapsible?: boolean;
211
+ /** Default collapsed state */
212
+ defaultCollapsed?: boolean;
109
213
  /** Children content */
110
214
  children: ReactNode;
111
215
  }
112
216
  /**
113
- * Grid layout component props
217
+ * Grid layout component props.
114
218
  */
115
219
  interface GridLayoutProps {
116
220
  /** Number of columns */
117
221
  cols?: number;
222
+ /** Gap between items */
223
+ gap?: number;
224
+ /** Custom CSS class name */
225
+ className?: string;
226
+ /** Children content */
227
+ children: ReactNode;
228
+ }
229
+ /**
230
+ * Default layout component props.
231
+ */
232
+ interface DefaultLayoutProps {
233
+ /** Custom CSS class name */
234
+ className?: string;
118
235
  /** Children content */
119
236
  children: ReactNode;
120
237
  }
121
238
  /**
122
- * Layout component props union
239
+ * Union of all layout component props.
123
240
  */
124
- type LayoutComponentProps = SectionLayoutProps | GridLayoutProps;
241
+ type LayoutComponentProps = SectionLayoutProps | GridLayoutProps | DefaultLayoutProps;
125
242
  /**
126
- * Field component type
243
+ * Layout component type.
127
244
  */
128
- type FieldComponent<TFieldValues extends FieldValues = FieldValues> = ComponentType<FieldComponentProps<TFieldValues>>;
245
+ type LayoutComponent<TProps extends LayoutComponentProps = LayoutComponentProps> = ComponentType<TProps>;
129
246
  /**
130
- * Layout component type
247
+ * Complete form schema configuration.
248
+ * @template TFieldValues - Form field values type for type-safe schemas
131
249
  */
132
- type LayoutComponent = ComponentType<any>;
250
+ interface FormSchema<TFieldValues extends FieldValues = FieldValues> {
251
+ /** Form sections */
252
+ sections: Section<TFieldValues>[];
253
+ }
133
254
  /**
134
- * Component registry mapping field types to components
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
+ * ```
135
269
  */
136
270
  interface ComponentRegistry {
137
- [key: string]: FieldComponent | ComponentRegistry;
271
+ [key: string]: FieldComponent<FieldValues> | ComponentRegistry;
138
272
  }
139
273
  /**
140
- * Layout registry mapping layout types to components
274
+ * Layout registry mapping layout types to components.
275
+ * Supports variant-specific layouts via nested objects.
141
276
  */
142
277
  interface LayoutRegistry {
143
- [key: string]: LayoutComponent | LayoutRegistry;
278
+ [key: string]: LayoutComponent<SectionLayoutProps | GridLayoutProps | DefaultLayoutProps> | LayoutRegistry;
144
279
  }
145
280
  /**
146
- * Form system context value
281
+ * Form system context value.
147
282
  */
148
283
  interface FormSystemContextValue {
149
284
  /** Registered field components */
@@ -152,7 +287,7 @@ interface FormSystemContextValue {
152
287
  layouts: LayoutRegistry;
153
288
  }
154
289
  /**
155
- * Form system provider props
290
+ * Form system provider props.
156
291
  */
157
292
  interface FormSystemProviderProps {
158
293
  /** Field component registry */
@@ -163,34 +298,61 @@ interface FormSystemProviderProps {
163
298
  children: ReactNode;
164
299
  }
165
300
  /**
166
- * Form generator props
301
+ * FormGenerator component props.
302
+ * @template TFieldValues - Form field values type
167
303
  */
168
304
  interface FormGeneratorProps<TFieldValues extends FieldValues = FieldValues> {
169
- /** Form schema */
170
- schema: FormSchema;
171
- /** React Hook Form control */
305
+ /** Form schema defining sections and fields */
306
+ schema: FormSchema<TFieldValues>;
307
+ /** React Hook Form control object */
172
308
  control?: Control<TFieldValues>;
173
- /** Global disabled state */
309
+ /** Global disabled state for all fields */
174
310
  disabled?: boolean;
175
- /** Global variant */
311
+ /** Global variant for all components */
176
312
  variant?: Variant;
313
+ /** Additional CSS class for the root element */
314
+ className?: string;
177
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;
178
334
 
179
335
  /**
180
336
  * FormGenerator - Headless Form Generator Component
181
337
  *
182
- * Renders a form based on a schema, using components injected via FormSystemProvider.
183
- * 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.
184
341
  *
185
- * @template TFieldValues - Form field values type
342
+ * @template TFieldValues - Form field values type for type safety
186
343
  *
187
344
  * @example
188
345
  * ```tsx
189
346
  * import { useForm } from 'react-hook-form';
190
347
  * import { FormGenerator } from '@classytic/formkit';
191
348
  *
349
+ * interface FormData {
350
+ * firstName: string;
351
+ * email: string;
352
+ * }
353
+ *
192
354
  * function MyForm() {
193
- * const { control } = useForm();
355
+ * const { control } = useForm<FormData>();
194
356
  *
195
357
  * const schema = {
196
358
  * sections: [
@@ -208,13 +370,47 @@ interface FormGeneratorProps<TFieldValues extends FieldValues = FieldValues> {
208
370
  * }
209
371
  * ```
210
372
  */
211
- 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;
212
408
 
213
409
  /**
214
410
  * FormSystemProvider
215
411
  *
216
- * Provides the component registry to the form system.
217
- * 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.
218
414
  *
219
415
  * @example
220
416
  * ```tsx
@@ -223,6 +419,10 @@ declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({
223
419
  * const components = {
224
420
  * text: TextInput,
225
421
  * select: SelectInput,
422
+ * // Variant-specific components
423
+ * compact: {
424
+ * text: CompactTextInput,
425
+ * },
226
426
  * };
227
427
  *
228
428
  * const layouts = {
@@ -239,9 +439,9 @@ declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({
239
439
  * }
240
440
  * ```
241
441
  */
242
- declare function FormSystemProvider({ components, layouts, children, }: FormSystemProviderProps): JSX.Element;
442
+ declare function FormSystemProvider({ components, layouts, children, }: FormSystemProviderProps): FormElement;
243
443
  /**
244
- * Hook to access the form system context
444
+ * Hook to access the form system context.
245
445
  *
246
446
  * @throws {Error} If used outside FormSystemProvider
247
447
  * @returns Form system context value
@@ -252,14 +452,60 @@ declare function FormSystemProvider({ components, layouts, children, }: FormSyst
252
452
  * ```
253
453
  */
254
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>;
255
487
 
256
488
  /**
257
- * Utility function to merge Tailwind CSS classes
258
- * 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.
259
493
  *
260
- * @param inputs - Class values to merge
261
- * @returns Merged class string
494
+ * @param inputs - Class values to merge (strings, arrays, objects, or conditionals)
495
+ * @returns Merged and deduplicated class string
496
+ *
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
+ * ```
262
508
  */
263
509
  declare function cn(...inputs: ClassValue[]): string;
264
510
 
265
- 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 };