{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "z-queued-message-stack",
  "title": "Z Queued Message Stack",
  "dependencies": [
    "@heroicons/react@2.2.0"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://ui.orchid.ai/r/orchid-foundation.json",
    "https://ui.orchid.ai/r/orchid-theme.json",
    "https://ui.orchid.ai/r/z-action-row.json",
    "https://ui.orchid.ai/r/z-button.json"
  ],
  "files": [
    {
      "path": "registry/ui/orchid/spectrum/molecules/z-queued-message-stack/z-queued-message-stack.tsx",
      "content": "\"use client\";\nimport { useDismissAffordance } from \"../../../hooks/use-dismiss-affordance\";\nimport { cn } from \"../../../lib/ui-utils\";\nimport { PencilIcon, XMarkIcon } from '@heroicons/react/16/solid';\nimport { ArrowUpIcon, ClockIcon } from '@heroicons/react/24/outline';\nimport { type FocusEvent, useEffect, useState } from 'react';\nimport ZButton from \"../../atoms/z-button/z-button\";\nimport ZActionRow, { ZActionRowText } from \"../z-action-row/z-action-row\";\nexport type QueuedMessageItem = {\n    id: string;\n    preview: string;\n    attachmentCount: number;\n};\ntype ZQueuedMessageStackProps = {\n    items: QueuedMessageItem[];\n    onDismiss: (id: string) => void;\n    onEdit: (id: string) => void;\n    onSendNow: (id: string) => void;\n};\nconst MAX_VISIBLE_STACK_CARDS = 3;\nconst COLLAPSED_STACK_OFFSET_PX = 10;\nconst PREVIEW_STACK_OFFSET_PX = 15;\nconst STACK_SCALE_STEP = 0.05;\nexport default function ZQueuedMessageStack({ items, onDismiss, onEdit, onSendNow, }: ZQueuedMessageStackProps) {\n    const [isStackPreviewing, setIsStackPreviewing] = useState(false);\n    const [isStackExpanded, setIsStackExpanded] = useState(false);\n    const [isExpandedStackActive, setIsExpandedStackActive] = useState(false);\n    useEffect(() => {\n        if (items.length === 0) {\n            setIsStackExpanded(false);\n            setIsStackPreviewing(false);\n            setIsExpandedStackActive(false);\n        }\n    }, [items.length]);\n    if (items.length === 0)\n        return null;\n    const stackedItems = items.slice(0, MAX_VISIBLE_STACK_CARDS);\n    const canExpand = items.length > 1;\n    const badgeCount = items.length - 1;\n    const previewCardCount = Math.max(0, stackedItems.length - 1);\n    const stackOffset = isStackPreviewing ? PREVIEW_STACK_OFFSET_PX : COLLAPSED_STACK_OFFSET_PX;\n    const peekHoverHeight = 44 + previewCardCount * PREVIEW_STACK_OFFSET_PX;\n    return (<>\n      {isStackExpanded ? (<div className=\"flex w-full max-w-2xl flex-col gap-2\">\n          {canExpand ? (<div className=\"flex justify-center\" onMouseEnter={() => setIsExpandedStackActive(true)} onMouseLeave={() => setIsExpandedStackActive(false)} onFocusCapture={() => setIsExpandedStackActive(true)} onBlurCapture={(event: FocusEvent<HTMLDivElement>) => {\n                    if (event.currentTarget.contains(event.relatedTarget as Node | null))\n                        return;\n                    setIsExpandedStackActive(false);\n                }}>\n              <ZButton size=\"sm\" variant=\"transparent\" onClick={() => setIsStackExpanded(false)} aria-label=\"Collapse queued messages\" className={cn('transition-opacity duration-150', isExpandedStackActive\n                    ? 'pointer-events-auto opacity-100'\n                    : 'pointer-events-none opacity-0')}>\n                Collapse\n              </ZButton>\n            </div>) : null}\n          {items.map((item, index) => {\n                const isCollapseTriggerCard = index === 0;\n                return (<div key={item.id} onMouseEnter={isCollapseTriggerCard ? () => setIsExpandedStackActive(true) : undefined} onMouseLeave={isCollapseTriggerCard ? () => setIsExpandedStackActive(false) : undefined} onFocusCapture={isCollapseTriggerCard ? () => setIsExpandedStackActive(true) : undefined} onBlurCapture={isCollapseTriggerCard\n                        ? (event: FocusEvent<HTMLDivElement>) => {\n                            if (event.currentTarget.contains(event.relatedTarget as Node | null))\n                                return;\n                            setIsExpandedStackActive(false);\n                        }\n                        : undefined}>\n                <QueuedMessageRow item={item} onDismiss={onDismiss} onEdit={onEdit} onSendNow={onSendNow}/>\n              </div>);\n            })}\n        </div>) : (<div className=\"relative w-full max-w-2xl\">\n          {canExpand ? (<div className=\"absolute inset-x-0 bottom-full z-20 flex items-end justify-center py-1\" style={{ height: `${peekHoverHeight}px` }} onMouseEnter={() => setIsStackPreviewing(true)} onMouseLeave={() => setIsStackPreviewing(false)} onFocusCapture={() => setIsStackPreviewing(true)} onBlurCapture={(event: FocusEvent<HTMLDivElement>) => {\n                    if (event.currentTarget.contains(event.relatedTarget as Node | null))\n                        return;\n                    setIsStackPreviewing(false);\n                }}>\n              <button type=\"button\" aria-label={`Show ${badgeCount} more queued messages`} onClick={() => {\n                    setIsStackExpanded(true);\n                    setIsStackPreviewing(false);\n                }} className={cn('text-copy text-neutral-subtle ease-out-expo bg-surface border-neutral focus-visible:ring-neutral-strong grid size-8 place-items-center rounded-full border-2 transition-transform duration-500 outline-none select-none focus-visible:ring-2 focus-visible:ring-inset', isStackPreviewing\n                    ? 'translate-y-0 scale-100 opacity-100'\n                    : 'translate-y-2 scale-50 opacity-0')}>\n                +{badgeCount}\n              </button>\n            </div>) : null}\n\n          <div className=\"relative\">\n            {stackedItems.map((item, index) => {\n                const isPrimary = index === 0;\n                const reversedIndex = index;\n                return (<div key={item.id} className={cn(isPrimary ? 'relative z-10' : 'pointer-events-none absolute inset-x-0 bottom-0')} style={{ zIndex: stackedItems.length - index }}>\n                  <div style={{\n                        transform: !isPrimary\n                            ? `scale(${1 - reversedIndex * STACK_SCALE_STEP}) translateY(${-reversedIndex * stackOffset}px)`\n                            : undefined,\n                        transitionDelay: `${reversedIndex * 40}ms`,\n                    }} className={cn(isPrimary ? '' : 'ease-out-expo origin-top transition-transform duration-700')}>\n                    <QueuedMessageRow item={item} onDismiss={onDismiss} onEdit={onEdit} onSendNow={onSendNow} previewOnly={!isPrimary}/>\n                  </div>\n                </div>);\n            })}\n          </div>\n        </div>)}\n    </>);\n}\nfunction QueuedMessageRow({ item, onDismiss, onEdit, onSendNow, previewOnly = false, }: {\n    item: QueuedMessageItem;\n    onDismiss: (id: string) => void;\n    onEdit: (id: string) => void;\n    onSendNow: (id: string) => void;\n    previewOnly?: boolean;\n}) {\n    const attachmentLabel = item.attachmentCount > 0\n        ? `${item.attachmentCount} attachment${item.attachmentCount === 1 ? '' : 's'}`\n        : null;\n    const { hideDismissUntilHover, showDismissLabel } = useDismissAffordance();\n    const dismissButton = (<ZButton variant=\"transparent\" size=\"sm\" onClick={() => onDismiss(item.id)} className={cn('text-neutral-subtle transition-opacity duration-150', hideDismissUntilHover &&\n            'pointer-events-none opacity-0 group-hover/queued-message:pointer-events-auto group-hover/queued-message:opacity-100 group-focus-within/queued-message:pointer-events-auto group-focus-within/queued-message:opacity-100')} aria-label=\"Dismiss queued message\">\n      {showDismissLabel ? 'Dismiss' : <XMarkIcon className=\"size-4\"/>}\n    </ZButton>);\n    return (<div className=\"group/queued-message bg-surface/95 border-neutral-subtle ease-out-expo overflow-hidden rounded-[14px] border px-2 py-1 shadow-xs backdrop-blur-sm transition-[transform,opacity] duration-200\">\n      <ZActionRow hasPadding icon={ClockIcon} iconClassName=\"text-neutral-subtle\" className=\"py-[3px]\" left={<div className=\"flex min-w-0 flex-col py-0.5\">\n            <ZActionRowText className=\"text-copy text-neutral\" lines={1} mobileLines={2}>\n              {item.preview}\n            </ZActionRowText>\n            {attachmentLabel ? (<span className=\"text-copy-sm text-neutral-subtle block truncate\">{attachmentLabel}</span>) : null}\n          </div>} right={previewOnly ? undefined : (<>\n            {dismissButton}\n            <ZButton size=\"sm\" variant=\"transparent\" onClick={() => onEdit(item.id)} leadingIcon={PencilIcon} aria-label=\"Edit queued message\"/>\n            <ZButton size=\"sm\" variant=\"transparent\" onClick={() => onSendNow(item.id)} leadingIcon={ArrowUpIcon} aria-label=\"Send queued message now\"/>\n          </>)}/>\n    </div>);\n}\n",
      "type": "registry:ui",
      "target": "@ui/orchid/spectrum/molecules/z-queued-message-stack/z-queued-message-stack.tsx"
    }
  ],
  "type": "registry:ui"
}