// // Copyright 2025 DXOS.org // import React, { type SyntheticEvent, useCallback, useRef, useState } from 'react'; import { type ThemedClassName } from '@dxos/react-ui'; import { mx } from '@dxos/ui-theme'; const cache = new Map(); export type ImageProps = ThemedClassName< { src: string; alt?: string; fit?: 'contain' | 'cover'; /** * Image CORS mode (sets the `` attribute). Omitted by default: cross-origin * images load without a CORS request (no console errors), but the dominant-color sampler can't * read their pixels off the canvas (the browser taints it). Opt in with `'anonymous'` when the * image host sends CORS headers and you want the dominant-color gradient. */ crossOrigin?: 'anonymous' | 'use-credentials' | ''; } & ColorOptions >; export const Image = ({ classNames, src, alt = '', fit = 'contain', crossOrigin, sampleSize = 64, contrast = 0.9, }: ImageProps) => { const [crossOriginState, setCrossOriginState] = useState(crossOrigin); const [dominantColor, setDominantColor] = useState(undefined); const [imageLoaded, setImageLoaded] = useState(false); const canvasRef = useRef(null); // CORS not supported by server. const handleImageError = (): void => { setCrossOriginState(undefined); }; const handleImageLoad = useCallback( ({ target }: SyntheticEvent): void => { const rgb = cache.get(src); if (rgb) { setDominantColor(rgb); setImageLoaded(true); return; } const img = target as HTMLImageElement; if (!canvasRef.current) { return; } try { const color = extractDominantColor(canvasRef.current, img, { sampleSize, contrast }); if (color) { const rgb = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; cache.set(src, rgb); setDominantColor(rgb); } } catch { setCrossOriginState(undefined); } setImageLoaded(true); }, [sampleSize, contrast, src], ); return (
's `z-10` stays scoped to this wrapper. Without it // the z-10 leaks into the parent's stacking context and elevates the // image above any pseudo-element rings (e.g. Focus.Item's // `dx-ring-pseudo` `::after`) painted on ancestors — most visibly, // the focus ring on a Card containing a Card.Poster. className={mx( `relative flex w-full justify-center overflow-hidden transition-all duration-700 isolate`, classNames, )} style={{ backgroundColor: dominantColor, }} > {/* Hidden canvas for color extraction. */}