import { useRef } from 'react' import { Dialog } from '@base-ui/react/dialog' import { IconX } from '@tabler/icons-react' import { cn } from './cn' /** * A picture, full screen, on a scrim — the thing a thumbnail opens into. * * Both apps had written this by hand and neither had got it right: Peek's * `ImageLightbox` and the `FrameLightbox` before it were `createPortal` plus a * fixed `
`, with no Escape, no focus trap and nothing to return focus to * (Peek scan F5, P4; migration B22, Katerina 10 September). Ship never had one * at all, so a screenshot in an issue could not be looked at. * * On Base UI's `Dialog`, like `DialogShell`: the portal, the backdrop, Escape, * the outside press and the focus trap are its, and what is written here is the * picture, the scrim and the close button. * * **The close button is this component's own**, not an `IconButton`: a 26px * square with no fill is hard to find on top of a photograph, which is why Peek * had written an escape note beside its own. A 32px round button on an elevated * fill with a hairline reads on any picture. */ export interface LightboxProps { /** The picture to show. Swapping it — a thumbnail for the original, once it * arrives — swaps what is on screen, with no flash of nothing. */ src: string /** What the picture is. Names the viewer for assistive tech too. */ alt: string onClose: () => void /** Extra classes on the picture itself. */ className?: string } export function Lightbox({ src, alt, onClose, className }: LightboxProps) { const popupRef = useRef(null) return ( { if (!open) onClose() }} > {alt} e.stopPropagation()} className={cn('max-w-full max-h-full object-contain rounded-lg', className)} /> {/* On the dark scrim in every theme, so it wears the tooltip's dark colours (Katerina, 28 September: Leaf's white circle stood out); in the other themes they are the elevated ground, the strong line and the secondary text it always had. */} ) }