feat: support optimize images

This commit is contained in:
2025-06-20 01:56:34 +08:00
parent 324427513c
commit 02c16a2972
37 changed files with 1781 additions and 698 deletions

View File

@@ -1,38 +1,45 @@
import { type ComponentProps, useMemo, useState } from "react";
import { useInject } from "@/infra/di/inject";
import { DOCUMENT } from "@/infra/platform/injection";
import { type ComponentProps, useMemo } from "react";
const URL_PARSE_REGEX = /^([^?#]*)(\?[^#]*)?(#.*)?$/;
function parseURL(url: string) {
const match = url.match(URL_PARSE_REGEX);
if (!match) {
return { other: url, search: "", hash: "" };
}
return {
other: match[1] || "",
search: match[2] || "",
hash: match[3] || "",
};
}
export type ImgProps = Omit<ComponentProps<"img">, "alt"> &
Required<Pick<ComponentProps<"img">, "alt">> & {
optimize?: boolean;
optimize?: "accept";
};
const LEGACY_IMAGE_REGEX = /\.(jpg|jpeg|png|gif|svg)$/;
export const Img = ({
src: propsSrc,
optimize = "accept",
...props
}: ImgProps) => {
const document = useInject(DOCUMENT);
const src = useMemo(() => {
const baseURI = document?.baseURI;
if (!propsSrc || !baseURI) {
return propsSrc;
}
const { other, search, hash } = parseURL(propsSrc);
const searchParams = new URLSearchParams(search);
searchParams.set("optimize", optimize);
return `${other}?${searchParams.toString()}${hash}`;
}, [propsSrc, optimize, document?.baseURI]);
export const Img = (props: ImgProps) => {
const src = props.src;
const isLegacy = useMemo(() => src?.match(LEGACY_IMAGE_REGEX), [src]);
const [isError, setIsError] = useState(false);
if (!src) {
// biome-ignore lint/nursery/noImgElement: <explanation>
return <img {...props} alt={props.alt} />;
}
return (
<picture {...props}>
{isLegacy && !isError && (
<>
<source
srcSet={src.replace(LEGACY_IMAGE_REGEX, ".webp")}
type="image/webp"
/>
<source
srcSet={src.replace(LEGACY_IMAGE_REGEX, ".avif")}
type="image/avif"
/>
</>
)}
<img {...props} alt={props.alt} onError={() => setIsError(true)} />
</picture>
);
// biome-ignore lint/nursery/noImgElement: <explanation>
return <img {...props} alt={props.alt} src={src} />;
};