Skip to content

Commit 014dd10

Browse files
committed
Correct layout / project browser height
1 parent 32d6814 commit 014dd10

6 files changed

Lines changed: 103 additions & 82 deletions

File tree

src/components/projects/grid/ProjectGrid.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ const ProjectGrid = forwardRef<ProjectGridHandle, ProjectGridProps>(({scrollCont
112112
const item = projectItems.find(p=>p.props.projectIndex === index);
113113
console.log('SCROLL TO ITEM:', index, item, jump);
114114
if(!item) return;
115-
const itemRef = projectRefs.current?.[item.props.refIndex];
116-
if(!itemRef) return;
115+
const itemRef = projectRefs.current?.[item.props.project.id];
116+
// if(!itemRef) return;
117117
itemRef?.current?.scrollIntoView(jump);
118118
}, [projectItems]);
119119

src/components/projects/overlay/ProjectCarousel.tsx

Lines changed: 75 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Carousel, CarouselContent, CarouselItem, CarouselNav, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
22
import { type EmblaViewportRefType } from "embla-carousel-react";
33
import React, { type PointerEventHandler } from "react";
4-
import type { EmblaCarouselType } from "embla-carousel";
4+
import type { EmblaCarouselType, EmblaEventType } from "embla-carousel";
55
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
66
import { DialogClose } from "./TransparentDialog";
77
import { XIcon } from "lucide-react";
@@ -18,7 +18,7 @@ type ProjectCarouselProps = Omit<React.ComponentProps<typeof Carousel>, 'externa
1818
slides: CarouselContentItemWithTitle[],
1919
prevRef?: React.RefObject<HTMLButtonElement | null>,
2020
nextRef?: React.RefObject<HTMLButtonElement | null>,
21-
onCarouselSelect: (emblaApi?: EmblaCarouselType) => void,
21+
onCarouselSelect: (emblaApi: EmblaCarouselType | undefined, evtType?: EmblaEventType) => void,
2222
externalApi?: EmblaCarouselType,
2323
showToast: ShowToastFn,
2424
getHovercardContentForIndex: (index: number) => React.ReactNode,
@@ -57,19 +57,43 @@ const CarouselSlideContentSkeleton = React.memo(()=>{
5757

5858

5959
const ProjectCarouselItem = React.memo(({ project, handleRef, startTransition, index: i, slide, onPointerDown: clickCallback, showToast, ...props }: ProjectCarouselItemProps) => {
60-
// const isCurrent = React.useDeferredValue<boolean>(isCurrentItem);
6160
const skeleton = React.useMemo(()=><CarouselSlideContentSkeleton/>, []);
6261
const [isCurrentItem, setIsCurrentItem] = React.useState<boolean>(false);
63-
64-
// const setIsCurrent = React.useCallback((isCurrent: boolean) => {
65-
// startTransition(()=>{
66-
// setIsCurrentItem(isCurrent);
67-
// });
68-
// }, [startTransition]);
62+
const isCurrent = React.useDeferredValue<boolean>(isCurrentItem);
63+
64+
const deactivationTimeout = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
65+
const activationTimeout = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
66+
67+
const setIsCurrent = React.useCallback((isCurrent: boolean) => {
68+
if(isCurrent) {
69+
clearTimeout(deactivationTimeout.current);
70+
deactivationTimeout.current = undefined;
71+
if(activationTimeout.current === undefined) {
72+
activationTimeout.current = setTimeout (()=>{
73+
React.startTransition(()=>
74+
setIsCurrentItem(isCurrent)
75+
);
76+
activationTimeout.current = undefined;
77+
}, 200);
78+
}
79+
return;
80+
}
81+
clearTimeout(activationTimeout.current);
82+
activationTimeout.current = undefined;
83+
if(deactivationTimeout.current === undefined) {
84+
deactivationTimeout.current = setTimeout(()=>{
85+
React.startTransition(()=>
86+
setIsCurrent(false)
87+
);
88+
deactivationTimeout.current = undefined;
89+
}, 2500);
90+
}
91+
}, []);
6992

7093
React.useImperativeHandle(handleRef, ()=>({
71-
setIsCurrent: setIsCurrentItem
72-
}), []);
94+
// setIsCurrent: setIsCurrentItem
95+
setIsCurrent
96+
}), [setIsCurrent]);
7397

7498
return <CarouselItem key={i} id={`slide-${i}`} className="pointer-events-auto h-min" {...props}>
7599
<Card className="relative w-full flex pointer-events-auto max-h-[calc(100vh-(--spacing(25)))] overflow-y-scroll">
@@ -89,12 +113,13 @@ const ProjectCarouselItem = React.memo(({ project, handleRef, startTransition, i
89113
{/* <DialogClose aria-label="Close carousel" data-slot="dialog-close" className="sr-only" /> */}
90114
{/* above sr-only DialogClose is a compact additional accessible control -- main visual close still has icon */}
91115
{/* <ShareButton className="absolute top-4 right-14 text-sm" showToast={showToast} openProjectId={slide.props["data-project-id"]} /> */}
92-
{/* <React.Suspense>
93-
{slide}
94-
</React.Suspense> */}
95-
{/* {isCurrentItem ? slide : skeleton} */}
96116
<ProjectProvider project={project}>
97-
{slide}
117+
{isCurrent ?
118+
<React.Suspense fallback={skeleton}>
119+
{slide}
120+
</React.Suspense>
121+
: skeleton
122+
}
98123
</ProjectProvider>
99124
</CardContent>
100125
</Card>
@@ -115,7 +140,8 @@ const ProjectCarousel = React.memo(({
115140
getHovercardContentForIndex,
116141
}: ProjectCarouselProps) => {
117142
const slideHandles = React.useRef<Record<string, React.RefObject<ProjectCarouselItemHandle>>>({});
118-
slideHandles.current = Object.fromEntries(slides.map((slide) => [slide.props["data-project-id"], slideHandles.current[slide.props['data-project-id'] ?? React.createRef()]]));
143+
slideHandles.current = Object.fromEntries(slides.map((slide) => [slide.props["data-project-id"], slideHandles.current[slide.props['data-project-id']] ?? React.createRef()]));
144+
119145
const [_isPending, startTransition] = React.useTransition();
120146

121147
const slideElems = React.useMemo(()=>{
@@ -124,41 +150,37 @@ const ProjectCarousel = React.memo(({
124150
))
125151
}, [slides, showToast]);
126152

127-
// const slideHovercards = React.useMemo(()=>{
128-
129-
// }, [])
130-
131-
// const onSelect0: typeof onSelect = React.useCallback((api) => {
132-
// // if(api) {
133-
// // const index = api.selectedScrollSnap();
134-
// // const prevIndex = api.previousScrollSnap();
135-
136-
// // const currId = slides[index]?.props['data-project-id'];
137-
// // const currHandle = currId ? slideHandles.current[currId] : undefined;
138-
139-
// // if(currHandle) {
140-
// // const prevId = slides[prevIndex]?.props['data-project-id'];
141-
// // const prevHandle = (prevIndex === index || !prevId) ? undefined : slideHandles.current[prevId];
142-
// // console.log(currHandle, prevHandle);
143-
// // React.startTransition(()=>{
144-
// // try {
145-
// // prevHandle?.current?.setIsCurrent(false);
146-
// // } finally {
147-
// // currHandle?.current?.setIsCurrent(true);
148-
// // }
149-
// // });
150-
// // }
151-
// // }
152-
// }, [slides]);
153-
154-
// const onSelect_: typeof onSelect = React.useCallback(api=>{
155-
// try {
156-
// onSelect0(api);
157-
// } finally {
158-
// // startTransition(()=>onSelect(api));
159-
// onSelect(api);
160-
// }
161-
// }, [onSelect, onSelect0]);
153+
const onSelect0: typeof onSelect = React.useCallback((api, _evtType) => {
154+
if(!api) return;
155+
const index = api.selectedScrollSnap();
156+
const prevIndex = api.previousScrollSnap();
157+
158+
const currId = slides[index]?.props['data-project-id'];
159+
const currHandle = currId ? slideHandles.current[currId] : undefined;
160+
161+
// console.log('api/currHandle:', api, currHandle);
162+
163+
// if(!currHandle) return;
164+
const prevId = slides[prevIndex]?.props['data-project-id'];
165+
const prevHandle = (prevIndex === index || !prevId) ? undefined : slideHandles.current[prevId];
166+
// console.log(currHandle, prevHandle);
167+
startTransition(()=>{
168+
try {
169+
prevHandle?.current?.setIsCurrent(false);
170+
} finally {
171+
currHandle?.current?.setIsCurrent(true);
172+
}
173+
});
174+
}, [slides]);
175+
176+
const onSelect_: typeof onSelect = React.useCallback((api, evtType) => {
177+
try {
178+
onSelect0(api, evtType);
179+
} finally {
180+
// startTransition(()=>onSelect(api));
181+
onSelect(api, evtType);
182+
}
183+
}, [onSelect, onSelect0]);
162184

163185
return <Carousel
164186
ref={emblaRef}
@@ -167,7 +189,7 @@ const ProjectCarousel = React.memo(({
167189
opts={opts}
168190
className="overflow-visible z-60 w-full max-w-[calc(min(100vw,var(--container-2xl)))] pointer-events-none
169191
"
170-
onCarouselSelect={onSelect}
192+
onCarouselSelect={onSelect_}
171193
>
172194
<CarouselContent
173195
id="embla-container"

src/components/projects/overlay/ProjectCarouselDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export default function ProjectCarouselDialog({ contentElements, showToast, scro
9090
if (!emblaApi) return;
9191

9292
const index = emblaApi.selectedScrollSnap();
93-
console.log('[Carousel] onSelect - scrolling to index:', index, 'current activeIndex:', activeProjectIndex);
93+
// console.log('[Carousel] onSelect - scrolling to index:', index, 'current activeIndex:', activeProjectIndex);
9494

9595
// Update the store's active project index
9696
// This will trigger URL sync automatically if carousel is open

src/components/ui/carousel.tsx

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ArrowLeft, ArrowRight } from "lucide-react"
88
import { cn } from "@/lib/utils"
99
import { Button } from "@/components/ui/button"
1010

11-
import type {EmblaCarouselType, /*EmblaEventType*/ } from "embla-carousel";
11+
import type {EmblaCarouselType, EmblaEventType, /*EmblaEventType*/ } from "embla-carousel";
1212
import { HoverCard, HoverCardContent, HoverCardTrigger } from "./hover-card"
1313
import CarouselSlider from "../projects/overlay/CarouselSlider"
1414
// import { useWindowSize } from "@/hooks/useWindowSize"
@@ -30,7 +30,7 @@ type CarouselProps = {
3030
plugins?: CarouselPlugin
3131
orientation?: "horizontal" | "vertical"
3232
setApi?: (api: CarouselApi) => void,
33-
onCarouselSelect?: (api: CarouselApi | undefined) => void,
33+
onCarouselSelect?: (api: CarouselApi | undefined, evtType?: EmblaEventType) => void,
3434
externalApi?: CarouselApi,
3535
externalCarouselRef?: EmblaViewportRefType
3636
}
@@ -101,16 +101,15 @@ function Carousel({ orientation = "horizontal",
101101
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
102102
const [canScrollNext, setCanScrollNext] = React.useState(false)
103103

104-
const onSelectInInit = React.useCallback((api?: CarouselApi) => {
104+
const onSelect = React.useCallback((api: CarouselApi | undefined, evtType?: EmblaEventType) => {
105105
if (!api) return
106106
setCanScrollPrev(api.canScrollPrev());
107107
setCanScrollNext(api.canScrollNext());
108-
// onSelect_?.(api);
109-
}, [setCanScrollNext, setCanScrollPrev]);
110-
const onSelect = React.useCallback((api: CarouselApi) => {
111-
onSelectInInit(api);
112-
onSelect_?.(api);
113-
}, [onSelectInInit, onSelect_]);
108+
onSelect_?.(api, evtType);
109+
}, [setCanScrollNext, setCanScrollPrev, onSelect_]);
110+
// const onSelect = React.useCallback((api: CarouselApi, evtType: EmblaEventType) => {
111+
// onSelectInInit(api, evtType);
112+
// }, [onSelectInInit]);
114113

115114

116115
const scrollPrev = React.useCallback(() => {
@@ -194,25 +193,25 @@ function Carousel({ orientation = "horizontal",
194193

195194
React.useEffect(() => {
196195
if (!api) return
197-
onSelectInInit(api)
198-
api.on("reInit", onSelectInInit)
199-
// api.on("select", onSelect)
200-
201-
return () => {
202-
api?.off('reInit', onSelectInInit)
203-
// api?.off("select", onSelect)
204-
}
205-
}, [api, onSelectInInit]);
206-
207-
React.useEffect(() => {
208-
if (!api) return
209-
// onSelect(api);
196+
onSelect(api)
197+
api.on("reInit", onSelect)
210198
api.on("select", onSelect)
211199

212200
return () => {
201+
api?.off('reInit', onSelect)
213202
api?.off("select", onSelect)
214203
}
215-
}, [api, onSelect])
204+
}, [api, onSelect]);
205+
206+
// React.useEffect(() => {
207+
// if (!api) return
208+
// // onSelect(api);
209+
// api.on("select", onSelect)
210+
211+
// return () => {
212+
// api?.off("select", onSelect)
213+
// }
214+
// }, [api, onSelect])
216215

217216

218217
// React.useEffect(() => {

src/layouts/Layout.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/GSDevTools.min.js"
118118
<!-- Height: 15vh -->
119119

120120
<!-- <div class="body-scroll-content"> -->
121-
<div id="root" class="body-content h-[85vh] overflow-y-auto overscroll-contain">
121+
<div id="root" class="body-content absolute h-screen w-screen top-0 overflow-y-auto overscroll-contain pt-18">
122122
<slot />
123123
<!-- {storyPage && <Footer />} -->
124124
{!storyPage && <Footer />}

src/styles/global.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ a.inline-block.leading-5.pl-12{
377377
}
378378

379379

380-
#modal-root {
380+
/* #modal-root {
381381
height: 100vh;
382382
width: 100vw;
383-
}
383+
} */

0 commit comments

Comments
 (0)