'use client';
import { X } from 'lucide-react';
import { useEffect, useState } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { cn } from '@djangocfg/ui-core/lib';
import { Z_INDEX } from '../constants';
import { useChatPresence } from './useChatPresence';
import type { ChatFABPosition } from './ChatFAB';
export interface ChatGreetingProps {
/** Controlled visibility — usually `!chatOpen && !userDismissed`. */
open: boolean;
/** Greeting text. Pass a string for the default bubble, or any ReactNode. */
children: ReactNode;
/** Click handler — typically opens the chat. Bubble is clickable when set. */
onClick?: () => void;
/** Close (×) button handler — typically marks the greeting as dismissed. */
onDismiss?: () => void;
/** Anchor relative to a FAB on the same side. @default 'bottom-right' */
position?: ChatFABPosition;
/**
* Horizontal pixel offset matching the FAB's `offset` prop, so the greeting
* lines up under the FAB. @default 24
*/
fabOffset?: number;
/**
* Vertical pixel offset above/below the FAB centerline. @default 96
* (room for an `md` FAB plus a small gap).
*/
fabClearance?: number;
/** Delay before the greeting appears, in ms. @default 1500 */
delayMs?: number;
/** z-index. @default 99 — companion tier, just below the open dock. */
zIndex?: number;
/** Override classes on the bubble. */
className?: string;
/** Override styles on the bubble. */
style?: CSSProperties;
/** Optional sender avatar / icon shown on the left. */
avatar?: ReactNode;
/** Optional sender label rendered above the text. */
senderName?: string;
/** ARIA label for the dismiss button. @default 'Dismiss' */
dismissLabel?: string;
/**
* Render in-place (no fixed positioning). Useful for stories and inline previews.
* @default false
*/
inline?: boolean;
}
function anchorStyle(
position: ChatFABPosition,
fabOffset: number,
fabClearance: number,
): CSSProperties {
const [vert, horiz] = position.split('-') as ['bottom' | 'top', 'right' | 'left'];
return { [vert]: fabClearance, [horiz]: fabOffset } as CSSProperties;
}
function originClass(position: ChatFABPosition): string {
// Scale-in origin matches the corner the bubble attaches to.
if (position === 'bottom-right') return 'origin-bottom-right';
if (position === 'bottom-left') return 'origin-bottom-left';
if (position === 'top-right') return 'origin-top-right';
return 'origin-top-left';
}
/**
* Greeting bubble shown next to a `ChatFAB` to invite the user to start a
* conversation (LiveChat / Intercom-style proactive prompt).
*
* Renders fixed-position, anchored to the same corner as the FAB. Owns its
* own delayed-mount + presence animation. Hide on chat open and/or after
* user dismissal.
*
* @example
* ```tsx
* const [open, setOpen] = useState(false);
* const [dismissed, setDismissed] = useState(false);
*
*