-
Notifications
You must be signed in to change notification settings - Fork 2
isom-1898 image gallery slideshow #1298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
isom-1898 image gallery slideshow #1298
Conversation
…holder images and descriptions
…d placeholder images
…ality; add constants for arrow SVGs and maximum preview images
…ty; simplify preview logic and enhance readability. Add unit tests for getPreviewIndices functionality.
… styles for improved accessibility and visual consistency.
…r effects and disable state for active images.
… enhancing accessibility and SEO.
…eryClient for improved modularity and maintainability. Introduce ImageGalleryClientProps type for better prop management.
…on and button state, improving clarity and consistency in index handling.
…layout consistency in the preview section.
…nd styles for image previews, improve button focus states, and refactor preview index handling for better clarity and maintainability.
… sets with captions for enhanced visual representation and testing.
…ved layout and visual separation in the image gallery.
Datadog ReportBranch report: ✅ 0 Failed, 566 Passed, 46 Skipped, 1m 35.05s Total Time |
const SingleImageSchema = Type.Object({ | ||
src: ImageSrcSchema, | ||
alt: AltTextSchema, | ||
caption: Type.Optional(Type.String({ maxLength: 100 })), | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[comment, no actionable] i wonder if we should consolidate this together iwth our existing image schema to avoid an explosion of similar yet subtly different schemas
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought about it - felt a bit hard since it's aldy not standardized at this moment, from schema shape to name of fields e.g. src
, imageSrc
packages/components/src/templates/next/components/complex/ImageGallery/constants.tsx
Outdated
Show resolved
Hide resolved
packages/components/src/templates/next/components/complex/ImageGallery/ImageGalleryClient.tsx
Outdated
Show resolved
Hide resolved
packages/components/src/templates/next/components/complex/ImageGallery/index.ts
Show resolved
Hide resolved
packages/components/src/templates/next/components/complex/ImageGallery/ImageGalleryClient.tsx
Show resolved
Hide resolved
packages/components/src/templates/next/components/complex/ImageGallery/ImageGalleryClient.tsx
Outdated
Show resolved
Hide resolved
if (image.getAttribute("loading") === "lazy") { | ||
image.removeAttribute("loading") | ||
const src = image.src | ||
image.src = "" | ||
image.src = src | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[comment] not sure if it's possible to do, but the lazy load appears to be a function of the current index and the maxPreviewImages
- should be just pass in the logic here to the lazyLoad
prop on ImageClient
? feels abit clearer and saves us the trouble of dom manipulation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if i understand correctly. maxPreviewImages
is determined by the breakpoints and not a config, and we will only know when the component is rendering
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To double check my understanding; my proposal was to do something like
<ImageClient
shouldLazyLoad = {shouldLazyLoad(numberOfImages, currentIndex, maxPreviewImages)}
/>
because this is client component (use client
directive at top), it seems like we'll already have access to the client sided APIs. this seems like a cleaner way (+ clearer) rather than having to do DOM manipulation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, these are two unrelated sections.
The shouldLazyLoad
logic in ImageClient
controls whether an image should be lazy/eager-loaded during the initial render.
In contrast, the DOM manipulation above is explicitly forcing an eager load. This change (from lazy to eager) is necessary to ensure the image loads immediately. We do that through preloadImage
, which is triggered by user interactions like hovering over buttons or preview images.
return Array.from({ length: numberOfImages }, (_, i) => i) | ||
} | ||
|
||
const numberOfImagesBeforeCurrent = Math.floor(maxPreviewImages / 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure i understand this line here. if i have an image array [0, 1, 2, 3, 4], and i'm at position 4, the number of images before current should be 4 isn't it? similarly if i'm at position 1, it's should be 0.
not sure i understand why it's maxPreviewImages / 2
rather than just the pos
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah this do some computes
What it does:
Creates a "sliding window" of image previews (e.g., 3 or 5 images).
How it works:
- If we have fewer images than the window size, it shows all of them.
- Otherwise (we have more images than the window size), it tries to center the currentIndex within this window
- It smartly adjusts the window to ensure it doesn't go off the edges (e.g., showing negative indices or indices beyond the last image). So, if currentIndex is near the start or end, the window shifts to stay within bounds.
To your question:
You've got a good point - the name numberOfImagesBeforeCurrent
is a bit confusing.
- This line
const numberOfImagesBeforeCurrent = Math.floor(maxPreviewImages / 2)
isn't counting the actual images before currentIndex in the whole array. Instead, it's figuring out how many images should appear before the currentIndex within the small, visible preview window to keep the currentIndex roughly centered. - If maxPreviewImages is 5, it tries to put 2 images before the current one in the preview.
- If maxPreviewImages is 3, it tries to put 1 image before the current one in the preview.
The rest of the function then uses this to position that "preview window" correctly within the full list of images, making sure it doesn't go out of bounds.
…image-gallery-slideshow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make sure i'm understanding the behaviour of the pr correctly:
we have 2 forms of images
- the big one shown when the user clicks in
- the preview we see down below
the big one is eagerly preloaded when user is +/- 1 or 2 positions away (depending on the window size)
all the images in the preview we see down below is loaded when the user hovers over the preview (and we load in window size)?
if (image.getAttribute("loading") === "lazy") { | ||
image.removeAttribute("loading") | ||
const src = image.src | ||
image.src = "" | ||
image.src = src | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To double check my understanding; my proposal was to do something like
<ImageClient
shouldLazyLoad = {shouldLazyLoad(numberOfImages, currentIndex, maxPreviewImages)}
/>
because this is client component (use client
directive at top), it seems like we'll already have access to the client sided APIs. this seems like a cleaner way (+ clearer) rather than having to do DOM manipulation
packages/components/src/templates/next/components/complex/ImageGallery/ImageGalleryClient.tsx
Show resolved
Hide resolved
const shouldPreload = | ||
// Preload the image if it is in the preview thumbnail sequence | ||
previewIndices.includes(index) || | ||
// Preload the last image if currently displaying the first image | ||
// to ensure smooth transitioning when navigating to the last image from the first | ||
(currentIndex === 0 && index === images.length - 1) || | ||
// Preload the first image if currently displaying the last image | ||
// to ensure smooth transitioning when navigating to the first image from the last | ||
(currentIndex === images.length - 1 && index === 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[clarification] shouldn't the preview indices for currentIndex == 0 || currentIndex = 1
alr include the first/last index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, but in the case wheres theres many images e.g. 20, the previewIndices won't include the first/last index
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[comment] i can't help but feel like this is abit confusing and probably clearer expressed with modulo arithmetic, but given its narrow scope, i'm fine w/ just keeping this as is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually i agree. Thought about it and decided to refactor it based on 51bdb44
its more hardcoded, but given that we are sure its gonna be 3/5, we can hardcode for the respective use case. More readable and maintanable
correct
correct + also as they onMouseEnter the "next"/"previous" button |
…ngohjw/isom-1898-image-gallery-slideshow
Problem
Closes https://linear.app/ogp/issue/ISOM-1898/image-gallery-slideshow
Solution
Breaking Changes
Features:
skipped the image preload optimization part - i feel like its not an issue at this point so decided to not overengineerupdate: decided to still add it otherwise navigation can feel sluggish, and not somethign im proud of. And since we rarely update components after shipping it, I rather do it well now (isom-1898 image gallery slideshow #1298 (comment))FYI @sehyunidaaa - lemme know otherwise
Before & After Screenshots
Without transition
Screen.Recording.2025-05-08.at.7.25.36.AM.mov
With transition
Screen.Recording.2025-05-08.at.7.59.29.AM.mov
Tests
Go to Studio to create a new Image Gallery block in a Content page, and add more than 5 images