|
| 1 | +import { cn, Icon, Text, Wrapper } from '@zerodev/react-ui' |
| 2 | + |
| 3 | +/** |
| 4 | + * The card that renders the deposit address in the SRA "Deposit funds" screen. |
| 5 | + * |
| 6 | + * Two visual states, each mapped 1:1 to a Figma node: |
| 7 | + * - Loading (Figma 17634:104343, "Smart Funding" card): 16px radius, 20% |
| 8 | + * white surface, centered loading icon + `loadingText`. Rendered when |
| 9 | + * `address` is omitted. |
| 10 | + * - Ready (Figma 17762:78875, address+QR row): 14px radius, 40% offWhite |
| 11 | + * surface, left-aligned address text + a 52×52 white QR button on the |
| 12 | + * right. Rendered when `address` is supplied. |
| 13 | + * |
| 14 | + * The two states have deliberately different visuals; they share this |
| 15 | + * component because the parent renders the same slot in both cases. |
| 16 | + */ |
| 17 | +export interface AddressDisplayProps { |
| 18 | + /** Deposit address to render; when omitted, the loading variant is shown. */ |
| 19 | + address?: string |
| 20 | + /** |
| 21 | + * Message rendered in the loading variant. Include chain-specific context |
| 22 | + * from the caller, e.g., `Watching for your deposit on Base…`. |
| 23 | + */ |
| 24 | + loadingText?: string |
| 25 | + /** Handler for the QR icon button (ready variant). */ |
| 26 | + onQrClick?: () => void |
| 27 | + className?: string |
| 28 | +} |
| 29 | + |
| 30 | +export function AddressDisplay({ |
| 31 | + address, |
| 32 | + loadingText = 'Watching for your deposit…', |
| 33 | + onQrClick, |
| 34 | + className, |
| 35 | +}: AddressDisplayProps) { |
| 36 | + if (address === undefined) { |
| 37 | + return ( |
| 38 | + <Wrapper |
| 39 | + variant="ghost" |
| 40 | + className={cn( |
| 41 | + // Figma: rounded-2xl (16px), p-16, centered content, gap 8px, 20% |
| 42 | + // white surface from Wrapper's ghost variant. Height pinned to |
| 43 | + // 68px so it matches the ready variant. |
| 44 | + 'zd:relative zd:flex zd:h-[68px] zd:w-full zd:items-center zd:justify-center zd:gap-2 zd:overflow-hidden zd:rounded-2xl zd:px-4', |
| 45 | + 'zd:shadow-[inset_0_-4px_4px_0_rgba(255,255,255,0.1),inset_0_3px_4px_0_rgba(0,0,0,0.02)]', |
| 46 | + className, |
| 47 | + )} |
| 48 | + data-testid="address-display-loading" |
| 49 | + > |
| 50 | + <Icon name="lineLoading" className="zd:size-4 zd:text-greyScale/50" /> |
| 51 | + <Text className="zd:text-body1 zd:text-greyScale/50"> |
| 52 | + {loadingText} |
| 53 | + </Text> |
| 54 | + </Wrapper> |
| 55 | + ) |
| 56 | + } |
| 57 | + |
| 58 | + return ( |
| 59 | + <Wrapper |
| 60 | + variant="ghost" |
| 61 | + // Figma's 40% offWhite surface — none of Wrapper's white-only variants |
| 62 | + // hit this tint, so override the backgroundColor. Inline style wins |
| 63 | + // over Wrapper's own style prop. |
| 64 | + style={{ backgroundColor: 'rgba(247, 245, 240, 0.4)' }} |
| 65 | + className={cn( |
| 66 | + // Figma: rounded-[14px], pl-16 pr-8 py-8, gap-12 items-center. |
| 67 | + // Height pinned to 68px so the loading and ready variants swap without |
| 68 | + // the layout jumping. |
| 69 | + 'zd:relative zd:flex zd:h-[68px] zd:w-full zd:items-center zd:gap-3 zd:overflow-hidden zd:rounded-[14px] zd:pl-4 zd:pr-2 zd:py-2', |
| 70 | + 'zd:shadow-[inset_0_-4px_4px_0_rgba(255,255,255,0.1),inset_0_3px_4px_0_rgba(0,0,0,0.02)]', |
| 71 | + className, |
| 72 | + )} |
| 73 | + data-testid="address-display-ready" |
| 74 | + > |
| 75 | + <Text |
| 76 | + className="zd:min-w-0 zd:flex-1 zd:break-all zd:text-body2" |
| 77 | + data-testid="address-display-address" |
| 78 | + > |
| 79 | + {address} |
| 80 | + </Text> |
| 81 | + <button |
| 82 | + type="button" |
| 83 | + onClick={onQrClick} |
| 84 | + aria-label="Show QR code" |
| 85 | + className="zd:flex zd:size-13 zd:shrink-0 zd:cursor-pointer zd:items-center zd:justify-center zd:rounded-2xl zd:bg-white" |
| 86 | + data-testid="address-display-qr-button" |
| 87 | + > |
| 88 | + <Icon name="qrCode" className="zd:size-5 zd:text-greyScale" /> |
| 89 | + </button> |
| 90 | + </Wrapper> |
| 91 | + ) |
| 92 | +} |
0 commit comments