import React, { useEffect, useMemo, useState } from 'react'; import type { CSSProperties, FC, MouseEvent } from 'react'; import TripleStarsIcon from '../icons/TripleStarsIcon'; import { hexToRgb, normalizeHex } from '../../utils/colors'; const DEFAULT_PRIMARY = '#8F0060'; const DEFAULT_SECONDARY = '#0494F7'; const DEFAULT_PRIMARY_RGB = '143, 0, 96'; const DEFAULT_SECONDARY_RGB = '4, 148, 247'; const INTRO_STEP_DURATION = 1500; const SIZES = { small: { orbSize: 56, orbRadius: 18, orbBorder: 3, faceRadius: 15, logoSize: 30, spinSize: 280, introIconSize: 24, fontSize: 12.5, bubbleMaxWidth: 210, }, medium: { orbSize: 66, orbRadius: 21, orbBorder: 3.5, faceRadius: 17.5, logoSize: 36, spinSize: 330, introIconSize: 28, fontSize: 13, bubbleMaxWidth: 235, }, big: { orbSize: 78, orbRadius: 25, orbBorder: 4, faceRadius: 21, logoSize: 42, spinSize: 390, introIconSize: 32, fontSize: 14, bubbleMaxWidth: 265, }, } as const; const InfoIcon = ({ color, size = 40 }: { color: string; size?: number }) => ( ); const DiscountIcon = ({ color, size = 40, }: { color: string; size?: number; }) => ( ); const HeartIcon = ({ color, size = 40 }: { color: string; size?: number }) => ( ); type IntroItem = { color: string; text: string; Icon: FC<{ color: string; size?: number }>; }; const INTRO_ITEMS: IntroItem[] = [ { color: '#0494F7', text: 'Product details', Icon: InfoIcon }, { color: '#00AF5B', text: 'Discounts', Icon: DiscountIcon }, { color: '#FF2828', text: 'Your favorites', Icon: HeartIcon }, ]; type BuySmartButtonProps = { image: string | File | null; position: string; popupBorderColorLeft: string; popupBorderColorRight: string; size?: 'small' | 'medium' | 'big'; engagementText?: string; expanded?: boolean; skipIntro?: boolean; }; export const BuySmartButton = ({ image, position, popupBorderColorLeft, popupBorderColorRight, size = 'medium', engagementText = 'Buy smarter with AI', expanded, skipIntro = false, }: BuySmartButtonProps) => { const metrics = SIZES[size] || SIZES.medium; const isOnLeftSide = position === 'left'; const normPrimary = normalizeHex(popupBorderColorLeft || DEFAULT_PRIMARY); const normSecondary = normalizeHex( popupBorderColorRight || DEFAULT_SECONDARY ); const primaryRgb = hexToRgb(normPrimary) || DEFAULT_PRIMARY_RGB; const secondaryRgb = hexToRgb(normSecondary) || DEFAULT_SECONDARY_RGB; const [introIndex, setIntroIndex] = useState( skipIntro ? INTRO_ITEMS.length : 0 ); const [introDone, setIntroDone] = useState(skipIntro); const [nudgeOpen, setNudgeOpen] = useState(expanded ?? true); const [nudgeDismissed, setNudgeDismissed] = useState(false); useEffect(() => { if (expanded !== undefined) setNudgeOpen(expanded); }, [expanded]); useEffect(() => { setNudgeDismissed(false); }, [expanded, skipIntro]); useEffect(() => { if (skipIntro) { setIntroDone(true); setIntroIndex(INTRO_ITEMS.length); } else { setIntroDone(false); setIntroIndex(0); } }, [skipIntro]); useEffect(() => { if (introDone) return; if (introIndex >= INTRO_ITEMS.length) { setIntroDone(true); return; } const timer = setTimeout( () => setIntroIndex(prev => prev + 1), INTRO_STEP_DURATION ); return () => clearTimeout(timer); }, [introIndex, introDone]); const logoUrl = useMemo( () => (image instanceof File ? URL.createObjectURL(image) : image), [image] ); useEffect(() => { if (image instanceof File && logoUrl) { return () => URL.revokeObjectURL(logoUrl); } }, [image, logoUrl]); const showIntro = !introDone; const safeIndex = Math.min(introIndex, INTRO_ITEMS.length - 1); const nudgeVisible = nudgeOpen && !nudgeDismissed && (showIntro || Boolean(engagementText)); const handleDismissNudge = (event: MouseEvent) => { event.stopPropagation(); setNudgeDismissed(true); }; const handleToggleNudge = () => { if (nudgeDismissed) { setNudgeDismissed(false); setNudgeOpen(true); return; } setNudgeOpen(prev => !prev); }; const noDragStyle: CSSProperties = { userSelect: 'none', WebkitUserSelect: 'none', }; return (
{engagementText}
)}