Skip to content

Commit

Permalink
Add ScrollingDots component to indicate slideshow pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmockett committed Nov 19, 2024
1 parent 445abd8 commit 8dbebd5
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 34 deletions.
78 changes: 78 additions & 0 deletions dotcom-rendering/src/components/ScrollingDots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { css } from '@emotion/react';
import { palette } from '../palette';

const dotSize = 7;
const dotGap = 4;
const dotsVisible = 5;
const scrollThreshold = Math.floor(dotsVisible / 2);

const scrollingDotContainerStyles = css`
overflow: hidden;
max-width: ${dotSize * dotsVisible + dotGap * (dotsVisible - 1) + 8}px;
`;

const scrollingDotStyles = (total: number) => css`
display: grid;
gap: ${dotGap}px;
grid-template-columns: repeat(${total}, ${dotSize}px);
padding: 4px;
transition: transform 0.25s ease;
`;

const dotStyles = css`
width: ${dotSize}px;
height: ${dotSize}px;
border-radius: 100%;
background-color: ${palette('--slideshow-pagination-dot')};
transition: all 0.25s ease;
`;

const activeDotStyles = css`
transform: scale(1.15);
background-color: ${palette('--slideshow-pagination-dot-active')};
`;

export const ScrollingDots = ({
total,
current,
}: {
total: number;
current: number;
}) => {
const scrollingDotOffset = () => {
const offsetPerDot = -(dotSize + dotGap);

if (total <= dotsVisible) return;

if (current < scrollThreshold) {
return { transform: 'translateX(0)' };
}

if (current >= total - scrollThreshold) {
return {
transform: `translateX(${
(total - dotsVisible) * offsetPerDot
}px)`,
};
}

return {
transform: `translateX(${
(current - scrollThreshold) * offsetPerDot
}px)`,
};
};

return (
<div css={scrollingDotContainerStyles}>
<div css={scrollingDotStyles(total)} style={scrollingDotOffset()}>
{Array.from({ length: total }, (_, index) => (
<span
css={[dotStyles, current === index && activeDotStyles]}
key={index}
/>
))}
</div>
</div>
);
};
54 changes: 20 additions & 34 deletions dotcom-rendering/src/components/SlideshowCarousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { palette } from '../palette';
import type { DCRSlideshowImage } from '../types/front';
import type { ImageSizeType } from './Card/components/ImageWrapper';
import { CardPicture } from './CardPicture';
import { ScrollingDots } from './ScrollingDots';

const themeButton: Partial<ThemeButton> = {
borderTertiary: palette('--carousel-chevron-border'),
Expand Down Expand Up @@ -68,37 +69,23 @@ const navigationStyles = css`
margin-top: ${space[2]}px;
`;

const buttonStyles = css`
display: flex;
gap: ${space[2]}px;
`;

/**
* Padding is added to the left of the navigation dots to match the width of the
* navigation buttons on the right so they are centred below the image.
* Padding is added to the left of the scrolling navigation dots to match the
* width of the navigation buttons on the right. This allows them to be centred
* below the slideshow image.
*/
const paginationStyles = css`
const scrollingDotStyles = css`
display: flex;
justify-content: center;
align-items: center;
gap: ${space[1]}px;
flex: 1 0 0;
padding-left: ${width.ctaSmall * 2 + space[2]}px;
`;

const dotStyles = css`
width: 7px;
height: 7px;
border-radius: 100%;
background-color: ${palette('--slideshow-pagination-dot')};
`;

const activeDotStyles = css`
width: 8px;
height: 8px;
background-color: ${palette('--slideshow-pagination-dot-active')};
`;

const buttonStyles = css`
display: flex;
gap: ${space[2]}px;
`;

export const SlideshowCarousel = ({
images,
imageSize,
Expand Down Expand Up @@ -166,14 +153,17 @@ export const SlideshowCarousel = ({
};
}, []);

const slideshowImages = takeFirst(images, 10);
const slideshowImageCount = slideshowImages.length;

return (
<div>
<ul
ref={carouselRef}
css={carouselStyles}
data-heatphan-type="carousel"
>
{takeFirst(images, 10).map((image, index) => {
{slideshowImages.map((image, index) => {
const loading = index > 0 ? 'lazy' : 'eager';
return (
<li css={carouselItemStyles} key={image.imageSrc}>
Expand All @@ -195,17 +185,13 @@ export const SlideshowCarousel = ({
);
})}
</ul>

<div css={navigationStyles}>
<div css={paginationStyles}>
{takeFirst(images, 10).map((image, index) => (
<span
css={[
dotStyles,
currentPage === index && activeDotStyles,
]}
key={image.imageSrc}
/>
))}
<div css={scrollingDotStyles}>
<ScrollingDots
total={slideshowImageCount}
current={currentPage}
/>
</div>
<div css={buttonStyles}>
<Button
Expand Down

0 comments on commit 8dbebd5

Please sign in to comment.