@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/CHANGELOG.md +78 -0
- package/README.md +660 -76
- package/dist/index.d.mts +914 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1040 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.d.mts +625 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +418 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +56 -30
- package/dist/index.cjs +0 -169
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -288
- package/dist/index.d.ts +0 -288
- package/dist/index.js +0 -164
- package/dist/index.js.map +0 -1
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
|
package/dist/index.cjs.map
DELETED
|
@@ -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 };
|