mirror of
https://github.com/Afilmory/afilmory
synced 2026-04-24 23:05:05 +00:00
2
apps/ssr/next-env.d.ts
vendored
2
apps/ssr/next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import './.next/dev/types/routes.d.ts'
|
||||
import './.next/types/routes.d.ts'
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
MaterialSymbolsShutterSpeed,
|
||||
StreamlineImageAccessoriesLensesPhotosCameraShutterPicturePhotographyPicturesPhotoLens,
|
||||
TablerAperture,
|
||||
} from '@afilmory/ui/icons/index.jsx'
|
||||
} from '@afilmory/ui'
|
||||
import { clsxm as cn } from '@afilmory/utils'
|
||||
import { thumbHashToDataURL } from 'thumbhash'
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useInView } from 'react-intersection-observer'
|
||||
|
||||
import { Thumbhash } from '@afilmory/ui'
|
||||
import { clsxm } from '@afilmory/utils'
|
||||
|
||||
export interface LazyImageProps {
|
||||
src: string
|
||||
alt: string
|
||||
thumbHash?: string | null
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
onLoad?: () => void
|
||||
onError?: () => void
|
||||
// Intersection observer options
|
||||
rootMargin?: string
|
||||
threshold?: number
|
||||
}
|
||||
|
||||
export const LazyImage = ({
|
||||
src,
|
||||
alt,
|
||||
thumbHash,
|
||||
className,
|
||||
style,
|
||||
onLoad,
|
||||
onError,
|
||||
rootMargin = '50px',
|
||||
threshold = 0.1,
|
||||
}: LazyImageProps) => {
|
||||
const [isLoaded, setIsLoaded] = useState(false)
|
||||
const [hasError, setHasError] = useState(false)
|
||||
|
||||
const { ref, inView } = useInView({
|
||||
triggerOnce: true,
|
||||
rootMargin,
|
||||
threshold,
|
||||
})
|
||||
|
||||
const handleLoad = useCallback(() => {
|
||||
setIsLoaded(true)
|
||||
onLoad?.()
|
||||
}, [onLoad])
|
||||
|
||||
const handleError = useCallback(() => {
|
||||
setHasError(true)
|
||||
onError?.()
|
||||
}, [onError])
|
||||
|
||||
const shouldLoadImage = inView && !hasError
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={clsxm('relative overflow-hidden', className)}
|
||||
style={style}
|
||||
>
|
||||
{/* Thumbhash placeholder */}
|
||||
{thumbHash && !isLoaded && (
|
||||
<Thumbhash
|
||||
thumbHash={thumbHash}
|
||||
className="absolute inset-0 scale-110 blur-sm"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Actual image */}
|
||||
{shouldLoadImage && (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={clsxm(
|
||||
'h-full w-full object-cover transition-opacity duration-300',
|
||||
isLoaded ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
onLoad={handleLoad}
|
||||
onError={handleError}
|
||||
loading="lazy"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Error state */}
|
||||
{hasError && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-gray-200 dark:bg-gray-800">
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Failed to load image
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user