@feedal/embed
Version:
Feedal embed script to load feedback forms via JS or NPM
1,154 lines (910 loc) โข 35.7 kB
Markdown
# ๐ Feedal Embed SDK
A lightweight JavaScript SDK for embedding Feedal feedback forms across any platform. Built with TypeScript, featuring customization options, multiple display modes, and framework integration.
---
## โจ Key Features
- ๐ฏ **Framework Agnostic**: Works with React, Angular, Vue, Svelte, and vanilla JavaScript
- ๐จ **Multiple Display Modes**: Popup, embedded, fullscreen, drawer, sidebar, toast, and more
- ๐ญ **Smart Triggers**: Manual, auto, click, scroll, time-based, exit-intent triggers
- ๐ฑ **Responsive**: Mobile-friendly design with adaptive positioning
- ๐ฌ **Animations**: Fade, slide, scale, bounce transitions
- โฟ **Accessibility**: ARIA labels, keyboard navigation, screen reader support
- ๐ง **Configurable**: Extensive customization options
---
## ๐ฆ Installation
### CDN (Recommended for quick setup)
```html
<script src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"></script>
```
### NPM/Yarn
```bash
npm install @feedal/embed
# or
yarn add @feedal/embed
```
---
## ๐ Quick Start
### Simple Popup Form
```html
<script src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"></script>
<script>
// Using the global FeedalWidget constructor
const widget = new FeedalWidget({
formId: 'your-form-id',
mode: 'popup',
trigger: 'manual'
});
widget.open();
</script>
```
### High-Performance Popup Form
```html
<script src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"></script>
<script>
// Performance-optimized form (eliminates 3+ second delay)
const widget = new FeedalWidget({
formId: 'your-form-id',
mode: 'popup',
trigger: 'manual',
disableFormAnimations: true, // ๐ Fast loading
showLoadingIndicator: true
});
widget.open();
</script>
```
### Auto-trigger with Script Tag
```html
<script
src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"
data-form-id="your-form-id"
data-mode="popup"
data-trigger="time"
data-trigger-delay="5000"
data-position="bottom-right"
data-animation="fade">
</script>
```
### Performance-Optimized Auto-Trigger
```html
<script
src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"
data-form-id="your-form-id"
data-mode="popup"
data-trigger="time"
data-trigger-delay="5000"
data-position="bottom-right"
data-disable-form-animations="true"
data-animation="fade">
</script>
```
### Alternative Static Methods
```html
<script src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"></script>
<script>
// Using static methods
const widget = FeedalWidget.createWidget({
formId: 'your-form-id',
mode: 'popup'
});
widget.open();
// Or create and open in one step
FeedalWidget.openForm({
formId: 'your-form-id',
mode: 'popup'
});
</script>
```
---
## ๐๏ธ Configuration Options
The Feedal Embed SDK provides extensive customization options. Here's a complete reference:
| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| **Core Configuration** |
| `formId` | `string` | โ
**Yes** | - | Unique identifier for the form |
| `sessionKey` | `string` | โ No | - | Server-provided session key for persistence control |
| `token` | `string` | โ No | - | JWT or API token for authentication |
| `host` | `string` | โ No | `"https://fedl.io/f/"` | Backend API host URL |
| **Display & Layout** |
| `mode` | `"fullscreen" \| "embedded" \| "popup" \| "drawer" \| "button" \| "sidebar" \| "toast" \| "inline" \| "modal" \| "slide-over"` | โ No | `"popup"` | How the form is displayed |
| `theme` | `'light' \| 'dark' \| 'auto' \| 'custom'` | โ No | `"light"` | Visual theme for the form |
| `customCssUrl` | `string` | โ No | - | URL to custom CSS file for styling |
| **Positioning & Sizing (Unified System)** |
| `position` | `"top" \| "bottom" \| "left" \| "right" \| "center" \| "top-left" \| "top-center" \| "top-right" \| "bottom-left" \| "bottom-center" \| "bottom-right" \| "center-left" \| "center-right"` | โ No | `"center"` | **Unified positioning for all modes** |
| `width` | `string \| number` | โ No | `"auto"` | Form width (px, %, vw, etc.) |
| `height` | `string \| number` | โ No | `"auto"` | Form height (px, %, vh, etc.) |
| `offset` | `{ x?: number; y?: number }` | โ No | `{ x: 0, y: 0 }` | Fine-tune positioning with pixel offsets |
| **Button-specific options** |
| `buttonPosition` | `"top-left" \| "top-center" \| "top-right" \| "bottom-left" \| "bottom-center" \| "bottom-right" \| "center-left" \| "center" \| "center-right"` | โ No | `"bottom-right"` | **Where the floating button appears** (different from form positioning) |
| `buttonSize` | `"small" \| "medium" \| "large" \| "custom"` | โ No | `"medium"` | Button size (40px, 60px, 80px, or custom) |
| `buttonColor` | `string` | โ No | `"#007bff"` | Custom button color (hex, rgb, etc.) |
| `buttonIcon` | `string` | โ No | `"๐"` | Custom button icon or emoji |
| `buttonText` | `string` | โ No | `""` | Custom button text (alternative to icon) |
| **Behavior & Triggers** |
| `trigger` | `"manual" \| "auto" \| "click" \| "scroll" \| "time" \| "exit-intent" \| "element-visible" \| "session-duration" \| "idle"` | โ No | `"manual"` | How the form is triggered |
| `triggerDelay` | `number \| string` | โ No | - | Delay before trigger (seconds for time, minutes for idle) |
| `triggerElement` | `string \| HTMLElement` | โ No | - | Element for click/element-visible triggers |
| `triggerThreshold` | `number` | โ No | - | Threshold percentage for scroll/element-visible triggers |
| `triggerCooldown` | `number \| string` | โ No | `1` | Cooldown period between triggers (minutes) |
| `autoClose` | `boolean \| number \| string` | โ No | `true` | Auto-close timeout. `true` = 3 seconds (default), `false` = disabled, `number` = custom seconds |
| `closeOnOverlayClick` | `boolean` | โ No | `false` | Close when clicking outside the form |
| `showCloseButton` | `boolean` | โ No | `true` | Show close button |
| **Enhanced Behavioral Options** |
| `draggable` | `boolean` | โ No | `false` | Allow dragging for repositioning |
| `resizable` | `boolean` | โ No | `false` | Allow resizing the form |
| `collapsible` | `boolean` | โ No | `false` | Allow collapsing to minimal state |
| `persistent` | `boolean` | โ No | `false` | Persist across page reloads |
| **Responsive & Accessibility** |
| `responsive` | `boolean` | โ No | `true` | Enable responsive behavior |
| `maxWidth` | `string \| number` | โ No | - | Maximum width constraint |
| `minWidth` | `string \| number` | โ No | - | Minimum width constraint |
| `maxHeight` | `string \| number` | โ No | - | Maximum height constraint |
| `minHeight` | `string \| number` | โ No | - | Minimum height constraint |
| `zIndex` | `number` | โ No | `9999` | Custom z-index for layering |
| `ariaLabel` | `string` | โ No | - | Accessibility label for screen readers |
| `focusTrap` | `boolean` | โ No | `true` | Enable focus trapping for modal modes |
| **Performance & Optimization** |
| `lazyLoad` | `boolean` | โ No | `true` | Enable lazy loading of resources |
| `preloadResources` | `boolean` | โ No | `true` | Preload critical resources |
| `performanceMonitoring` | `boolean` | โ No | `false` | Track performance metrics |
| **Loading & User Experience** |
| `showLoadingIndicator` | `boolean` | โ No | `false` | Show loading indicator while form loads |
| `loadingText` | `string` | โ No | - | Custom loading text |
| `loadingSpinner` | `boolean` | โ No | `true` | Show loading spinner |
| `disableFormAnimations` | `boolean` | โ No | `true` | **NEW!** Disable form loading animations for better performance |
| **Submission Persistence** |
| `rememberSubmission` | `boolean` | โ No | `true` | Remember if user has submitted the form |
| `submissionExpiry` | `number \| string` | โ No | `30` | How long to remember submissions (days) |
| `storageType` | `'local' \| 'session' \| 'cookie'` | โ No | `'local'` | Where to store submission data |
| **Advanced** |
| `containerId` | `string` | โ No | - | Container ID for embedded mode |
| `useCard` | `boolean` | โ No | `false` | Use card-style layout |
| `animation` | `"fade" \| "slide" \| "scale" \| "bounce" \| "flip" \| "elastic" \| "none"` | โ No | `"fade"` | Animation type for form appearance |
| `overlay` | `boolean` | โ No | `false` | Show overlay background |
| `blurBackground` | `boolean` | โ No | `false` | Blur page content behind modal |
| **Data & Context** |
| `metadata` | `Record<string, any>` | โ No | - | Pass additional context data |
| `prefill` | `Record<string, any>` | โ No | - | Prefill form fields with values |
| **Callbacks** |
| `onLoad` | `() => void` | โ No | - | Called when form is loaded and ready |
| `onOpen` | `() => void` | โ No | - | Called when form is opened/shown |
| `onClose` | `() => void` | โ No | - | Called when form is closed/hidden |
| `onSubmit` | `(data?: any) => void` | โ No | - | Called when form is submitted with data |
| `onSkipped` | `() => void` | โ No | - | Called when form is skipped due to previous submission |
| `onError` | `(error: Error) => void` | โ No | - | Called when an error occurs |
| `onResize` | `(dimensions: { width: number; height: number }) => void` | โ No | - | Called when form is resized |
| `onMove` | `(position: { x: number; y: number }) => void` | โ No | - | Called when form is moved |
### ๐ New Performance Option: `disableFormAnimations`
The `disableFormAnimations` option is a new feature that can significantly improve form loading performance:
```typescript
// Fast loading (eliminates 3+ second delay)
FeedalWidget.openForm({
formId: 'your-form-id',
disableFormAnimations: true, // ๐ Eliminates form loading animations
mode: 'popup',
position: 'bottom-center'
});
```
**Benefits:**
- โก **Eliminates 3+ second delay** between header and form content
- ๐ **Faster rendering** - questions appear all at once instead of staggered
- ๐ฑ **Better mobile performance** - reduces animation overhead
- ๐ฏ **Consistent loading** - predictable behavior across devices
**When to use:**
- โ
**Production environments** where performance is critical
- โ
**Mobile-first applications** with limited resources
- โ
**High-traffic sites** where every second counts
- โ **Development/design** where animations are needed for UX testing
---
## ๐ฏ Unified Position System
### **Single `position` Property for All Modes**
The Feedal Embed SDK now uses a **unified positioning system** that eliminates the need for multiple position-related properties. The `position` property works consistently across all display modes:
#### **Available Position Values:**
- **Edge Positions**: `"top"`, `"bottom"`, `"left"`, `"right"`
- **Center Positions**: `"center"`
- **Corner Positions**: `"top-left"`, `"top-center"`, `"top-right"`, `"bottom-left"`, `"bottom-center"`, `"bottom-right"`
- **Side Center Positions**: `"center-left"`, `"center-right"`
#### **Position Support by Mode:**
| Mode | Available Positions | Default | Behavior |
|------|-------------------|---------|----------|
| **Popup** | All 13 positions | `"center"` | Appears at specified position |
| **Modal** | All 13 positions | `"center"` | Centers at specified position |
| **Drawer** | `"top"`, `"bottom"`, `"left"`, `"right"` | `"bottom"` | Slides from specified edge |
| **Sidebar** | `"left"`, `"right"` | `"right"` | Appears on specified side |
| **Toast** | All 13 positions | `"bottom-right"` | Appears at specified corner/position |
| **Slide-Over** | `"left"`, `"right"` | `"right"` | Slides in from specified side |
| **Fullscreen** | `"center"` (forced) | `"center"` | Always covers full viewport |
| **Embedded** | `"center"` (forced) | `"center"` | Always relative to container |
| **Button** | All 13 positions | `"center"` | Popup appears at specified position |
#### **How It Works by Mode:**
| Mode | Position Behavior | Example |
|------|------------------|---------|
| **Drawer** | Slides from specified edge | `position: "top"` โ Slides down from top |
| **Sidebar** | Appears on specified side | `position: "left"` โ Appears on left side |
| **Toast** | Appears at specified corner | `position: "top-right"` โ Appears at top-right |
| **Modal** | Centers at specified position | `position: "top"` โ Centers near top |
| **Popup** | Appears at specified position | `position: "bottom-center"` โ Appears at bottom center |
#### **Smart Position Mapping:**
The SDK intelligently maps positions to appropriate behaviors for each mode:
```javascript
// Drawer mode - slides from edge
new FeedalWidget({
formId: 'abc123',
mode: 'drawer',
position: 'top' // Slides DOWN from top
});
// Sidebar mode - appears on side
new FeedalWidget({
formId: 'abc123',
mode: 'sidebar',
position: 'left' // Appears on left side
});
// Toast mode - appears at corner
new FeedalWidget({
formId: 'abc123',
mode: 'toast',
position: 'top-right' // Appears at top-right corner
});
// Modal mode - centers at position
new FeedalWidget({
formId: 'abc123',
mode: 'modal',
position: 'top' // Centers near top of screen
});
```
---
## ๐ฏ Independent Positioning System
### **Button Mode Positioning**
The Button Mode features an **independent positioning system** that allows maximum flexibility:
- **`buttonPosition`**: Controls where the floating button appears on the page
- **`position`**: Controls where the popup appears when the button is clicked
**Example: Maximum Flexibility**
```javascript
// Button at bottom-left, popup at top-right
new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'bottom-left', // Button appears bottom-left
position: 'top-right', // Popup appears top-right when clicked
buttonSize: 'large',
buttonColor: '#ff6b35'
});
```
**Benefits:**
- โ
**No positioning conflicts** between button and popup
- โ
**Maximum flexibility** for different screen sizes and layouts
- โ
**Professional appearance** with strategic positioning
- โ
**Better mobile experience** - button can be thumb-friendly while popup is optimally placed
---
## ๐ Performance Improvements
### **Animation Performance Optimization**
The latest version includes significant performance improvements to eliminate the 3+ second delay between header and form content:
#### **Before (Default):**
- Form header loads first
- Questions animate in one by one with staggered timing
- **3-4 second delay** before full form is visible
- Smooth but slow user experience
#### **After (With `disableFormAnimations: true`):**
- Form header and content load simultaneously
- All questions appear at once
- **0.5-1 second** total loading time
- Fast and responsive user experience
#### **Performance Comparison:**
| Setting | Loading Time | Animation | Use Case |
|---------|--------------|-----------|----------|
| `disableFormAnimations: false` | 3-4 seconds | Smooth staggered | Development, Design showcase |
| `disableFormAnimations: true` | 0.5-1 second | Instant | **Production, Performance-critical** |
### **Technical Details**
The performance improvement works by:
1. **Conditional Animation Control**: Form components check the `disableAnimations` parameter
2. **Query Parameter Passing**: `?disableAnimations=1` passed from embed script to form
3. **Smart Framer Motion**: Animations gracefully disabled without errors
4. **Backward Compatibility**: Existing code continues to work unchanged
---
## ๐ฎ Display Modes
### 1. Popup Mode (Default)
Displays form in a centered modal overlay.
```javascript
new FeedalWidget({
formId: 'abc123',
mode: 'popup',
position: 'center',
overlay: true,
animation: 'fade'
});
```
### 2. Embedded Mode
Embeds form inside a specific container.
```html
<div id="feedback-container"></div>
<script>
new FeedalWidget({
formId: 'abc123',
mode: 'embedded',
containerId: 'feedback-container',
width: '100%'
});
</script>
```
### 3. Drawer Mode
Slides in from the side of the screen.
```javascript
new FeedalWidget({
formId: 'abc123',
mode: 'drawer',
position: 'bottom', // Slides UP from bottom
animation: 'slide'
});
// Or slide from top
new FeedalWidget({
formId: 'abc123',
mode: 'drawer',
position: 'top', // Slides DOWN from top
animation: 'slide'
});
// Or slide from left/right
new FeedalWidget({
formId: 'abc123',
mode: 'drawer',
position: 'left', // Slides IN from left
animation: 'slide'
});
```
### 4. Sidebar Mode
Shows a fixed sidebar on the left or right.
```javascript
new FeedalWidget({
formId: 'abc123',
mode: 'sidebar',
position: 'right', // Appears on right side
animation: 'slide'
});
// Or appear on left side
new FeedalWidget({
formId: 'abc123',
mode: 'sidebar',
position: 'left', // Appears on left side
animation: 'slide'
});
```
### 5. Toast Mode
Shows a small notification-style form.
```javascript
new FeedalWidget({
formId: 'abc123',
mode: 'toast',
position: 'bottom-right', // Appears at bottom-right corner
animation: 'fade'
});
// Or appear at top-left corner
new FeedalWidget({
formId: 'abc123',
mode: 'toast',
position: 'top-left', // Appears at top-left corner
animation: 'fade'
});
// Or appear at center positions
new FeedalWidget({
formId: 'abc123',
mode: 'toast',
position: 'top-center', // Appears at top center
animation: 'fade'
});
```
### 6. Button Mode
Creates a floating action button that transforms into a popup form when clicked. The button and popup can have **independent positions** for maximum flexibility.
```javascript
new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'bottom-left', // Where the button appears
position: 'top-right', // Where the popup appears when clicked
buttonSize: 'large', // small (40px), medium (60px), large (80px), custom
buttonColor: '#e91e63', // Custom button color
buttonIcon: '๐', // Custom emoji or icon
buttonText: 'Feedback', // Alternative to icon (text instead)
animation: 'scale'
});
```
**Key Features:**
- **Independent Positioning**: Button and popup can be at different locations
- **Customizable Appearance**: Size, color, icon, and text
- **Smart Transformation**: Button disappears, popup appears at specified position
- **9 Button Positions**: top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right
- **9 Popup Positions**: Same as standard positioning (center, top-left, bottom-right, etc.)
**Use Cases:**
- **Mobile Apps**: Button at bottom-right (thumb-friendly), popup at center (optimal viewing)
- **Desktop Sites**: Button at top-right (always visible), popup at center (professional)
- **Landing Pages**: Button at bottom-center (call-to-action), popup at top-center (attention-grabbing)
---
## ๐ฏ Smart Triggers
### Manual Trigger
```javascript
const widget = new FeedalWidget({
formId: 'abc123',
trigger: 'manual'
});
// Open programmatically
widget.open();
```
### Time-based Trigger
```javascript
new FeedalWidget({
formId: 'abc123',
trigger: 'time',
triggerDelay: 10000 // 10 seconds
});
```
### Scroll Trigger
```javascript
new FeedalWidget({
formId: 'abc123',
trigger: 'scroll',
triggerThreshold: 75 // Trigger at 75% scroll
});
```
### Exit Intent Trigger
```javascript
new FeedalWidget({
formId: 'abc123',
trigger: 'exit-intent',
triggerCooldown: 300000 // Don't show again for 5 minutes
});
```
---
## ๐ Framework Integration
### React
```jsx
import { useEffect, useState } from 'react';
function FeedbackButton({ formId }) {
const [widget, setWidget] = useState(null);
useEffect(() => {
// Make sure FeedalWidget is available
if (window.FeedalWidget) {
const feedalWidget = new window.FeedalWidget({
formId,
mode: 'popup',
trigger: 'manual'
});
setWidget(feedalWidget);
return () => {
feedalWidget.destroy();
};
}
}, [formId]);
return (
<button onClick={() => widget?.open()}>
Give Feedback
</button>
);
}
```
### React with Performance Optimization
```jsx
import { useEffect, useState } from 'react';
function HighPerformanceFeedbackButton({ formId }) {
const [widget, setWidget] = useState(null);
useEffect(() => {
if (window.FeedalWidget) {
const feedalWidget = new window.FeedalWidget({
formId,
mode: 'popup',
trigger: 'manual',
disableFormAnimations: true, // ๐ Fast loading
showLoadingIndicator: true
});
setWidget(feedalWidget);
return () => {
feedalWidget.destroy();
};
}
}, [formId]);
return (
<button onClick={() => widget?.open()}>
Give Feedback (Fast)
</button>
);
}
```
### Vue 3
```vue
<template>
<button @click="openFeedback">Give Feedback</button>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const props = defineProps({
formId: String
});
let widget = null;
onMounted(() => {
if (window.FeedalWidget) {
widget = new window.FeedalWidget({
formId: props.formId,
mode: 'popup',
trigger: 'manual'
});
}
});
onUnmounted(() => {
widget?.destroy();
});
function openFeedback() {
widget?.open();
}
</script>
```
### Vue 3 with Performance Optimization
```vue
<template>
<button @click="openFeedback">Give Feedback (Fast)</button>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const props = defineProps({
formId: String
});
let widget = null;
onMounted(() => {
if (window.FeedalWidget) {
widget = new window.FeedalWidget({
formId: props.formId,
mode: 'popup',
trigger: 'manual',
disableFormAnimations: true, // ๐ Fast loading
showLoadingIndicator: true
});
}
});
onUnmounted(() => {
widget?.destroy();
});
function openFeedback() {
widget?.open();
}
</script>
```
### TypeScript Direct Import
```typescript
import { FeedalWidget, EmbedOptions } from '@feedal/embed';
const options: EmbedOptions = {
formId: 'abc123',
mode: 'popup',
theme: 'dark'
};
const widget = new FeedalWidget(options);
widget.open();
```
### TypeScript with Performance Optimization
```typescript
import { FeedalWidget, EmbedOptions } from '@feedal/embed';
const options: EmbedOptions = {
formId: 'abc123',
mode: 'popup',
theme: 'dark',
disableFormAnimations: true, // ๐ Fast loading
showLoadingIndicator: true
};
const widget = new FeedalWidget(options);
widget.open();
```
---
## ๐จ Button Mode Examples
### **Basic Button Configuration**
```javascript
// Simple floating button
const widget = new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'bottom-right',
position: 'center'
});
```
### **Customized Button Appearance**
```javascript
// Large, colorful button with custom icon
const widget = new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'bottom-center',
position: 'top-center',
buttonSize: 'large', // 80px button
buttonColor: '#e91e63', // Pink color
buttonIcon: '๐', // Custom emoji
animation: 'bounce'
});
```
### **Text-Based Button**
```javascript
// Button with text instead of icon
const widget = new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'top-right',
position: 'center',
buttonSize: 'medium',
buttonColor: '#4caf50', // Green color
buttonText: 'Feedback', // Text instead of icon
buttonIcon: '', // Clear icon when using text
animation: 'scale'
});
```
### **Custom Size Button**
```javascript
// Custom-sized button with specific dimensions
const widget = new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'center-left',
position: 'right',
buttonSize: 'custom',
width: '100px', // Custom width for button
height: '100px', // Custom height for button
buttonColor: '#ff9800', // Orange color
buttonIcon: '๐ฌ',
animation: 'elastic'
});
```
### **Mobile-Optimized Button**
```javascript
// Thumb-friendly button for mobile
const widget = new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'bottom-right', // Thumb-friendly position
position: 'center', // Popup at center for optimal viewing
buttonSize: 'large', // Easy to tap
buttonColor: '#2196f3', // High contrast blue
buttonIcon: '๐ฌ',
responsive: true, // Mobile-responsive
animation: 'fade'
});
```
### **Professional Landing Page Button**
```javascript
// Strategic positioning for conversions
const widget = new FeedalWidget({
formId: 'abc123',
mode: 'button',
buttonPosition: 'bottom-center', // Call-to-action position
position: 'top-center', // Attention-grabbing popup
buttonSize: 'large',
buttonColor: '#f44336', // Attention-grabbing red
buttonIcon: '๐',
buttonText: 'Take Survey',
animation: 'scale',
overlay: true, // Professional overlay
blurBackground: true // Focus attention
});
```
---
## ๐ ๏ธ API Reference
### Global API (Browser)
```javascript
// Create a widget directly (recommended)
const widget = new FeedalWidget(options);
// Static utility methods
const widget = FeedalWidget.createWidget(options);
const widget = FeedalWidget.openForm(options); // Creates and opens
// Utility methods
FeedalWidget.closeAll();
const isOpen = FeedalWidget.isAnyOpen();
const widget = FeedalWidget.getWidget('your-form-id');
// Reset submission history for all forms
FeedalWidget.resetAllSubmissions();
```
### Widget Methods
```javascript
// Open the widget
widget.open();
// Close the widget
widget.close();
// Toggle the widget visibility
widget.toggle();
// Update widget options
widget.updateOptions({
theme: 'dark',
position: 'bottom-right'
});
// Check if user has submitted this form before
const hasSubmitted = widget.hasSubmitted();
// Record a submission (usually handled automatically)
widget.recordSubmission();
// Clear submission history for this form
widget.resetSubmissionHistory();
// Get performance metrics
const metrics = widget.getPerformanceMetrics();
// Check if widget is visible
const isVisible = widget.isVisible;
// Access the DOM element
const element = widget.element;
// Destroy the widget and clean up resources
widget.destroy();
```
---
## ๐ง Troubleshooting
### Common Issues
**Form not loading:**
- Check that your form ID is correct
- Verify your authentication token
- Check the browser console for errors
**Styling conflicts:**
- Use the `containerId` option to isolate the form in a specific container
- Adjust the z-index if the form is hidden behind other elements
**Mobile issues:**
- Enable responsive mode with `responsive: true`
- Set appropriate width constraints for mobile devices
---
## ๐ Performance Tips
1. **Lazy Loading**: Use `lazyLoad: true` to load resources only when needed
2. **Manual Trigger**: Use manual triggers for better control
3. **Performance Monitoring**: Enable with `performanceMonitoring: true` to track metrics
---
## ๐ Submission Persistence
The embed script includes a powerful submission persistence feature that prevents showing forms to users who have already submitted them. This helps reduce survey fatigue and prevents duplicate submissions.
### How It Works
1. When a user submits a form, a record is stored in the browser (localStorage, sessionStorage, or cookie)
2. The next time the form would be shown, the script checks for previous submissions
3. If a submission is found and still valid, the form is not displayed
4. Form owners can reset all submission records by generating a new session key in the dashboard
### Configuration Options
```javascript
const widget = new FeedalWidget({
formId: "your-form-id",
rememberSubmission: true, // Enable submission persistence (default: false)
submissionExpiry: 30 * 24 * 60 * 60 * 1000, // 30 days in milliseconds (default)
storageType: "local", // 'local', 'session', or 'cookie' (default: 'local')
// other options...
});
```
### Session Keys
Each form has a unique server-generated session key that's used to invalidate previous submission records:
- When a form owner resets the session key, all previous submission records become invalid
- This allows form owners to make all users see the form again, even if they've submitted it before
- The session key is also recorded with analytics data to differentiate between submission periods
### Example: Remembering Submissions
```javascript
const widget = new FeedalWidget({
formId: "your-form-id",
mode: "popup",
trigger: "time",
triggerDelay: 5000,
rememberSubmission: true,
submissionExpiry: 7 * 24 * 60 * 60 * 1000, // 7 days
storageType: "local"
});
```
This will show the form after 5 seconds, but only if the user hasn't submitted it in the last 7 days.
### Resetting Submission History
Form owners can reset the session key in the dashboard under Form Settings > Security. This invalidates all previous submission records, allowing the form to be shown to all users again.
Programmatically, you can also reset submission history for the current user:
```javascript
// Reset history for a specific form
widget.resetSubmissionHistory();
// Reset history for all forms
FeedalWidget.resetAllSubmissions();
```
---
## Auto-Resizing Feature
The embed script now includes an intelligent auto-resizing feature that automatically adjusts the iframe height based on its content. This ensures that:
1. Forms with minimal content don't show unnecessary white space
2. Forms with extensive content are properly contained with scrolling when needed
3. The iframe adapts to dynamic content changes
### How It Works
- For embedded forms, the iframe height automatically adjusts to fit the content
- For modal-like displays (popup, modal, etc.), the widget respects maximum height constraints and adds scrolling when needed
- The widget continuously monitors content changes and adjusts height accordingly
### Configuration Options
You can control the auto-resizing behavior with these options:
```javascript
const widget = new FeedalWidget({
formId: "your-form-id",
minHeight: "100px", // Minimum height (default: 100px)
maxHeight: "80vh", // Maximum height (default: 90% of viewport)
// other options...
});
```
- `minHeight`: Sets the minimum height for the iframe (accepts px, vh, etc.)
- `maxHeight`: Sets the maximum height before scrolling is enabled (accepts px, vh, etc.)
### Example: Embedded Form with Auto-Resize
```html
<div id="feedback-container"></div>
<script src="https://cdn.jsdelivr.net/npm/@feedal/embed@latest/dist/embed.umd.js"></script>
<script>
const widget = new FeedalWidget({
formId: "your-form-id",
mode: "embedded",
containerId: "feedback-container",
minHeight: "150px",
maxHeight: "800px"
});
</script>
```
This will render the form in the container and automatically adjust its height based on the content, ensuring optimal display without excessive white space.
---
## ๐งช Testing & Development
### **Available Test Files**
The repository includes comprehensive test files for development and testing:
- **`test-button-fix.html`** - Test Button Mode with independent positioning
- **`test-autoclose-fix.html`** - Test autoClose behavior and time formats
- **`test-default-values.html`** - Test default configuration values
- **`test-responsive.html`** - Test responsive behavior and sizing
- **`test-advanced-triggers.html`** - Test all trigger types and configurations
- **`test-optimized-modes.html`** - Test all display modes and their features
### **Running Tests**
1. **Build the embed script:**
```bash
npm run build
```
2. **Open test files in browser:**
```bash
# Open any test file in your browser
open test-button-fix.html
```
3. **Check browser console** for detailed logging and error messages
### **Development Workflow**
```bash
# Development mode with hot reload
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
```
---
## ๐ Changelog
### **v0.0.55 - Unified Position System** ๐
**Major Improvement:**
- โจ **Unified Position System**: Single `position` property for all display modes
- ๐๏ธ **Removed Redundant Properties**: Eliminated `drawerPosition`, `sidebarPosition`, `toastPosition`, `modalVariant`
- ๐ **Smart Position Mapping**: Intelligent mapping of positions to appropriate behaviors for each mode
- ๐ **Updated Documentation**: Comprehensive examples and migration guide
**New Position Values:**
- **Edge Positions**: `"top"`, `"bottom"`, `"left"`, `"right"`
- **Center Positions**: `"center"`
- **Corner Positions**: `"top-left"`, `"top-center"`, `"top-right"`, `"bottom-left"`, `"bottom-center"`, `"bottom-right"`
- **Side Center Positions**: `"center-left"`, `"center-right"`
**Migration Guide:**
```javascript
// โ OLD: Multiple position properties
drawerPosition: 'top' โ position: 'top'
sidebarPosition: 'left' โ position: 'left'
toastPosition: 'top-right' โ position: 'top-right'
modalVariant: 'center' โ position: 'center'
// โ
NEW: Single position property
position: 'top' // Works for all modes
```
**Benefits:**
- โ
**Simpler Configuration** - One property instead of multiple
- โ
**Consistent Behavior** - Same positioning logic across all modes
- โ
**Easier Maintenance** - Less code duplication
- โ
**Better Developer Experience** - Intuitive and predictable
### **v0.0.45 - Button Mode & Independent Positioning** ๐
**New Features:**
- โจ **Button Mode**: New floating action button display mode
- ๐ฏ **Independent Positioning**: Button and popup can have different positions
- ๐จ **Button Customization**: Size, color, icon, and text options
- ๐ฑ **9 Button Positions**: Full positioning flexibility (top-left, center, bottom-right, etc.)
- ๐ **Smart Transformation**: Button disappears, popup appears at specified location
**Button Configuration Options:**
- `buttonPosition`: Control where the button appears on the page
- `buttonSize`: small (40px), medium (60px), large (80px), custom
- `buttonColor`: Custom button color (hex, rgb, etc.)
- `buttonIcon`: Custom emoji or icon (default: ๐ฌ)
- `buttonText`: Custom text alternative to icon
**Performance Improvements:**
- ๐ **Form Animation Control**: `disableFormAnimations` option for faster loading
- โก **Reduced Loading Time**: Eliminates 3+ second delay between header and content
- ๐ฑ **Mobile Optimization**: Better performance on resource-constrained devices
**Bug Fixes:**
- ๐ Fixed widget re-creation issues in drawer/sidebar modes
- ๐ Fixed DOM manipulation errors in render methods
- ๐ Fixed autoClose behavior (true now correctly equals 3 seconds)
- ๐ Improved positioning logic for all display modes
---
## ๐ Support & Resources
- ๐ **Documentation**: [docs.feedal.io](https://docs.feedal.io)
- ๐ฌ **Support**: [support@feedal.io](mailto:support@feedal.io)
---
## ๐ License
This project is licensed under the [MIT License](LICENSE).
---
**Made with โค๏ธ by the Feedal Team**