new-personal-site/components/lib/layout/Img.tsx
2025-10-02 08:16:11 +01:00

139 lines
3.7 KiB
TypeScript

import _ from "lodash";
import React, { DetailedHTMLProps, ImgHTMLAttributes, ReactNode } from "react";
import { twMerge } from "tailwind-merge";
export type TWUIImageProps = DetailedHTMLProps<
ImgHTMLAttributes<HTMLImageElement>,
HTMLImageElement
> & {
alt: string;
size?: number;
circle?: boolean;
bgImg?: boolean;
backgroundImage?: boolean;
fallbackImageSrc?: string;
srcLight?: string;
srcDark?: string;
imgErrSrc?: string;
imgErrComp?: ReactNode;
imgErrSrcLight?: string;
imgErrSrcDark?: string;
};
/**
* # Image Component
* @className twui-img
*/
export default function Img({
imgErrSrc,
imgErrComp,
imgErrSrcDark,
imgErrSrcLight,
...props
}: TWUIImageProps) {
const width = props.size || props.width;
const height = props.size || props.height;
const sizeRatio = width && height ? Number(width) / Number(height) : 1;
const [imageError, setImageError] = React.useState(false);
const finalProps = _.omit(props, [
"size",
"circle",
"bgImg",
"backgroundImage",
"fallbackImageSrc",
"srcLight",
"srcDark",
]);
const interpolatedProps: typeof props = {
...finalProps,
width: width,
height: height,
className: twMerge(
"object-cover",
props.circle && "rounded-full",
props.bgImg || props.backgroundImage
? "absolute top-0 left-0 w-full h-full object-cover z-0"
: "",
"twui-img",
props.className
),
onError: (e) => {
if (props.fallbackImageSrc) {
e.currentTarget.src = props.fallbackImageSrc;
}
props.onError?.(e);
},
style: {
...(props.size
? {
width: `${props.size}px`,
minWidth: `${props.size}px`,
height: `${props.size}px`,
}
: {}),
...props.style,
},
};
if (imageError) {
return (
imgErrComp || (
<img
loading="lazy"
{...interpolatedProps}
src={
imgErrSrc ||
"https://static.datasquirel.com/images/user-images/user-2/castcord-image-preset_thumbnail.jpg"
}
/>
)
);
}
if (props.srcDark && props.srcLight) {
return (
<React.Fragment>
<img
loading="lazy"
{...interpolatedProps}
className={twMerge(
"hidden dark:block",
interpolatedProps.className
)}
src={props.srcDark}
onError={(e) => {
setImageError(true);
props.onError?.(e);
}}
/>
<img
loading="lazy"
{...interpolatedProps}
className={twMerge(
"block dark:hidden",
interpolatedProps.className
)}
src={props.srcLight}
onError={(e) => {
setImageError(true);
props.onError?.(e);
}}
/>
</React.Fragment>
);
}
return (
<img
{...interpolatedProps}
onError={(e) => {
setImageError(true);
props.onError?.(e);
}}
/>
);
}