UNPKG

@diludilshad/form-builder-library

Version:

A powerful React library for building dynamic, drag-and-drop forms with a rich set of field types and customization options. Uses Tailwind CSS classes - requires Tailwind CSS as a peer dependency in your project.

1,041 lines (853 loc) 26.4 kB
# Form Builder Library A powerful React library for building dynamic, drag-and-drop forms with a rich set of field types and customization options. Uses Tailwind CSS classes - requires Tailwind CSS as a peer dependency in your project. ## Features - 🎨 **Visual Form Builder** - Drag and drop interface for creating forms - 📝 **Rich Field Types** - 25+ field types including text, numbers, choices, dates, uploads, and more - 🔧 **Field Customization** - Extensive settings for each field type - 👀 **Real-time Preview** - See your form as users will see it - 📱 **Responsive Design** - Works perfectly on desktop and mobile - 💾 **Local Storage** - Automatic form persistence - 🎯 **Form Validation** - Built-in validation rules and custom logic - 🎨 **Icon Support** - Choose from 30+ icons for each field - 🎨 **Tailwind CSS** - Uses Tailwind utility classes for styling - 📦 **Minimal Dependencies** - Only React, React DOM, and Tailwind CSS required ## Installation ```bash npm install @diludilshad/form-builder-library ``` ### Prerequisites This library requires Tailwind CSS to be installed and configured in your project. If you haven't already set up Tailwind CSS, follow these steps: 1. **Install Tailwind CSS:** ```bash npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p ``` 2. **Configure your `tailwind.config.js`:** ```javascript /** @type {import('tailwindcss').Config} */ export default { content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", // Add this line to scan the form builder library "./node_modules/@diludilshad/form-builder-library/**/*.{js,jsx}", ], theme: { extend: {}, }, plugins: [], }; ``` 3. **Add Tailwind directives to your CSS:** ```css @tailwind base; @tailwind components; @tailwind utilities; ``` ### Why Peer Dependency? The Form Builder Library uses Tailwind CSS classes for styling. By using Tailwind as a peer dependency instead of bundling it: - **No Style Conflicts** - Avoids duplicate Tailwind base styles - **Consistent Theming** - Components use your project's Tailwind configuration - **Smaller Bundle Size** - No duplicate CSS utilities - **Customization** - Easy to customize colors, spacing, and other design tokens ## Using in Your Project ### Step 1: Create a New React Project ```bash # Create a new React project npx create-react-app my-form-app cd my-form-app # Or with Vite (recommended) npm create vite@latest my-form-app -- --template react cd my-form-app npm install ``` ### Step 2: Install Dependencies ```bash # Install the form builder library npm install @diludilshad/form-builder-library # Install required peer dependencies npm install tailwindcss postcss autoprefixer ``` ### Step 3: Setup Tailwind CSS ```bash # Initialize Tailwind npx tailwindcss init -p ``` Update your `tailwind.config.js`: ```javascript /** @type {import('tailwindcss').Config} */ export default { content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", // IMPORTANT: Include the form builder library "./node_modules/@diludilshad/form-builder-library/**/*.{js,jsx}", ], theme: { extend: { // You can extend the theme to customize form builder appearance colors: { primary: { 50: "#eff6ff", 500: "#3b82f6", 600: "#2563eb", 700: "#1d4ed8", }, }, }, }, plugins: [], }; ``` Add to your `src/index.css`: ```css @tailwind base; @tailwind components; @tailwind utilities; ``` ### Step 4: Basic Implementation Create `src/App.jsx`: ```jsx import React, { useState } from "react"; import { BuilderForm, FormReader } from "@diludilshad/form-builder-library"; function App() { const [currentView, setCurrentView] = useState("builder"); const [form, setForm] = useState({ name: "", type: "", description: "", structure: [], formsRules: [], }); const handleSave = (formData) => { console.log("Form saved:", formData); setForm(formData); // Here you would typically save to your backend }; const handleFormSubmit = (values) => { console.log("Form submitted:", values); // Handle form submission }; return ( <div className="min-h-screen bg-gray-100"> {/* Navigation */} <nav className="bg-white shadow-sm border-b px-6 py-4"> <div className="flex space-x-4"> <button onClick={() => setCurrentView("builder")} className={`px-4 py-2 rounded-md ${ currentView === "builder" ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-700" }`} > Form Builder </button> <button onClick={() => setCurrentView("reader")} className={`px-4 py-2 rounded-md ${ currentView === "reader" ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-700" }`} disabled={!form.structure?.length} > Form Preview </button> </div> </nav> {/* Content */} {currentView === "builder" ? ( <BuilderForm editFormId="demo-form" form={form} setForm={setForm} onSave={handleSave} /> ) : ( <div className="p-6"> <div className="max-w-2xl mx-auto bg-white rounded-lg shadow-sm p-6"> <FormReader formData={form} onSubmit={handleFormSubmit} onFieldChange={(fieldId, value) => { console.log(`Field ${fieldId} changed:`, value); }} /> </div> </div> )} </div> ); } export default App; ``` ## Quick Start ### Basic Form Builder ```jsx import React, { useState } from "react"; import { BuilderForm } from "@diludilshad/form-builder-library"; function App() { const [form, setForm] = useState({ name: "", type: "", description: "", structure: [], formsRules: [], }); const handleSave = (formData) => { console.log("Form saved:", formData); setForm(formData); }; return ( <BuilderForm editFormId="my-form-1" form={form} setForm={setForm} onSave={handleSave} /> ); } ``` ### Form Reader (Display Forms) ```jsx import React from "react"; import { FormReader } from "@diludilshad/form-builder-library"; function FormDisplay() { const formData = { name: "Contact Form", description: "Please fill out this contact form", structure: [ { id: "name", type: "single-line", label: "Full Name", placeholder: "Enter your full name", required: true, selectedIcon: "person", }, { id: "email", type: "email", label: "Email Address", placeholder: "Enter your email", required: true, selectedIcon: "mail", }, ], }; const handleSubmit = (values) => { console.log("Form submitted:", values); }; return ( <FormReader formData={formData} onSubmit={handleSubmit} onFieldChange={(fieldId, value) => console.log(fieldId, value)} /> ); } ``` ## Components ### BuilderForm The main form builder component with drag-and-drop functionality. #### Props | Prop | Type | Required | Default | Description | | ------------ | -------- | -------- | ------- | ---------------------------------------------------------- | | `editFormId` | string | Yes | - | Unique identifier for the form (used for localStorage key) | | `form` | object | Yes | - | Current form state object | | `setForm` | function | Yes | - | Function to update form state | | `onSave` | function | No | - | Callback function called when form is saved | #### Form Object Structure ```javascript { name: string, // Form name type: string, // Form type description: string, // Form description structure: array, // Array of form fields formsRules: array // Array of validation rules } ``` ### FormReader Component for displaying and collecting data from forms. #### Props | Prop | Type | Required | Default | Description | | ------------------ | -------- | -------- | -------- | ------------------------------------ | | `formData` | object | Yes | - | Form data object with structure | | `onSubmit` | function | Yes | - | Called when form is submitted | | `onFieldChange` | function | No | - | Called when any field value changes | | `initialValues` | object | No | {} | Initial values for form fields | | `customSubmitText` | string | No | "Submit" | Custom submit button text | | `customCancelText` | string | No | "Cancel" | Custom cancel button text | | `onCancel` | function | No | - | Called when cancel button is clicked | | `className` | string | No | "" | Additional CSS classes | | `formRules` | array | No | [] | Form validation rules | ### FormPreview Component for previewing forms in builder mode. #### Props | Prop | Type | Required | Default | Description | | --------------- | -------- | -------- | ------- | ------------------------------- | | `formFields` | array | Yes | - | Array of form fields | | `previewData` | object | No | {} | Preview data | | `isTestMode` | boolean | No | false | Whether in test mode | | `formRules` | array | No | [] | Form validation rules | | `onExit` | function | Yes | - | Called when exiting preview | | `onSubmit` | function | Yes | - | Called when form is submitted | | `onFieldUpdate` | function | No | - | Called when field values change | ## Field Types ### Basic Information Fields - **Name** (`name`) - Person icon, blue color - **Address** (`address`) - Location icon, green color - **Phone** (`phone`) - Call icon, purple color - **Email** (`email`) - Mail icon, red color - **Website** (`website`) - Globe icon, indigo color ### Text Fields - **Single Line** (`single-line`) - Document icon, orange color - **Dynamic Single Line** (`dynamic-single-line`) - Document icon, orange color - **Multi Line** (`multi-line`) - Reader icon, teal color - **Rich Text** (`rich-text`) - Options icon, indigo color ### Number Fields - **Number** (`number`) - 123 icon, purple color - **Decimal** (`decimal`) - .00 icon, emerald color - **Formula** (`formula`) - fx icon, pink color - **Currency** (`currency`) - Wallet icon, yellow color ### Choice Fields - **Dropdown** (`dropdown`) - Chevron down icon, blue color - **Radio** (`radio`) - Radio button icon, green color - **Checkbox** (`checkbox`) - Checkbox icon, purple color - **Multiple Choice** (`multiple-choice`) - List icon, teal color - **Image Choices** (`image-choices`) - Images icon, pink color - **Matrix Choice** (`matrix-choice`) - Grid icon, indigo color - **Table** (`table`) - Grid icon, orange color ### Date & Time Fields - **Date** (`date`) - Calendar icon, orange color - **Time** (`time`) - Time icon, blue color - **Date-Time** (`date-time`) - Calendar clear icon, green color - **Month-Year** (`month-year`) - MY icon, amber color ### Upload Fields - **File Upload** (`file-upload`) - Document attach icon, green color - **Image Upload** (`image-upload`) - Cloud upload icon, teal color - **Audio/Video Upload** (`audio-video-upload`) - Musical notes icon, purple color ### Rating & Scale Fields - **Rating** (`rating`) - Star icon, yellow color - **Slider** (`slider`) - Options icon, pink color ### Layout Fields - **Section** (`section-header`) - Remove icon, gray color ## Field Structure Each field in the form structure has the following properties: ```javascript { id: string, // Unique field identifier type: string, // Field type (see above) label: string, // Field label placeholder: string, // Field placeholder text required: boolean, // Whether field is required selectedIcon: string, // Icon identifier iconColor: string, // Icon color class options: array, // Options for choice fields validation: object, // Field-specific validation rules settings: object, // Field-specific settings // ... other field-specific properties } ``` ## Form Rules Form rules allow you to create conditional logic and validation: ```javascript { id: string, // Unique rule identifier name: string, // Rule name conditions: array, // Array of conditions actions: array, // Array of actions enabled: boolean // Whether rule is active } ``` ## LocalStorage Persistence The BuilderForm component automatically saves form data to localStorage using the `editFormId` as the key: ```javascript localStorage.setItem(`form_${editFormId}`, JSON.stringify(formData)); ``` ## Available Icons The library includes 30+ icons from react-icons/io5: - `person`, `location`, `call`, `mail`, `globe` - `document`, `reader`, `wallet`, `calendar`, `time` - `business`, `briefcase`, `home`, `car`, `airplane` - `train`, `bus`, `restaurant`, `shirt`, `ticket` - `id-card`, `checkmark`, `heart`, `music`, `camera` - `game`, `book`, `school`, `medical`, `fitness` - `palette` ## Examples ### Complete Example with Both Builder and Reader ```jsx import React, { useState } from "react"; import { BuilderForm, FormReader } from "@diludilshad/form-builder-library"; function FormBuilderApp() { const [formData, setFormData] = useState(null); const [isBuilderMode, setIsBuilderMode] = useState(true); const [formValues, setFormValues] = useState({}); const handleSaveForm = (formData) => { console.log("Form saved:", formData); setFormData(formData); }; const handleFormSubmit = (values) => { console.log("Form submitted:", values); setFormValues(values); alert("Form submitted successfully!"); }; return ( <div className="min-h-screen bg-gray-50"> <div className="max-w-7xl mx-auto py-8 px-4"> <div className="text-center mb-8"> <h1 className="text-3xl font-bold text-gray-900 mb-4"> Form Builder Library </h1> <div className="flex justify-center space-x-4 mb-8"> <button onClick={() => setIsBuilderMode(true)} className={`px-4 py-2 rounded-md font-medium ${ isBuilderMode ? "bg-blue-600 text-white" : "bg-white text-gray-700 border border-gray-300 hover:bg-gray-50" }`} > Builder Mode </button> <button onClick={() => setIsBuilderMode(false)} className={`px-4 py-2 rounded-md font-medium ${ !isBuilderMode ? "bg-blue-600 text-white" : "bg-white text-gray-700 border border-gray-300 hover:bg-gray-50" }`} > Reader Mode </button> </div> </div> {isBuilderMode ? ( <BuilderForm editFormId="example-form" form={ formData || { name: "", type: "", description: "", structure: [], formsRules: [], } } setForm={setFormData} onSave={handleSaveForm} /> ) : ( formData && ( <div className="max-w-2xl mx-auto"> <FormReader formData={formData} onSubmit={handleFormSubmit} onFieldChange={(fieldId, value) => console.log("Field changed:", fieldId, value) } /> </div> ) )} </div> </div> ); } export default FormBuilderApp; ``` ### Custom Form with Validation ```jsx import React from "react"; import { FormReader } from "@diludilshad/form-builder-library"; function CustomForm() { const formData = { name: "Registration Form", description: "Please complete your registration", structure: [ { id: "fullName", type: "single-line", label: "Full Name", placeholder: "Enter your full name", required: true, selectedIcon: "person", validation: { minLength: 2, maxLength: 50, pattern: "^[a-zA-Z\\s]+$", }, }, { id: "email", type: "email", label: "Email Address", placeholder: "Enter your email", required: true, selectedIcon: "mail", validation: { pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$", }, }, { id: "country", type: "dropdown", label: "Country", placeholder: "Select your country", required: true, selectedIcon: "location", options: [ { value: "us", label: "United States" }, { value: "uk", label: "United Kingdom" }, { value: "ca", label: "Canada" }, { value: "au", label: "Australia" }, ], }, ], }; const formRules = [ { id: "email-validation", name: "Email Validation", conditions: [ { fieldId: "email", operator: "contains", value: "@", }, ], actions: [ { type: "show_field", fieldId: "fullName", }, ], enabled: true, }, ]; return ( <FormReader formData={formData} formRules={formRules} onSubmit={(values) => { console.log("Registration submitted:", values); // Handle form submission }} onFieldChange={(fieldId, value) => { console.log(`${fieldId} changed to:`, value); }} customSubmitText="Register" customCancelText="Back" /> ); } ``` ## Development ### Prerequisites - Node.js >= 16.0.0 - React >= 18.0.0 - React DOM >= 18.0.0 ### Installation ```bash git clone https://github.com/diludilshad/form-builder-library.git cd form-builder-library npm install ``` ### Development Server ```bash npm run dev ``` ### Building ```bash npm run build ``` ### Type Checking ```bash npm run type-check ``` ### Linting ```bash npm run lint ``` ## Advanced Usage & Integration ### Backend Integration #### Saving Forms to Database ```jsx import React, { useState, useEffect } from "react"; import { BuilderForm } from "@diludilshad/form-builder-library"; function FormBuilderPage() { const [form, setForm] = useState(null); const [loading, setLoading] = useState(true); // Load existing form useEffect(() => { const loadForm = async () => { try { const response = await fetch("/api/forms/123"); const formData = await response.json(); setForm(formData); } catch (error) { console.error("Failed to load form:", error); setForm({ name: "", type: "", description: "", structure: [], formsRules: [], }); } finally { setLoading(false); } }; loadForm(); }, []); const handleSave = async (formData) => { try { const response = await fetch("/api/forms/123", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(formData), }); if (response.ok) { console.log("Form saved successfully!"); setForm(formData); } } catch (error) { console.error("Failed to save form:", error); } }; if (loading) return <div className="p-6">Loading...</div>; return ( <BuilderForm editFormId="form-123" form={form} setForm={setForm} onSave={handleSave} /> ); } ``` #### Processing Form Submissions ```jsx import React from "react"; import { FormReader } from "@diludilshad/form-builder-library"; function PublicForm({ formId }) { const [formData, setFormData] = useState(null); useEffect(() => { fetch(`/api/forms/${formId}/public`) .then((res) => res.json()) .then(setFormData); }, [formId]); const handleSubmit = async (values) => { try { const response = await fetch(`/api/forms/${formId}/submissions`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ formId, submittedAt: new Date().toISOString(), values, }), }); if (response.ok) { alert("Form submitted successfully!"); } } catch (error) { console.error("Submission failed:", error); alert("Failed to submit form. Please try again."); } }; if (!formData) return <div>Loading form...</div>; return ( <div className="max-w-2xl mx-auto p-6"> <FormReader formData={formData} onSubmit={handleSubmit} onFieldChange={(fieldId, value) => { // Optional: Auto-save draft localStorage.setItem( `draft_${formId}`, JSON.stringify({ ...JSON.parse(localStorage.getItem(`draft_${formId}`) || "{}"), [fieldId]: value, }) ); }} /> </div> ); } ``` ### Framework Integration #### Next.js Setup ```javascript // next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ["@diludilshad/form-builder-library"], }; module.exports = nextConfig; ``` ```javascript // tailwind.config.js /** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./pages/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}", // Include form builder library "./node_modules/@diludilshad/form-builder-library/**/*.{js,jsx}", ], theme: { extend: {}, }, plugins: [], }; ``` #### Vite Setup ```javascript // vite.config.js import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [react()], optimizeDeps: { include: ["@diludilshad/form-builder-library"], }, }); ``` ### Custom Styling & Theming #### Override Default Colors ```javascript // tailwind.config.js module.exports = { theme: { extend: { colors: { // Override primary colors used by form builder primary: { 50: "#fdf2f8", 500: "#ec4899", // Pink instead of blue 600: "#db2777", 700: "#be185d", }, }, }, }, }; ``` #### Custom CSS Classes ```css /* src/index.css */ @tailwind base; @tailwind components; @tailwind utilities; @layer components { /* Customize form builder styles */ .form-builder-container { @apply bg-gradient-to-br from-purple-50 to-pink-50; } .form-field-item { @apply border-purple-200 hover:border-purple-400; } .form-builder-button { @apply bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700; } } ``` ### State Management Integration #### Redux Integration ```jsx import { useSelector, useDispatch } from "react-redux"; import { BuilderForm } from "@diludilshad/form-builder-library"; import { updateForm, saveForm } from "./store/formSlice"; function ReduxFormBuilder() { const dispatch = useDispatch(); const form = useSelector((state) => state.forms.currentForm); return ( <BuilderForm editFormId="redux-form" form={form} setForm={(formData) => dispatch(updateForm(formData))} onSave={(formData) => dispatch(saveForm(formData))} /> ); } ``` ### Performance Optimization #### Lazy Loading ```jsx import React, { lazy, Suspense } from "react"; // Lazy load the form builder const BuilderForm = lazy(() => import("@diludilshad/form-builder-library").then((module) => ({ default: module.BuilderForm, })) ); function App() { return ( <Suspense fallback={<div className="p-6">Loading Form Builder...</div>}> <BuilderForm {...props} /> </Suspense> ); } ``` ### Troubleshooting #### Common Issues **1. Styles not applying:** ```bash # Make sure Tailwind is scanning the library # Check your tailwind.config.js includes: "./node_modules/@diludilshad/form-builder-library/**/*.{js,jsx}" ``` **2. TypeScript errors:** ```javascript // Add to your tsconfig.json { "compilerOptions": { "moduleResolution": "node", "skipLibCheck": true } } ``` **3. Build errors in production:** ```javascript // For Webpack-based builds, add to webpack.config.js module.exports = { resolve: { alias: { "@diludilshad/form-builder-library": require.resolve( "@diludilshad/form-builder-library" ), }, }, }; ``` ## Browser Support - Chrome >= 88 - Firefox >= 85 - Safari >= 14 - Edge >= 88 ## License MIT License - see [LICENSE](LICENSE) file for details. ## Contributing 1. Fork the repository 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 4. Push to the branch (`git push origin feature/amazing-feature`) 5. Open a Pull Request ## Support - 📧 Email: diludilshad@example.com - 🐛 Issues: [GitHub Issues](https://github.com/diludilshad/form-builder-library/issues) - 📖 Documentation: [GitHub Wiki](https://github.com/diludilshad/form-builder-library/wiki) ## Changelog ### v1.0.5 - Added 30+ field types - Improved drag and drop functionality - Enhanced form validation - Added icon support - Bundled Tailwind CSS - Zero additional dependencies --- **Made with ❤️ by Dilshad**