/** * React adapter for the chat widget * * Provides a React component wrapper around the Svelte widget. */ import { useEffect, useRef, useMemo } from 'react'; import { initChatWidget } from '../index.js'; import type { ChatWidgetConfig, PageContext, ChatWidgetAPI } from '../index.js'; export type { ChatWidgetConfig, PageContext }; // Extend React JSX to include custom element declare module 'react' { namespace JSX { interface IntrinsicElements { 'chat-widget': React.DetailedHTMLProps< React.HTMLAttributes & { 'api-base': string; site: string; 'site-name': string; title?: string; 'logo-url'?: string; 'primary-color'?: string; 'welcome-message'?: string; 'welcome-topics'?: string; 'disclaimer-html'?: string; position?: 'bottom-right' | 'bottom-left'; fullscreen?: boolean; 'storage-prefix'?: string; }, HTMLElement >; } } } export interface ChatWidgetProps extends ChatWidgetConfig { className?: string; style?: React.CSSProperties; } /** * React component wrapper for the chat widget * * The widget is initialized on mount and destroyed on unmount. * Config changes trigger a remount to apply new settings. */ export function ChatWidget(props: ChatWidgetProps) { const { className, style, ...config } = props; const containerRef = useRef(null); const widgetRef = useRef(null); // Memoize config to prevent unnecessary remounts // Only recreate when relevant config values change const memoizedConfig = useMemo( () => config, [ config.apiBase, config.site, config.siteName, config.title, config.primaryColor, config.welcomeMessage, // Stringify arrays/objects for stable comparison JSON.stringify(config.welcomeTopics), config.disclaimerHtml, config.position, config.fullscreen, config.storagePrefix, config.logoUrl, config.sentryDsn, config.environment, ] ); useEffect(() => { if (!containerRef.current) return; // Initialize widget widgetRef.current = initChatWidget(containerRef.current, memoizedConfig); return () => { // Cleanup: destroy widget to prevent memory leaks if (widgetRef.current) { widgetRef.current.destroy(); widgetRef.current = null; } }; }, [memoizedConfig]); return (
); } /** * Alternative: Use the custom element directly in React * * This is a simpler approach that doesn't require the Svelte mount API. */ export function ChatWidgetElement(props: ChatWidgetProps) { const { apiBase, site, siteName, title, logoUrl, primaryColor, welcomeMessage, welcomeTopics, disclaimerHtml, position, fullscreen, storagePrefix, className, } = props; // Import the index to ensure custom element is registered useEffect(() => { import('../index.js'); }, []); return ( ); } export default ChatWidget;