mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
## Description This PR ensures that the default prettier config is used for examples and templates. This config is compatible with `prettier@3` as well (upgrading prettier is bigger change that can be a future PR). ## Changes - Updated `.prettierrc.json` in root with `"trailingComma": "es5"` (will be needed upgrading to prettier@3) - Added `examples/.prettierrc.json` with default config (this will change every example) - Added `packages/create-next-app/templates/.prettierrc.json` with default config (this will change every template) ## Related - Fixes #54402 - Closes #54409
226 lines
4.5 KiB
TypeScript
226 lines
4.5 KiB
TypeScript
import React, { useCallback, useState } from "react";
|
|
import { useInView } from "react-intersection-observer";
|
|
|
|
type State = {
|
|
lazyLoad: boolean;
|
|
isSsr: boolean;
|
|
isIntersectionObserverAvailable: boolean;
|
|
inView?: boolean;
|
|
loaded: boolean;
|
|
};
|
|
|
|
const imageAddStrategy = ({
|
|
lazyLoad,
|
|
isSsr,
|
|
isIntersectionObserverAvailable,
|
|
inView,
|
|
loaded,
|
|
}: State) => {
|
|
if (!lazyLoad) {
|
|
return true;
|
|
}
|
|
|
|
if (isSsr) {
|
|
return false;
|
|
}
|
|
|
|
if (isIntersectionObserverAvailable) {
|
|
return inView || loaded;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const imageShowStrategy = ({
|
|
lazyLoad,
|
|
isSsr,
|
|
isIntersectionObserverAvailable,
|
|
loaded,
|
|
}: State) => {
|
|
if (!lazyLoad) {
|
|
return true;
|
|
}
|
|
|
|
if (isSsr) {
|
|
return false;
|
|
}
|
|
|
|
if (isIntersectionObserverAvailable) {
|
|
return loaded;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
type ImageData = {
|
|
aspectRatio: number;
|
|
base64?: string;
|
|
height?: number;
|
|
width: number;
|
|
sizes?: string;
|
|
src?: string;
|
|
srcSet?: string;
|
|
webpSrcSet?: string;
|
|
bgColor?: string;
|
|
alt?: string;
|
|
title?: string;
|
|
};
|
|
|
|
type ImageProps = {
|
|
data: ImageData;
|
|
className?: string;
|
|
pictureClassName?: string;
|
|
fadeInDuration?: number;
|
|
intersectionTreshold?: number;
|
|
intersectionThreshold?: number;
|
|
intersectionMargin?: string;
|
|
lazyLoad?: boolean;
|
|
style?: React.CSSProperties;
|
|
pictureStyle?: React.CSSProperties;
|
|
explicitWidth?: boolean;
|
|
};
|
|
|
|
const Image = function ({
|
|
className,
|
|
fadeInDuration,
|
|
intersectionTreshold,
|
|
intersectionMargin,
|
|
pictureClassName,
|
|
lazyLoad = true,
|
|
style,
|
|
pictureStyle,
|
|
explicitWidth,
|
|
data,
|
|
}: ImageProps) {
|
|
const [loaded, setLoaded] = useState(false);
|
|
|
|
const handleLoad = useCallback(() => {
|
|
setLoaded(true);
|
|
}, []);
|
|
|
|
const [ref, inView] = useInView({
|
|
threshold: intersectionTreshold || 0,
|
|
rootMargin: intersectionMargin || "0px 0px 0px 0px",
|
|
triggerOnce: true,
|
|
});
|
|
|
|
const isSsr = typeof window === "undefined";
|
|
|
|
const isIntersectionObserverAvailable = isSsr
|
|
? false
|
|
: !!window.IntersectionObserver;
|
|
|
|
const absolutePositioning: React.CSSProperties = {
|
|
position: "absolute",
|
|
left: 0,
|
|
top: 0,
|
|
bottom: 0,
|
|
right: 0,
|
|
};
|
|
|
|
const addImage = imageAddStrategy({
|
|
lazyLoad,
|
|
isSsr,
|
|
isIntersectionObserverAvailable,
|
|
inView,
|
|
loaded,
|
|
});
|
|
const showImage = imageShowStrategy({
|
|
lazyLoad,
|
|
isSsr,
|
|
isIntersectionObserverAvailable,
|
|
inView,
|
|
loaded,
|
|
});
|
|
|
|
const webpSource = data.webpSrcSet && (
|
|
<source srcSet={data.webpSrcSet} sizes={data.sizes} type="image/webp" />
|
|
);
|
|
|
|
const regularSource = data.srcSet && (
|
|
<source srcSet={data.srcSet} sizes={data.sizes} />
|
|
);
|
|
|
|
const placeholder = (
|
|
<div
|
|
style={{
|
|
backgroundImage: data.base64 ? `url(${data.base64})` : null,
|
|
backgroundColor: data.bgColor,
|
|
backgroundSize: "cover",
|
|
opacity: showImage ? 0 : 1,
|
|
transition:
|
|
!fadeInDuration || fadeInDuration > 0
|
|
? `opacity ${fadeInDuration || 500}ms ${fadeInDuration || 500}ms`
|
|
: null,
|
|
...absolutePositioning,
|
|
}}
|
|
/>
|
|
);
|
|
|
|
const { width, aspectRatio } = data;
|
|
const height = data.height || width / aspectRatio;
|
|
|
|
const sizer = (
|
|
<svg
|
|
className={pictureClassName}
|
|
style={{
|
|
width: explicitWidth ? `${width}px` : "100%",
|
|
height: "auto",
|
|
display: "block",
|
|
...pictureStyle,
|
|
}}
|
|
height={height}
|
|
width={width}
|
|
/>
|
|
);
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={className}
|
|
style={{
|
|
display: "inline-block",
|
|
overflow: "hidden",
|
|
...style,
|
|
position: "relative",
|
|
}}
|
|
>
|
|
{sizer}
|
|
{placeholder}
|
|
{addImage && (
|
|
<picture
|
|
style={{
|
|
...absolutePositioning,
|
|
opacity: showImage ? 1 : 0,
|
|
transition:
|
|
!fadeInDuration || fadeInDuration > 0
|
|
? `opacity ${fadeInDuration || 500}ms`
|
|
: null,
|
|
}}
|
|
>
|
|
{webpSource}
|
|
{regularSource}
|
|
{data.src && (
|
|
<img
|
|
src={data.src}
|
|
alt={data.alt}
|
|
title={data.title}
|
|
onLoad={handleLoad}
|
|
style={{ width: "100%" }}
|
|
/>
|
|
)}
|
|
</picture>
|
|
)}
|
|
<noscript>
|
|
<picture className={pictureClassName} style={{ ...pictureStyle }}>
|
|
{webpSource}
|
|
{regularSource}
|
|
{data.src && <img src={data.src} alt={data.alt} title={data.title} />}
|
|
</picture>
|
|
</noscript>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Image;
|