@classytic/formkit 1.0.2 → 1.2.0

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.cjs DELETED
@@ -1,169 +0,0 @@
1
- 'use strict';
2
-
3
- var reactHookForm = require('react-hook-form');
4
- var react = require('react');
5
- var jsxRuntime = require('react/jsx-runtime');
6
- var clsx = require('clsx');
7
- var tailwindMerge = require('tailwind-merge');
8
-
9
- // src/FormGenerator.tsx
10
- var FormSystemContext = react.createContext(null);
11
- function FormSystemProvider({
12
- components,
13
- layouts,
14
- children
15
- }) {
16
- const value = react.useMemo(
17
- () => ({
18
- components: components || {},
19
- layouts: layouts || {}
20
- }),
21
- [components, layouts]
22
- );
23
- return /* @__PURE__ */ jsxRuntime.jsx(FormSystemContext.Provider, { value, children });
24
- }
25
- function useFormSystem() {
26
- const context = react.useContext(FormSystemContext);
27
- if (!context) {
28
- throw new Error("useFormSystem must be used within a FormSystemProvider");
29
- }
30
- return context;
31
- }
32
- function useFieldComponent(type, variant) {
33
- const { components } = useFormSystem();
34
- if (variant && typeof components[variant] === "object" && components[variant]) {
35
- const variantComponents = components[variant];
36
- if (variantComponents[type]) {
37
- return variantComponents[type];
38
- }
39
- }
40
- const Component = components[type] || components["default"] || components["text"];
41
- if (!Component) {
42
- if (process.env.NODE_ENV === "development") {
43
- console.warn(`FormKit: No component found for type "${type}" (variant: ${variant})`);
44
- return () => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "red", padding: 4, border: "1px dashed red" }, children: [
45
- "Missing: ",
46
- type
47
- ] });
48
- }
49
- return () => null;
50
- }
51
- return Component;
52
- }
53
- function useLayoutComponent(type, variant) {
54
- const { layouts } = useFormSystem();
55
- if (variant && typeof layouts[variant] === "object" && layouts[variant]) {
56
- const variantLayouts = layouts[variant];
57
- if (variantLayouts[type]) {
58
- return variantLayouts[type];
59
- }
60
- }
61
- return layouts[type] || layouts["default"] || DefaultLayout;
62
- }
63
- var DefaultLayout = ({ children, className }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className, children });
64
- function cn(...inputs) {
65
- return tailwindMerge.twMerge(clsx.clsx(inputs));
66
- }
67
- function FormGenerator({
68
- schema,
69
- control,
70
- disabled = false,
71
- variant
72
- }) {
73
- const formContext = reactHookForm.useFormContext();
74
- const activeControl = control || formContext?.control;
75
- if (!schema?.sections) return null;
76
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("form-generator-root", variant && `form-variant-${variant}`), children: schema.sections.map((section, idx) => /* @__PURE__ */ jsxRuntime.jsx(
77
- SectionRenderer,
78
- {
79
- section,
80
- control: activeControl,
81
- disabled,
82
- variant
83
- },
84
- section.id || idx
85
- )) });
86
- }
87
- function SectionRenderer({
88
- section,
89
- control,
90
- disabled,
91
- variant
92
- }) {
93
- const activeVariant = section.variant || variant;
94
- const SectionLayout = useLayoutComponent("section", activeVariant);
95
- if (section.condition && !section.condition(control)) {
96
- return null;
97
- }
98
- return /* @__PURE__ */ jsxRuntime.jsx(
99
- SectionLayout,
100
- {
101
- title: section.title,
102
- description: section.description,
103
- icon: section.icon,
104
- variant: activeVariant,
105
- className: section.className,
106
- children: section.render ? section.render({ control, disabled, section }) : /* @__PURE__ */ jsxRuntime.jsx(
107
- GridRenderer,
108
- {
109
- fields: section.fields,
110
- cols: section.cols,
111
- control,
112
- disabled,
113
- variant: activeVariant
114
- }
115
- )
116
- }
117
- );
118
- }
119
- function GridRenderer({
120
- fields,
121
- cols = 1,
122
- control,
123
- disabled,
124
- variant
125
- }) {
126
- const GridLayout = useLayoutComponent("grid", variant);
127
- if (!fields || fields.length === 0) return null;
128
- return /* @__PURE__ */ jsxRuntime.jsx(GridLayout, { cols, children: fields.map((field, idx) => /* @__PURE__ */ jsxRuntime.jsx(
129
- FieldWrapper,
130
- {
131
- field,
132
- control,
133
- disabled,
134
- variant
135
- },
136
- field.name || idx
137
- )) });
138
- }
139
- function FieldWrapper({
140
- field,
141
- control,
142
- disabled,
143
- variant
144
- }) {
145
- const formValues = reactHookForm.useWatch({ control });
146
- if (field.condition && !field.condition(formValues)) {
147
- return null;
148
- }
149
- const activeVariant = field.variant || variant;
150
- const FieldComponent = useFieldComponent(field.type, activeVariant);
151
- if (!FieldComponent) return null;
152
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: field.fullWidth ? "col-span-full" : "", children: /* @__PURE__ */ jsxRuntime.jsx(
153
- FieldComponent,
154
- {
155
- field,
156
- control,
157
- disabled: disabled || field.disabled,
158
- variant: activeVariant,
159
- ...field
160
- }
161
- ) });
162
- }
163
-
164
- exports.FormGenerator = FormGenerator;
165
- exports.FormSystemProvider = FormSystemProvider;
166
- exports.cn = cn;
167
- exports.useFormSystem = useFormSystem;
168
- //# sourceMappingURL=index.cjs.map
169
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/FormSystemContext.tsx","../src/utils.ts","../src/FormGenerator.tsx"],"names":["createContext","useMemo","jsx","useContext","jsxs","twMerge","clsx","useFormContext","useWatch"],"mappings":"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","file":"index.cjs","sourcesContent":["\"use client\";\r\n\r\nimport { createContext, useContext, useMemo } from \"react\";\r\nimport type {\r\n FormSystemContextValue,\r\n FormSystemProviderProps,\r\n FieldComponent,\r\n LayoutComponent,\r\n FieldType,\r\n LayoutType,\r\n Variant,\r\n} from \"./types\";\r\n\r\nconst FormSystemContext = createContext<FormSystemContextValue | null>(null);\r\n\r\n/**\r\n * FormSystemProvider\r\n *\r\n * Provides the component registry to the form system.\r\n * This is the root provider that enables FormGenerator to work.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { FormSystemProvider } from '@classytic/formkit';\r\n *\r\n * const components = {\r\n * text: TextInput,\r\n * select: SelectInput,\r\n * };\r\n *\r\n * const layouts = {\r\n * section: SectionLayout,\r\n * grid: GridLayout,\r\n * };\r\n *\r\n * function App() {\r\n * return (\r\n * <FormSystemProvider components={components} layouts={layouts}>\r\n * <YourFormComponent />\r\n * </FormSystemProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function FormSystemProvider({\r\n components,\r\n layouts,\r\n children,\r\n}: FormSystemProviderProps): JSX.Element {\r\n const value = useMemo<FormSystemContextValue>(\r\n () => ({\r\n components: components || {},\r\n layouts: layouts || {},\r\n }),\r\n [components, layouts]\r\n );\r\n\r\n return <FormSystemContext.Provider value={value}>{children}</FormSystemContext.Provider>;\r\n}\r\n\r\n/**\r\n * Hook to access the form system context\r\n *\r\n * @throws {Error} If used outside FormSystemProvider\r\n * @returns Form system context value\r\n *\r\n * @example\r\n * ```tsx\r\n * const { components, layouts } = useFormSystem();\r\n * ```\r\n */\r\nexport function useFormSystem(): FormSystemContextValue {\r\n const context = useContext(FormSystemContext);\r\n if (!context) {\r\n throw new Error(\"useFormSystem must be used within a FormSystemProvider\");\r\n }\r\n return context;\r\n}\r\n\r\n/**\r\n * Helper to resolve a component for a specific field type and variant\r\n *\r\n * @param type - Field type identifier\r\n * @param variant - Optional variant name\r\n * @returns Field component or fallback\r\n *\r\n * @internal\r\n */\r\nexport function useFieldComponent(type: FieldType, variant?: Variant): FieldComponent {\r\n const { components } = useFormSystem();\r\n\r\n // 1. Try variant-specific component\r\n if (variant && typeof components[variant] === \"object\" && components[variant]) {\r\n const variantComponents = components[variant] as Record<string, FieldComponent>;\r\n if (variantComponents[type]) {\r\n return variantComponents[type];\r\n }\r\n }\r\n\r\n // 2. Fallback to standard component\r\n const Component =\r\n (components[type] as FieldComponent) ||\r\n (components[\"default\"] as FieldComponent) ||\r\n (components[\"text\"] as FieldComponent);\r\n\r\n if (!Component) {\r\n // Library-safe fallback: Don't crash, just warn and render nothing or a placeholder\r\n if (process.env.NODE_ENV === \"development\") {\r\n console.warn(`FormKit: No component found for type \"${type}\" (variant: ${variant})`);\r\n return () => (\r\n <div style={{ color: \"red\", padding: 4, border: \"1px dashed red\" }}>\r\n Missing: {type}\r\n </div>\r\n );\r\n }\r\n return () => null;\r\n }\r\n\r\n return Component;\r\n}\r\n\r\n/**\r\n * Helper to resolve a layout component\r\n *\r\n * @param type - Layout type identifier\r\n * @param variant - Optional variant name\r\n * @returns Layout component or fallback\r\n *\r\n * @internal\r\n */\r\nexport function useLayoutComponent(type: LayoutType, variant?: Variant): LayoutComponent {\r\n const { layouts } = useFormSystem();\r\n\r\n // 1. Try variant-specific layout\r\n if (variant && typeof layouts[variant] === \"object\" && layouts[variant]) {\r\n const variantLayouts = layouts[variant] as Record<string, LayoutComponent>;\r\n if (variantLayouts[type]) {\r\n return variantLayouts[type];\r\n }\r\n }\r\n\r\n // 2. Fallback to standard layout\r\n return (\r\n (layouts[type] as LayoutComponent) ||\r\n (layouts[\"default\"] as LayoutComponent) ||\r\n DefaultLayout\r\n );\r\n}\r\n\r\n/**\r\n * Default layout component - simple div wrapper\r\n */\r\nconst DefaultLayout: LayoutComponent = ({ children, className }: any) => (\r\n <div className={className}>{children}</div>\r\n);\r\n","import { type ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\n/**\r\n * Utility function to merge Tailwind CSS classes\r\n * Combines clsx and tailwind-merge for conflict-free class merging\r\n *\r\n * @param inputs - Class values to merge\r\n * @returns Merged class string\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n","\"use client\";\r\n\r\nimport { useFormContext, useWatch } from \"react-hook-form\";\r\nimport type { Control, FieldValues } from \"react-hook-form\";\r\nimport { useFieldComponent, useLayoutComponent } from \"./FormSystemContext\";\r\nimport { cn } from \"./utils\";\r\nimport type { FormGeneratorProps, Section, BaseField, Variant } from \"./types\";\r\n\r\n/**\r\n * FormGenerator - Headless Form Generator Component\r\n *\r\n * Renders a form based on a schema, using components injected via FormSystemProvider.\r\n * Supports conditional fields, dynamic layouts, and variants.\r\n *\r\n * @template TFieldValues - Form field values type\r\n *\r\n * @example\r\n * ```tsx\r\n * import { useForm } from 'react-hook-form';\r\n * import { FormGenerator } from '@classytic/formkit';\r\n *\r\n * function MyForm() {\r\n * const { control } = useForm();\r\n *\r\n * const schema = {\r\n * sections: [\r\n * {\r\n * title: \"User Details\",\r\n * fields: [\r\n * { name: \"firstName\", type: \"text\", label: \"First Name\" },\r\n * { name: \"email\", type: \"email\", label: \"Email\" }\r\n * ]\r\n * }\r\n * ]\r\n * };\r\n *\r\n * return <FormGenerator schema={schema} control={control} />;\r\n * }\r\n * ```\r\n */\r\nexport function FormGenerator<TFieldValues extends FieldValues = FieldValues>({\r\n schema,\r\n control,\r\n disabled = false,\r\n variant,\r\n}: FormGeneratorProps<TFieldValues>): JSX.Element | null {\r\n // Use provided control or fall back to context\r\n const formContext = useFormContext<TFieldValues>();\r\n const activeControl = control || formContext?.control;\r\n\r\n if (!schema?.sections) return null;\r\n\r\n return (\r\n <div className={cn(\"form-generator-root\", variant && `form-variant-${variant}`)}>\r\n {schema.sections.map((section, idx) => (\r\n <SectionRenderer\r\n key={section.id || idx}\r\n section={section}\r\n control={activeControl}\r\n disabled={disabled}\r\n variant={variant}\r\n />\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\n/**\r\n * Section Renderer Props\r\n */\r\ninterface SectionRendererProps<TFieldValues extends FieldValues = FieldValues> {\r\n section: Section;\r\n control?: Control<TFieldValues>;\r\n disabled?: boolean;\r\n variant?: Variant;\r\n}\r\n\r\n/**\r\n * Renders a single section with its fields\r\n */\r\nfunction SectionRenderer<TFieldValues extends FieldValues = FieldValues>({\r\n section,\r\n control,\r\n disabled,\r\n variant,\r\n}: SectionRendererProps<TFieldValues>): JSX.Element | null {\r\n // Allow section to override variant, or use global variant\r\n const activeVariant = section.variant || variant;\r\n const SectionLayout = useLayoutComponent(\"section\", activeVariant);\r\n\r\n // Check condition if present\r\n if (section.condition && !section.condition(control)) {\r\n return null;\r\n }\r\n\r\n return (\r\n <SectionLayout\r\n title={section.title}\r\n description={section.description}\r\n icon={section.icon}\r\n variant={activeVariant}\r\n className={section.className}\r\n >\r\n {section.render ? (\r\n section.render({ control, disabled, section })\r\n ) : (\r\n <GridRenderer\r\n fields={section.fields}\r\n cols={section.cols}\r\n control={control}\r\n disabled={disabled}\r\n variant={activeVariant}\r\n />\r\n )}\r\n </SectionLayout>\r\n );\r\n}\r\n\r\n/**\r\n * Grid Renderer Props\r\n */\r\ninterface GridRendererProps<TFieldValues extends FieldValues = FieldValues> {\r\n fields?: BaseField[];\r\n cols?: number;\r\n control?: Control<TFieldValues>;\r\n disabled?: boolean;\r\n variant?: Variant;\r\n}\r\n\r\n/**\r\n * Renders a grid of fields with specified column layout\r\n */\r\nfunction GridRenderer<TFieldValues extends FieldValues = FieldValues>({\r\n fields,\r\n cols = 1,\r\n control,\r\n disabled,\r\n variant,\r\n}: GridRendererProps<TFieldValues>): JSX.Element | null {\r\n const GridLayout = useLayoutComponent(\"grid\", variant);\r\n\r\n if (!fields || fields.length === 0) return null;\r\n\r\n return (\r\n <GridLayout cols={cols}>\r\n {fields.map((field, idx) => (\r\n <FieldWrapper\r\n key={field.name || idx}\r\n field={field}\r\n control={control}\r\n disabled={disabled}\r\n variant={variant}\r\n />\r\n ))}\r\n </GridLayout>\r\n );\r\n}\r\n\r\n/**\r\n * Field Wrapper Props\r\n */\r\ninterface FieldWrapperProps<TFieldValues extends FieldValues = FieldValues> {\r\n field: BaseField;\r\n control?: Control<TFieldValues>;\r\n disabled?: boolean;\r\n variant?: Variant;\r\n}\r\n\r\n/**\r\n * Renders a single field wrapper\r\n * Handles conditional rendering and field visibility\r\n */\r\nfunction FieldWrapper<TFieldValues extends FieldValues = FieldValues>({\r\n field,\r\n control,\r\n disabled,\r\n variant,\r\n}: FieldWrapperProps<TFieldValues>): JSX.Element | null {\r\n // Watch values for conditional rendering\r\n const formValues = useWatch({ control });\r\n\r\n if (field.condition && !field.condition(formValues)) {\r\n return null;\r\n }\r\n\r\n // Allow field to override variant\r\n const activeVariant = field.variant || variant;\r\n const FieldComponent = useFieldComponent(field.type, activeVariant);\r\n\r\n if (!FieldComponent) return null;\r\n\r\n return (\r\n <div className={field.fullWidth ? \"col-span-full\" : \"\"}>\r\n <FieldComponent\r\n field={field}\r\n control={control as any}\r\n disabled={disabled || field.disabled}\r\n variant={activeVariant}\r\n // Pass all field props to the component\r\n {...field}\r\n />\r\n </div>\r\n );\r\n}\r\n"]}
package/dist/index.d.cts DELETED
@@ -1,288 +0,0 @@
1
- import { FieldValues, Control } from 'react-hook-form';
2
- import { ReactNode, ComponentType } from 'react';
3
- import { ClassValue } from 'clsx';
4
-
5
- /**
6
- * Field type identifier
7
- */
8
- type FieldType = string;
9
- /**
10
- * Layout type identifier
11
- */
12
- type LayoutType = "section" | "grid" | "default" | string;
13
- /**
14
- * Variant identifier for component styling
15
- */
16
- type Variant = string | undefined;
17
- /**
18
- * Base field configuration
19
- */
20
- interface BaseField {
21
- /** Field name (maps to form field) */
22
- name: string;
23
- /** Field type (text, select, checkbox, etc.) */
24
- type: FieldType;
25
- /** Field label */
26
- label?: string;
27
- /** Placeholder text */
28
- placeholder?: string;
29
- /** Whether field is disabled */
30
- disabled?: boolean;
31
- /** Whether field is required */
32
- required?: boolean;
33
- /** Field variant */
34
- variant?: Variant;
35
- /** Whether field should span full width */
36
- fullWidth?: boolean;
37
- /** Custom CSS class name */
38
- className?: string;
39
- /** Conditional rendering function */
40
- condition?: (formValues: FieldValues) => boolean;
41
- /** Default value */
42
- defaultValue?: any;
43
- /** Additional field-specific props */
44
- [key: string]: any;
45
- }
46
- /**
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
- * ```
71
- */
72
- interface FieldComponentProps<TFieldValues extends FieldValues = FieldValues> extends BaseField {
73
- /** Field configuration object (contains all field props) */
74
- field: BaseField;
75
- /** React Hook Form control (for Controller integration) */
76
- control?: Control<TFieldValues> | Control<any>;
77
- /** Whether field is globally disabled */
78
- disabled?: boolean;
79
- /** Component variant (e.g., "compact", "default") */
80
- variant?: Variant;
81
- }
82
- /**
83
- * Section configuration
84
- */
85
- interface Section {
86
- /** Section ID (optional) */
87
- id?: string;
88
- /** Section title */
89
- title?: string;
90
- /** Section description */
91
- description?: string;
92
- /** Section icon */
93
- 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;
104
- /** Section variant */
105
- variant?: Variant;
106
- /** Custom CSS class name */
107
- className?: string;
108
- /** Conditional rendering function */
109
- condition?: (control?: Control<any>) => boolean;
110
- }
111
- /**
112
- * Form schema configuration
113
- */
114
- interface FormSchema {
115
- /** Form sections */
116
- sections: Section[];
117
- }
118
- /**
119
- * Section layout component props
120
- */
121
- interface SectionLayoutProps {
122
- /** Section title */
123
- title?: string;
124
- /** Section description */
125
- description?: string;
126
- /** Section icon */
127
- icon?: ReactNode;
128
- /** Layout variant */
129
- variant?: Variant;
130
- /** Custom CSS class name */
131
- className?: string;
132
- /** Children content */
133
- children: ReactNode;
134
- }
135
- /**
136
- * Grid layout component props
137
- */
138
- interface GridLayoutProps {
139
- /** Number of columns */
140
- cols?: number;
141
- /** Children content */
142
- children: ReactNode;
143
- }
144
- /**
145
- * Layout component props union
146
- */
147
- type LayoutComponentProps = SectionLayoutProps | GridLayoutProps;
148
- /**
149
- * Field component type
150
- */
151
- type FieldComponent<TFieldValues extends FieldValues = FieldValues> = ComponentType<FieldComponentProps<TFieldValues>>;
152
- /**
153
- * Layout component type
154
- */
155
- type LayoutComponent = ComponentType<any>;
156
- /**
157
- * Component registry mapping field types to components
158
- */
159
- interface ComponentRegistry {
160
- [key: string]: FieldComponent | ComponentRegistry;
161
- }
162
- /**
163
- * Layout registry mapping layout types to components
164
- */
165
- interface LayoutRegistry {
166
- [key: string]: LayoutComponent | LayoutRegistry;
167
- }
168
- /**
169
- * Form system context value
170
- */
171
- interface FormSystemContextValue {
172
- /** Registered field components */
173
- components: ComponentRegistry;
174
- /** Registered layout components */
175
- layouts: LayoutRegistry;
176
- }
177
- /**
178
- * Form system provider props
179
- */
180
- interface FormSystemProviderProps {
181
- /** Field component registry */
182
- components?: ComponentRegistry;
183
- /** Layout component registry */
184
- layouts?: LayoutRegistry;
185
- /** Children content */
186
- children: ReactNode;
187
- }
188
- /**
189
- * Form generator props
190
- */
191
- interface FormGeneratorProps<TFieldValues extends FieldValues = FieldValues> {
192
- /** Form schema */
193
- schema: FormSchema;
194
- /** React Hook Form control */
195
- control?: Control<TFieldValues>;
196
- /** Global disabled state */
197
- disabled?: boolean;
198
- /** Global variant */
199
- variant?: Variant;
200
- }
201
-
202
- /**
203
- * FormGenerator - Headless Form Generator Component
204
- *
205
- * Renders a form based on a schema, using components injected via FormSystemProvider.
206
- * Supports conditional fields, dynamic layouts, and variants.
207
- *
208
- * @template TFieldValues - Form field values type
209
- *
210
- * @example
211
- * ```tsx
212
- * import { useForm } from 'react-hook-form';
213
- * import { FormGenerator } from '@classytic/formkit';
214
- *
215
- * function MyForm() {
216
- * const { control } = useForm();
217
- *
218
- * const schema = {
219
- * sections: [
220
- * {
221
- * title: "User Details",
222
- * fields: [
223
- * { name: "firstName", type: "text", label: "First Name" },
224
- * { name: "email", type: "email", label: "Email" }
225
- * ]
226
- * }
227
- * ]
228
- * };
229
- *
230
- * return <FormGenerator schema={schema} control={control} />;
231
- * }
232
- * ```
233
- */
234
- declare function FormGenerator<TFieldValues extends FieldValues = FieldValues>({ schema, control, disabled, variant, }: FormGeneratorProps<TFieldValues>): JSX.Element | null;
235
-
236
- /**
237
- * FormSystemProvider
238
- *
239
- * Provides the component registry to the form system.
240
- * This is the root provider that enables FormGenerator to work.
241
- *
242
- * @example
243
- * ```tsx
244
- * import { FormSystemProvider } from '@classytic/formkit';
245
- *
246
- * const components = {
247
- * text: TextInput,
248
- * select: SelectInput,
249
- * };
250
- *
251
- * const layouts = {
252
- * section: SectionLayout,
253
- * grid: GridLayout,
254
- * };
255
- *
256
- * function App() {
257
- * return (
258
- * <FormSystemProvider components={components} layouts={layouts}>
259
- * <YourFormComponent />
260
- * </FormSystemProvider>
261
- * );
262
- * }
263
- * ```
264
- */
265
- declare function FormSystemProvider({ components, layouts, children, }: FormSystemProviderProps): JSX.Element;
266
- /**
267
- * Hook to access the form system context
268
- *
269
- * @throws {Error} If used outside FormSystemProvider
270
- * @returns Form system context value
271
- *
272
- * @example
273
- * ```tsx
274
- * const { components, layouts } = useFormSystem();
275
- * ```
276
- */
277
- declare function useFormSystem(): FormSystemContextValue;
278
-
279
- /**
280
- * Utility function to merge Tailwind CSS classes
281
- * Combines clsx and tailwind-merge for conflict-free class merging
282
- *
283
- * @param inputs - Class values to merge
284
- * @returns Merged class string
285
- */
286
- declare function cn(...inputs: ClassValue[]): string;
287
-
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 };