UNPKG

@feedal/embed

Version:

Feedal embed script to load feedback forms via JS or NPM

1,154 lines (910 loc) โ€ข 35.7 kB
# ๐Ÿš€ 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**