UNPKG

@flowcsolutions/react-form-builder

Version:

A powerful, responsive form builder built with React, TypeScript, HeroUI, and TailwindCSS

1,531 lines (1,530 loc) 201 kB
import { jsx as e, jsxs as r, Fragment as j } from "react/jsx-runtime"; import oe, { createContext as rt, useContext as at, useReducer as st, useRef as nt, useState as $, useEffect as lt, useCallback as it } from "react"; import { v4 as Ve } from "uuid"; import { useDroppable as ot, useDraggable as ct, DndContext as dt } from "@dnd-kit/core"; import { useSortable as pt, SortableContext as mt, verticalListSortingStrategy as ut } from "@dnd-kit/sortable"; import { Button as F, Input as A, Divider as ne, Card as z, CardBody as T, Switch as U, Checkbox as pe, RadioGroup as Me, Radio as je, Select as V, SelectItem as b, Autocomplete as Be, AutocompleteItem as _e, Textarea as he, TimeInput as ht, DateInput as Oe, ButtonGroup as ve, CardHeader as R, Chip as _, Slider as bt, useDisclosure as X, Modal as Z, ModalContent as ee, ModalHeader as te, ModalBody as re, ModalFooter as be, Accordion as gt, AccordionItem as xt, Tabs as we, Tab as H, Navbar as yt, NavbarBrand as Nt, NavbarContent as De, NavbarItem as ft, Dropdown as vt, DropdownTrigger as wt, DropdownMenu as Ct, DropdownItem as ge, Progress as Ft, Spacer as We, Snippet as G, Code as k, Link as ze, HeroUIProvider as St } from "@heroui/react"; import * as Te from "lucide-react"; import { RotateCcw as At, Check as le, Star as qe, Upload as Y, Smartphone as Et, Tablet as Ot, Monitor as Dt, GripVertical as zt, Edit as Tt, Copy as Ce, Trash2 as Fe, Plus as ae, Layout as Pt, Settings as se, Rows4 as It, MoreVertical as Rt, Download as Ue, FileJson as $t, CheckCircle as xe, AlertCircle as Je, FileText as Lt, Gift as kt, Rocket as me, Zap as ue, Clock as Vt, Target as Mt, Sparkles as ye, BookOpen as He, Code as Pe, Terminal as jt, Users as Bt, ExternalLink as Ie, Github as _t, Package as Ke, Shield as Re, Palette as Wt } from "lucide-react"; import { CSS as qt } from "@dnd-kit/utilities"; function Ut(t, s) { if (t.name && t.name.trim()) return t.name; const o = t.type.replace(/[-_]/g, "_").toLowerCase(), l = s.map((g) => g.name).filter(Boolean); if (!l.includes(o)) return o; let i = 2, n = `${o}${i}`; for (; l.includes(n); ) i++, n = `${o}${i}`; return n; } const Jt = { currentForm: { id: Ve(), title: "New Form", description: "", fields: [], settings: { submitButtonText: "Submit", allowMultipleSubmissions: !0, requireAuth: !1, captchaEnabled: !1, theme: "auto" } }, selectedFieldId: null, previewMode: !1, deviceView: "desktop" }; function Ht(t, s) { switch (s.type) { case "SET_FORM": return { ...t, currentForm: s.payload, selectedFieldId: null }; case "ADD_FIELD": const o = { ...s.payload, name: Ut(s.payload, t.currentForm.fields) }; return { ...t, currentForm: { ...t.currentForm, fields: [...t.currentForm.fields, o] }, selectedFieldId: s.payload.id }; case "UPDATE_FIELD": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, ...s.payload.updates, // Deep merge nested objects to prevent component remounting properties: s.payload.updates.properties ? { ...n.properties, ...s.payload.updates.properties } : n.properties, advanced: s.payload.updates.advanced ? { ...n.advanced, ...s.payload.updates.advanced } : n.advanced, custom: s.payload.updates.custom ? { ...n.custom, ...s.payload.updates.custom } : n.custom, events: s.payload.updates.events ? { ...n.events, ...s.payload.updates.events } : n.events, schema: s.payload.updates.schema ? { ...n.schema, ...s.payload.updates.schema } : n.schema, layout: s.payload.updates.layout ? { ...n.layout, ...s.payload.updates.layout } : n.layout } : n ) } }; case "UPDATE_FIELD_PROPERTIES": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, properties: { ...n.properties, ...s.payload.properties } } : n ) } }; case "UPDATE_FIELD_ADVANCED": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, advanced: { ...n.advanced, ...s.payload.advanced } } : n ) } }; case "UPDATE_FIELD_CUSTOM": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, custom: { ...n.custom, ...s.payload.custom } } : n ) } }; case "UPDATE_FIELD_EVENTS": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, events: { ...n.events, ...s.payload.events } } : n ) } }; case "UPDATE_FIELD_SCHEMA": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, schema: { ...n.schema, ...s.payload.schema } } : n ) } }; case "UPDATE_FIELD_LAYOUT": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.map( (n) => n.id === s.payload.id ? { ...n, layout: { ...n.layout, ...s.payload.layout } } : n ) } }; case "DELETE_FIELD": return { ...t, currentForm: { ...t.currentForm, fields: t.currentForm.fields.filter((n) => n.id !== s.payload) }, selectedFieldId: t.selectedFieldId === s.payload ? null : t.selectedFieldId }; case "REORDER_FIELDS": const l = [...t.currentForm.fields], [i] = l.splice(s.payload.oldIndex, 1); return l.splice(s.payload.newIndex, 0, i), { ...t, currentForm: { ...t.currentForm, fields: l } }; case "SELECT_FIELD": return { ...t, selectedFieldId: s.payload }; case "SET_PREVIEW_MODE": return { ...t, previewMode: s.payload, selectedFieldId: s.payload ? null : t.selectedFieldId }; case "SET_DEVICE_VIEW": return { ...t, deviceView: s.payload }; case "UPDATE_FORM_SETTINGS": return { ...t, currentForm: { ...t.currentForm, settings: { ...t.currentForm.settings, ...s.payload } } }; case "UPDATE_FORM_META": return { ...t, currentForm: { ...t.currentForm, title: s.payload.title ?? t.currentForm.title, description: s.payload.description ?? t.currentForm.description } }; default: return t; } } const Ge = rt(void 0); function Kt({ children: t }) { const [s, o] = st(Ht, Jt), l = { setForm: (i) => o({ type: "SET_FORM", payload: i }), addField: (i) => o({ type: "ADD_FIELD", payload: i }), updateField: (i, n) => o({ type: "UPDATE_FIELD", payload: { id: i, updates: n } }), updateFieldProperties: (i, n) => o({ type: "UPDATE_FIELD_PROPERTIES", payload: { id: i, properties: n } }), updateFieldAdvanced: (i, n) => o({ type: "UPDATE_FIELD_ADVANCED", payload: { id: i, advanced: n } }), updateFieldCustom: (i, n) => o({ type: "UPDATE_FIELD_CUSTOM", payload: { id: i, custom: n } }), updateFieldEvents: (i, n) => o({ type: "UPDATE_FIELD_EVENTS", payload: { id: i, events: n } }), updateFieldSchema: (i, n) => o({ type: "UPDATE_FIELD_SCHEMA", payload: { id: i, schema: n } }), updateFieldLayout: (i, n) => o({ type: "UPDATE_FIELD_LAYOUT", payload: { id: i, layout: n } }), deleteField: (i) => o({ type: "DELETE_FIELD", payload: i }), reorderFields: (i, n) => o({ type: "REORDER_FIELDS", payload: { oldIndex: i, newIndex: n } }), selectField: (i) => o({ type: "SELECT_FIELD", payload: i }), setPreviewMode: (i) => o({ type: "SET_PREVIEW_MODE", payload: i }), setDeviceView: (i) => o({ type: "SET_DEVICE_VIEW", payload: i }), updateFormSettings: (i) => o({ type: "UPDATE_FORM_SETTINGS", payload: i }), updateFormMeta: (i) => o({ type: "UPDATE_FORM_META", payload: i }) }; return /* @__PURE__ */ e(Ge.Provider, { value: { state: s, dispatch: o, actions: l }, children: t }); } function J() { const t = at(Ge); if (t === void 0) throw new Error("useFormBuilder must be used within a FormBuilderProvider"); return t; } function C(t) { const s = { id: Ve(), type: t, label: Gt(t), required: !1, properties: {}, advanced: { valued: !0, valueType: "string", dataBindingType: "twoWay", calculable: !1, localizable: !1, readOnly: !1, disabled: !1, asyncValidation: !1, deferFieldCalculation: !1 }, layout: { columnSpan: 12, gridClass: "col-span-12" }, custom: { cssClasses: [], dataAttributes: {} }, events: {}, schema: { componentKind: "component", category: Yt(t), typeName: t, icon: Qt(t), nestingLevel: 0, builderOnly: !1 } }; switch (t) { case "text": case "email": case "password": case "phone": case "url": return { ...s, placeholder: `Enter ${s.label.toLowerCase()}`, properties: { ...s.properties, width: "full", maxLength: 255, colorVariant: "default", size: "md", borderRadius: "md" } }; case "textarea": return { ...s, placeholder: `Enter ${s.label.toLowerCase()}`, properties: { ...s.properties, width: "full", rows: 4, maxLength: 1e3, colorVariant: "default", size: "md", borderRadius: "md" } }; case "number": case "number-format": return { ...s, placeholder: "Enter number", properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valueType: "number" } }; case "select": case "radio": case "checkbox": case "multiselect": case "autocomplete": return { ...s, options: [ { label: "Option 1", value: "option1" }, { label: "Option 2", value: "option2" } ], properties: { ...s.properties, width: "full", colorVariant: "default", size: "md", borderRadius: "md", orientation: t === "radio" || t === "checkbox" ? "vertical" : void 0, componentAlignment: t === "radio" || t === "checkbox" ? "left" : void 0 }, advanced: { ...s.advanced, valueType: t === "multiselect" || t === "checkbox" ? "array" : "string" } }; case "date": case "datetime": case "time": case "calendar": return { ...s, properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valueType: "date" } }; case "file": return { ...s, properties: { ...s.properties, width: "full", accept: "", multiple: !1 }, advanced: { ...s.advanced, valueType: "array" } }; case "rating": case "range": return { ...s, properties: { ...s.properties, width: "full", max: 5, componentAlignment: t === "rating" ? "left" : void 0 }, advanced: { ...s.advanced, valueType: "number" } }; case "switch": return { ...s, properties: { ...s.properties, width: "full", size: "md", componentAlignment: "left" }, advanced: { ...s.advanced, valueType: "boolean" } }; case "signature": return { ...s, properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valueType: "string" } }; // Static content fields case "header": return { ...s, label: "Header", properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valued: !1 } }; case "paragraph": case "label": case "message": return { ...s, label: "Paragraph", properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valued: !1 } }; case "image": return { ...s, label: "Image", properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valued: !1 } }; case "button": return { ...s, label: "Button", properties: { ...s.properties, width: "full", colorVariant: "primary", size: "md", borderRadius: "md", variant: "solid" }, advanced: { ...s.advanced, valued: !1 } }; // Structure fields case "section": case "pagebreak": return { ...s, label: "Section", properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valued: !1 } }; case "container": case "card": return { ...s, label: "Container", properties: { ...s.properties, width: "full" }, advanced: { ...s.advanced, valued: !1 } }; default: return s; } } function Gt(t) { return { // Input fields text: "Text Input", email: "Email", password: "Password", number: "Number", phone: "Phone", url: "URL", textarea: "Text Area", // Selection fields select: "Select", multiselect: "Multi Select", radio: "Radio Group", checkbox: "Checkbox Group", switch: "Switch", autocomplete: "Auto Complete", search: "Search", // Date/Time fields date: "Date", datetime: "Date Time", time: "Time", calendar: "Calendar", // Special fields file: "File Upload", rating: "Rating", signature: "Signature", range: "Range Slider", "rich-text": "Rich Text", "number-format": "Formatted Number", "pattern-format": "Pattern Format", // Static content button: "Button", label: "Label", header: "Header", paragraph: "Paragraph", image: "Image", message: "Message", "progress-line": "Progress Line", "progress-circle": "Progress Circle", tooltip: "Tooltip", "qr-code": "QR Code", html: "HTML", // Structure container: "Container", card: "Card", tab: "Tab", breadcrumb: "Breadcrumb", section: "Section", pagebreak: "Page Break", repeater: "Repeater", // Template fields slot: "Slot", template: "Template", // Error fields "error-message": "Error Message" }[t] || "Field"; } function Yt(t) { return { // Input fields text: "fields", email: "fields", password: "fields", number: "fields", phone: "fields", url: "fields", textarea: "fields", // Selection fields select: "fields", multiselect: "fields", radio: "fields", checkbox: "fields", switch: "fields", autocomplete: "fields", search: "fields", // Date/Time fields date: "fields", datetime: "fields", time: "fields", calendar: "fields", // Special fields file: "fields", rating: "fields", signature: "fields", range: "fields", "rich-text": "fields", "number-format": "fields", "pattern-format": "fields", // Static content button: "static", label: "static", header: "static", paragraph: "static", image: "static", message: "static", "progress-line": "static", "progress-circle": "static", tooltip: "static", "qr-code": "static", html: "static", // Structure container: "structure", card: "structure", tab: "structure", breadcrumb: "structure", section: "structure", pagebreak: "structure", repeater: "structure", // Template fields slot: "templates", template: "templates", // Error fields "error-message": "error" }[t] || "fields"; } function Qt(t) { return { // Input fields text: "Type", email: "Mail", password: "Key", number: "Hash", phone: "Phone", url: "Link", textarea: "FileText", // Selection fields select: "ChevronDown", multiselect: "List", radio: "Circle", checkbox: "Square", switch: "ToggleLeft", autocomplete: "Search", search: "Search", // Date/Time fields date: "Calendar", datetime: "Clock", time: "Clock", calendar: "Calendar", // Special fields file: "Upload", rating: "Star", signature: "PenTool", range: "Sliders", "rich-text": "FileText", "number-format": "Hash", "pattern-format": "Hash", // Static content button: "MousePointer", label: "Tag", header: "Heading", paragraph: "Type", image: "Image", message: "MessageSquare", "progress-line": "TrendingUp", "progress-circle": "Circle", tooltip: "Info", "qr-code": "QrCode", html: "Code", // Structure container: "Box", card: "Square", tab: "Tabs", breadcrumb: "ChevronRight", section: "Layout", pagebreak: "Scissors", repeater: "Copy", // Template fields slot: "Grid", template: "FileTemplate", // Error fields "error-message": "AlertCircle" }[t] || "Square"; } const Rr = { // Input fields text: C("text"), email: C("email"), password: C("password"), number: C("number"), phone: C("phone"), url: C("url"), textarea: C("textarea"), // Selection fields select: C("select"), multiselect: C("multiselect"), radio: C("radio"), checkbox: C("checkbox"), switch: C("switch"), autocomplete: C("autocomplete"), search: C("search"), // Date/Time fields date: C("date"), datetime: C("datetime"), time: C("time"), calendar: C("calendar"), // Special fields file: C("file"), rating: C("rating"), signature: C("signature"), range: C("range"), "rich-text": C("rich-text"), "number-format": C("number-format"), "pattern-format": C("pattern-format"), // Static content button: C("button"), label: C("label"), header: C("header"), paragraph: C("paragraph"), image: C("image"), message: C("message"), "progress-line": C("progress-line"), "progress-circle": C("progress-circle"), tooltip: C("tooltip"), "qr-code": C("qr-code"), html: C("html"), // Structure container: C("container"), card: C("card"), tab: C("tab"), breadcrumb: C("breadcrumb"), section: C("section"), pagebreak: C("pagebreak"), repeater: C("repeater"), // Template fields slot: C("slot"), template: C("template"), // Error fields "error-message": C("error-message") }, Xt = [ // Most common input fields { id: "text", type: "text", label: "Text Input", icon: "Type", category: "fields" }, { id: "email", type: "email", label: "Email", icon: "Mail", category: "fields" }, { id: "password", type: "password", label: "Password", icon: "Key", category: "fields" }, { id: "number", type: "number", label: "Number", icon: "Hash", category: "fields" }, { id: "phone", type: "phone", label: "Phone", icon: "Phone", category: "fields" }, { id: "textarea", type: "textarea", label: "Text Area", icon: "FileText", category: "fields" }, // Selection fields { id: "select", type: "select", label: "Select", icon: "ChevronDown", category: "fields" }, { id: "autocomplete", type: "autocomplete", label: "Auto Complete", icon: "Search", category: "fields" }, { id: "radio", type: "radio", label: "Radio Group", icon: "Circle", category: "fields" }, { id: "checkbox", type: "checkbox", label: "Checkbox Group", icon: "Square", category: "fields" }, { id: "switch", type: "switch", label: "Switch", icon: "ToggleLeft", category: "fields" }, // Date/Time fields { id: "date", type: "date", label: "Date", icon: "Calendar", category: "fields" }, { id: "time", type: "time", label: "Time", icon: "Clock", category: "fields" }, // Special fields { id: "file", type: "file", label: "File Upload", icon: "Upload", category: "fields" }, { id: "rating", type: "rating", label: "Rating", icon: "Star", category: "fields" }, // Static content { id: "header", type: "header", label: "Header", icon: "Heading", category: "static" }, { id: "paragraph", type: "paragraph", label: "Paragraph", icon: "Type", category: "static" }, { id: "image", type: "image", label: "Image", icon: "Image", category: "static" }, { id: "button", type: "button", label: "Button", icon: "MousePointer", category: "static" }, // Structure { id: "section", type: "section", label: "Section", icon: "Layout", category: "structure" }, { id: "container", type: "container", label: "Container", icon: "Box", category: "structure" }, { id: "card", type: "card", label: "Card", icon: "Square", category: "structure" } ], Zt = [ { id: "fields", label: "Form Fields", description: "Input and selection fields" }, { id: "static", label: "Static Content", description: "Text, images, and layout elements" }, { id: "structure", label: "Structure", description: "Containers and layout components" }, { id: "templates", label: "Templates", description: "Pre-built field combinations" }, { id: "error", label: "Error Handling", description: "Error display components" } ]; function Se(t, s = !1) { const o = tr(t.properties?.borderRadius), l = rr(t), i = Ye(t, s), n = t.properties?.customClasses || "", g = t.custom?.cssClasses?.join(" ") || "", m = [n, g].filter(Boolean).join(" "), S = er(m), c = t.properties?.classNames || {}, p = (d) => d ? (d.includes("text-") || d.includes("bg-") || d.includes("border-"), d) : ""; return { // Base wrapper/container classes - for outer spacing and visibility base: [ l.margin, l.padding, s ? "" : i.visibility, // Only apply responsive hiding in preview/renderer S.wrapper, c.base || "" ].filter(Boolean).join(" "), // Label classes - ensure custom classes take precedence label: [ "text-left", // Ensure labels are left-aligned by default S.label, p(c.label || "") ].filter(Boolean).join(" "), // Input wrapper classes - for border, shadow, background, etc. inputWrapper: [ o, S.inputWrapper, p(c.inputWrapper || "") ].filter(Boolean).join(" "), // Inner wrapper classes innerWrapper: [ p(c.innerWrapper || "") ].filter(Boolean).join(" "), // Main wrapper classes mainWrapper: [ p(c.mainWrapper || "") ].filter(Boolean).join(" "), // Input field classes - ensure custom text styles take precedence input: [ t.properties?.alignment || "", S.input, p(c.input || "") ].filter(Boolean).join(" "), // Clear button classes clearButton: [ p(c.clearButton || "") ].filter(Boolean).join(" "), // Helper wrapper classes helperWrapper: [ p(c.helperWrapper || "") ].filter(Boolean).join(" "), // Description classes description: [ "text-left", // Ensure description text is left-aligned S.description, p(c.description || "") ].filter(Boolean).join(" "), // Error message classes errorMessage: [ p(c.errorMessage || "") ].filter(Boolean).join(" ") }; } function er(t) { if (!t.trim()) return { wrapper: "", inputWrapper: "", input: "", label: "", description: "" }; const s = t.trim().split(/\s+/), o = { wrapper: [], inputWrapper: [], input: [], label: [], description: [] }; return s.forEach((l) => { l.match(/^(border|shadow|bg-|rounded|ring)/) ? o.inputWrapper.push(l) : l.match(/^(text-|font-|placeholder|italic|underline)/) ? o.input.push(l) : l.match(/^(m[tlrb]?-|p[tlrb]?-|w-|h-|max-|min-|flex|grid|col-|row-|gap-|space-|justify-|items-|self-)/) ? o.wrapper.push(l) : o.inputWrapper.push(l); }), { wrapper: o.wrapper.join(" "), inputWrapper: o.inputWrapper.join(" "), input: o.input.join(" "), label: o.label.join(" "), description: o.description.join(" ") }; } function tr(t) { switch (t) { case "none": return "rounded-none"; case "small": return "rounded-sm"; case "default": return "rounded-md"; case "large": return "rounded-lg"; case "full": return "rounded-full"; default: return ""; } } function rr(t) { const s = t.properties?.marginTop, o = t.properties?.marginBottom, l = t.properties?.padding; return { margin: [ s || "", o || "" ].filter(Boolean).join(" "), padding: l || "" }; } function Ye(t, s = !1) { const o = []; return s || (t.properties?.hideOnMobile && o.push("hidden sm:block"), t.properties?.hideOnTablet && o.push("sm:hidden lg:block"), t.properties?.hideOnDesktop && o.push("lg:hidden")), { visibility: o.join(" ") }; } function $r(t) { return Se(t).base; } function Ne(t, s = !1) { const o = []; t.layout?.gridClass && o.push(t.layout.gridClass); const l = Ye(t, s); return l.visibility && o.push(l.visibility), o.join(" "); } function Qe({ value: t, onChange: s, width: o = 400, height: l = 200, disabled: i = !1, className: n = "" }) { const g = nt(null), [m, S] = $(!1), [c, p] = $(!0), [a, d] = $({ x: 0, y: 0 }); lt(() => { const y = g.current; if (!y) return; const x = y.getContext("2d"); if (x && (y.width = o, y.height = l, x.lineCap = "round", x.lineJoin = "round", x.strokeStyle = "#000000", x.lineWidth = 2, x.fillStyle = "#ffffff", x.fillRect(0, 0, o, l), t && t.startsWith("data:image"))) { const E = new Image(); E.onload = () => { x.clearRect(0, 0, o, l), x.fillStyle = "#ffffff", x.fillRect(0, 0, o, l), x.drawImage(E, 0, 0, o, l), p(!1); }, E.src = t; } }, [o, l, t]); const u = (y) => { const x = g.current; if (!x) return { x: 0, y: 0 }; const E = x.getBoundingClientRect(), I = x.width / E.width, M = x.height / E.height; if ("touches" in y) { const B = y.touches[0]; return { x: (B.clientX - E.left) * I, y: (B.clientY - E.top) * M }; } else return { x: (y.clientX - E.left) * I, y: (y.clientY - E.top) * M }; }, v = (y) => { if (i) return; y.preventDefault(); const x = u(y); S(!0), d(x), p(!1); }, h = (y) => { if (!m || i) return; y.preventDefault(); const x = g.current, E = x?.getContext("2d"); if (!x || !E) return; const I = u(y); E.beginPath(), E.moveTo(a.x, a.y), E.lineTo(I.x, I.y), E.stroke(), d(I); }, w = () => { m && (S(!1), D()); }, D = () => { const y = g.current; if (!y) return; const x = y.toDataURL("image/png"); s?.(x); }, L = () => { const y = g.current, x = y?.getContext("2d"); !y || !x || (x.fillStyle = "#ffffff", x.fillRect(0, 0, o, l), p(!0), s?.("")); }; return /* @__PURE__ */ r("div", { className: `signature-pad ${n}`, children: [ /* @__PURE__ */ e("div", { className: "border-2 border-default-300 rounded-lg overflow-hidden bg-white", children: /* @__PURE__ */ e( "canvas", { ref: g, className: "block cursor-crosshair touch-none", style: { width: "100%", height: "auto", maxWidth: `${o}px` }, onMouseDown: v, onMouseMove: h, onMouseUp: w, onMouseLeave: w, onTouchStart: v, onTouchMove: h, onTouchEnd: w } ) }), /* @__PURE__ */ r("div", { className: "flex justify-between items-center mt-3", children: [ /* @__PURE__ */ e("div", { className: "text-xs text-default-500", children: c ? "Sign above" : "Signature captured" }), /* @__PURE__ */ r("div", { className: "flex gap-2", children: [ /* @__PURE__ */ e( F, { size: "sm", variant: "flat", color: "warning", startContent: /* @__PURE__ */ e(At, { size: 16 }), onPress: L, isDisabled: i || c, children: "Clear" } ), !c && /* @__PURE__ */ e( F, { size: "sm", color: "success", variant: "flat", startContent: /* @__PURE__ */ e(le, { size: 16 }), onPress: D, isDisabled: i, children: "Save" } ) ] }) ] }) ] }); } function fe({ field: t, value: s, onChange: o }) { const l = (h) => { o && o(h); }; if (t.advanced?.hidden || t.properties?.hidden) return null; const i = Se(t, !0), n = i.base, m = (() => { const h = []; return t.properties?.hideOnMobile && h.push("Hidden on Mobile"), t.properties?.hideOnTablet && h.push("Hidden on Tablet"), t.properties?.hideOnDesktop && h.push("Hidden on Desktop"), h; })(), S = ({ children: h }) => m.length === 0 ? /* @__PURE__ */ e(j, { children: h }) : /* @__PURE__ */ r("div", { className: "relative", children: [ h, /* @__PURE__ */ e("div", { className: "absolute top-0 right-0 flex items-center gap-1 -mt-2 -mr-2 z-10", children: m.map((w) => /* @__PURE__ */ e( "span", { className: "text-danger-700 text-xs px-1.5 py-0.5 rounded-full ", title: w, children: w.includes("Mobile") ? /* @__PURE__ */ e(Et, { size: 15 }) : w.includes("Tablet") ? /* @__PURE__ */ e(Ot, { size: 15 }) : /* @__PURE__ */ e(Dt, { size: 15 }) }, w )) }) ] }), c = (h) => /* @__PURE__ */ e(S, { children: h }), p = { label: t.label, placeholder: t.placeholder, isRequired: t.required, description: t.properties?.description, isDisabled: t.advanced?.disabled || t.properties?.disabled, isReadOnly: t.advanced?.readOnly || t.properties?.readonly, size: t.properties?.size, color: t.properties?.colorVariant, variant: t.properties?.variant, radius: t.properties?.borderRadius, classNames: { base: i.base, label: i.label, inputWrapper: i.inputWrapper, innerWrapper: i.innerWrapper, mainWrapper: i.mainWrapper, input: i.input, clearButton: i.clearButton, helperWrapper: i.helperWrapper, description: i.description, errorMessage: i.errorMessage } }, d = (() => { const h = {}, w = t.custom?.dataAttributes || {}; return Object.entries(w).forEach(([D, L]) => { h[`data-${D}`] = L; }), t.custom?.role && (h.role = t.custom.role), t.custom?.tabIndex !== void 0 && (h.tabIndex = t.custom.tabIndex), t.properties?.ariaLabel && (h["aria-label"] = t.properties.ariaLabel), t.type === "number" && (t.properties?.min !== void 0 && (h.min = t.properties.min), t.properties?.max !== void 0 && (h.max = t.properties.max), t.properties?.step !== void 0 && (h.step = t.properties.step)), h; })(), v = (() => { if (t.properties?.showCharacterCount && (t.type === "text" || t.type === "textarea")) { const h = s ? s.length : 0, w = t.properties?.maxLength; return w ? `${h}/${w}` : `${h}`; } return null; })(); switch (t.type) { case "text": return /* @__PURE__ */ r(S, { children: [ /* @__PURE__ */ e( A, { ...p, ...d, type: "text", value: s || "", onValueChange: (N) => l(N), maxLength: t.properties?.maxLength }, `text-${t.id}` ), v && /* @__PURE__ */ e("div", { className: "text-xs text-default-500 mt-1 text-right", children: v }) ] }); case "email": return /* @__PURE__ */ e(S, { children: /* @__PURE__ */ e( A, { ...p, ...d, type: "email", value: s || "", onValueChange: (N) => l(N) }, `email-${t.id}` ) }); case "password": return /* @__PURE__ */ e(S, { children: /* @__PURE__ */ e( A, { ...p, ...d, type: "password", value: s || "", onValueChange: (N) => l(N) }, `password-${t.id}` ) }); case "number": return c( /* @__PURE__ */ e( A, { ...p, ...d, type: "number", value: s || "", onValueChange: (N) => { const P = parseFloat(N); !isNaN(P) && (t.properties?.min !== void 0 && P < t.properties.min || t.properties?.max !== void 0 && P > t.properties.max) || l(N); } }, `number-${t.id}` ) ); case "date": return c( /* @__PURE__ */ e( Oe, { ...p, value: s || null, onChange: (N) => { l(N ? N.toString() : ""); } }, `date-${t.id}` ) ); case "datetime": return /* @__PURE__ */ e( Oe, { ...p, granularity: "second", value: s || null, onChange: (N) => { l(N ? N.toString() : ""); } }, `datetime-${t.id}` ); case "time": return /* @__PURE__ */ e( ht, { ...p, value: s || null, onChange: (N) => { l(N ? N.toString() : ""); } }, `time-${t.id}` ); case "textarea": return c( /* @__PURE__ */ r(j, { children: [ /* @__PURE__ */ e( he, { ...p, ...d, value: s || "", onValueChange: (N) => l(N), rows: t.properties?.rows || 4, maxLength: t.properties?.maxLength }, `textarea-${t.id}` ), v && /* @__PURE__ */ e("div", { className: "text-xs text-default-500 mt-1 text-right", children: v }) ] }) ); case "select": return c( /* @__PURE__ */ e( V, { ...p, ...d, selectedKeys: s ? [s] : [], onSelectionChange: (N) => { const P = Array.from(N)[0]; l(P); }, children: t.options?.map((N) => /* @__PURE__ */ e(b, { children: N.label }, N.value)) || [] } ) ); case "autocomplete": return c( /* @__PURE__ */ e( Be, { ...p, ...d, selectedKey: s || "", onSelectionChange: (N) => { l(N); }, allowsCustomValue: !0, children: t.options?.map((N) => /* @__PURE__ */ e(_e, { children: N.label }, N.value)) || [] } ) ); case "multiselect": return /* @__PURE__ */ e( V, { ...p, selectionMode: "multiple", selectedKeys: s || [], onSelectionChange: (N) => { l(Array.from(N)); }, children: t.options?.map((N) => /* @__PURE__ */ e(b, { children: N.label }, N.value)) || [] } ); case "radio": const h = t.properties?.orientation || "vertical", w = t.properties?.componentAlignment || "left", D = h === "horizontal" ? w === "center" ? "justify-center" : w === "right" ? "justify-end" : "justify-start" : w === "center" ? "items-center" : w === "right" ? "items-end" : "items-start", L = w === "center" ? "text-center" : w === "right" ? "text-right" : "text-left"; return /* @__PURE__ */ e("div", { className: w === "center" ? "flex flex-col items-center" : w === "right" ? "flex flex-col items-end" : "flex flex-col items-start", children: /* @__PURE__ */ e( Me, { label: t.label, description: t.properties?.description, isRequired: t.required, value: s || "", onValueChange: l, size: t.properties?.size, color: t.properties?.colorVariant, isDisabled: t.advanced?.disabled || t.properties?.disabled, isReadOnly: t.advanced?.readOnly || t.properties?.readonly, orientation: h, classNames: { base: i.base, label: `${i.label} ${L}`, wrapper: `${i.inputWrapper} ${D}`, description: `${i.description} ${L}` }, children: t.options?.map((N) => /* @__PURE__ */ e(je, { value: N.value, children: N.label }, N.value)) || [] } ) }); case "checkbox": if (t.options && t.options.length > 1) { const N = t.properties?.orientation || "vertical", P = t.properties?.componentAlignment || "left", W = N === "horizontal" ? "flex flex-wrap gap-4" : "flex flex-col space-y-2", f = N === "horizontal" ? P === "center" ? "justify-center" : P === "right" ? "justify-end" : "justify-start" : P === "center" ? "items-center" : P === "right" ? "items-end" : "items-start", O = P === "center" ? "text-center" : P === "right" ? "text-right" : "text-left"; return /* @__PURE__ */ r("div", { className: `space-y-2 ${n} ${P === "center" ? "flex flex-col items-center" : P === "right" ? "flex flex-col items-end" : "flex flex-col items-start"}`, children: [ /* @__PURE__ */ r("label", { className: `text-sm font-medium ${O}`, children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger", children: "*" }) ] }), t.properties?.description && /* @__PURE__ */ e("p", { className: `text-xs text-default-500 ${O}`, children: t.properties?.description }), /* @__PURE__ */ e("div", { className: `${W} ${f}`, children: t.options.map((q) => /* @__PURE__ */ e( pe, { value: q.value, isSelected: s?.includes?.(q.value) || !1, onValueChange: (ce) => { const de = s || []; l(ce ? [...de, q.value] : de.filter((K) => K !== q.value)); }, size: t.properties?.size, color: t.properties?.colorVariant, radius: t.properties?.borderRadius, isDisabled: t.advanced?.disabled || t.properties?.disabled, isReadOnly: t.advanced?.readOnly || t.properties?.readonly, children: q.label }, q.value )) }) ] }); } else { const N = t.properties?.componentAlignment || "left", P = N === "center" ? "flex justify-center" : N === "right" ? "flex justify-end" : "flex justify-start", W = N === "center" ? "text-center" : N === "right" ? "text-right" : "text-left"; return /* @__PURE__ */ r("div", { className: `${n} ${P} flex-col items-start`, children: [ /* @__PURE__ */ r("label", { className: `text-sm font-medium ${W} block mb-2`, children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger", children: "*" }) ] }), t.properties?.description && /* @__PURE__ */ e("p", { className: `text-xs text-default-500 mb-2 ${W}`, children: t.properties?.description }), /* @__PURE__ */ e( pe, { isSelected: s || !1, onValueChange: l, size: t.properties?.size, color: t.properties?.colorVariant, radius: t.properties?.borderRadius, isDisabled: t.advanced?.disabled || t.properties?.disabled, isReadOnly: t.advanced?.readOnly || t.properties?.readonly, children: "Check to confirm" } ) ] }); } case "switch": const x = t.properties?.componentAlignment || "left", E = x === "center" ? "flex justify-center" : x === "right" ? "flex justify-end" : "flex justify-start"; return /* @__PURE__ */ r("div", { className: `space-y-2 ${n}`, children: [ /* @__PURE__ */ e("div", { className: E, children: /* @__PURE__ */ r( U, { isSelected: s || !1, onValueChange: l, size: t.properties?.size, color: t.properties?.colorVariant, isDisabled: t.advanced?.disabled || t.properties?.disabled, isReadOnly: t.advanced?.readOnly || t.properties?.readonly, children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger ml-1", children: "*" }) ] } ) }), t.properties?.description && /* @__PURE__ */ e("div", { className: E, children: /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: t.properties?.description }) }) ] }); case "file": return /* @__PURE__ */ r("div", { className: `space-y-2 ${n}`, children: [ /* @__PURE__ */ r("label", { className: "text-sm font-medium", children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger", children: "*" }) ] }), t.properties?.description && /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: t.properties?.description }), /* @__PURE__ */ r("div", { className: "border-2 border-dashed border-default-300 rounded-lg p-6 text-center hover:border-default-400 transition-colors relative", children: [ /* @__PURE__ */ e(Y, { className: "mx-auto text-default-400 mb-2", size: 32 }), /* @__PURE__ */ e("p", { className: "text-sm text-default-600 mb-2", children: "Click to upload or drag and drop" }), /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: t.properties?.accept || "All file types" }), /* @__PURE__ */ e( "input", { type: "file", accept: t.properties?.accept, multiple: t.properties?.multiple, className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer", onChange: (N) => { const P = Array.from(N.target.files || []); l(t.properties?.multiple ? P : P[0]); } } ) ] }) ] }); case "rating": const I = t.properties?.max || 5, M = t.properties?.componentAlignment || "left", B = M === "center" ? "flex justify-center" : M === "right" ? "flex justify-end" : "flex justify-start"; return /* @__PURE__ */ r("div", { className: `space-y-2 ${n}`, children: [ /* @__PURE__ */ e("div", { className: B, children: /* @__PURE__ */ r("label", { className: "text-sm font-medium", children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger", children: "*" }) ] }) }), t.properties?.description && /* @__PURE__ */ e("div", { className: B, children: /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: t.properties?.description }) }), /* @__PURE__ */ e("div", { className: B, children: /* @__PURE__ */ e("div", { className: "flex gap-1", children: Array.from({ length: I }, (N, P) => P + 1).map( (N) => /* @__PURE__ */ e( "button", { type: "button", onClick: () => l(N), className: "focus:outline-none", children: /* @__PURE__ */ e( qe, { className: `w-6 h-6 ${N <= (s || 0) ? "text-warning fill-current" : "text-default-300"}` } ) }, N ) ) }) }) ] }); case "signature": return /* @__PURE__ */ r("div", { className: `space-y-2 ${n}`, children: [ /* @__PURE__ */ r("label", { className: "text-sm font-medium", children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger", children: "*" }) ] }), t.properties?.description && /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: t.properties?.description }), /* @__PURE__ */ e( Qe, { value: s, onChange: l, width: 380, height: 150, className: "w-full" } ) ] }); case "section": return /* @__PURE__ */ e("div", { className: n, children: /* @__PURE__ */ e(z, { children: /* @__PURE__ */ r(T, { className: "py-4", children: [ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold mb-2", children: t.label }), t.properties?.description && /* @__PURE__ */ e("p", { className: "text-default-600", children: t.properties?.description }) ] }) }) }); case "paragraph": return /* @__PURE__ */ r("div", { className: `py-2 ${n}`, children: [ /* @__PURE__ */ e("h4", { className: "text-md font-medium mb-1", children: t.label }), t.properties?.description && /* @__PURE__ */ e("p", { className: "text-default-600", children: t.properties?.description }) ] }); case "pagebreak": return /* @__PURE__ */ r("div", { className: `my-8 ${n}`, children: [ /* @__PURE__ */ e(ne, { className: "my-4" }), /* @__PURE__ */ e("div", { className: "text-center text-sm text-default-500", children: "Page Break" }), /* @__PURE__ */ e(ne, { className: "my-4" }) ] }); case "html": return /* @__PURE__ */ e( "div", { className: `prose prose-sm max-w-none ${n}`, dangerouslySetInnerHTML: { __html: t.properties?.description || t.label } } ); case "range": return /* @__PURE__ */ r("div", { className: `space-y-2 ${n}`, children: [ /* @__PURE__ */ r("label", { className: "text-sm font-medium", children: [ t.label, t.required && /* @__PURE__ */ e("span", { className: "text-danger", children: "*" }) ] }), t.properties?.description && /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: t.properties?.description }), /* @__PURE__ */ r("div", { className: "flex items-center gap-4", children: [ /* @__PURE__ */ e( "input", { type: "range", min: t.properties?.min || 0, max: t.properties?.max || 100, step: t.properties?.step || 1, value: s || t.properties?.min || 0, onChange: (N) => l(Number(N.target.value)), className: "flex-1 h-2 bg-default-200 rounded-lg appearance-none cursor-pointer" } ), /* @__PURE__ */ e("span", { className: "text-sm text-default-600 min-w-[3rem] text-right", children: s || t.properties?.min || 0 }) ] }) ] }); case "phone": return /* @__PURE__ */ e( A, { ...p, type: "tel", value: s || "", onValueChange: (N) => l(N) } ); case "url": return /* @__PURE__ */ e( A, { ...p, type: "url", value: s || "", onValueChange: (N) => l(N) } ); case "button": return c( /* @__PURE__ */ e( F, { color: t.properties?.colorVariant, size: t.properties?.size, variant: t.properties?.variant, radius: t.properties?.borderRadius, isDisabled: t.advanced?.disabled || t.properties?.disabled, className: i.base, onPress: () => { l("clicked"); }, ...d, children: t.label }, `button-${t.id}` ) ); default: return /* @__PURE__ */ e( "div", { className: `p-4 border border-dashed border-default-300 rounded-lg text-center ${n}`, children: /* @__PURE__ */ r("p", { className: "text-default-500", children: [ "Unsupported field type: ", t.type ] }) } ); } } oe.memo(fe); function $e({ field: t, isPreview: s }) { const { state: o, actions: l } = J(), { selectedFieldId: i } = o, { attributes: n, listeners: g, setNodeRef: m, transform: S, transition: c, isDragging: p } = pt({ id: t.id }), a = { transform: qt.Transform.toString(S), transition: c }, d = i === t.id, u = () => { s || l.selectField(t.id); }, v = () => { const L = { ...t, id: crypto.randomUUID(), label: `${t.label} (Copy)` }; l.addField(L); }, h = () => { l.deleteField(t.id); }, w = () => { l.selectField(t.id); }, D = Ne(t, !s); return s ? /* @__PURE__ */ e("div", { className: `w-full ${D}`, children: /* @__PURE__ */ e(fe, { field: t }) }) : /* @__PURE__ */ e( "div", { ref: m,