11'use client' ;
22import { tcls } from '@/lib/tailwind' ;
3- import { useRef } from 'react' ;
4- import { useResizeObserver } from 'usehooks-ts' ;
53import type { ImageSize } from '../utils' ;
4+ import { getRecommendedCoverDimensions } from './coverDimensions' ;
5+ import { useCoverPosition } from './useCoverPosition' ;
66
77interface ImageAttributes {
88 src : string ;
@@ -18,31 +18,25 @@ interface Images {
1818 dark ?: ImageAttributes ;
1919}
2020
21- const PAGE_COVER_SIZE : ImageSize = { width : 1990 , height : 480 } ;
21+ export function PageCoverImage ( { imgs, y, height } : { imgs : Images ; y : number ; height : number } ) {
22+ const { containerRef, objectPositionY, isLoading } = useCoverPosition ( imgs , y ) ;
2223
23- function getTop ( container : { height ?: number ; width ?: number } , y : number , img : ImageAttributes ) {
24- // When the size of the image hasn't been determined, we fallback to the center position
25- if ( ! img . size || y === 0 ) return '50%' ;
26- const ratio =
27- container . height && container . width
28- ? Math . max ( container . width / img . size . width , container . height / img . size . height )
29- : 1 ;
30- const scaledHeight = img . size ? img . size . height * ratio : PAGE_COVER_SIZE . height ;
31- const top =
32- container . height && img . size ? ( container . height - scaledHeight ) / 2 + y * ratio : y ;
33- return `${ top } px` ;
34- }
35-
36- export function PageCoverImage ( { imgs, y } : { imgs : Images ; y : number } ) {
37- const containerRef = useRef < HTMLDivElement > ( null ) ;
24+ // Calculate the recommended aspect ratio for this height
25+ // This maintains the 4:1 ratio, allowing images to scale proportionally
26+ // and adapt their height when container width doesn't match the ideal ratio
27+ const recommendedDimensions = getRecommendedCoverDimensions ( height ) ;
28+ const aspectRatio = recommendedDimensions . width / recommendedDimensions . height ;
3829
39- const container = useResizeObserver ( {
40- // @ts -expect-error wrong types
41- ref : containerRef ,
42- } ) ;
30+ if ( isLoading ) {
31+ return (
32+ < div className = "h-full w-full overflow-hidden" ref = { containerRef } >
33+ < div className = "h-full w-full animate-pulse bg-gradient-to-br from-gray-100 to-gray-200 dark:from-gray-800 dark:to-gray-900" />
34+ </ div >
35+ ) ;
36+ }
4337
4438 return (
45- < div className = "h-full w-full overflow-hidden" ref = { containerRef } >
39+ < div className = "h-full w-full overflow-hidden" ref = { containerRef } style = { { height } } >
4640 < img
4741 src = { imgs . light . src }
4842 srcSet = { imgs . light . srcSet }
@@ -51,8 +45,8 @@ export function PageCoverImage({ imgs, y }: { imgs: Images; y: number }) {
5145 alt = "Page cover"
5246 className = { tcls ( 'w-full' , 'object-cover' , imgs . dark ? 'dark:hidden' : '' ) }
5347 style = { {
54- aspectRatio : `${ PAGE_COVER_SIZE . width } / ${ PAGE_COVER_SIZE . height } ` ,
55- objectPosition : `50% ${ getTop ( container , y , imgs . light ) } ` ,
48+ aspectRatio : `${ aspectRatio } ` ,
49+ objectPosition : `50% ${ objectPositionY } % ` ,
5650 } }
5751 />
5852 { imgs . dark && (
@@ -64,8 +58,8 @@ export function PageCoverImage({ imgs, y }: { imgs: Images; y: number }) {
6458 alt = "Page cover"
6559 className = { tcls ( 'w-full' , 'object-cover' , 'dark:inline' , 'hidden' ) }
6660 style = { {
67- aspectRatio : `${ PAGE_COVER_SIZE . width } / ${ PAGE_COVER_SIZE . height } ` ,
68- objectPosition : `50% ${ getTop ( container , y , imgs . dark ) } ` ,
61+ aspectRatio : `${ aspectRatio } ` ,
62+ objectPosition : `50% ${ objectPositionY } % ` ,
6963 } }
7064 />
7165 ) }
0 commit comments