From b09db3d0f7eea2364f0c7adda759a7ac4c39a232 Mon Sep 17 00:00:00 2001 From: Shrey Purohit <91727034+ShreyPurohit@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:55:05 +0530 Subject: [PATCH] Add files via upload --- npm-dynamic-form/README.md | 163 ++++++++++++++++++ npm-dynamic-form/dist/assets/interfaces.d.ts | 20 +++ npm-dynamic-form/dist/assets/interfaces.js | 2 + .../dist/components/DynamicForm.d.ts | 4 + .../dist/components/DynamicForm.js | 36 ++++ npm-dynamic-form/dist/index.d.ts | 4 + npm-dynamic-form/dist/index.js | 7 + npm-dynamic-form/package-lock.json | 147 ++++++++++++++++ npm-dynamic-form/package.json | 41 +++++ npm-dynamic-form/src/assets/interfaces.ts | 19 ++ .../src/components/DynamicForm.tsx | 98 +++++++++++ npm-dynamic-form/src/index.ts | 5 + npm-dynamic-form/tsconfig.json | 16 ++ 13 files changed, 562 insertions(+) create mode 100644 npm-dynamic-form/README.md create mode 100644 npm-dynamic-form/dist/assets/interfaces.d.ts create mode 100644 npm-dynamic-form/dist/assets/interfaces.js create mode 100644 npm-dynamic-form/dist/components/DynamicForm.d.ts create mode 100644 npm-dynamic-form/dist/components/DynamicForm.js create mode 100644 npm-dynamic-form/dist/index.d.ts create mode 100644 npm-dynamic-form/dist/index.js create mode 100644 npm-dynamic-form/package-lock.json create mode 100644 npm-dynamic-form/package.json create mode 100644 npm-dynamic-form/src/assets/interfaces.ts create mode 100644 npm-dynamic-form/src/components/DynamicForm.tsx create mode 100644 npm-dynamic-form/src/index.ts create mode 100644 npm-dynamic-form/tsconfig.json diff --git a/npm-dynamic-form/README.md b/npm-dynamic-form/README.md new file mode 100644 index 0000000..1ae21ea --- /dev/null +++ b/npm-dynamic-form/README.md @@ -0,0 +1,163 @@ +# Dynamic Form Component + +A highly customizable and easy-to-use dynamic form component built with React, TypeScript, and React Hook Form. This component allows you to generate a form dynamically based on an array of field configurations, making it suitable for various use cases. + +## Installation +To install the package, run: +```bash +npm install dynamic-form-component +``` + +## Peer Dependencies +Ensure you have the following peer dependencies installed: +* `react` +* `react-dom` +* `react-hook-form` +* `react-icons` + +You can install them with: +```bash +npm install react react-dom react-hook-form react-icons +``` + +## Usage +### Importing The Component +```bash +import DynamicForm, { IFormField } from 'dynamic-form-component'; +``` +### Example Usage +Below is an example of how to use the `DynamicForm` component in your react project: +```js +import DynamicForm, { IFormField } from 'dynamic-form-component'; +import { RegisterOptions } from 'react-hook-form'; +import { AiOutlineLock, AiOutlineMail, AiOutlineNumber, AiOutlineUser } from 'react-icons/ai'; + +const App = () => { + const formFields: IFormField[] = [ + { + id: 'name', + errorId: 'nameErr', + label: 'Name', + type: 'text', + placeholder: 'Enter your name', + required: true, + validation: { + required: 'Name is required', + maxLength: { + value: 20, + message: 'Name must be less than 20 characters', + }, + }, + icon: + }, + { + id: 'email', + errorId: 'emailErr', + label: 'Email', + type: 'email', + placeholder: 'Enter your email', + validation: { + required: 'Email is required', + pattern: { + value: /^\S+@\S+$/i, + message: 'Enter a valid email address', + }, + }, + icon: + }, + { + id: 'age', + errorId: 'ageError', + label: 'Age', + type: 'number', + placeholder: 'Enter your age', + validation: { + min: { + value: 18, + message: 'You must be at least 18 years old', + }, + }, + icon: + }, + { + id: 'password', + errorId: 'passwordErr', + label: 'Password', + type: 'password', + placeholder: 'Enter your Password', + validation: { + required: 'Password is required', + }, + icon: + }, + { + id: 'hobbies', + errorId: 'subscriptionErr', + label: 'Hobbies', + type: 'checkbox', + options: [ + { value: 'reading', label: "Reading" }, + { value: 'sports', label: "Sports" }, + { value: 'music', label: "Music" }, + { value: 'travelling', label: "Travelling" }, + ], + validation: {}, + }, + { + id: 'gender', + errorId: 'genderErr', + label: 'Gender', + type: 'select', + options: [ + { value: 'male', label: 'Male' }, + { value: 'female', label: 'Female' }, + { value: 'other', label: 'Other' }, + ], + validation: { + required: 'Gender is required', + }, + }, + ]; + + const handleFormSubmit = (data: Record) => { + console.log('Form Data:', data); + }; + + return ( +
+

