Initial commit: OmniAI Web Frontend
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { useState, useCallback, type CSSProperties, type ImgHTMLAttributes } from "react";
|
||||
|
||||
const FALLBACK_SRC = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'%3E%3Crect fill='%23222' width='80' height='80' rx='8'/%3E%3Cpath d='M28 52l8-12 6 8 12-16 10 20H28z' fill='%23444'/%3E%3Ccircle cx='32' cy='32' r='5' fill='%23444'/%3E%3C/svg%3E";
|
||||
|
||||
const baseStyle: CSSProperties = {
|
||||
transition: "opacity 0.3s ease",
|
||||
};
|
||||
|
||||
interface OptimizedImageProps extends ImgHTMLAttributes<HTMLImageElement> {
|
||||
fallbackSrc?: string;
|
||||
}
|
||||
|
||||
export default function OptimizedImage({
|
||||
src,
|
||||
alt = "",
|
||||
fallbackSrc = FALLBACK_SRC,
|
||||
style,
|
||||
onLoad,
|
||||
onError,
|
||||
...rest
|
||||
}: OptimizedImageProps) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [errored, setErrored] = useState(false);
|
||||
|
||||
const handleLoad = useCallback(
|
||||
(e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||
setLoaded(true);
|
||||
onLoad?.(e);
|
||||
},
|
||||
[onLoad],
|
||||
);
|
||||
|
||||
const handleError = useCallback(
|
||||
(e: React.SyntheticEvent<HTMLImageElement>) => {
|
||||
if (!errored) {
|
||||
setErrored(true);
|
||||
(e.target as HTMLImageElement).src = fallbackSrc;
|
||||
}
|
||||
onError?.(e);
|
||||
},
|
||||
[errored, fallbackSrc, onError],
|
||||
);
|
||||
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
loading="lazy"
|
||||
style={{ ...baseStyle, opacity: loaded || errored ? 1 : 0, ...style }}
|
||||
onLoad={handleLoad}
|
||||
onError={handleError}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user