74 lines
2.0 KiB
TypeScript
74 lines
2.0 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
import { Photo } from '@/photo';
|
|
import PhotoOGTile, { OGLoadingState } from './PhotoOGTile';
|
|
|
|
const DEFAULT_MAX_CONCURRENCY = 3;
|
|
|
|
type PhotoLoadingState = Record<string, OGLoadingState>;
|
|
|
|
export default function StaggeredOgPhotos({
|
|
photos,
|
|
maxConcurrency = DEFAULT_MAX_CONCURRENCY,
|
|
onLastPhotoVisible,
|
|
}: {
|
|
photos: Photo[]
|
|
maxConcurrency?: number
|
|
onLastPhotoVisible?: () => void
|
|
}) {
|
|
const [loadingState, setLoadingState] = useState(
|
|
photos.reduce((acc, photo) => ({
|
|
...acc,
|
|
[photo.id]: 'unloaded' as const,
|
|
}), {} as PhotoLoadingState),
|
|
);
|
|
|
|
const recomputeLoadingState = useCallback((
|
|
updatedState: PhotoLoadingState = {},
|
|
) => setLoadingState(currentLoadingState => {
|
|
const initialLoadingState = {
|
|
...currentLoadingState,
|
|
...updatedState,
|
|
};
|
|
const updatedLoadingState = {
|
|
...currentLoadingState,
|
|
...updatedState,
|
|
};
|
|
|
|
let imagesLoadingCount = 0;
|
|
Object.entries(initialLoadingState).forEach(([id, state]) => {
|
|
if (state === 'loading') {
|
|
imagesLoadingCount++;
|
|
} else if (imagesLoadingCount < maxConcurrency && state === 'unloaded') {
|
|
updatedLoadingState[id] = 'loading';
|
|
imagesLoadingCount++;
|
|
}
|
|
});
|
|
|
|
return updatedLoadingState;
|
|
})
|
|
, [maxConcurrency]);
|
|
|
|
useEffect(() => {
|
|
recomputeLoadingState();
|
|
}, [recomputeLoadingState]);
|
|
|
|
return (
|
|
<div className="grid gap-3 grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
|
{photos.map((photo, index) =>
|
|
<PhotoOGTile
|
|
key={photo.id}
|
|
photo={photo}
|
|
loadingState={loadingState[photo.id]}
|
|
onLoad={() => recomputeLoadingState({ [photo.id]: 'loaded' })}
|
|
onFail={() => recomputeLoadingState({ [photo.id]: 'failed' })}
|
|
onVisible={index === photos.length - 1
|
|
? onLastPhotoVisible
|
|
:undefined}
|
|
riseOnHover
|
|
/>)}
|
|
</div>
|
|
);
|
|
};
|