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
Source Map (JSON)
{"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"]}