UNPKG

formik-yup-smartform

Version:

formik-yup-smartform is a lightweight React component that automatically generates forms using Formik and Yup. Just pass a validation schema, field configuration, and submit handler — the form is built for you with validation, error messages, and minimal

1 lines 12.7 kB
{"version":3,"sources":["../src/index.ts","../src/components/SmartForm.tsx"],"sourcesContent":["import \"./index.css\";\r\nimport SmartForm from \"./components/SmartForm\";\r\n\r\nexport { SmartForm };\r\n","import { useFormik } from \"formik\";\r\nimport * as Yup from \"yup\";\r\n\r\ntype FieldType =\r\n | \"text\"\r\n | \"number\"\r\n | \"email\"\r\n | \"password\"\r\n | \"date\"\r\n | \"checkbox\"\r\n | \"radio\"\r\n | \"file\"\r\n | \"color\"\r\n | \"range\"\r\n | \"tel\"\r\n | \"time\";\r\n\r\ntype Field = {\r\n name: string;\r\n type: FieldType;\r\n label?: string;\r\n placeholder?: string;\r\n options?: string[];\r\n required?: boolean;\r\n};\r\n\r\ntype Props = {\r\n formValues: Field[];\r\n width?: string;\r\n formSubmit: (values: Record<string, unknown>) => void;\r\n validationSchema?: Yup.ObjectSchema<any>;\r\n isLoading?: boolean;\r\n buttonBgColor?: string;\r\n buttonTextColor?: string;\r\n labelTextColor?: string;\r\n};\r\n\r\nconst SmartForm = ({\r\n formValues,\r\n width = \"max-w-xl\",\r\n formSubmit,\r\n validationSchema,\r\n isLoading = false,\r\n buttonBgColor = \"bg-blue-600 hover:bg-blue-700\",\r\n buttonTextColor = \"text-white\",\r\n labelTextColor = \"text-gray-800 dark:text-gray-100\",\r\n}: Props) => {\r\n const initialValues = formValues.reduce((acc, field) => {\r\n switch (field.type) {\r\n case \"checkbox\":\r\n acc[field.name] = false;\r\n break;\r\n case \"file\":\r\n acc[field.name] = null;\r\n break;\r\n default:\r\n acc[field.name] = \"\";\r\n }\r\n return acc;\r\n }, {} as Record<string, unknown>);\r\n\r\n const schema =\r\n validationSchema ||\r\n Yup.object().shape(\r\n formValues.reduce((shape, field) => {\r\n if (!field.required) {\r\n shape[field.name] = Yup.mixed();\r\n return shape;\r\n }\r\n\r\n switch (field.type) {\r\n case \"checkbox\":\r\n shape[field.name] = Yup.boolean().oneOf(\r\n [true],\r\n `${field.label || field.name} is required`\r\n );\r\n break;\r\n case \"email\":\r\n shape[field.name] = Yup.string()\r\n .email(\"Invalid email\")\r\n .required(`${field.label || field.name} is required`);\r\n break;\r\n case \"number\":\r\n case \"range\":\r\n shape[field.name] = Yup.number()\r\n .typeError(`${field.label || field.name} must be a number`)\r\n .required(`${field.label || field.name} is required`);\r\n break;\r\n case \"file\":\r\n shape[field.name] = Yup.mixed().required(\r\n `${field.label || field.name} is required`\r\n );\r\n break;\r\n default:\r\n shape[field.name] = Yup.string().required(\r\n `${field.label || field.name} is required`\r\n );\r\n }\r\n\r\n return shape;\r\n }, {} as Record<string, Yup.AnySchema>)\r\n );\r\n\r\n const {\r\n handleChange,\r\n values,\r\n handleSubmit,\r\n setFieldValue,\r\n resetForm,\r\n errors,\r\n touched,\r\n handleBlur,\r\n } = useFormik({\r\n initialValues,\r\n validationSchema: schema,\r\n onSubmit: (vals) => {\r\n formSubmit(vals);\r\n resetForm();\r\n },\r\n });\r\n\r\n return (\r\n <form onSubmit={handleSubmit} className={`${width} mx-auto p-6 space-y-5`}>\r\n {formValues.map((field) => {\r\n const { name, type, label, placeholder, options } = field;\r\n\r\n // Checkbox\r\n if (type === \"checkbox\") {\r\n return (\r\n <div key={name} className=\"space-y-1\">\r\n <label\r\n className={`flex items-center space-x-3 cursor-pointer ${labelTextColor}`}\r\n >\r\n <input\r\n type=\"checkbox\"\r\n name={name}\r\n checked={values[name] as boolean}\r\n onChange={handleChange}\r\n onBlur={handleBlur}\r\n className=\"h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-400\"\r\n />\r\n <span className=\"font-medium\">{label || name}</span>\r\n </label>\r\n {touched[name] && errors[name] && (\r\n <p className=\"text-red-500 text-sm\">{errors[name] as string}</p>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n // Radio\r\n if (type === \"radio\" && options) {\r\n return (\r\n <div key={name} className=\"space-y-1\">\r\n <p className={`font-medium ${labelTextColor}`}>{label || name}</p>\r\n <div className=\"flex space-x-4\">\r\n {options.map((opt) => (\r\n <label\r\n key={opt}\r\n className=\"flex items-center space-x-2 cursor-pointer\"\r\n >\r\n <input\r\n type=\"radio\"\r\n name={name}\r\n value={opt}\r\n checked={values[name] === opt}\r\n onChange={handleChange}\r\n onBlur={handleBlur}\r\n className=\"h-4 w-4 text-blue-600 focus:ring-2 focus:ring-blue-400\"\r\n />\r\n <span className={`font-medium`}>{opt}</span>\r\n </label>\r\n ))}\r\n </div>\r\n {touched[name] && errors[name] && (\r\n <p className=\"text-red-500 text-sm\">{errors[name] as string}</p>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n // File\r\n if (type === \"file\") {\r\n return (\r\n <div key={name} className=\"space-y-1\">\r\n <label className={`font-medium ${labelTextColor}`}>\r\n {label || name}\r\n </label>\r\n <input\r\n type=\"file\"\r\n name={name}\r\n onChange={(e) =>\r\n setFieldValue(name, e.currentTarget.files?.[0] || null)\r\n }\r\n onBlur={handleBlur}\r\n className={`block w-full ${labelTextColor} bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400`}\r\n />\r\n {touched[name] && errors[name] && (\r\n <p className=\"text-red-500 text-sm\">{errors[name] as string}</p>\r\n )}\r\n </div>\r\n );\r\n }\r\n\r\n // Default input\r\n return (\r\n <div key={name} className=\"space-y-1\">\r\n <label className={`block font-medium ${labelTextColor}`}>\r\n {label || name}\r\n </label>\r\n <input\r\n type={type}\r\n name={name}\r\n placeholder={placeholder || `Enter ${label || name}`}\r\n value={\r\n values[name] as string | number | readonly string[] | undefined\r\n }\r\n onChange={handleChange}\r\n onBlur={handleBlur}\r\n className={`block w-full ${labelTextColor} bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400`}\r\n />\r\n {touched[name] && errors[name] && (\r\n <p className=\"text-red-500 text-sm\">{errors[name] as string}</p>\r\n )}\r\n </div>\r\n );\r\n })}\r\n\r\n <button\r\n type=\"submit\"\r\n disabled={isLoading}\r\n className={`w-full flex items-center justify-center gap-2 ${buttonBgColor} ${buttonTextColor} font-semibold py-2 rounded-lg shadow-md transition-all duration-200`}\r\n >\r\n {isLoading && (\r\n <svg\r\n className=\"animate-spin h-5 w-5 text-white\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n fill=\"none\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <circle\r\n className=\"opacity-25\"\r\n cx=\"12\"\r\n cy=\"12\"\r\n r=\"10\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"4\"\r\n ></circle>\r\n <path\r\n className=\"opacity-75\"\r\n fill=\"currentColor\"\r\n d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\"\r\n ></path>\r\n </svg>\r\n )}\r\n {isLoading ? \"Submitting...\" : \"Submit\"}\r\n </button>\r\n </form>\r\n );\r\n};\r\n\r\nexport default SmartForm;\r\n"],"mappings":"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","names":["index_exports","__export","SmartForm_default","__toCommonJS","import_formik","Yup","import_jsx_runtime","SmartForm","formValues","width","formSubmit","validationSchema","isLoading","buttonBgColor","buttonTextColor","labelTextColor","initialValues","acc","field","schema","shape","handleChange","values","handleSubmit","setFieldValue","resetForm","errors","touched","handleBlur","vals","name","type","label","placeholder","options","opt","e","SmartForm_default"]}