Scroll photo grid into view when selecting photos

This commit is contained in:
Sam Becker 2025-09-07 16:15:55 -05:00
parent 2d72f12ddd
commit 57879706a2
2 changed files with 43 additions and 7 deletions

View File

@ -7,6 +7,7 @@ import { usePathname } from 'next/navigation';
import { useAppState } from '@/app/AppState'; import { useAppState } from '@/app/AppState';
import useClientSearchParams from '@/utility/useClientSearchParams'; import useClientSearchParams from '@/utility/useClientSearchParams';
import { pushPathWithEvent } from '@/utility/url'; import { pushPathWithEvent } from '@/utility/url';
import { isElementPartiallyInViewport } from '@/utility/dom';
export const DATA_KEY_PHOTO_GRID = 'data-photo-grid'; export const DATA_KEY_PHOTO_GRID = 'data-photo-grid';
@ -28,10 +29,14 @@ export default function SelectPhotosProvider({
const [isPerformingSelectEdit, setIsPerformingSelectEdit] = const [isPerformingSelectEdit, setIsPerformingSelectEdit] =
useState(false); useState(false);
const getPhotoGridElements = useCallback(() =>
document.querySelectorAll(`[${DATA_KEY_PHOTO_GRID}]`)
, []);
useEffect(() => { useEffect(() => {
setCanCurrentPageSelectPhotos(document const doesPageHavePhotoGrids = getPhotoGridElements().length > 0;
.querySelector(`[${DATA_KEY_PHOTO_GRID}]`) !== null); setCanCurrentPageSelectPhotos(doesPageHavePhotoGrids);
}, [pathname]); }, [pathname, getPhotoGridElements]);
const isSelectingPhotos = useMemo(() => const isSelectingPhotos = useMemo(() =>
isUserSignedIn && isUserSignedIn &&
@ -50,10 +55,18 @@ export default function SelectPhotosProvider({
, [pathname]); , [pathname]);
useEffect(() => { useEffect(() => {
if (!isSelectingPhotos) { if (isSelectingPhotos) {
const photoGrids = Array.from(getPhotoGridElements());
const isSomePhotoGridVisible = photoGrids
.some(element => isElementPartiallyInViewport(element, -20));
if (!isSomePhotoGridVisible) {
console.log('scrolling to photo grid');
photoGrids[0]?.scrollIntoView({ behavior: 'smooth' });
}
} else {
setSelectedPhotoIds([]); setSelectedPhotoIds([]);
} }
}, [isSelectingPhotos]); }, [isSelectingPhotos, getPhotoGridElements]);
return ( return (
<SelectPhotosContext.Provider value={{ <SelectPhotosContext.Provider value={{

View File

@ -1,5 +1,5 @@
export const isElementEntirelyInViewport = ( export const isElementEntirelyInViewport = (
element?: HTMLElement | null, element?: Element | null,
) => { ) => {
if (element) { if (element) {
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
@ -11,7 +11,8 @@ export const isElementEntirelyInViewport = (
document.documentElement.clientHeight document.documentElement.clientHeight
) && ) &&
rect.right <= ( rect.right <= (
window.innerWidth || document.documentElement.clientWidth window.innerWidth ||
document.documentElement.clientWidth
) )
); );
} else { } else {
@ -19,6 +20,28 @@ export const isElementEntirelyInViewport = (
} }
}; };
export function isElementPartiallyInViewport(
element?: Element | null,
// Expand the viewport by `offset` pixels (negative offset = stricter)
offset = 0,
): boolean {
if (element) {
const rect = element.getBoundingClientRect();
const vh = window.innerHeight || document.documentElement.clientHeight;
const vw = window.innerWidth || document.documentElement.clientWidth;
const topVisible = rect.bottom >= -offset;
const leftVisible = rect.right >= -offset;
const bottomVisible = rect.top <= vh + offset;
const rightVisible = rect.left <= vw + offset;
return topVisible && leftVisible && bottomVisible && rightVisible;
} else {
return false;
}
}
export const clearGlobalFocus = () => { export const clearGlobalFocus = () => {
if (document.activeElement instanceof HTMLElement) { if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur(); document.activeElement.blur();