From f1e130dcf3f57de7261efa8f6de24ab2152a42ac Mon Sep 17 00:00:00 2001 From: Sitaram8472 Date: Thu, 9 Oct 2025 18:23:00 +0530 Subject: [PATCH] Add Form input component --- package-lock.json | 60 ++++ src/app/components/FormInput/DatePicker.jsx | 23 ++ src/app/components/FormInput/FileUpload.jsx | 45 +++ .../components/FormInput/FormValidation.jsx | 33 ++ src/app/components/FormInput/Slider.jsx | 24 ++ src/app/components/page.jsx | 309 +++++++++++++----- 6 files changed, 419 insertions(+), 75 deletions(-) create mode 100644 src/app/components/FormInput/DatePicker.jsx create mode 100644 src/app/components/FormInput/FileUpload.jsx create mode 100644 src/app/components/FormInput/FormValidation.jsx create mode 100644 src/app/components/FormInput/Slider.jsx diff --git a/package-lock.json b/package-lock.json index 3d7401c..66138b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1211,6 +1211,66 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.14", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.14.tgz", diff --git a/src/app/components/FormInput/DatePicker.jsx b/src/app/components/FormInput/DatePicker.jsx new file mode 100644 index 0000000..3a3451f --- /dev/null +++ b/src/app/components/FormInput/DatePicker.jsx @@ -0,0 +1,23 @@ +import React, { useState } from "react"; + +export const DatePicker = ({ label = "Select Date", onChange }) => { + const [date, setDate] = useState(""); + + const handleChange = (e) => { + setDate(e.target.value); + if (onChange) onChange(e.target.value); + }; + + return ( +
+ + + {date &&

Selected Date: {date}

} +
+ ); +}; diff --git a/src/app/components/FormInput/FileUpload.jsx b/src/app/components/FormInput/FileUpload.jsx new file mode 100644 index 0000000..df3fb85 --- /dev/null +++ b/src/app/components/FormInput/FileUpload.jsx @@ -0,0 +1,45 @@ +import React, { useState } from "react"; + +export const FileUpload = ({ onFileSelect }) => { + const [file, setFile] = useState(null); + + const handleFile = (file) => { + setFile(file); + if (onFileSelect) onFileSelect(file); + }; + + const handleDrop = (e) => { + e.preventDefault(); + handleFile(e.dataTransfer.files[0]); + }; + + const handleChange = (e) => { + handleFile(e.target.files[0]); + }; + + return ( +
+ +
+ ); +}; diff --git a/src/app/components/FormInput/FormValidation.jsx b/src/app/components/FormInput/FormValidation.jsx new file mode 100644 index 0000000..ba426b5 --- /dev/null +++ b/src/app/components/FormInput/FormValidation.jsx @@ -0,0 +1,33 @@ +import React, { useState } from "react"; + +export const FormValidation = ({ minLength = 3, onSubmit }) => { + const [value, setValue] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = (e) => { + e.preventDefault(); + if (value.trim() === "") { + setError("Field cannot be empty"); + } else if (value.length < minLength) { + setError(`Minimum ${minLength} characters required`); + } else { + setError(""); + if (onSubmit) onSubmit(value); + alert("Form submitted: " + value); + } + }; + + return ( +
+ setValue(e.target.value)} + style={{ padding: "0.5rem", marginRight: "0.5rem" }} + placeholder="Enter text" + /> + + {error &&

{error}

} +
+ ); +}; diff --git a/src/app/components/FormInput/Slider.jsx b/src/app/components/FormInput/Slider.jsx new file mode 100644 index 0000000..fc59b45 --- /dev/null +++ b/src/app/components/FormInput/Slider.jsx @@ -0,0 +1,24 @@ +import React, { useState } from "react"; + +export const Slider = ({ min = 0, max = 100, step = 1, onChange }) => { + const [value, setValue] = useState((min + max) / 2); + + const handleChange = (e) => { + setValue(e.target.value); + if (onChange) onChange(e.target.value); + }; + + return ( +
+ +

Value: {value}

+
+ ); +}; diff --git a/src/app/components/page.jsx b/src/app/components/page.jsx index f49e3f8..03dab54 100644 --- a/src/app/components/page.jsx +++ b/src/app/components/page.jsx @@ -1,9 +1,9 @@ "use client"; -import React, { useState, useEffect } from 'react' -import { Search, X } from 'lucide-react' +import React, { useState, useEffect } from "react"; +import { Search, X } from "lucide-react"; -import { useAnalytics } from '../context/AnalyticsContext' -import { useTheme } from '../context/ThemeContext' +import { useAnalytics } from "../context/AnalyticsContext"; +import { useTheme } from "../context/ThemeContext"; // Button Imports // import PrimaryButton from '@/components/buttons/PrimaryButton' // import SecondaryButton from '@/components/buttons/SecondaryButton' @@ -52,22 +52,27 @@ import Breadcrumb from "./navigation/Breadcrumb"; import Pagination from "./navigation/Pagination"; import RainbowButton from "@/app/components/buttons/RainbowButton"; +// form Input + +import { DatePicker } from "./FormInput/DatePicker"; +import { FileUpload } from "./FormInput/FileUpload"; +import { FormValidation } from "./FormInput/FormValidation"; +import { Slider } from "./FormInput/Slider"; export default function Page() { // Search and Filter State - const [searchTerm, setSearchTerm] = useState(''); - const [filterType, setFilterType] = useState('all'); + const [searchTerm, setSearchTerm] = useState(""); + const [filterType, setFilterType] = useState("all"); - // Analytics const { trackComponentView } = useAnalytics(); - + // Theme const { darkMode } = useTheme(); // Track page view - only once on mount useEffect(() => { - trackComponentView('ComponentsPage'); + trackComponentView("ComponentsPage"); }, []); // Empty dependency array to run only once // Inputs @@ -75,8 +80,6 @@ export default function Page() { const [selectValue, setSelectValue] = React.useState(""); const [checkboxValue, setCheckboxValue] = React.useState(false); - - // Data const selectOptions = [ { value: "option1", label: "Option 1" }, @@ -107,57 +110,171 @@ export default function Page() { { label: "Breadcrumb" }, ]; - - // All components with search data const allComponents = { buttons: [ - { name: 'Primary Button', component: Primary, keywords: ['primary', 'main', 'action', 'cta'] , desc : "Used for Main Actions"}, - { name: 'Secondary Button', component: Secondary, keywords: ['secondary', 'alternate'] , desc: "Used for secondary Actions" }, - { name: 'Ghost Button', component: Ghost, keywords: ['ghost', 'transparent', 'subtle'] , desc : "Used for minimal actions"}, - { name: 'Outline Button', component: Outline, keywords: ['outline', 'border', 'stroke'] , desc : "Used for gives outline"}, - { name: 'Danger Button', component: Danger, keywords: ['danger', 'error', 'delete', 'warning', 'red'] , desc : "Used for destructive actions" }, - { name: 'Success Button', component: Success, keywords: ['success', 'confirm', 'done', 'green'] , desc : "Used for success actions"}, - { name: 'Icon Button', component: , keywords: ['icon', 'star', 'symbol'] , desc : "Used for icons"}, - { name: 'Rainbow Button', component: Rainbow, keywords: ['rainbow', 'action', 'colorful'] , desc : "Used for call to actions"} + { + name: "Primary Button", + component: Primary, + keywords: ["primary", "main", "action", "cta"], + desc: "Used for Main Actions", + }, + { + name: "Secondary Button", + component: Secondary, + keywords: ["secondary", "alternate"], + desc: "Used for secondary Actions", + }, + { + name: "Ghost Button", + component: Ghost, + keywords: ["ghost", "transparent", "subtle"], + desc: "Used for minimal actions", + }, + { + name: "Outline Button", + component: Outline, + keywords: ["outline", "border", "stroke"], + desc: "Used for gives outline", + }, + { + name: "Danger Button", + component: Danger, + keywords: ["danger", "error", "delete", "warning", "red"], + desc: "Used for destructive actions", + }, + { + name: "Success Button", + component: Success, + keywords: ["success", "confirm", "done", "green"], + desc: "Used for success actions", + }, + { + name: "Icon Button", + component: , + keywords: ["icon", "star", "symbol"], + desc: "Used for icons", + }, + { + name: "Rainbow Button", + component: Rainbow, + keywords: ["rainbow", "action", "colorful"], + desc: "Used for call to actions", + }, ], cards: [ - { name: 'Simple Card', component: , keywords: ['simple', 'basic', 'minimal'] }, - { name: 'Image Card', component: , keywords: ['image', 'picture', 'visual'] }, - { name: 'Feature Card', component: , keywords: ['feature', 'highlight', 'benefit'] }, - { name: 'Pricing Card', component: , keywords: ['pricing', 'plan', 'subscription', 'price'] }, - { name: 'Data Card', component: , keywords: ['data', 'stats', 'analytics', 'metrics'] } + { + name: "Simple Card", + component: ( + + ), + keywords: ["simple", "basic", "minimal"], + }, + { + name: "Image Card", + component: ( + + ), + keywords: ["image", "picture", "visual"], + }, + { + name: "Feature Card", + component: ( + + ), + keywords: ["feature", "highlight", "benefit"], + }, + { + name: "Pricing Card", + component: ( + + ), + keywords: ["pricing", "plan", "subscription", "price"], + }, + { + name: "Data Card", + component: ( + + ), + keywords: ["data", "stats", "analytics", "metrics"], + }, ], inputs: [ - { name: 'Text Input', component: , keywords: ['text', 'input', 'field', 'form'] }, - { name: 'Select', component: , + keywords: ["select", "dropdown", "options", "choice"], + }, + { + name: "Checkbox", + component: ( + {}} + /> + ), + keywords: ["checkbox", "check", "toggle", "boolean"], + }, ], navigation: [ - { name: 'Breadcrumb', component: , keywords: ['breadcrumb', 'navigation', 'path', 'hierarchy'] }, - { name: 'Tabs', component: , keywords: ['tabs', 'navigation', 'switch', 'toggle'] }, - { name: 'Pagination', component: , keywords: ['pagination', 'pages', 'navigation', 'paging'] } - ] + { + name: "Breadcrumb", + component: , + keywords: ["breadcrumb", "navigation", "path", "hierarchy"], + }, + { + name: "Tabs", + component: , + keywords: ["tabs", "navigation", "switch", "toggle"], + }, + { + name: "Pagination", + component: ( + + ), + keywords: ["pagination", "pages", "navigation", "paging"], + }, + ], }; // Filter logic const getFilteredComponents = () => { let components = {}; - + // Apply type filter - if (filterType === 'all') { + if (filterType === "all") { components = allComponents; } else { components = { [filterType]: allComponents[filterType] }; } - + // Apply search filter if (searchTerm) { const filtered = {}; - Object.keys(components).forEach(type => { - const matchedComponents = components[type].filter(comp => - comp.name.toLowerCase().includes(searchTerm.toLowerCase()) || - comp.keywords.some(keyword => keyword.toLowerCase().includes(searchTerm.toLowerCase())) + Object.keys(components).forEach((type) => { + const matchedComponents = components[type].filter( + (comp) => + comp.name.toLowerCase().includes(searchTerm.toLowerCase()) || + comp.keywords.some((keyword) => + keyword.toLowerCase().includes(searchTerm.toLowerCase()) + ) ); if (matchedComponents.length > 0) { filtered[type] = matchedComponents; @@ -165,12 +282,15 @@ export default function Page() { }); return filtered; } - + return components; }; const filteredComponents = getFilteredComponents(); - const totalResults = Object.values(filteredComponents).reduce((total, components) => total + components.length, 0); + const totalResults = Object.values(filteredComponents).reduce( + (total, components) => total + components.length, + 0 + ); return (
@@ -186,7 +306,7 @@ export default function Page() { Beautiful, modern & responsive component demo – each below section is styled for clarity, vibrance, and accessibility.

- + {/* Search Bar */}
@@ -200,7 +320,7 @@ export default function Page() { /> {searchTerm && (
))} {/* Show additional examples if all inputs are visible */} - {filteredComponents.inputs.length === allComponents.inputs.length && ( + {filteredComponents.inputs.length === + allComponents.inputs.length && ( <> setSelectValue(e.target.value)} required - className="text-gray-100 bg-gray-600 px-4 py-2" /> - )} + )} {/* Navigation Section */} {filteredComponents.navigation && ( @@ -369,16 +492,16 @@ export default function Page() { className="bg-gradient-to-br from-yellow-50 via-orange-50 to-pink-50 dark:from-[#3a3020] dark:via-[#412920] dark:to-[#16101a] border border-yellow-100 dark:border-yellow-900 shadow-xl rounded-2xl p-10" >

- Navigation Components ({filteredComponents.navigation.length}) + + Navigation Components ({filteredComponents.navigation.length}) +

{filteredComponents.navigation.map((item, index) => (

{item.name}

-
- {item.component} -
+
{item.component}
))}
@@ -386,10 +509,8 @@ export default function Page() { )} {/* Feedback Section - Always show when no specific filter is applied */} - {(filterType === 'all' && !searchTerm) && ( -
+ {filterType === "all" && !searchTerm && ( +

Feedback Components @@ -414,8 +535,46 @@ export default function Page() {

)} - + + {filterType === "all" && !searchTerm && ( +
+ {/* Heading */} +

+ Form Helper Components + +

+ + {/* Components Grid */} +
+ {/* Date Picker Card */} +
+

📅 Date Picker

+ +
+ + {/* Slider Card */} +
+

🎚 Slider

+ +
+ + {/* File Upload Card */} +
+

📁 File Upload

+ +
+ + {/* Form Validation Card */} +
+

+ ✅ Form Validation +

+ +
+
+
+ )}
); -} \ No newline at end of file +}