@startsimpli/forms 0.1.3 → 0.1.5

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/package.json CHANGED
@@ -1,18 +1,13 @@
1
1
  {
2
2
  "name": "@startsimpli/forms",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Thin form state management with Zod validation for StartSimpli apps",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
7
  "exports": {
8
- ".": {
9
- "types": "./dist/index.d.ts",
10
- "import": "./dist/index.mjs",
11
- "require": "./dist/index.js"
12
- }
8
+ ".": "./src/index.ts"
13
9
  },
14
10
  "files": [
15
- "dist",
16
11
  "src",
17
12
  "README.md"
18
13
  ],
@@ -38,6 +33,5 @@
38
33
  "typescript": "^5.0.0",
39
34
  "vitest": "^2.0.0",
40
35
  "zod": "^3.22.0"
41
- },
42
- "module": "./dist/index.mjs"
36
+ }
43
37
  }
package/dist/index.d.mts DELETED
@@ -1,52 +0,0 @@
1
- import { ZodSchema } from 'zod';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import React$1 from 'react';
4
-
5
- interface UseFormOptions<T> {
6
- initial?: Partial<T>;
7
- }
8
- interface UseFormReturn<T extends Record<string, unknown>> {
9
- /** Current form values. Partial because fields may not be set yet.
10
- * For the fully validated T, use the data passed to handleSubmit's onValid callback. */
11
- values: Partial<T>;
12
- errors: Partial<Record<keyof T, string>>;
13
- isSubmitting: boolean;
14
- isDirty: boolean;
15
- setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void;
16
- setError: (field: keyof T, message: string) => void;
17
- clearError: (field: keyof T) => void;
18
- /** Returns a submit handler. The onValid callback receives the fully validated T from schema.safeParse. */
19
- handleSubmit: (onValid: (data: T) => Promise<void>) => (e?: React.FormEvent) => Promise<void>;
20
- reset: (values?: Partial<T>) => void;
21
- }
22
- declare function useForm<T extends Record<string, unknown>>(schema: ZodSchema<T>, options?: UseFormOptions<T>): UseFormReturn<T>;
23
-
24
- interface FormFieldProps {
25
- label: string;
26
- error?: string;
27
- required?: boolean;
28
- children: React$1.ReactNode;
29
- className?: string;
30
- }
31
- declare function FormField({ label, error, required, children, className, }: FormFieldProps): react_jsx_runtime.JSX.Element;
32
-
33
- interface FormSectionProps {
34
- title?: string;
35
- children: React$1.ReactNode;
36
- className?: string;
37
- }
38
- declare function FormSection({ title, children, className, }: FormSectionProps): react_jsx_runtime.JSX.Element;
39
-
40
- interface FormErrorProps {
41
- message: string;
42
- className?: string;
43
- }
44
- declare function FormError({ message, className }: FormErrorProps): react_jsx_runtime.JSX.Element;
45
-
46
- interface FormRowProps {
47
- children: React$1.ReactNode;
48
- className?: string;
49
- }
50
- declare function FormRow({ children, className }: FormRowProps): react_jsx_runtime.JSX.Element;
51
-
52
- export { FormError, type FormErrorProps, FormField, type FormFieldProps, FormRow, type FormRowProps, FormSection, type FormSectionProps, type UseFormOptions, type UseFormReturn, useForm };
package/dist/index.d.ts DELETED
@@ -1,52 +0,0 @@
1
- import { ZodSchema } from 'zod';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import React$1 from 'react';
4
-
5
- interface UseFormOptions<T> {
6
- initial?: Partial<T>;
7
- }
8
- interface UseFormReturn<T extends Record<string, unknown>> {
9
- /** Current form values. Partial because fields may not be set yet.
10
- * For the fully validated T, use the data passed to handleSubmit's onValid callback. */
11
- values: Partial<T>;
12
- errors: Partial<Record<keyof T, string>>;
13
- isSubmitting: boolean;
14
- isDirty: boolean;
15
- setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void;
16
- setError: (field: keyof T, message: string) => void;
17
- clearError: (field: keyof T) => void;
18
- /** Returns a submit handler. The onValid callback receives the fully validated T from schema.safeParse. */
19
- handleSubmit: (onValid: (data: T) => Promise<void>) => (e?: React.FormEvent) => Promise<void>;
20
- reset: (values?: Partial<T>) => void;
21
- }
22
- declare function useForm<T extends Record<string, unknown>>(schema: ZodSchema<T>, options?: UseFormOptions<T>): UseFormReturn<T>;
23
-
24
- interface FormFieldProps {
25
- label: string;
26
- error?: string;
27
- required?: boolean;
28
- children: React$1.ReactNode;
29
- className?: string;
30
- }
31
- declare function FormField({ label, error, required, children, className, }: FormFieldProps): react_jsx_runtime.JSX.Element;
32
-
33
- interface FormSectionProps {
34
- title?: string;
35
- children: React$1.ReactNode;
36
- className?: string;
37
- }
38
- declare function FormSection({ title, children, className, }: FormSectionProps): react_jsx_runtime.JSX.Element;
39
-
40
- interface FormErrorProps {
41
- message: string;
42
- className?: string;
43
- }
44
- declare function FormError({ message, className }: FormErrorProps): react_jsx_runtime.JSX.Element;
45
-
46
- interface FormRowProps {
47
- children: React$1.ReactNode;
48
- className?: string;
49
- }
50
- declare function FormRow({ children, className }: FormRowProps): react_jsx_runtime.JSX.Element;
51
-
52
- export { FormError, type FormErrorProps, FormField, type FormFieldProps, FormRow, type FormRowProps, FormSection, type FormSectionProps, type UseFormOptions, type UseFormReturn, useForm };
package/dist/index.js DELETED
@@ -1,129 +0,0 @@
1
- 'use strict';
2
-
3
- var react = require('react');
4
- var jsxRuntime = require('react/jsx-runtime');
5
-
6
- function mapZodErrors(error) {
7
- const mapped = {};
8
- for (const issue of error.issues) {
9
- const key = issue.path[0];
10
- if (key !== void 0 && !(key in mapped)) {
11
- mapped[key] = issue.message;
12
- }
13
- }
14
- return mapped;
15
- }
16
- function useForm(schema, options) {
17
- const initialValues = options?.initial ?? {};
18
- const initialRef = react.useRef(initialValues);
19
- const [values, setValues] = react.useState(initialValues);
20
- const [errors, setErrors] = react.useState({});
21
- const [isSubmitting, setIsSubmitting] = react.useState(false);
22
- const [isDirty, setIsDirty] = react.useState(false);
23
- const setFieldValue = react.useCallback((field, value) => {
24
- setValues((prev) => ({ ...prev, [field]: value }));
25
- setIsDirty(true);
26
- setErrors((prev) => {
27
- if (!(field in prev)) return prev;
28
- const next = { ...prev };
29
- delete next[field];
30
- return next;
31
- });
32
- }, []);
33
- const setError = react.useCallback((field, message) => {
34
- setErrors((prev) => ({ ...prev, [field]: message }));
35
- }, []);
36
- const clearError = react.useCallback((field) => {
37
- setErrors((prev) => {
38
- if (!(field in prev)) return prev;
39
- const next = { ...prev };
40
- delete next[field];
41
- return next;
42
- });
43
- }, []);
44
- const handleSubmit = react.useCallback(
45
- (onValid) => {
46
- return async (e) => {
47
- if (e) e.preventDefault();
48
- const result = schema.safeParse(values);
49
- if (!result.success) {
50
- setErrors(mapZodErrors(result.error));
51
- return;
52
- }
53
- setIsSubmitting(true);
54
- try {
55
- await onValid(result.data);
56
- } finally {
57
- setIsSubmitting(false);
58
- }
59
- };
60
- },
61
- [schema, values]
62
- );
63
- const reset = react.useCallback(
64
- (newValues) => {
65
- setValues(newValues ?? initialRef.current);
66
- setErrors({});
67
- setIsDirty(false);
68
- },
69
- []
70
- );
71
- return {
72
- values,
73
- errors,
74
- isSubmitting,
75
- isDirty,
76
- setFieldValue,
77
- setError,
78
- clearError,
79
- handleSubmit,
80
- reset
81
- };
82
- }
83
- function FormField({
84
- label,
85
- error,
86
- required = false,
87
- children,
88
- className = ""
89
- }) {
90
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `space-y-2 ${className}`.trim(), children: [
91
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "text-sm font-medium leading-none", children: [
92
- label,
93
- required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
94
- ] }),
95
- children,
96
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600", children: error })
97
- ] });
98
- }
99
- function FormSection({
100
- title,
101
- children,
102
- className = ""
103
- }) {
104
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `space-y-4 ${className}`.trim(), children: [
105
- title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium leading-none", children: title }),
106
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children })
107
- ] });
108
- }
109
- function FormError({ message, className = "" }) {
110
- return /* @__PURE__ */ jsxRuntime.jsx(
111
- "div",
112
- {
113
- role: "alert",
114
- className: `text-sm text-red-600 bg-red-50 p-3 rounded border border-red-200 ${className}`.trim(),
115
- children: message
116
- }
117
- );
118
- }
119
- function FormRow({ children, className = "" }) {
120
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `grid gap-4 sm:grid-cols-2 ${className}`.trim(), children });
121
- }
122
-
123
- exports.FormError = FormError;
124
- exports.FormField = FormField;
125
- exports.FormRow = FormRow;
126
- exports.FormSection = FormSection;
127
- exports.useForm = useForm;
128
- //# sourceMappingURL=index.js.map
129
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useForm.ts","../src/components/FormField.tsx","../src/components/FormSection.tsx","../src/components/FormError.tsx","../src/components/FormRow.tsx"],"names":["useRef","useState","useCallback","jsxs","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import { useState, useCallback, useRef } from 'react'\nimport type { ZodSchema, ZodError } from 'zod'\n\nexport interface UseFormOptions<T> {\n initial?: Partial<T>\n}\n\nexport interface UseFormReturn<T extends Record<string, unknown>> {\n /** Current form values. Partial because fields may not be set yet.\n * For the fully validated T, use the data passed to handleSubmit's onValid callback. */\n values: Partial<T>\n errors: Partial<Record<keyof T, string>>\n isSubmitting: boolean\n isDirty: boolean\n setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void\n setError: (field: keyof T, message: string) => void\n clearError: (field: keyof T) => void\n /** Returns a submit handler. The onValid callback receives the fully validated T from schema.safeParse. */\n handleSubmit: (onValid: (data: T) => Promise<void>) => (e?: React.FormEvent) => Promise<void>\n reset: (values?: Partial<T>) => void\n}\n\nfunction mapZodErrors<T extends Record<string, unknown>>(\n error: ZodError\n): Partial<Record<keyof T, string>> {\n const mapped: Partial<Record<keyof T, string>> = {}\n for (const issue of error.issues) {\n const key = issue.path[0]\n if (key !== undefined && !(key in mapped)) {\n mapped[key as keyof T] = issue.message\n }\n }\n return mapped\n}\n\nexport function useForm<T extends Record<string, unknown>>(\n schema: ZodSchema<T>,\n options?: UseFormOptions<T>\n): UseFormReturn<T> {\n const initialValues = options?.initial ?? {}\n const initialRef = useRef(initialValues)\n\n const [values, setValues] = useState<Partial<T>>(initialValues)\n const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({})\n const [isSubmitting, setIsSubmitting] = useState(false)\n const [isDirty, setIsDirty] = useState(false)\n\n const setFieldValue = useCallback(<K extends keyof T>(field: K, value: T[K]) => {\n setValues(prev => ({ ...prev, [field]: value }))\n setIsDirty(true)\n // Clear error for this field when user changes it\n setErrors(prev => {\n if (!(field in prev)) return prev\n const next = { ...prev }\n delete next[field]\n return next\n })\n }, [])\n\n const setError = useCallback((field: keyof T, message: string) => {\n setErrors(prev => ({ ...prev, [field]: message }))\n }, [])\n\n const clearError = useCallback((field: keyof T) => {\n setErrors(prev => {\n if (!(field in prev)) return prev\n const next = { ...prev }\n delete next[field]\n return next\n })\n }, [])\n\n const handleSubmit = useCallback(\n (onValid: (data: T) => Promise<void>) => {\n return async (e?: React.FormEvent) => {\n if (e) e.preventDefault()\n\n const result = schema.safeParse(values)\n\n if (!result.success) {\n setErrors(mapZodErrors<T>(result.error))\n return\n }\n\n setIsSubmitting(true)\n try {\n await onValid(result.data)\n } finally {\n setIsSubmitting(false)\n }\n }\n },\n [schema, values]\n )\n\n const reset = useCallback(\n (newValues?: Partial<T>) => {\n setValues(newValues ?? initialRef.current)\n setErrors({})\n setIsDirty(false)\n },\n []\n )\n\n return {\n values,\n errors,\n isSubmitting,\n isDirty,\n setFieldValue,\n setError,\n clearError,\n handleSubmit,\n reset,\n }\n}\n","import React from 'react'\n\nexport interface FormFieldProps {\n label: string\n error?: string\n required?: boolean\n children: React.ReactNode\n className?: string\n}\n\nexport function FormField({\n label,\n error,\n required = false,\n children,\n className = '',\n}: FormFieldProps) {\n return (\n <div className={`space-y-2 ${className}`.trim()}>\n <label className=\"text-sm font-medium leading-none\">\n {label}\n {required && <span className=\"text-red-500 ml-0.5\">*</span>}\n </label>\n {children}\n {error && <p className=\"text-sm text-red-600\">{error}</p>}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface FormSectionProps {\n title?: string\n children: React.ReactNode\n className?: string\n}\n\nexport function FormSection({\n title,\n children,\n className = '',\n}: FormSectionProps) {\n return (\n <div className={`space-y-4 ${className}`.trim()}>\n {title && (\n <h3 className=\"text-sm font-medium leading-none\">{title}</h3>\n )}\n <div className=\"space-y-4\">{children}</div>\n </div>\n )\n}\n","import React from 'react'\n\nexport interface FormErrorProps {\n message: string\n className?: string\n}\n\nexport function FormError({ message, className = '' }: FormErrorProps) {\n return (\n <div\n role=\"alert\"\n className={`text-sm text-red-600 bg-red-50 p-3 rounded border border-red-200 ${className}`.trim()}\n >\n {message}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface FormRowProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FormRow({ children, className = '' }: FormRowProps) {\n return (\n <div className={`grid gap-4 sm:grid-cols-2 ${className}`.trim()}>\n {children}\n </div>\n )\n}\n"]}
package/dist/index.mjs DELETED
@@ -1,123 +0,0 @@
1
- import { useRef, useState, useCallback } from 'react';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
3
-
4
- function mapZodErrors(error) {
5
- const mapped = {};
6
- for (const issue of error.issues) {
7
- const key = issue.path[0];
8
- if (key !== void 0 && !(key in mapped)) {
9
- mapped[key] = issue.message;
10
- }
11
- }
12
- return mapped;
13
- }
14
- function useForm(schema, options) {
15
- const initialValues = options?.initial ?? {};
16
- const initialRef = useRef(initialValues);
17
- const [values, setValues] = useState(initialValues);
18
- const [errors, setErrors] = useState({});
19
- const [isSubmitting, setIsSubmitting] = useState(false);
20
- const [isDirty, setIsDirty] = useState(false);
21
- const setFieldValue = useCallback((field, value) => {
22
- setValues((prev) => ({ ...prev, [field]: value }));
23
- setIsDirty(true);
24
- setErrors((prev) => {
25
- if (!(field in prev)) return prev;
26
- const next = { ...prev };
27
- delete next[field];
28
- return next;
29
- });
30
- }, []);
31
- const setError = useCallback((field, message) => {
32
- setErrors((prev) => ({ ...prev, [field]: message }));
33
- }, []);
34
- const clearError = useCallback((field) => {
35
- setErrors((prev) => {
36
- if (!(field in prev)) return prev;
37
- const next = { ...prev };
38
- delete next[field];
39
- return next;
40
- });
41
- }, []);
42
- const handleSubmit = useCallback(
43
- (onValid) => {
44
- return async (e) => {
45
- if (e) e.preventDefault();
46
- const result = schema.safeParse(values);
47
- if (!result.success) {
48
- setErrors(mapZodErrors(result.error));
49
- return;
50
- }
51
- setIsSubmitting(true);
52
- try {
53
- await onValid(result.data);
54
- } finally {
55
- setIsSubmitting(false);
56
- }
57
- };
58
- },
59
- [schema, values]
60
- );
61
- const reset = useCallback(
62
- (newValues) => {
63
- setValues(newValues ?? initialRef.current);
64
- setErrors({});
65
- setIsDirty(false);
66
- },
67
- []
68
- );
69
- return {
70
- values,
71
- errors,
72
- isSubmitting,
73
- isDirty,
74
- setFieldValue,
75
- setError,
76
- clearError,
77
- handleSubmit,
78
- reset
79
- };
80
- }
81
- function FormField({
82
- label,
83
- error,
84
- required = false,
85
- children,
86
- className = ""
87
- }) {
88
- return /* @__PURE__ */ jsxs("div", { className: `space-y-2 ${className}`.trim(), children: [
89
- /* @__PURE__ */ jsxs("label", { className: "text-sm font-medium leading-none", children: [
90
- label,
91
- required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
92
- ] }),
93
- children,
94
- error && /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600", children: error })
95
- ] });
96
- }
97
- function FormSection({
98
- title,
99
- children,
100
- className = ""
101
- }) {
102
- return /* @__PURE__ */ jsxs("div", { className: `space-y-4 ${className}`.trim(), children: [
103
- title && /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium leading-none", children: title }),
104
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children })
105
- ] });
106
- }
107
- function FormError({ message, className = "" }) {
108
- return /* @__PURE__ */ jsx(
109
- "div",
110
- {
111
- role: "alert",
112
- className: `text-sm text-red-600 bg-red-50 p-3 rounded border border-red-200 ${className}`.trim(),
113
- children: message
114
- }
115
- );
116
- }
117
- function FormRow({ children, className = "" }) {
118
- return /* @__PURE__ */ jsx("div", { className: `grid gap-4 sm:grid-cols-2 ${className}`.trim(), children });
119
- }
120
-
121
- export { FormError, FormField, FormRow, FormSection, useForm };
122
- //# sourceMappingURL=index.mjs.map
123
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useForm.ts","../src/components/FormField.tsx","../src/components/FormSection.tsx","../src/components/FormError.tsx","../src/components/FormRow.tsx"],"names":["jsxs","jsx"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useCallback, useRef } from 'react'\nimport type { ZodSchema, ZodError } from 'zod'\n\nexport interface UseFormOptions<T> {\n initial?: Partial<T>\n}\n\nexport interface UseFormReturn<T extends Record<string, unknown>> {\n /** Current form values. Partial because fields may not be set yet.\n * For the fully validated T, use the data passed to handleSubmit's onValid callback. */\n values: Partial<T>\n errors: Partial<Record<keyof T, string>>\n isSubmitting: boolean\n isDirty: boolean\n setFieldValue: <K extends keyof T>(field: K, value: T[K]) => void\n setError: (field: keyof T, message: string) => void\n clearError: (field: keyof T) => void\n /** Returns a submit handler. The onValid callback receives the fully validated T from schema.safeParse. */\n handleSubmit: (onValid: (data: T) => Promise<void>) => (e?: React.FormEvent) => Promise<void>\n reset: (values?: Partial<T>) => void\n}\n\nfunction mapZodErrors<T extends Record<string, unknown>>(\n error: ZodError\n): Partial<Record<keyof T, string>> {\n const mapped: Partial<Record<keyof T, string>> = {}\n for (const issue of error.issues) {\n const key = issue.path[0]\n if (key !== undefined && !(key in mapped)) {\n mapped[key as keyof T] = issue.message\n }\n }\n return mapped\n}\n\nexport function useForm<T extends Record<string, unknown>>(\n schema: ZodSchema<T>,\n options?: UseFormOptions<T>\n): UseFormReturn<T> {\n const initialValues = options?.initial ?? {}\n const initialRef = useRef(initialValues)\n\n const [values, setValues] = useState<Partial<T>>(initialValues)\n const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({})\n const [isSubmitting, setIsSubmitting] = useState(false)\n const [isDirty, setIsDirty] = useState(false)\n\n const setFieldValue = useCallback(<K extends keyof T>(field: K, value: T[K]) => {\n setValues(prev => ({ ...prev, [field]: value }))\n setIsDirty(true)\n // Clear error for this field when user changes it\n setErrors(prev => {\n if (!(field in prev)) return prev\n const next = { ...prev }\n delete next[field]\n return next\n })\n }, [])\n\n const setError = useCallback((field: keyof T, message: string) => {\n setErrors(prev => ({ ...prev, [field]: message }))\n }, [])\n\n const clearError = useCallback((field: keyof T) => {\n setErrors(prev => {\n if (!(field in prev)) return prev\n const next = { ...prev }\n delete next[field]\n return next\n })\n }, [])\n\n const handleSubmit = useCallback(\n (onValid: (data: T) => Promise<void>) => {\n return async (e?: React.FormEvent) => {\n if (e) e.preventDefault()\n\n const result = schema.safeParse(values)\n\n if (!result.success) {\n setErrors(mapZodErrors<T>(result.error))\n return\n }\n\n setIsSubmitting(true)\n try {\n await onValid(result.data)\n } finally {\n setIsSubmitting(false)\n }\n }\n },\n [schema, values]\n )\n\n const reset = useCallback(\n (newValues?: Partial<T>) => {\n setValues(newValues ?? initialRef.current)\n setErrors({})\n setIsDirty(false)\n },\n []\n )\n\n return {\n values,\n errors,\n isSubmitting,\n isDirty,\n setFieldValue,\n setError,\n clearError,\n handleSubmit,\n reset,\n }\n}\n","import React from 'react'\n\nexport interface FormFieldProps {\n label: string\n error?: string\n required?: boolean\n children: React.ReactNode\n className?: string\n}\n\nexport function FormField({\n label,\n error,\n required = false,\n children,\n className = '',\n}: FormFieldProps) {\n return (\n <div className={`space-y-2 ${className}`.trim()}>\n <label className=\"text-sm font-medium leading-none\">\n {label}\n {required && <span className=\"text-red-500 ml-0.5\">*</span>}\n </label>\n {children}\n {error && <p className=\"text-sm text-red-600\">{error}</p>}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface FormSectionProps {\n title?: string\n children: React.ReactNode\n className?: string\n}\n\nexport function FormSection({\n title,\n children,\n className = '',\n}: FormSectionProps) {\n return (\n <div className={`space-y-4 ${className}`.trim()}>\n {title && (\n <h3 className=\"text-sm font-medium leading-none\">{title}</h3>\n )}\n <div className=\"space-y-4\">{children}</div>\n </div>\n )\n}\n","import React from 'react'\n\nexport interface FormErrorProps {\n message: string\n className?: string\n}\n\nexport function FormError({ message, className = '' }: FormErrorProps) {\n return (\n <div\n role=\"alert\"\n className={`text-sm text-red-600 bg-red-50 p-3 rounded border border-red-200 ${className}`.trim()}\n >\n {message}\n </div>\n )\n}\n","import React from 'react'\n\nexport interface FormRowProps {\n children: React.ReactNode\n className?: string\n}\n\nexport function FormRow({ children, className = '' }: FormRowProps) {\n return (\n <div className={`grid gap-4 sm:grid-cols-2 ${className}`.trim()}>\n {children}\n </div>\n )\n}\n"]}