@forms.expert/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -0
- package/dist/core/index.cjs +365 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +322 -0
- package/dist/core/index.d.ts +322 -0
- package/dist/core/index.js +334 -0
- package/dist/core/index.js.map +1 -0
- package/dist/react/index.cjs +1014 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +430 -0
- package/dist/react/index.d.ts +430 -0
- package/dist/react/index.js +991 -0
- package/dist/react/index.js.map +1 -0
- package/dist/vanilla/index.cjs +209 -0
- package/dist/vanilla/index.cjs.map +1 -0
- package/dist/vanilla/index.d.cts +188 -0
- package/dist/vanilla/index.d.ts +188 -0
- package/dist/vanilla/index.global.js +209 -0
- package/dist/vanilla/index.global.js.map +1 -0
- package/dist/vanilla/index.js +209 -0
- package/dist/vanilla/index.js.map +1 -0
- package/dist/vue/index.cjs +482 -0
- package/dist/vue/index.cjs.map +1 -0
- package/dist/vue/index.d.cts +197 -0
- package/dist/vue/index.d.ts +197 -0
- package/dist/vue/index.js +453 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../vanilla/index.ts","../../vanilla/styles.ts","../../vanilla/renderer.ts","../../core/types.ts","../../core/api-client.ts","../../core/forms-sdk.ts","../../vanilla/widget.ts"],"sourcesContent":["// Vanilla JS exports\nexport * from './styles';\nexport * from './renderer';\nexport * from './widget';\n","import { FormStyling } from '../core/types';\n\nconst defaultStyling: FormStyling = {\n theme: 'light',\n primaryColor: '#3b82f6',\n backgroundColor: '#ffffff',\n textColor: '#1f2937',\n borderRadius: 'md',\n fontSize: 'md',\n buttonStyle: 'filled',\n labelPosition: 'top',\n};\n\nfunction getBorderRadius(radius: FormStyling['borderRadius']): string {\n switch (radius) {\n case 'none': return '0';\n case 'sm': return '0.125rem';\n case 'md': return '0.375rem';\n case 'lg': return '0.5rem';\n default: return '0.375rem';\n }\n}\n\nfunction getFontSize(size: FormStyling['fontSize']): string {\n switch (size) {\n case 'sm': return '0.875rem';\n case 'md': return '1rem';\n case 'lg': return '1.125rem';\n default: return '1rem';\n }\n}\n\nexport function generateFormStyles(styling: FormStyling = defaultStyling): string {\n const s = { ...defaultStyling, ...styling };\n const radius = getBorderRadius(s.borderRadius);\n const fontSize = getFontSize(s.fontSize);\n\n return `\n.forms-expert {\n font-family: system-ui, -apple-system, sans-serif;\n font-size: ${fontSize};\n color: ${s.textColor};\n background-color: ${s.backgroundColor};\n padding: 1.5rem;\n border-radius: ${radius};\n box-sizing: border-box;\n}\n\n.forms-expert * {\n box-sizing: border-box;\n}\n\n.forms-expert-group {\n margin-bottom: 1rem;\n ${s.labelPosition === 'left' ? 'display: flex; align-items: flex-start; gap: 1rem;' : ''}\n}\n\n.forms-expert-label {\n display: block;\n font-weight: 500;\n color: ${s.textColor};\n ${s.labelPosition === 'left' ? 'width: 33%; flex-shrink: 0; padding-top: 0.5rem;' : 'margin-bottom: 0.25rem;'}\n}\n\n.forms-expert-required {\n color: #ef4444;\n margin-left: 0.25rem;\n}\n\n.forms-expert-input-wrapper {\n ${s.labelPosition === 'left' ? 'flex: 1;' : ''}\n}\n\n.forms-expert-input,\n.forms-expert-textarea,\n.forms-expert-select {\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: 1px solid ${s.theme === 'dark' ? '#4b5563' : '#d1d5db'};\n border-radius: ${radius};\n font-size: ${fontSize};\n font-family: inherit;\n background-color: ${s.theme === 'dark' ? '#374151' : '#ffffff'};\n color: ${s.textColor};\n transition: border-color 0.15s, box-shadow 0.15s;\n}\n\n.forms-expert-input:focus,\n.forms-expert-textarea:focus,\n.forms-expert-select:focus {\n outline: none;\n border-color: ${s.primaryColor};\n box-shadow: 0 0 0 2px ${s.primaryColor}33;\n}\n\n.forms-expert-input.forms-expert-error,\n.forms-expert-textarea.forms-expert-error,\n.forms-expert-select.forms-expert-error {\n border-color: #ef4444;\n}\n\n.forms-expert-textarea {\n min-height: 100px;\n resize: vertical;\n}\n\n.forms-expert-checkbox-group {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 1rem;\n}\n\n.forms-expert-checkbox {\n width: 1rem;\n height: 1rem;\n accent-color: ${s.primaryColor};\n cursor: pointer;\n}\n\n.forms-expert-file-wrapper {\n position: relative;\n}\n\n.forms-expert-file {\n width: 100%;\n padding: 0.5rem 0.75rem;\n border: 1px solid ${s.theme === 'dark' ? '#4b5563' : '#d1d5db'};\n border-radius: ${radius};\n font-size: ${fontSize};\n background-color: ${s.theme === 'dark' ? '#374151' : '#ffffff'};\n cursor: pointer;\n}\n\n.forms-expert-error-message {\n color: #ef4444;\n font-size: 0.875rem;\n margin-top: 0.25rem;\n}\n\n.forms-expert-button {\n width: 100%;\n padding: 0.625rem 1.25rem;\n margin-top: 1rem;\n font-weight: 500;\n font-size: ${fontSize};\n font-family: inherit;\n border-radius: ${radius};\n cursor: pointer;\n transition: opacity 0.2s, transform 0.1s;\n ${s.buttonStyle === 'filled' \n ? `background-color: ${s.primaryColor}; color: white; border: none;`\n : `background-color: transparent; color: ${s.primaryColor}; border: 2px solid ${s.primaryColor};`\n }\n}\n\n.forms-expert-button:hover {\n opacity: 0.9;\n}\n\n.forms-expert-button:active {\n transform: scale(0.98);\n}\n\n.forms-expert-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n.forms-expert-button-loading {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n}\n\n.forms-expert-spinner {\n width: 1rem;\n height: 1rem;\n border: 2px solid transparent;\n border-top-color: currentColor;\n border-radius: 50%;\n animation: forms-expert-spin 0.6s linear infinite;\n}\n\n@keyframes forms-expert-spin {\n to { transform: rotate(360deg); }\n}\n\n.forms-expert-honeypot {\n position: absolute;\n left: -9999px;\n opacity: 0;\n}\n\n.forms-expert-success {\n text-align: center;\n padding: 2rem;\n color: ${s.textColor};\n}\n\n.forms-expert-success-icon {\n width: 3rem;\n height: 3rem;\n margin: 0 auto 1rem;\n color: #22c55e;\n}\n\n.forms-expert-success-message {\n font-size: 1.125rem;\n font-weight: 500;\n margin-bottom: 0.5rem;\n}\n\n.forms-expert-branding {\n text-align: center;\n margin-top: 1rem;\n padding-top: 0.75rem;\n border-top: 1px solid ${s.theme === 'dark' ? '#374151' : '#e5e7eb'};\n}\n\n.forms-expert-branding a {\n color: ${s.theme === 'dark' ? '#9ca3af' : '#6b7280'};\n text-decoration: none;\n font-size: 0.75rem;\n}\n\n.forms-expert-branding a:hover {\n text-decoration: underline;\n}\n\n${s.customCss || ''}\n`.trim();\n}\n","import { FormField, FormSchema, ValidationError } from '../core/types';\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Layout field types that don't capture data\n */\nconst LAYOUT_TYPES = new Set(['heading', 'divider', 'paragraph']);\n\n/**\n * Create a form field element\n */\nexport function renderField(\n field: FormField,\n value: unknown,\n error?: string\n): HTMLElement {\n const group = document.createElement('div');\n\n // Layout fields\n if (field.type === 'heading') {\n group.className = 'forms-expert-group';\n const h = document.createElement('h3');\n h.className = 'forms-expert-heading';\n h.textContent = field.label || '';\n group.appendChild(h);\n if (field.content) {\n const p = document.createElement('p');\n p.className = 'forms-expert-heading-subtitle';\n p.textContent = field.content;\n group.appendChild(p);\n }\n return group;\n }\n\n if (field.type === 'divider') {\n const hr = document.createElement('hr');\n hr.className = 'forms-expert-divider';\n return hr;\n }\n\n if (field.type === 'paragraph') {\n group.className = 'forms-expert-group';\n if (field.label) {\n const p = document.createElement('p');\n p.className = 'forms-expert-paragraph-label';\n p.textContent = field.label;\n group.appendChild(p);\n }\n if (field.content) {\n const p = document.createElement('p');\n p.className = 'forms-expert-paragraph';\n p.textContent = field.content;\n group.appendChild(p);\n }\n return group;\n }\n\n // Hidden field\n if (field.type === 'hidden') {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = field.name;\n input.value = String(field.defaultValue ?? value ?? '');\n group.appendChild(input);\n group.style.display = 'none';\n return group;\n }\n\n // Checkbox / toggle / consent\n if (field.type === 'checkbox' || field.type === 'toggle' || field.type === 'consent') {\n group.className = 'forms-expert-checkbox-group';\n \n const input = document.createElement('input');\n input.type = 'checkbox';\n input.id = `mira-field-${field.name}`;\n input.name = field.name;\n input.className = 'forms-expert-checkbox';\n input.checked = Boolean(value);\n if (field.required) input.required = true;\n \n const label = document.createElement('label');\n label.htmlFor = input.id;\n const text = field.type === 'consent' ? (field.consentText || field.label || field.name) : (field.label || field.name);\n label.innerHTML = `${escapeHtml(text)}${field.required ? '<span class=\"forms-expert-required\">*</span>' : ''}`;\n \n group.appendChild(input);\n group.appendChild(label);\n\n if (field.type === 'consent' && field.consentUrl) {\n const link = document.createElement('a');\n link.href = field.consentUrl;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n link.textContent = 'View policy';\n link.className = 'forms-expert-consent-link';\n group.appendChild(link);\n }\n\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n group.appendChild(errorEl);\n }\n return group;\n }\n\n // All other fields\n group.className = 'forms-expert-group';\n \n // Label\n const label = document.createElement('label');\n label.className = 'forms-expert-label';\n label.htmlFor = `mira-field-${field.name}`;\n label.innerHTML = `${escapeHtml(field.label || field.name)}${field.required ? '<span class=\"forms-expert-required\">*</span>' : ''}`;\n group.appendChild(label);\n \n // Input wrapper\n const wrapper = document.createElement('div');\n wrapper.className = 'forms-expert-input-wrapper';\n \n let input: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n \n switch (field.type) {\n case 'textarea':\n case 'richText':\n input = document.createElement('textarea');\n input.className = 'forms-expert-textarea';\n input.value = String(value || '');\n if (field.maxLength) input.maxLength = field.maxLength;\n break;\n \n case 'select':\n case 'dropdown': {\n input = document.createElement('select');\n input.className = 'forms-expert-select';\n \n const defaultOption = document.createElement('option');\n defaultOption.value = '';\n defaultOption.textContent = field.placeholder || 'Select an option...';\n input.appendChild(defaultOption);\n \n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const option = document.createElement('option');\n option.value = opt;\n option.textContent = opt;\n if (value === opt) option.selected = true;\n input.appendChild(option);\n });\n break;\n }\n\n case 'radio': {\n // Render as radio group\n const radioGroup = document.createElement('div');\n radioGroup.className = 'forms-expert-radio-group';\n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const radioWrapper = document.createElement('label');\n radioWrapper.className = 'forms-expert-radio-item';\n const radioInput = document.createElement('input');\n radioInput.type = 'radio';\n radioInput.name = field.name;\n radioInput.value = opt;\n radioInput.checked = value === opt;\n radioWrapper.appendChild(radioInput);\n radioWrapper.appendChild(document.createTextNode(` ${opt}`));\n radioGroup.appendChild(radioWrapper);\n });\n wrapper.appendChild(radioGroup);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'multiselect': {\n const checkGroup = document.createElement('div');\n checkGroup.className = 'forms-expert-multiselect-group';\n const selected = (value as string[]) || [];\n const opts = (field.options || []) as string[];\n opts.forEach((opt) => {\n const checkWrapper = document.createElement('label');\n checkWrapper.className = 'forms-expert-checkbox-item';\n const checkInput = document.createElement('input');\n checkInput.type = 'checkbox';\n checkInput.name = field.name;\n checkInput.value = opt;\n checkInput.checked = selected.includes(opt);\n checkWrapper.appendChild(checkInput);\n checkWrapper.appendChild(document.createTextNode(` ${opt}`));\n checkGroup.appendChild(checkWrapper);\n });\n wrapper.appendChild(checkGroup);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'rating': {\n const ratingContainer = document.createElement('div');\n ratingContainer.className = 'forms-expert-rating';\n const max = field.ratingMax || 5;\n const current = (value as number) || 0;\n for (let i = 1; i <= max; i++) {\n const star = document.createElement('button');\n star.type = 'button';\n star.className = `forms-expert-rating-star ${i <= current ? 'active' : ''}`;\n star.textContent = '★';\n star.dataset.value = String(i);\n ratingContainer.appendChild(star);\n }\n wrapper.appendChild(ratingContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'scale':\n case 'opinionScale': {\n const scaleContainer = document.createElement('div');\n scaleContainer.className = 'forms-expert-scale';\n const min = field.min ?? (field.type === 'opinionScale' ? 0 : 1);\n const max = field.max ?? (field.type === 'opinionScale' ? 10 : 5);\n const current = value as number | undefined;\n for (let i = min; i <= max; i++) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = `forms-expert-scale-btn ${current === i ? 'active' : ''}`;\n btn.textContent = String(i);\n btn.dataset.value = String(i);\n scaleContainer.appendChild(btn);\n }\n wrapper.appendChild(scaleContainer);\n if (field.lowLabel || field.highLabel) {\n const labels = document.createElement('div');\n labels.className = 'forms-expert-scale-labels';\n labels.innerHTML = `<span>${escapeHtml(field.lowLabel || '')}</span><span>${escapeHtml(field.highLabel || '')}</span>`;\n wrapper.appendChild(labels);\n }\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'slider': {\n input = document.createElement('input');\n input.type = 'range';\n input.className = 'forms-expert-slider';\n input.min = String(field.min ?? 0);\n input.max = String(field.max ?? 100);\n input.step = String(field.step ?? 1);\n input.value = String(value ?? field.min ?? 0);\n break;\n }\n\n case 'file':\n input = document.createElement('input');\n input.type = 'file';\n input.className = 'forms-expert-file';\n if (field.allowedMimeTypes?.length) {\n input.accept = field.allowedMimeTypes.join(',');\n }\n if (field.multiple) {\n input.multiple = true;\n }\n break;\n\n case 'currency': {\n input = document.createElement('input');\n input.type = 'number';\n input.className = 'forms-expert-input';\n input.value = String(value ?? '');\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n input.step = String(field.step || 0.01);\n break;\n }\n\n case 'phone':\n input = document.createElement('input');\n input.type = 'tel';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'url':\n input = document.createElement('input');\n input.type = 'url';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'password':\n input = document.createElement('input');\n input.type = 'password';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'time':\n input = document.createElement('input');\n input.type = 'time';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'datetime':\n input = document.createElement('input');\n input.type = 'datetime-local';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n break;\n\n case 'colorPicker':\n input = document.createElement('input');\n input.type = 'color';\n input.className = 'forms-expert-color';\n input.value = String(value || '#000000');\n break;\n\n case 'dateRange': {\n // Two date inputs\n const rangeContainer = document.createElement('div');\n rangeContainer.className = 'forms-expert-date-range';\n const range = (value as { start?: string; end?: string }) || {};\n const startInput = document.createElement('input');\n startInput.type = 'date';\n startInput.className = 'forms-expert-input';\n startInput.name = `${field.name}.start`;\n startInput.value = range.start || '';\n const endInput = document.createElement('input');\n endInput.type = 'date';\n endInput.className = 'forms-expert-input';\n endInput.name = `${field.name}.end`;\n endInput.value = range.end || '';\n rangeContainer.appendChild(startInput);\n rangeContainer.appendChild(endInput);\n wrapper.appendChild(rangeContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'address': {\n const addrContainer = document.createElement('div');\n addrContainer.className = 'forms-expert-address';\n const addrFields = field.addressFields || ['street', 'city', 'state', 'zip', 'country'];\n const addr = (value as Record<string, string>) || {};\n const labels: Record<string, string> = { street: 'Street', street2: 'Street Line 2', city: 'City', state: 'State', zip: 'ZIP', country: 'Country' };\n addrFields.forEach((af) => {\n const inp = document.createElement('input');\n inp.type = 'text';\n inp.className = 'forms-expert-input';\n inp.name = `${field.name}.${af}`;\n inp.placeholder = labels[af] || af;\n inp.value = addr[af] || '';\n addrContainer.appendChild(inp);\n });\n wrapper.appendChild(addrContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'name': {\n const nameContainer = document.createElement('div');\n nameContainer.className = 'forms-expert-name';\n const nameFields = field.nameFields || ['first', 'last'];\n const nameVal = (value as Record<string, string>) || {};\n const labels: Record<string, string> = { prefix: 'Prefix', first: 'First Name', middle: 'Middle', last: 'Last Name', suffix: 'Suffix' };\n nameFields.forEach((nf) => {\n const inp = document.createElement('input');\n inp.type = 'text';\n inp.className = 'forms-expert-input';\n inp.name = `${field.name}.${nf}`;\n inp.placeholder = labels[nf] || nf;\n inp.value = nameVal[nf] || '';\n nameContainer.appendChild(inp);\n });\n wrapper.appendChild(nameContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'imageChoice': {\n const imgContainer = document.createElement('div');\n imgContainer.className = 'forms-expert-image-choice';\n const opts = (field.options || []) as Array<{ label: string; value: string; imageUrl?: string }>;\n const selected = value as string;\n opts.forEach((opt) => {\n const card = document.createElement('button');\n card.type = 'button';\n card.className = `forms-expert-image-choice-item ${selected === opt.value ? 'active' : ''}`;\n card.dataset.value = opt.value;\n if (opt.imageUrl) {\n const img = document.createElement('img');\n img.src = opt.imageUrl;\n img.alt = opt.label;\n card.appendChild(img);\n }\n const lbl = document.createElement('span');\n lbl.textContent = opt.label;\n card.appendChild(lbl);\n imgContainer.appendChild(card);\n });\n wrapper.appendChild(imgContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'ranking': {\n const rankContainer = document.createElement('div');\n rankContainer.className = 'forms-expert-ranking';\n const opts = (field.options || []) as string[];\n const ranked = (value as string[]) || [...opts];\n ranked.forEach((item, i) => {\n const row = document.createElement('div');\n row.className = 'forms-expert-ranking-item';\n row.textContent = `${i + 1}. ${item}`;\n row.dataset.value = item;\n rankContainer.appendChild(row);\n });\n wrapper.appendChild(rankContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n case 'location': {\n const locContainer = document.createElement('div');\n locContainer.className = 'forms-expert-location';\n const loc = (value as { lat?: number; lng?: number; address?: string }) || {};\n const addrInput = document.createElement('input');\n addrInput.type = 'text';\n addrInput.className = 'forms-expert-input';\n addrInput.name = `${field.name}.address`;\n addrInput.placeholder = 'Address';\n addrInput.value = loc.address || '';\n locContainer.appendChild(addrInput);\n const coordRow = document.createElement('div');\n coordRow.className = 'forms-expert-location-coords';\n const latInput = document.createElement('input');\n latInput.type = 'number';\n latInput.className = 'forms-expert-input';\n latInput.name = `${field.name}.lat`;\n latInput.placeholder = 'Latitude';\n latInput.step = 'any';\n latInput.value = loc.lat !== undefined ? String(loc.lat) : '';\n const lngInput = document.createElement('input');\n lngInput.type = 'number';\n lngInput.className = 'forms-expert-input';\n lngInput.name = `${field.name}.lng`;\n lngInput.placeholder = 'Longitude';\n lngInput.step = 'any';\n lngInput.value = loc.lng !== undefined ? String(loc.lng) : '';\n coordRow.appendChild(latInput);\n coordRow.appendChild(lngInput);\n locContainer.appendChild(coordRow);\n wrapper.appendChild(locContainer);\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n group.appendChild(wrapper);\n return group;\n }\n\n default:\n input = document.createElement('input');\n input.type = field.type === 'email' ? 'email' : field.type === 'number' ? 'number' : field.type === 'date' ? 'date' : 'text';\n input.className = 'forms-expert-input';\n input.value = String(value || '');\n if (field.type === 'number') {\n if (field.min !== undefined) input.min = String(field.min);\n if (field.max !== undefined) input.max = String(field.max);\n if (field.step !== undefined) input.step = String(field.step);\n }\n break;\n }\n \n input!.id = `mira-field-${field.name}`;\n input!.name = field.name;\n if (field.placeholder && 'placeholder' in input!) {\n (input as HTMLInputElement | HTMLTextAreaElement).placeholder = field.placeholder;\n }\n if (field.required) input!.required = true;\n \n if (error) {\n input!.classList.add('forms-expert-error');\n }\n \n wrapper.appendChild(input!);\n \n // Error message\n if (error) {\n const errorEl = document.createElement('div');\n errorEl.className = 'forms-expert-error-message';\n errorEl.textContent = error;\n wrapper.appendChild(errorEl);\n }\n \n group.appendChild(wrapper);\n \n return group;\n}\n\n/**\n * Render the entire form\n */\nexport function renderForm(\n schema: FormSchema,\n values: Record<string, unknown> = {},\n errors: Record<string, string> = {},\n options: {\n honeypot?: boolean;\n showBranding?: boolean;\n brandingText?: string;\n brandingUrl?: string;\n submitText?: string;\n isLoading?: boolean;\n } = {}\n): HTMLFormElement {\n const form = document.createElement('form');\n form.className = 'forms-expert';\n \n // Render fields\n schema.fields.forEach((field) => {\n const fieldEl = renderField(field, values[field.name], errors[field.name]);\n form.appendChild(fieldEl);\n });\n \n // Honeypot\n if (options.honeypot) {\n const honeypot = document.createElement('input');\n honeypot.type = 'text';\n honeypot.name = '_hp';\n honeypot.className = 'forms-expert-honeypot';\n honeypot.tabIndex = -1;\n honeypot.autocomplete = 'off';\n form.appendChild(honeypot);\n }\n \n // Hidden page URL\n const pageUrl = document.createElement('input');\n pageUrl.type = 'hidden';\n pageUrl.name = 'pageUrl';\n pageUrl.value = typeof window !== 'undefined' ? window.location.href : '';\n form.appendChild(pageUrl);\n \n // Submit button\n const button = document.createElement('button');\n button.type = 'submit';\n button.className = 'forms-expert-button';\n button.disabled = options.isLoading || false;\n \n if (options.isLoading) {\n button.innerHTML = `\n <span class=\"forms-expert-button-loading\">\n <span class=\"forms-expert-spinner\"></span>\n Submitting...\n </span>\n `;\n } else {\n button.textContent = options.submitText || 'Submit';\n }\n \n form.appendChild(button);\n \n // Branding\n if (options.showBranding !== false) {\n const brandingText = options.brandingText || 'Powered by Forms Expert';\n const brandingUrl = options.brandingUrl || 'https://mira.io';\n const branding = document.createElement('div');\n branding.className = 'forms-expert-branding';\n branding.innerHTML = `<a href=\"${brandingUrl}\" target=\"_blank\" rel=\"noopener\">${brandingText}</a>`;\n form.appendChild(branding);\n }\n \n return form;\n}\n\n/**\n * Render success message\n */\nexport function renderSuccess(message: string): HTMLElement {\n const container = document.createElement('div');\n container.className = 'forms-expert-success';\n \n container.innerHTML = `\n <svg class=\"forms-expert-success-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\"></path>\n </svg>\n <div class=\"forms-expert-success-message\">${escapeHtml(message)}</div>\n `;\n \n return container;\n}\n\n/**\n * Convert validation errors array to record\n */\nexport function errorsToRecord(errors: ValidationError[]): Record<string, string> {\n return errors.reduce(\n (acc, err) => ({ ...acc, [err.field]: err.message }),\n {}\n );\n}\n","/**\n * Form field types\n */\nexport type BasicFieldType =\n | 'text' | 'email' | 'number' | 'textarea' | 'select'\n | 'checkbox' | 'file' | 'date' | 'hidden';\n\nexport type InteractiveFieldType =\n | 'radio' | 'multiselect' | 'rating' | 'scale' | 'toggle'\n | 'ranking' | 'imageChoice' | 'phone' | 'url' | 'password'\n | 'richText' | 'slider' | 'currency' | 'time' | 'datetime'\n | 'dateRange' | 'address' | 'name' | 'dropdown' | 'colorPicker'\n | 'location' | 'opinionScale' | 'consent';\n\nexport type LayoutFieldType = 'heading' | 'divider' | 'paragraph';\n\nexport type FormFieldType = BasicFieldType | InteractiveFieldType | LayoutFieldType;\n\nexport interface FormFieldOption {\n label: string;\n value: string;\n imageUrl?: string;\n}\n\n/**\n * Form field definition\n */\nexport interface FormField {\n name: string;\n type: FormFieldType;\n label?: string;\n placeholder?: string;\n required?: boolean;\n options?: string[] | FormFieldOption[];\n defaultValue?: unknown;\n maxFileSize?: number;\n allowedMimeTypes?: string[];\n multiple?: boolean;\n min?: number;\n max?: number;\n step?: number;\n ratingMax?: number;\n lowLabel?: string;\n highLabel?: string;\n defaultCountryCode?: string;\n currencyCode?: string;\n currencySymbol?: string;\n addressFields?: ('street' | 'street2' | 'city' | 'state' | 'zip' | 'country')[];\n nameFields?: ('prefix' | 'first' | 'middle' | 'last' | 'suffix')[];\n content?: string;\n consentText?: string;\n consentUrl?: string;\n maxLength?: number;\n stepId?: string;\n visibleWhen?: {\n field: string;\n operator: 'eq' | 'neq' | 'contains' | 'gt' | 'lt';\n value: unknown;\n };\n}\n\n/**\n * Form styling configuration\n */\nexport interface FormStyling {\n theme: 'light' | 'dark' | 'system';\n primaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: 'none' | 'sm' | 'md' | 'lg';\n fontSize: 'sm' | 'md' | 'lg';\n buttonStyle: 'filled' | 'outline';\n labelPosition: 'top' | 'left' | 'floating';\n customCss?: string;\n}\n\n/**\n * Form schema\n */\nexport interface FormSchema {\n fields: FormField[];\n styling?: FormStyling;\n}\n\n/**\n * Form captcha settings\n */\nexport interface CaptchaSettings {\n enabled: boolean;\n provider?: 'turnstile' | 'recaptcha' | 'hcaptcha';\n siteKey?: string;\n}\n\n/**\n * Form branding configuration\n */\nexport interface FormBranding {\n enabled: boolean;\n text?: string;\n url?: string;\n}\n\n/**\n * Form status response from API\n */\nexport interface FormStatusResponse {\n active: boolean;\n formId?: string;\n name?: string;\n mode?: 'free' | 'schema';\n schema?: FormSchema;\n error?: string;\n settings?: {\n captcha: CaptchaSettings;\n honeypot: boolean;\n allowAttachments: boolean;\n maxAttachments?: number;\n maxAttachmentSize?: number;\n successMessage?: string;\n redirectUrl?: string;\n };\n /** Branding configuration */\n branding?: FormBranding;\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n field: string;\n message: string;\n}\n\n/**\n * Validation response from API\n */\nexport interface ValidationResponse {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Submission response from API\n */\nexport interface SubmissionResponse {\n success: boolean;\n submissionId: string;\n message: string;\n}\n\n/**\n * Submit options\n */\nexport interface SubmitOptions {\n pageUrl?: string;\n captchaToken?: string;\n /** Callback for upload progress */\n onProgress?: (progress: UploadProgress) => void;\n}\n\n/**\n * Upload progress information\n */\nexport interface UploadProgress {\n loaded: number;\n total: number;\n percentage: number;\n}\n\n/**\n * File validation error\n */\nexport interface FileValidationError {\n field: string;\n file: string;\n error: 'size' | 'type' | 'count';\n message: string;\n}\n\n/**\n * SDK configuration\n */\nexport interface FormsSDKConfig {\n apiKey: string;\n resourceId: string;\n baseUrl?: string;\n}\n\n/**\n * Form handler options\n */\nexport interface FormHandlerOptions {\n /** Track form views for analytics (completion rate) */\n trackViews?: boolean;\n onSubmitStart?: () => void;\n onSubmitSuccess?: (response: SubmissionResponse) => void;\n onSubmitError?: (error: FormsError) => void;\n onValidationError?: (errors: ValidationError[]) => void;\n}\n\n/**\n * Forms SDK error\n */\nexport class FormsError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode: number,\n public retryAfter?: number\n ) {\n super(message);\n this.name = 'FormsError';\n }\n}\n\n/**\n * Validation error class\n */\nexport class FormValidationError extends Error {\n constructor(public errors: ValidationError[]) {\n super('Validation failed');\n this.name = 'FormValidationError';\n }\n}\n","import {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormsError,\n UploadProgress,\n} from './types';\n\n/**\n * API client for forms backend\n */\nexport class FormsApiClient {\n private baseUrl: string;\n private apiKey: string;\n private resourceId: string;\n\n constructor(config: FormsSDKConfig) {\n this.apiKey = config.apiKey;\n this.resourceId = config.resourceId;\n this.baseUrl = (config.baseUrl || 'https://api.formsapp.io/api/v1').replace(/\\/$/, '');\n }\n\n /**\n * Build URL with token query parameter\n */\n private buildUrl(path: string): string {\n const separator = path.includes('?') ? '&' : '?';\n return `${this.baseUrl}${path}${separator}token=${encodeURIComponent(this.apiKey)}`;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: object\n ): Promise<T> {\n const url = this.buildUrl(path);\n\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data = await response.json();\n\n if (!response.ok) {\n throw new FormsError(\n data.message || 'Request failed',\n data.code || 'UNKNOWN_ERROR',\n response.status,\n data.retryAfter\n );\n }\n\n return data;\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string): Promise<FormStatusResponse> {\n return this.request('GET', `/f/${this.resourceId}/${slug}/is-active`);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.request('POST', `/f/${this.resourceId}/${slug}/validate`, {\n data,\n });\n }\n\n /**\n * Submit form data (supports files)\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}`);\n \n // Check if data contains files\n const hasFiles = Object.values(data).some(\n (v) => v instanceof File || (v instanceof FileList && v.length > 0)\n );\n\n if (hasFiles || options?.onProgress) {\n // Use FormData and XMLHttpRequest for file uploads with progress\n return this.submitWithFormData(url, data, options);\n }\n\n // Use regular JSON request for non-file submissions\n return this.request('POST', `/f/${this.resourceId}/${slug}`, {\n data,\n pageUrl: options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : undefined),\n captchaToken: options?.captchaToken,\n });\n }\n\n /**\n * Submit with FormData (for file uploads with progress tracking)\n */\n private submitWithFormData(\n url: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return new Promise((resolve, reject) => {\n const formData = new FormData();\n\n // Add data fields\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof File) {\n formData.append(key, value);\n } else if (value instanceof FileList) {\n Array.from(value).forEach((file) => formData.append(key, file));\n } else if (value !== undefined && value !== null) {\n formData.append(`data[${key}]`, String(value));\n }\n }\n\n // Add metadata\n const pageUrl = options?.pageUrl || (typeof window !== 'undefined' ? window.location.href : '');\n if (pageUrl) {\n formData.append('pageUrl', pageUrl);\n }\n if (options?.captchaToken) {\n formData.append('captchaToken', options.captchaToken);\n }\n\n const xhr = new XMLHttpRequest();\n\n // Progress tracking\n if (options?.onProgress) {\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n options.onProgress!({\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100),\n });\n }\n });\n }\n\n xhr.addEventListener('load', () => {\n try {\n const response = JSON.parse(xhr.responseText);\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve(response);\n } else {\n reject(new FormsError(\n response.message || 'Submission failed',\n response.code || 'UNKNOWN_ERROR',\n xhr.status,\n response.retryAfter\n ));\n }\n } catch {\n reject(new FormsError('Invalid response', 'PARSE_ERROR', xhr.status));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new FormsError('Network error', 'NETWORK_ERROR', 0));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new FormsError('Request aborted', 'ABORTED', 0));\n });\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n const url = this.buildUrl(`/f/${this.resourceId}/${slug}/view`);\n await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).catch(() => {});\n }\n\n /**\n * Get resource ID\n */\n getResourceId(): string {\n return this.resourceId;\n }\n\n /**\n * Get base URL\n */\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n","import { FormsApiClient } from './api-client';\nimport {\n FormsSDKConfig,\n FormStatusResponse,\n ValidationResponse,\n SubmissionResponse,\n SubmitOptions,\n FormHandlerOptions,\n FormsError,\n FormValidationError,\n} from './types';\n\n/**\n * Form handler for a specific form\n */\nexport class FormHandler {\n private apiClient: FormsApiClient;\n private slug: string;\n private config: FormStatusResponse | null = null;\n private options: FormHandlerOptions;\n\n constructor(\n apiClient: FormsApiClient,\n slug: string,\n options: FormHandlerOptions = {}\n ) {\n this.apiClient = apiClient;\n this.slug = slug;\n this.options = options;\n }\n\n /**\n * Initialize form handler and fetch configuration\n */\n async initialize(): Promise<FormStatusResponse> {\n this.config = await this.apiClient.isActive(this.slug);\n if (this.options.trackViews) {\n this.apiClient.trackView(this.slug);\n }\n return this.config;\n }\n\n /**\n * Get cached form configuration\n */\n getConfig(): FormStatusResponse | null {\n return this.config;\n }\n\n /**\n * Check if form is active\n */\n isActive(): boolean {\n return this.config?.active ?? false;\n }\n\n /**\n * Check if captcha is required\n */\n requiresCaptcha(): boolean {\n return this.config?.settings?.captcha?.enabled ?? false;\n }\n\n /**\n * Get captcha provider\n */\n getCaptchaProvider(): 'turnstile' | 'recaptcha' | 'hcaptcha' | undefined {\n return this.config?.settings?.captcha?.provider;\n }\n\n /**\n * Get form schema\n */\n getSchema() {\n return this.config?.schema;\n }\n\n /**\n * Validate form data\n */\n async validate(data: Record<string, unknown>): Promise<ValidationResponse> {\n return this.apiClient.validate(this.slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n this.options.onSubmitStart?.();\n\n try {\n // Validate first if in schema mode\n if (this.config?.mode === 'schema') {\n const validation = await this.validate(data);\n if (!validation.valid) {\n this.options.onValidationError?.(validation.errors);\n throw new FormValidationError(validation.errors);\n }\n }\n\n const response = await this.apiClient.submit(this.slug, data, options);\n this.options.onSubmitSuccess?.(response);\n return response;\n } catch (error) {\n if (error instanceof FormsError) {\n this.options.onSubmitError?.(error);\n }\n throw error;\n }\n }\n\n /**\n * Get success message from config\n */\n getSuccessMessage(): string {\n return this.config?.settings?.successMessage || 'Form submitted successfully!';\n }\n\n /**\n * Get redirect URL from config\n */\n getRedirectUrl(): string | undefined {\n return this.config?.settings?.redirectUrl;\n }\n}\n\n/**\n * Main Forms SDK class\n */\nexport class FormsSDK {\n private apiClient: FormsApiClient;\n\n constructor(config: FormsSDKConfig) {\n this.apiClient = new FormsApiClient(config);\n }\n\n /**\n * Check if form is active and get configuration\n */\n async isActive(slug: string): Promise<FormStatusResponse> {\n return this.apiClient.isActive(slug);\n }\n\n /**\n * Validate form data without submitting\n */\n async validate(\n slug: string,\n data: Record<string, unknown>\n ): Promise<ValidationResponse> {\n return this.apiClient.validate(slug, data);\n }\n\n /**\n * Submit form data\n */\n async submit(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions\n ): Promise<SubmissionResponse> {\n return this.apiClient.submit(slug, data, options);\n }\n\n /**\n * Create a form handler for a specific form\n */\n form(slug: string, options?: FormHandlerOptions): FormHandler {\n return new FormHandler(this.apiClient, slug, options);\n }\n\n /**\n * Track a form view (for analytics completion rate)\n */\n async trackView(slug: string): Promise<void> {\n return this.apiClient.trackView(slug);\n }\n\n /**\n * Submit with retry logic for rate limits\n */\n async submitWithRetry(\n slug: string,\n data: Record<string, unknown>,\n options?: SubmitOptions & { maxRetries?: number }\n ): Promise<SubmissionResponse> {\n const maxRetries = options?.maxRetries ?? 3;\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await this.submit(slug, data, options);\n } catch (error) {\n lastError = error as Error;\n\n if (error instanceof FormsError) {\n // Don't retry validation or auth errors\n if (\n [\n 'VALIDATION_ERROR',\n 'CAPTCHA_REQUIRED',\n 'ORIGIN_NOT_ALLOWED',\n ].includes(error.code)\n ) {\n throw error;\n }\n\n // Retry rate limits with backoff\n if (error.code.includes('RATE_LIMIT')) {\n const retryAfter = error.retryAfter || Math.pow(2, attempt) * 1000;\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n continue;\n }\n }\n\n // Exponential backoff for network errors\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 1000)\n );\n }\n }\n\n throw lastError!;\n }\n}\n","import {\n FormsSDK,\n FormsSDKConfig,\n FormStatusResponse,\n SubmissionResponse,\n ValidationError,\n FormValidationError,\n FormsError,\n} from '../core';\nimport { generateFormStyles } from './styles';\nimport { renderForm, renderSuccess, errorsToRecord } from './renderer';\n\nexport interface FormWidgetOptions {\n /** Target element or selector */\n target: string | HTMLElement;\n /** Form slug */\n slug: string;\n /** Track form views for analytics (completion rate). Default: false */\n trackViews?: boolean;\n /** Custom submit button text */\n submitText?: string;\n /** Success callback */\n onSuccess?: (response: SubmissionResponse) => void;\n /** Error callback */\n onError?: (error: Error) => void;\n /** Validation error callback */\n onValidationError?: (errors: ValidationError[]) => void;\n /** Reset form after submission */\n resetOnSuccess?: boolean;\n /** Redirect after success (overrides form config) */\n redirectUrl?: string;\n}\n\n/**\n * Form widget for embedding forms\n */\nexport class FormWidget {\n private sdk: FormsSDK;\n private container: HTMLElement;\n private config: FormStatusResponse | null = null;\n private values: Record<string, unknown> = {};\n private errors: Record<string, string> = {};\n private isLoading = false;\n private isSubmitted = false;\n private options: FormWidgetOptions;\n private styleEl: HTMLStyleElement | null = null;\n\n constructor(sdkConfig: FormsSDKConfig, options: FormWidgetOptions) {\n this.sdk = new FormsSDK(sdkConfig);\n this.options = options;\n\n // Get container element\n const target = options.target;\n if (typeof target === 'string') {\n const el = document.querySelector(target);\n if (!el) throw new Error(`Element not found: ${target}`);\n this.container = el as HTMLElement;\n } else {\n this.container = target;\n }\n }\n\n /**\n * Initialize and render the form\n */\n async init(): Promise<void> {\n try {\n this.config = await this.sdk.isActive(this.options.slug);\n\n if (!this.config.active) {\n this.renderError('This form is not available');\n return;\n }\n\n if (this.options.trackViews) {\n this.sdk.trackView(this.options.slug);\n }\n\n this.injectStyles();\n this.render();\n } catch (error) {\n this.renderError('Failed to load form');\n this.options.onError?.(error as Error);\n }\n }\n\n /**\n * Inject CSS styles\n */\n private injectStyles(): void {\n if (this.styleEl) return;\n\n this.styleEl = document.createElement('style');\n this.styleEl.id = `forms-expert-styles-${this.options.slug}`;\n this.styleEl.textContent = generateFormStyles(this.config?.schema?.styling);\n document.head.appendChild(this.styleEl);\n }\n\n /**\n * Render the form\n */\n private render(): void {\n if (!this.config?.schema) return;\n\n if (this.isSubmitted) {\n this.container.innerHTML = '';\n const successMessage =\n this.config.settings?.successMessage || 'Form submitted successfully!';\n this.container.appendChild(renderSuccess(successMessage));\n return;\n }\n\n const form = renderForm(this.config.schema, this.values, this.errors, {\n honeypot: this.config.settings?.honeypot,\n showBranding: this.config.branding?.enabled !== false,\n brandingText: this.config.branding?.text,\n brandingUrl: this.config.branding?.url,\n submitText: this.options.submitText,\n isLoading: this.isLoading,\n });\n\n // Handle input changes\n form.addEventListener('input', (e) => {\n const target = e.target as HTMLInputElement;\n if (target.name && target.name !== '_hp' && target.name !== 'pageUrl') {\n if (target.type === 'checkbox') {\n this.values[target.name] = target.checked;\n } else if (target.type === 'file') {\n // Store FileList for multiple files, single File for single uploads\n this.values[target.name] = target.multiple ? target.files : target.files?.[0];\n } else {\n this.values[target.name] = target.value;\n }\n // Clear error on change\n if (this.errors[target.name]) {\n delete this.errors[target.name];\n this.render();\n }\n }\n });\n\n // Handle submit\n form.addEventListener('submit', (e) => {\n e.preventDefault();\n this.handleSubmit();\n });\n\n this.container.innerHTML = '';\n this.container.appendChild(form);\n }\n\n /**\n * Handle form submission\n */\n private async handleSubmit(): Promise<void> {\n if (this.isLoading || !this.config) return;\n\n this.isLoading = true;\n this.errors = {};\n this.render();\n\n try {\n const response = await this.sdk.submit(this.options.slug, this.values);\n\n this.isLoading = false;\n this.isSubmitted = true;\n this.render();\n\n this.options.onSuccess?.(response);\n\n // Handle redirect\n const redirectUrl =\n this.options.redirectUrl || this.config.settings?.redirectUrl;\n if (redirectUrl) {\n setTimeout(() => {\n window.location.href = redirectUrl;\n }, 1500);\n }\n\n // Reset form after delay if configured\n if (this.options.resetOnSuccess) {\n setTimeout(() => {\n this.reset();\n }, 3000);\n }\n } catch (error) {\n this.isLoading = false;\n\n if (error instanceof FormValidationError) {\n this.errors = errorsToRecord(error.errors);\n this.options.onValidationError?.(error.errors);\n } else {\n this.options.onError?.(error as Error);\n }\n\n this.render();\n }\n }\n\n /**\n * Reset form to initial state\n */\n reset(): void {\n this.values = {};\n this.errors = {};\n this.isLoading = false;\n this.isSubmitted = false;\n this.render();\n }\n\n /**\n * Render error message\n */\n private renderError(message: string): void {\n this.container.innerHTML = `\n <div class=\"forms-expert\" style=\"text-align: center; padding: 2rem; color: #ef4444;\">\n <p>${message}</p>\n </div>\n `;\n }\n\n /**\n * Destroy widget\n */\n destroy(): void {\n this.container.innerHTML = '';\n this.styleEl?.remove();\n this.styleEl = null;\n }\n}\n\n/**\n * Auto-initialize forms on page load\n */\nexport function autoInit(): void {\n const forms = document.querySelectorAll('[data-forms-expert]');\n\n forms.forEach((el) => {\n const apiKey = el.getAttribute('data-api-key');\n const resourceId = el.getAttribute('data-resource-id');\n const slug = el.getAttribute('data-forms-expert');\n const baseUrl = el.getAttribute('data-base-url') || undefined;\n\n if (!apiKey || !resourceId || !slug) {\n console.error('Forms Expert: Missing required attributes', {\n apiKey: !!apiKey,\n resourceId: !!resourceId,\n slug: !!slug,\n });\n return;\n }\n\n const widget = new FormWidget(\n { apiKey, resourceId, baseUrl },\n {\n target: el as HTMLElement,\n slug,\n trackViews: el.getAttribute('data-track-views') === 'true',\n submitText: el.getAttribute('data-submit-text') || undefined,\n resetOnSuccess: el.getAttribute('data-reset') === 'true',\n }\n );\n\n widget.init();\n });\n}\n\n// Auto-init on DOMContentLoaded\nif (typeof window !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n autoInit();\n }\n}\n"],"mappings":"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","names":["vanilla_exports","__export","FormWidget","autoInit","errorsToRecord","generateFormStyles","renderField","renderForm","renderSuccess","defaultStyling","getBorderRadius","radius","getFontSize","size","generateFormStyles","styling","s","fontSize","escapeHtml","text","div","renderField","field","value","error","group","h","p","hr","input","label","text","escapeHtml","link","errorEl","wrapper","defaultOption","opt","option","radioGroup","radioWrapper","radioInput","checkGroup","selected","checkWrapper","checkInput","ratingContainer","max","current","star","scaleContainer","min","i","btn","labels","rangeContainer","range","startInput","endInput","addrContainer","addrFields","addr","af","inp","nameContainer","nameFields","nameVal","nf","imgContainer","opts","card","img","lbl","rankContainer","item","row","locContainer","loc","addrInput","coordRow","latInput","lngInput","renderForm","schema","values","errors","options","form","fieldEl","honeypot","pageUrl","button","brandingText","brandingUrl","branding","renderSuccess","message","container","errorsToRecord","acc","err","FormsError","message","code","statusCode","retryAfter","FormValidationError","errors","FormsApiClient","config","path","separator","method","body","url","response","data","FormsError","slug","options","v","resolve","reject","formData","key","value","file","pageUrl","xhr","event","FormHandler","apiClient","slug","options","data","validation","FormValidationError","response","error","FormsError","FormsSDK","config","FormsApiClient","maxRetries","lastError","attempt","retryAfter","resolve","FormWidget","sdkConfig","options","FormsSDK","target","el","error","generateFormStyles","successMessage","renderSuccess","form","renderForm","e","response","redirectUrl","FormValidationError","errorsToRecord","message","autoInit","apiKey","resourceId","slug","baseUrl"]}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
var C={theme:"light",primaryColor:"#3b82f6",backgroundColor:"#ffffff",textColor:"#1f2937",borderRadius:"md",fontSize:"md",buttonStyle:"filled",labelPosition:"top"};function R(t){switch(t){case"none":return"0";case"sm":return"0.125rem";case"md":return"0.375rem";case"lg":return"0.5rem";default:return"0.375rem"}}function T(t){switch(t){case"sm":return"0.875rem";case"md":return"1rem";case"lg":return"1.125rem";default:return"1rem"}}function v(t=C){let e={...C,...t},o=R(e.borderRadius),r=T(e.fontSize);return`
|
|
2
|
+
.forms-expert {
|
|
3
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
4
|
+
font-size: ${r};
|
|
5
|
+
color: ${e.textColor};
|
|
6
|
+
background-color: ${e.backgroundColor};
|
|
7
|
+
padding: 1.5rem;
|
|
8
|
+
border-radius: ${o};
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.forms-expert * {
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.forms-expert-group {
|
|
17
|
+
margin-bottom: 1rem;
|
|
18
|
+
${e.labelPosition==="left"?"display: flex; align-items: flex-start; gap: 1rem;":""}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.forms-expert-label {
|
|
22
|
+
display: block;
|
|
23
|
+
font-weight: 500;
|
|
24
|
+
color: ${e.textColor};
|
|
25
|
+
${e.labelPosition==="left"?"width: 33%; flex-shrink: 0; padding-top: 0.5rem;":"margin-bottom: 0.25rem;"}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.forms-expert-required {
|
|
29
|
+
color: #ef4444;
|
|
30
|
+
margin-left: 0.25rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.forms-expert-input-wrapper {
|
|
34
|
+
${e.labelPosition==="left"?"flex: 1;":""}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.forms-expert-input,
|
|
38
|
+
.forms-expert-textarea,
|
|
39
|
+
.forms-expert-select {
|
|
40
|
+
width: 100%;
|
|
41
|
+
padding: 0.5rem 0.75rem;
|
|
42
|
+
border: 1px solid ${e.theme==="dark"?"#4b5563":"#d1d5db"};
|
|
43
|
+
border-radius: ${o};
|
|
44
|
+
font-size: ${r};
|
|
45
|
+
font-family: inherit;
|
|
46
|
+
background-color: ${e.theme==="dark"?"#374151":"#ffffff"};
|
|
47
|
+
color: ${e.textColor};
|
|
48
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.forms-expert-input:focus,
|
|
52
|
+
.forms-expert-textarea:focus,
|
|
53
|
+
.forms-expert-select:focus {
|
|
54
|
+
outline: none;
|
|
55
|
+
border-color: ${e.primaryColor};
|
|
56
|
+
box-shadow: 0 0 0 2px ${e.primaryColor}33;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.forms-expert-input.forms-expert-error,
|
|
60
|
+
.forms-expert-textarea.forms-expert-error,
|
|
61
|
+
.forms-expert-select.forms-expert-error {
|
|
62
|
+
border-color: #ef4444;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.forms-expert-textarea {
|
|
66
|
+
min-height: 100px;
|
|
67
|
+
resize: vertical;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.forms-expert-checkbox-group {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 0.5rem;
|
|
74
|
+
margin-bottom: 1rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.forms-expert-checkbox {
|
|
78
|
+
width: 1rem;
|
|
79
|
+
height: 1rem;
|
|
80
|
+
accent-color: ${e.primaryColor};
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.forms-expert-file-wrapper {
|
|
85
|
+
position: relative;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.forms-expert-file {
|
|
89
|
+
width: 100%;
|
|
90
|
+
padding: 0.5rem 0.75rem;
|
|
91
|
+
border: 1px solid ${e.theme==="dark"?"#4b5563":"#d1d5db"};
|
|
92
|
+
border-radius: ${o};
|
|
93
|
+
font-size: ${r};
|
|
94
|
+
background-color: ${e.theme==="dark"?"#374151":"#ffffff"};
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.forms-expert-error-message {
|
|
99
|
+
color: #ef4444;
|
|
100
|
+
font-size: 0.875rem;
|
|
101
|
+
margin-top: 0.25rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.forms-expert-button {
|
|
105
|
+
width: 100%;
|
|
106
|
+
padding: 0.625rem 1.25rem;
|
|
107
|
+
margin-top: 1rem;
|
|
108
|
+
font-weight: 500;
|
|
109
|
+
font-size: ${r};
|
|
110
|
+
font-family: inherit;
|
|
111
|
+
border-radius: ${o};
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
transition: opacity 0.2s, transform 0.1s;
|
|
114
|
+
${e.buttonStyle==="filled"?`background-color: ${e.primaryColor}; color: white; border: none;`:`background-color: transparent; color: ${e.primaryColor}; border: 2px solid ${e.primaryColor};`}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.forms-expert-button:hover {
|
|
118
|
+
opacity: 0.9;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.forms-expert-button:active {
|
|
122
|
+
transform: scale(0.98);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.forms-expert-button:disabled {
|
|
126
|
+
opacity: 0.5;
|
|
127
|
+
cursor: not-allowed;
|
|
128
|
+
transform: none;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.forms-expert-button-loading {
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
justify-content: center;
|
|
135
|
+
gap: 0.5rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.forms-expert-spinner {
|
|
139
|
+
width: 1rem;
|
|
140
|
+
height: 1rem;
|
|
141
|
+
border: 2px solid transparent;
|
|
142
|
+
border-top-color: currentColor;
|
|
143
|
+
border-radius: 50%;
|
|
144
|
+
animation: forms-expert-spin 0.6s linear infinite;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@keyframes forms-expert-spin {
|
|
148
|
+
to { transform: rotate(360deg); }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.forms-expert-honeypot {
|
|
152
|
+
position: absolute;
|
|
153
|
+
left: -9999px;
|
|
154
|
+
opacity: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.forms-expert-success {
|
|
158
|
+
text-align: center;
|
|
159
|
+
padding: 2rem;
|
|
160
|
+
color: ${e.textColor};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.forms-expert-success-icon {
|
|
164
|
+
width: 3rem;
|
|
165
|
+
height: 3rem;
|
|
166
|
+
margin: 0 auto 1rem;
|
|
167
|
+
color: #22c55e;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.forms-expert-success-message {
|
|
171
|
+
font-size: 1.125rem;
|
|
172
|
+
font-weight: 500;
|
|
173
|
+
margin-bottom: 0.5rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.forms-expert-branding {
|
|
177
|
+
text-align: center;
|
|
178
|
+
margin-top: 1rem;
|
|
179
|
+
padding-top: 0.75rem;
|
|
180
|
+
border-top: 1px solid ${e.theme==="dark"?"#374151":"#e5e7eb"};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.forms-expert-branding a {
|
|
184
|
+
color: ${e.theme==="dark"?"#9ca3af":"#6b7280"};
|
|
185
|
+
text-decoration: none;
|
|
186
|
+
font-size: 0.75rem;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.forms-expert-branding a:hover {
|
|
190
|
+
text-decoration: underline;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
${e.customCss||""}
|
|
194
|
+
`.trim()}function f(t){let e=document.createElement("div");return e.textContent=t,e.innerHTML}function N(t,e,o){let r=document.createElement("div");if(t.type==="heading"){r.className="forms-expert-group";let n=document.createElement("h3");if(n.className="forms-expert-heading",n.textContent=t.label||"",r.appendChild(n),t.content){let l=document.createElement("p");l.className="forms-expert-heading-subtitle",l.textContent=t.content,r.appendChild(l)}return r}if(t.type==="divider"){let n=document.createElement("hr");return n.className="forms-expert-divider",n}if(t.type==="paragraph"){if(r.className="forms-expert-group",t.label){let n=document.createElement("p");n.className="forms-expert-paragraph-label",n.textContent=t.label,r.appendChild(n)}if(t.content){let n=document.createElement("p");n.className="forms-expert-paragraph",n.textContent=t.content,r.appendChild(n)}return r}if(t.type==="hidden"){let n=document.createElement("input");return n.type="hidden",n.name=t.name,n.value=String(t.defaultValue??e??""),r.appendChild(n),r.style.display="none",r}if(t.type==="checkbox"||t.type==="toggle"||t.type==="consent"){r.className="forms-expert-checkbox-group";let n=document.createElement("input");n.type="checkbox",n.id=`mira-field-${t.name}`,n.name=t.name,n.className="forms-expert-checkbox",n.checked=!!e,t.required&&(n.required=!0);let l=document.createElement("label");l.htmlFor=n.id;let c=t.type==="consent"?t.consentText||t.label||t.name:t.label||t.name;if(l.innerHTML=`${f(c)}${t.required?'<span class="forms-expert-required">*</span>':""}`,r.appendChild(n),r.appendChild(l),t.type==="consent"&&t.consentUrl){let i=document.createElement("a");i.href=t.consentUrl,i.target="_blank",i.rel="noopener noreferrer",i.textContent="View policy",i.className="forms-expert-consent-link",r.appendChild(i)}if(o){let i=document.createElement("div");i.className="forms-expert-error-message",i.textContent=o,r.appendChild(i)}return r}r.className="forms-expert-group";let d=document.createElement("label");d.className="forms-expert-label",d.htmlFor=`mira-field-${t.name}`,d.innerHTML=`${f(t.label||t.name)}${t.required?'<span class="forms-expert-required">*</span>':""}`,r.appendChild(d);let m=document.createElement("div");m.className="forms-expert-input-wrapper";let s;switch(t.type){case"textarea":case"richText":s=document.createElement("textarea"),s.className="forms-expert-textarea",s.value=String(e||""),t.maxLength&&(s.maxLength=t.maxLength);break;case"select":case"dropdown":{s=document.createElement("select"),s.className="forms-expert-select";let n=document.createElement("option");n.value="",n.textContent=t.placeholder||"Select an option...",s.appendChild(n),(t.options||[]).forEach(c=>{let i=document.createElement("option");i.value=c,i.textContent=c,e===c&&(i.selected=!0),s.appendChild(i)});break}case"radio":{let n=document.createElement("div");if(n.className="forms-expert-radio-group",(t.options||[]).forEach(c=>{let i=document.createElement("label");i.className="forms-expert-radio-item";let a=document.createElement("input");a.type="radio",a.name=t.name,a.value=c,a.checked=e===c,i.appendChild(a),i.appendChild(document.createTextNode(` ${c}`)),n.appendChild(i)}),m.appendChild(n),o){let c=document.createElement("div");c.className="forms-expert-error-message",c.textContent=o,m.appendChild(c)}return r.appendChild(m),r}case"multiselect":{let n=document.createElement("div");n.className="forms-expert-multiselect-group";let l=e||[];if((t.options||[]).forEach(i=>{let a=document.createElement("label");a.className="forms-expert-checkbox-item";let p=document.createElement("input");p.type="checkbox",p.name=t.name,p.value=i,p.checked=l.includes(i),a.appendChild(p),a.appendChild(document.createTextNode(` ${i}`)),n.appendChild(a)}),m.appendChild(n),o){let i=document.createElement("div");i.className="forms-expert-error-message",i.textContent=o,m.appendChild(i)}return r.appendChild(m),r}case"rating":{let n=document.createElement("div");n.className="forms-expert-rating";let l=t.ratingMax||5,c=e||0;for(let i=1;i<=l;i++){let a=document.createElement("button");a.type="button",a.className=`forms-expert-rating-star ${i<=c?"active":""}`,a.textContent="\u2605",a.dataset.value=String(i),n.appendChild(a)}if(m.appendChild(n),o){let i=document.createElement("div");i.className="forms-expert-error-message",i.textContent=o,m.appendChild(i)}return r.appendChild(m),r}case"scale":case"opinionScale":{let n=document.createElement("div");n.className="forms-expert-scale";let l=t.min??(t.type==="opinionScale"?0:1),c=t.max??(t.type==="opinionScale"?10:5),i=e;for(let a=l;a<=c;a++){let p=document.createElement("button");p.type="button",p.className=`forms-expert-scale-btn ${i===a?"active":""}`,p.textContent=String(a),p.dataset.value=String(a),n.appendChild(p)}if(m.appendChild(n),t.lowLabel||t.highLabel){let a=document.createElement("div");a.className="forms-expert-scale-labels",a.innerHTML=`<span>${f(t.lowLabel||"")}</span><span>${f(t.highLabel||"")}</span>`,m.appendChild(a)}if(o){let a=document.createElement("div");a.className="forms-expert-error-message",a.textContent=o,m.appendChild(a)}return r.appendChild(m),r}case"slider":{s=document.createElement("input"),s.type="range",s.className="forms-expert-slider",s.min=String(t.min??0),s.max=String(t.max??100),s.step=String(t.step??1),s.value=String(e??t.min??0);break}case"file":s=document.createElement("input"),s.type="file",s.className="forms-expert-file",t.allowedMimeTypes?.length&&(s.accept=t.allowedMimeTypes.join(",")),t.multiple&&(s.multiple=!0);break;case"currency":{s=document.createElement("input"),s.type="number",s.className="forms-expert-input",s.value=String(e??""),t.min!==void 0&&(s.min=String(t.min)),t.max!==void 0&&(s.max=String(t.max)),s.step=String(t.step||.01);break}case"phone":s=document.createElement("input"),s.type="tel",s.className="forms-expert-input",s.value=String(e||"");break;case"url":s=document.createElement("input"),s.type="url",s.className="forms-expert-input",s.value=String(e||"");break;case"password":s=document.createElement("input"),s.type="password",s.className="forms-expert-input",s.value=String(e||"");break;case"time":s=document.createElement("input"),s.type="time",s.className="forms-expert-input",s.value=String(e||"");break;case"datetime":s=document.createElement("input"),s.type="datetime-local",s.className="forms-expert-input",s.value=String(e||"");break;case"colorPicker":s=document.createElement("input"),s.type="color",s.className="forms-expert-color",s.value=String(e||"#000000");break;case"dateRange":{let n=document.createElement("div");n.className="forms-expert-date-range";let l=e||{},c=document.createElement("input");c.type="date",c.className="forms-expert-input",c.name=`${t.name}.start`,c.value=l.start||"";let i=document.createElement("input");if(i.type="date",i.className="forms-expert-input",i.name=`${t.name}.end`,i.value=l.end||"",n.appendChild(c),n.appendChild(i),m.appendChild(n),o){let a=document.createElement("div");a.className="forms-expert-error-message",a.textContent=o,m.appendChild(a)}return r.appendChild(m),r}case"address":{let n=document.createElement("div");n.className="forms-expert-address";let l=t.addressFields||["street","city","state","zip","country"],c=e||{},i={street:"Street",street2:"Street Line 2",city:"City",state:"State",zip:"ZIP",country:"Country"};if(l.forEach(a=>{let p=document.createElement("input");p.type="text",p.className="forms-expert-input",p.name=`${t.name}.${a}`,p.placeholder=i[a]||a,p.value=c[a]||"",n.appendChild(p)}),m.appendChild(n),o){let a=document.createElement("div");a.className="forms-expert-error-message",a.textContent=o,m.appendChild(a)}return r.appendChild(m),r}case"name":{let n=document.createElement("div");n.className="forms-expert-name";let l=t.nameFields||["first","last"],c=e||{},i={prefix:"Prefix",first:"First Name",middle:"Middle",last:"Last Name",suffix:"Suffix"};if(l.forEach(a=>{let p=document.createElement("input");p.type="text",p.className="forms-expert-input",p.name=`${t.name}.${a}`,p.placeholder=i[a]||a,p.value=c[a]||"",n.appendChild(p)}),m.appendChild(n),o){let a=document.createElement("div");a.className="forms-expert-error-message",a.textContent=o,m.appendChild(a)}return r.appendChild(m),r}case"imageChoice":{let n=document.createElement("div");n.className="forms-expert-image-choice";let l=t.options||[],c=e;if(l.forEach(i=>{let a=document.createElement("button");if(a.type="button",a.className=`forms-expert-image-choice-item ${c===i.value?"active":""}`,a.dataset.value=i.value,i.imageUrl){let g=document.createElement("img");g.src=i.imageUrl,g.alt=i.label,a.appendChild(g)}let p=document.createElement("span");p.textContent=i.label,a.appendChild(p),n.appendChild(a)}),m.appendChild(n),o){let i=document.createElement("div");i.className="forms-expert-error-message",i.textContent=o,m.appendChild(i)}return r.appendChild(m),r}case"ranking":{let n=document.createElement("div");n.className="forms-expert-ranking";let l=t.options||[];if((e||[...l]).forEach((i,a)=>{let p=document.createElement("div");p.className="forms-expert-ranking-item",p.textContent=`${a+1}. ${i}`,p.dataset.value=i,n.appendChild(p)}),m.appendChild(n),o){let i=document.createElement("div");i.className="forms-expert-error-message",i.textContent=o,m.appendChild(i)}return r.appendChild(m),r}case"location":{let n=document.createElement("div");n.className="forms-expert-location";let l=e||{},c=document.createElement("input");c.type="text",c.className="forms-expert-input",c.name=`${t.name}.address`,c.placeholder="Address",c.value=l.address||"",n.appendChild(c);let i=document.createElement("div");i.className="forms-expert-location-coords";let a=document.createElement("input");a.type="number",a.className="forms-expert-input",a.name=`${t.name}.lat`,a.placeholder="Latitude",a.step="any",a.value=l.lat!==void 0?String(l.lat):"";let p=document.createElement("input");if(p.type="number",p.className="forms-expert-input",p.name=`${t.name}.lng`,p.placeholder="Longitude",p.step="any",p.value=l.lng!==void 0?String(l.lng):"",i.appendChild(a),i.appendChild(p),n.appendChild(i),m.appendChild(n),o){let g=document.createElement("div");g.className="forms-expert-error-message",g.textContent=o,m.appendChild(g)}return r.appendChild(m),r}default:s=document.createElement("input"),s.type=t.type==="email"?"email":t.type==="number"?"number":t.type==="date"?"date":"text",s.className="forms-expert-input",s.value=String(e||""),t.type==="number"&&(t.min!==void 0&&(s.min=String(t.min)),t.max!==void 0&&(s.max=String(t.max)),t.step!==void 0&&(s.step=String(t.step)));break}if(s.id=`mira-field-${t.name}`,s.name=t.name,t.placeholder&&"placeholder"in s&&(s.placeholder=t.placeholder),t.required&&(s.required=!0),o&&s.classList.add("forms-expert-error"),m.appendChild(s),o){let n=document.createElement("div");n.className="forms-expert-error-message",n.textContent=o,m.appendChild(n)}return r.appendChild(m),r}function S(t,e={},o={},r={}){let d=document.createElement("form");if(d.className="forms-expert",t.fields.forEach(n=>{let l=N(n,e[n.name],o[n.name]);d.appendChild(l)}),r.honeypot){let n=document.createElement("input");n.type="text",n.name="_hp",n.className="forms-expert-honeypot",n.tabIndex=-1,n.autocomplete="off",d.appendChild(n)}let m=document.createElement("input");m.type="hidden",m.name="pageUrl",m.value=typeof window<"u"?window.location.href:"",d.appendChild(m);let s=document.createElement("button");if(s.type="submit",s.className="forms-expert-button",s.disabled=r.isLoading||!1,r.isLoading?s.innerHTML=`
|
|
195
|
+
<span class="forms-expert-button-loading">
|
|
196
|
+
<span class="forms-expert-spinner"></span>
|
|
197
|
+
Submitting...
|
|
198
|
+
</span>
|
|
199
|
+
`:s.textContent=r.submitText||"Submit",d.appendChild(s),r.showBranding!==!1){let n=r.brandingText||"Powered by Forms Expert",l=r.brandingUrl||"https://mira.io",c=document.createElement("div");c.className="forms-expert-branding",c.innerHTML=`<a href="${l}" target="_blank" rel="noopener">${n}</a>`,d.appendChild(c)}return d}function w(t){let e=document.createElement("div");return e.className="forms-expert-success",e.innerHTML=`
|
|
200
|
+
<svg class="forms-expert-success-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
201
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
202
|
+
</svg>
|
|
203
|
+
<div class="forms-expert-success-message">${f(t)}</div>
|
|
204
|
+
`,e}function k(t){return t.reduce((e,o)=>({...e,[o.field]:o.message}),{})}var u=class extends Error{constructor(o,r,d,m){super(o);this.code=r;this.statusCode=d;this.retryAfter=m;this.name="FormsError"}},h=class extends Error{constructor(o){super("Validation failed");this.errors=o;this.name="FormValidationError"}};var x=class{constructor(e){this.apiKey=e.apiKey,this.resourceId=e.resourceId,this.baseUrl=(e.baseUrl||"https://api.formsapp.io/api/v1").replace(/\/$/,"")}buildUrl(e){let o=e.includes("?")?"&":"?";return`${this.baseUrl}${e}${o}token=${encodeURIComponent(this.apiKey)}`}async request(e,o,r){let d=this.buildUrl(o),m=await fetch(d,{method:e,headers:{"Content-Type":"application/json"},body:r?JSON.stringify(r):void 0}),s=await m.json();if(!m.ok)throw new u(s.message||"Request failed",s.code||"UNKNOWN_ERROR",m.status,s.retryAfter);return s}async isActive(e){return this.request("GET",`/f/${this.resourceId}/${e}/is-active`)}async validate(e,o){return this.request("POST",`/f/${this.resourceId}/${e}/validate`,{data:o})}async submit(e,o,r){let d=this.buildUrl(`/f/${this.resourceId}/${e}`);return Object.values(o).some(s=>s instanceof File||s instanceof FileList&&s.length>0)||r?.onProgress?this.submitWithFormData(d,o,r):this.request("POST",`/f/${this.resourceId}/${e}`,{data:o,pageUrl:r?.pageUrl||(typeof window<"u"?window.location.href:void 0),captchaToken:r?.captchaToken})}submitWithFormData(e,o,r){return new Promise((d,m)=>{let s=new FormData;for(let[c,i]of Object.entries(o))i instanceof File?s.append(c,i):i instanceof FileList?Array.from(i).forEach(a=>s.append(c,a)):i!=null&&s.append(`data[${c}]`,String(i));let n=r?.pageUrl||(typeof window<"u"?window.location.href:"");n&&s.append("pageUrl",n),r?.captchaToken&&s.append("captchaToken",r.captchaToken);let l=new XMLHttpRequest;r?.onProgress&&l.upload.addEventListener("progress",c=>{c.lengthComputable&&r.onProgress({loaded:c.loaded,total:c.total,percentage:Math.round(c.loaded/c.total*100)})}),l.addEventListener("load",()=>{try{let c=JSON.parse(l.responseText);l.status>=200&&l.status<300?d(c):m(new u(c.message||"Submission failed",c.code||"UNKNOWN_ERROR",l.status,c.retryAfter))}catch{m(new u("Invalid response","PARSE_ERROR",l.status))}}),l.addEventListener("error",()=>{m(new u("Network error","NETWORK_ERROR",0))}),l.addEventListener("abort",()=>{m(new u("Request aborted","ABORTED",0))}),l.open("POST",e),l.send(s)})}async trackView(e){let o=this.buildUrl(`/f/${this.resourceId}/${e}/view`);await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"}}).catch(()=>{})}getResourceId(){return this.resourceId}getBaseUrl(){return this.baseUrl}};var y=class{constructor(e,o,r={}){this.config=null;this.apiClient=e,this.slug=o,this.options=r}async initialize(){return this.config=await this.apiClient.isActive(this.slug),this.options.trackViews&&this.apiClient.trackView(this.slug),this.config}getConfig(){return this.config}isActive(){return this.config?.active??!1}requiresCaptcha(){return this.config?.settings?.captcha?.enabled??!1}getCaptchaProvider(){return this.config?.settings?.captcha?.provider}getSchema(){return this.config?.schema}async validate(e){return this.apiClient.validate(this.slug,e)}async submit(e,o){this.options.onSubmitStart?.();try{if(this.config?.mode==="schema"){let d=await this.validate(e);if(!d.valid)throw this.options.onValidationError?.(d.errors),new h(d.errors)}let r=await this.apiClient.submit(this.slug,e,o);return this.options.onSubmitSuccess?.(r),r}catch(r){throw r instanceof u&&this.options.onSubmitError?.(r),r}}getSuccessMessage(){return this.config?.settings?.successMessage||"Form submitted successfully!"}getRedirectUrl(){return this.config?.settings?.redirectUrl}},b=class{constructor(e){this.apiClient=new x(e)}async isActive(e){return this.apiClient.isActive(e)}async validate(e,o){return this.apiClient.validate(e,o)}async submit(e,o,r){return this.apiClient.submit(e,o,r)}form(e,o){return new y(this.apiClient,e,o)}async trackView(e){return this.apiClient.trackView(e)}async submitWithRetry(e,o,r){let d=r?.maxRetries??3,m=null;for(let s=0;s<d;s++)try{return await this.submit(e,o,r)}catch(n){if(m=n,n instanceof u){if(["VALIDATION_ERROR","CAPTCHA_REQUIRED","ORIGIN_NOT_ALLOWED"].includes(n.code))throw n;if(n.code.includes("RATE_LIMIT")){let l=n.retryAfter||Math.pow(2,s)*1e3;await new Promise(c=>setTimeout(c,l));continue}}await new Promise(l=>setTimeout(l,Math.pow(2,s)*1e3))}throw m}};var E=class{constructor(e,o){this.config=null;this.values={};this.errors={};this.isLoading=!1;this.isSubmitted=!1;this.styleEl=null;this.sdk=new b(e),this.options=o;let r=o.target;if(typeof r=="string"){let d=document.querySelector(r);if(!d)throw new Error(`Element not found: ${r}`);this.container=d}else this.container=r}async init(){try{if(this.config=await this.sdk.isActive(this.options.slug),!this.config.active){this.renderError("This form is not available");return}this.options.trackViews&&this.sdk.trackView(this.options.slug),this.injectStyles(),this.render()}catch(e){this.renderError("Failed to load form"),this.options.onError?.(e)}}injectStyles(){this.styleEl||(this.styleEl=document.createElement("style"),this.styleEl.id=`forms-expert-styles-${this.options.slug}`,this.styleEl.textContent=v(this.config?.schema?.styling),document.head.appendChild(this.styleEl))}render(){if(!this.config?.schema)return;if(this.isSubmitted){this.container.innerHTML="";let o=this.config.settings?.successMessage||"Form submitted successfully!";this.container.appendChild(w(o));return}let e=S(this.config.schema,this.values,this.errors,{honeypot:this.config.settings?.honeypot,showBranding:this.config.branding?.enabled!==!1,brandingText:this.config.branding?.text,brandingUrl:this.config.branding?.url,submitText:this.options.submitText,isLoading:this.isLoading});e.addEventListener("input",o=>{let r=o.target;r.name&&r.name!=="_hp"&&r.name!=="pageUrl"&&(r.type==="checkbox"?this.values[r.name]=r.checked:r.type==="file"?this.values[r.name]=r.multiple?r.files:r.files?.[0]:this.values[r.name]=r.value,this.errors[r.name]&&(delete this.errors[r.name],this.render()))}),e.addEventListener("submit",o=>{o.preventDefault(),this.handleSubmit()}),this.container.innerHTML="",this.container.appendChild(e)}async handleSubmit(){if(!(this.isLoading||!this.config)){this.isLoading=!0,this.errors={},this.render();try{let e=await this.sdk.submit(this.options.slug,this.values);this.isLoading=!1,this.isSubmitted=!0,this.render(),this.options.onSuccess?.(e);let o=this.options.redirectUrl||this.config.settings?.redirectUrl;o&&setTimeout(()=>{window.location.href=o},1500),this.options.resetOnSuccess&&setTimeout(()=>{this.reset()},3e3)}catch(e){this.isLoading=!1,e instanceof h?(this.errors=k(e.errors),this.options.onValidationError?.(e.errors)):this.options.onError?.(e),this.render()}}}reset(){this.values={},this.errors={},this.isLoading=!1,this.isSubmitted=!1,this.render()}renderError(e){this.container.innerHTML=`
|
|
205
|
+
<div class="forms-expert" style="text-align: center; padding: 2rem; color: #ef4444;">
|
|
206
|
+
<p>${e}</p>
|
|
207
|
+
</div>
|
|
208
|
+
`}destroy(){this.container.innerHTML="",this.styleEl?.remove(),this.styleEl=null}};function F(){document.querySelectorAll("[data-forms-expert]").forEach(e=>{let o=e.getAttribute("data-api-key"),r=e.getAttribute("data-resource-id"),d=e.getAttribute("data-forms-expert"),m=e.getAttribute("data-base-url")||void 0;if(!o||!r||!d){console.error("Forms Expert: Missing required attributes",{apiKey:!!o,resourceId:!!r,slug:!!d});return}new E({apiKey:o,resourceId:r,baseUrl:m},{target:e,slug:d,trackViews:e.getAttribute("data-track-views")==="true",submitText:e.getAttribute("data-submit-text")||void 0,resetOnSuccess:e.getAttribute("data-reset")==="true"}).init()})}typeof window<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",F):F());export{E as FormWidget,F as autoInit,k as errorsToRecord,v as generateFormStyles,N as renderField,S as renderForm,w as renderSuccess};
|
|
209
|
+
//# sourceMappingURL=index.js.map
|