Dynamic Form Example

+ +
+ ) +} + +export default App +``` +### Field Configuration +* id: Unique identifier for the field. +* errorId: Identifier for displaying error messages. +* label: Label for the form field. +* type: Type of the field (text, number, email, password, checkbox, select). +* options: (Optional) Array of options for select and checkbox fields. +* placeholder: (Optional) Placeholder text for the input. +* required: (Optional) Boolean indicating if the field is required. +* validation: (Optional) Validation rules based on React Hook Form's RegisterOptions. +* icon: (Optional) Icon component to display alongside the input. + +### Form Submission +The `onSubmit` function passed to `DynamicForm` will receive the form data as a `Record`. You can handle form submission by implementing your own logic in this function. + +Example Form Data +```bash + { + "name": "John Doe", + "email": "john.doe@example.com", + "age": 25, + "password": "password123", + "hobbies": Array ["reading", "travelling"], + "gender": "male" + } +``` + +## Contributing +If you have any ideas, suggestions, or issues, feel free to open an issue or contribute with a pull request. \ No newline at end of file diff --git a/npm-dynamic-form/dist/assets/interfaces.d.ts b/npm-dynamic-form/dist/assets/interfaces.d.ts new file mode 100644 index 0000000..6af711f --- /dev/null +++ b/npm-dynamic-form/dist/assets/interfaces.d.ts @@ -0,0 +1,20 @@ +import { RegisterOptions, FieldValues } from "react-hook-form"; +export interface IFormField { + id: string; + errorId: string; + label: string; + type: 'text' | 'number' | 'email' | 'password' | 'checkbox' | 'select'; + options?: { + value: string; + label: string; + }[]; + placeholder?: string; + required?: boolean; + validation?: RegisterOptions; + icon?: React.ReactNode; + value?: string; +} +export interface DynamicFormProps { + fields: IFormField[]; + onSubmit: (data: FieldValues) => void; +} diff --git a/npm-dynamic-form/dist/assets/interfaces.js b/npm-dynamic-form/dist/assets/interfaces.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/npm-dynamic-form/dist/assets/interfaces.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/npm-dynamic-form/dist/components/DynamicForm.d.ts b/npm-dynamic-form/dist/components/DynamicForm.d.ts new file mode 100644 index 0000000..5816559 --- /dev/null +++ b/npm-dynamic-form/dist/components/DynamicForm.d.ts @@ -0,0 +1,4 @@ +import { DynamicFormProps } from '../assets/interfaces'; +import React from 'react'; +declare const DynamicForm: React.FC; +export default DynamicForm; diff --git a/npm-dynamic-form/dist/components/DynamicForm.js b/npm-dynamic-form/dist/components/DynamicForm.js new file mode 100644 index 0000000..3119c41 --- /dev/null +++ b/npm-dynamic-form/dist/components/DynamicForm.js @@ -0,0 +1,36 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const react_1 = require("react"); +const react_hook_form_1 = require("react-hook-form"); +const ai_1 = require("react-icons/ai"); +const react_2 = __importDefault(require("react")); +const DynamicForm = ({ fields, onSubmit }) => { + const [showPassword, setShowPassword] = (0, react_1.useState)(false); + const { register, handleSubmit, formState: { errors } } = (0, react_hook_form_1.useForm)({ mode: 'all' }); + const renderField = (field) => { + var _a, _b; + const error = (_a = errors[field.id]) === null || _a === void 0 ? void 0 : _a.message; + const isPasswordField = field.type === 'password'; + const inputType = isPasswordField && showPassword ? 'text' : field.type; + return (react_2.default.createElement("div", { id: 'input-wrapper' }, + react_2.default.createElement("div", { id: 'input-container' }, field.type === 'checkbox' && field.options ? (react_2.default.createElement("div", { className: 'grid grid-cols-2 gap-4' }, field.options.map(option => (react_2.default.createElement("div", { key: option.value, className: 'flex gap-1 items-center justify-center' }, + react_2.default.createElement("label", { htmlFor: `${field.id}-${option.value}`, className: 'ml-2 mt-1' }, + option.label, + " :"), + react_2.default.createElement("input", Object.assign({ id: `${field.id}-${option.value}` }, register(field.id, Object.assign({}, field.validation)), { type: field.type, value: option.value, className: 'w-max' }))))))) : field.type === 'select' ? (react_2.default.createElement("select", Object.assign({}, register(field.id, Object.assign({}, field.validation)), { id: field.id }), (_b = field.options) === null || _b === void 0 ? void 0 : _b.map((option) => (react_2.default.createElement("option", { key: option.value, value: option.value }, option.label))))) : (react_2.default.createElement("div", { className: 'flex items-center p-1 border' }, + field.icon && react_2.default.createElement("div", { className: 'ml-2' }, field.icon), + react_2.default.createElement("input", Object.assign({ id: field.id }, register(field.id, Object.assign({}, field.validation)), { type: inputType, placeholder: field.placeholder })), + isPasswordField && (react_2.default.createElement("div", { id: "prop-icon", className: "hover:cursor-pointer mr-3", onClick: () => setShowPassword(!showPassword) }, showPassword ? react_2.default.createElement(ai_1.AiOutlineEye, null) : react_2.default.createElement(ai_1.AiOutlineEyeInvisible, null)))))), + error && react_2.default.createElement("span", { id: field.errorId }, error))); + }; + return (react_2.default.createElement("main", { className: 'md:w-3/4 m-auto md:h-0' }, + react_2.default.createElement("form", { onSubmit: handleSubmit(onSubmit), className: "flex flex-col md:w-1/2 m-auto p-5 gap-5 md:mt-5 md:border border-black" }, + fields.map((field) => (react_2.default.createElement("div", { key: field.id }, + react_2.default.createElement("label", { htmlFor: field.id }, field.label), + renderField(field)))), + react_2.default.createElement("button", { type: 'submit', disabled: Object.keys(errors).length ? true : false, className: 'px-4 py-2 bg-slate-400 hover:bg-slate-600 hover:transition hover:text-white rounded-lg disabled:bg-gray-600 disabled:hover:bg-gray-600' }, "Submit")))); +}; +exports.default = DynamicForm; diff --git a/npm-dynamic-form/dist/index.d.ts b/npm-dynamic-form/dist/index.d.ts new file mode 100644 index 0000000..08beb73 --- /dev/null +++ b/npm-dynamic-form/dist/index.d.ts @@ -0,0 +1,4 @@ +import DynamicForm from './components/DynamicForm'; +import { IFormField, DynamicFormProps } from './assets/interfaces'; +export default DynamicForm; +export { IFormField, DynamicFormProps }; diff --git a/npm-dynamic-form/dist/index.js b/npm-dynamic-form/dist/index.js new file mode 100644 index 0000000..1d11079 --- /dev/null +++ b/npm-dynamic-form/dist/index.js @@ -0,0 +1,7 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const DynamicForm_1 = __importDefault(require("./components/DynamicForm")); +exports.default = DynamicForm_1.default; diff --git a/npm-dynamic-form/package-lock.json b/npm-dynamic-form/package-lock.json new file mode 100644 index 0000000..1972190 --- /dev/null +++ b/npm-dynamic-form/package-lock.json @@ -0,0 +1,147 @@ +{ + "name": "dynamic-form-component", + "version": "1.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dynamic-form-component", + "version": "1.0.1", + "license": "MIT", + "devDependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react-hook-form": "^7.0.0", + "react-icons": "^4.0.0", + "typescript": "^5.0.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-hook-form": "^7.0.0", + "react-icons": "^4.0.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.53.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz", + "integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==", + "dev": true, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-icons": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "dev": true, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/npm-dynamic-form/package.json b/npm-dynamic-form/package.json new file mode 100644 index 0000000..7ff7b4b --- /dev/null +++ b/npm-dynamic-form/package.json @@ -0,0 +1,41 @@ +{ + "name": "dynamic-form-component", + "version": "1.0.12", + "description": "A dynamic form component for React that renders forms based on provided configuration.", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "prepare": "npm run build" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-hook-form": "^7.0.0", + "react-icons": "^4.0.0" + }, + "devDependencies": { + "typescript": "^5.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react-hook-form": "^7.0.0", + "react-icons": "^4.0.0" + }, + "files": [ + "dist", + "README.md" + ], + "author": "Shrey Purohit", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/ShreyPurohit/Medium/tree/6c29c1c77ee5a29c4b5915e6669c28449931dbbe/React-Projects/dynamic-form-medium" + }, + "keywords": [ + "react", + "form", + "dynamic", + "react-hook-form", + "typescript" + ] +} \ No newline at end of file diff --git a/npm-dynamic-form/src/assets/interfaces.ts b/npm-dynamic-form/src/assets/interfaces.ts new file mode 100644 index 0000000..7c150cf --- /dev/null +++ b/npm-dynamic-form/src/assets/interfaces.ts @@ -0,0 +1,19 @@ +import { RegisterOptions, FieldValues } from "react-hook-form"; + +export interface IFormField { + id: string; + errorId: string; + label: string; + type: 'text' | 'number' | 'email' | 'password' | 'checkbox' | 'select'; + options?: { value: string; label: string }[]; + placeholder?: string; + required?: boolean; + validation?: RegisterOptions; + icon?: React.ReactNode; + value?: string +} + +export interface DynamicFormProps { + fields: IFormField[]; + onSubmit: (data: FieldValues) => void; +} \ No newline at end of file diff --git a/npm-dynamic-form/src/components/DynamicForm.tsx b/npm-dynamic-form/src/components/DynamicForm.tsx new file mode 100644 index 0000000..f1a8be1 --- /dev/null +++ b/npm-dynamic-form/src/components/DynamicForm.tsx @@ -0,0 +1,98 @@ +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai'; +import { DynamicFormProps, IFormField } from '../assets/interfaces'; +import React from 'react'; + + +const DynamicForm: React.FC = ({ fields, onSubmit }) => { + const [showPassword, setShowPassword] = useState(false); + + const { register, handleSubmit, formState: { errors } } = useForm({ mode: 'all' }); + + const renderField = (field: IFormField) => { + const error = errors[field.id]?.message as string; + + const isPasswordField = field.type === 'password'; + const inputType = isPasswordField && showPassword ? 'text' : field.type; + + return ( +
+
+ { + field.type === 'checkbox' && field.options ? ( +
+ {field.options.map(option => ( +
+ + +
+ ))} +
+ ) : field.type === 'select' ? ( + + ) : ( +
+ {field.icon &&
{field.icon}
} + + {isPasswordField && ( +
setShowPassword(!showPassword)} + > + {showPassword ? : } +
+ )} +
+ ) + } +
+ {error && {error}} +
+ ); + }; + + return ( +
+
+ {fields.map((field) => ( +
+ + {renderField(field)} +
+ )) + } + +
+
+ ); +}; + +export default DynamicForm; \ No newline at end of file diff --git a/npm-dynamic-form/src/index.ts b/npm-dynamic-form/src/index.ts new file mode 100644 index 0000000..164f884 --- /dev/null +++ b/npm-dynamic-form/src/index.ts @@ -0,0 +1,5 @@ +import DynamicForm from './components/DynamicForm' +import { IFormField, DynamicFormProps } from './assets/interfaces' + +export default DynamicForm +export { IFormField, DynamicFormProps } \ No newline at end of file diff --git a/npm-dynamic-form/tsconfig.json b/npm-dynamic-form/tsconfig.json new file mode 100644 index 0000000..832b21d --- /dev/null +++ b/npm-dynamic-form/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": [ + "src" + ], /* Include only the src directory */ + "compilerOptions": { + "jsx": "react", + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "module": "commonjs" /* Specify what module code is generated. */, + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "outDir": "dist", + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + "strict": true /* Enable all strict type-checking options. */, + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} \ No newline at end of file