Skip to content

Commit 1d4873e

Browse files
committed
feat: slideshows at top of main page
1 parent 31a792c commit 1d4873e

File tree

7 files changed

+370
-10
lines changed

7 files changed

+370
-10
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
"use client";
2+
3+
import { FC } from "react";
4+
import { useTranslation } from "react-i18next";
5+
import Image from "next/image";
6+
import Link from "next/link";
7+
import Frame from "@rubin-epo/epo-react-lib/Frame";
8+
import CarouselLayout from "@rubin-epo/epo-react-lib/CarouselLayout";
9+
import Container from "@rubin-epo/epo-react-lib/Container";
10+
import Stack from "@rubin-epo/epo-react-lib/Stack";
11+
import styles from "./styles.module.scss";
12+
import { addLocaleUriSegment } from "@/lib/i18n";
13+
14+
interface SlideshowCarouselProps {
15+
slideshows: Array<{
16+
id: string | null;
17+
title: string | null;
18+
uri: string | null;
19+
richTextDescription: string | null;
20+
image?: {
21+
url: string;
22+
width: number;
23+
height: number;
24+
altText: string | null;
25+
};
26+
}>;
27+
slideshowsUri?: string;
28+
}
29+
30+
const SlideshowCarousel: FC<SlideshowCarouselProps> = ({
31+
slideshows,
32+
slideshowsUri,
33+
}) => {
34+
const {
35+
t,
36+
i18n: { language },
37+
} = useTranslation();
38+
39+
return (
40+
<Container
41+
width="wide"
42+
paddingSize="medium"
43+
bgColor="color-background-page-invert"
44+
>
45+
<Stack className={styles.slideshowCarousel}>
46+
<header className={styles.carouselHeader}>
47+
<h2>{t("gallery.curated-slideshows")}</h2>
48+
{slideshowsUri && (
49+
<Link
50+
className={styles.slideshowsLink}
51+
href={addLocaleUriSegment(language, slideshowsUri)}
52+
>
53+
{t("gallery.see-all")}
54+
</Link>
55+
)}
56+
</header>
57+
<CarouselLayout className={styles.carouselWrapper}>
58+
{slideshows.map(
59+
({ title, uri, id, richTextDescription, image }, i) => {
60+
return uri ? (
61+
<div className={styles.carouselSlide} key={id}>
62+
<Frame className={styles.previewImage} aspectRatio="5 / 8">
63+
{image && (
64+
<Image
65+
src={image.url}
66+
width={image.width}
67+
height={image.height}
68+
alt={image.altText || ""}
69+
/>
70+
)}
71+
</Frame>
72+
<Stack space="var(--size-spacing-2xs)">
73+
<div>
74+
<span>{t("gallery.slideshow")}</span>
75+
<h3>
76+
<Link
77+
href={uri}
78+
className={styles.slideLink}
79+
prefetch={i === 0}
80+
>
81+
{title}
82+
</Link>
83+
</h3>
84+
</div>
85+
{richTextDescription && (
86+
<div
87+
dangerouslySetInnerHTML={{
88+
__html: richTextDescription,
89+
}}
90+
/>
91+
)}
92+
</Stack>
93+
</div>
94+
) : null;
95+
}
96+
)}
97+
</CarouselLayout>
98+
</Stack>
99+
</Container>
100+
);
101+
};
102+
103+
SlideshowCarousel.displayName = "Organism.SlideshowCarousel";
104+
105+
export default SlideshowCarousel;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
@use "abstracts/mixins/base";
2+
@use "abstracts/functions";
3+
4+
.slideshowCarousel {
5+
color: var(--color-font-invert);
6+
}
7+
8+
.slideshowsLink {
9+
color: var(--color-font-accent);
10+
text-decoration: none;
11+
}
12+
13+
.carouselHeader {
14+
display: flex;
15+
flex-wrap: wrap;
16+
gap: var(--size-spacing-2xs);
17+
padding-block-end: var(--size-spacing-xs);
18+
border-block-end: 10px solid #009fa1;
19+
}
20+
21+
.carouselWrapper {
22+
--opacity-slide: 0;
23+
24+
min-height: 20rem;
25+
transition: 0.2s opacity;
26+
27+
& *[role="group"]:not(:first-of-type) {
28+
opacity: var(--opacity-slide);
29+
}
30+
31+
&:has(> :global(.flickity-enabled)) {
32+
--opacity-slide: 1;
33+
}
34+
}
35+
36+
.carouselSlide {
37+
position: relative;
38+
display: grid;
39+
grid-template-columns: 1fr;
40+
grid-auto-rows: min-content;
41+
gap: var(--size-spacing-2xs);
42+
43+
@include base.respond(functions.break(mobile), min) {
44+
grid-template-rows: 1fr;
45+
grid-template-columns: 1fr 1fr;
46+
}
47+
}
48+
49+
.previewImage {
50+
width: 100%;
51+
background-color: var(--color-background-tile-light);
52+
}
53+
54+
.slideLink {
55+
text-decoration: none;
56+
57+
&::after {
58+
position: absolute;
59+
inset: 0;
60+
content: "";
61+
}
62+
}

components/templates/GalleryLandingPage/index.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { generateMetadata as baseGeneradataMetadata } from "../../../app/[locale
33
import { getMainGallery } from "@/lib/api/galleries";
44
import { notFound } from "next/navigation";
55
import GalleryPage from "../GalleryPage";
6+
import SlideshowCarousel from "@/components/organisms/SlideshowCarousel";
67

78
export const generateMetadata = async ({ params: { locale } }: LocaleProps) => {
89
const data = await getMainGallery(locale);
@@ -29,9 +30,16 @@ const GalleryLandingPage: FC<
2930
notFound();
3031
}
3132

32-
const { gallery } = data;
33+
const { gallery, slideshows, slideshowsUri } = data;
3334

34-
return <GalleryPage {...{ locale, gallery, searchParams }} />;
35+
return (
36+
<>
37+
{slideshows.length > 0 && (
38+
<SlideshowCarousel {...{ slideshows, slideshowsUri }} />
39+
)}
40+
<GalleryPage {...{ locale, gallery, searchParams }} />
41+
</>
42+
);
3543
};
3644

3745
GalleryLandingPage.displayName = "Template.GalleryLandingPage";

gql/gql.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const documents = {
2222
types.GalleryImageQueryDocument,
2323
'\n query RandomAssetQuery($site: [String], $scheme: [String]) {\n galleriesEntries(site: $site) {\n ... on galleries_gallery_Entry {\n title\n slug\n assetAlbum(\n random: 1\n whereNotIn: { key: "scheme", values: $scheme }\n ) {\n additional {\n AltTextEN\n AltTextES\n CaptionEN\n CaptionES\n Credit\n TitleEN\n TitleES\n }\n default {\n ContentType\n DateCreated\n DateModified\n DateUploaded\n Size\n }\n approvalStatus\n height\n id\n name\n owner\n ownerName\n scheme\n size\n smartTags\n tag\n time\n url {\n directUrlOriginal\n directUrlPreview\n directUrlPreviewPlay\n download\n metadata\n preview\n PNG\n HighJPG\n }\n width\n }\n }\n }\n }\n ':
2424
types.RandomAssetQueryDocument,
25-
'\n query MainGalleryQuery($site: [String]) {\n pagesEntries(site: $site, uri: "gallery") {\n ... on pages_galleryLandingPage_Entry {\n __typename\n isVisible\n galleryEntry {\n ... on galleries_gallery_Entry {\n __typename\n slug\n }\n }\n slideshowEntry {\n ... on slideshows_slideshow_Entry {\n id\n uri\n title\n richTextDescription\n }\n }\n }\n }\n }\n ':
25+
'\n query MainGalleryQuery($site: [String]) {\n pagesEntries(site: $site, uri: "gallery") {\n ... on pages_galleryLandingPage_Entry {\n __typename\n isVisible\n galleryEntry {\n ... on galleries_gallery_Entry {\n __typename\n slug\n }\n }\n slideshowEntry {\n ... on slideshows_slideshow_Entry {\n __typename\n id\n uri\n title\n richTextDescription\n images: representativeAssetVariant {\n ... on assetVariants_Asset {\n __typename\n altText\n width\n height\n url\n }\n }\n }\n }\n }\n }\n slideshowsMain: pagesEntries(site: $site, slug: "slideshows") {\n ... on pages_pages_Entry {\n __typename\n uri\n }\n }\n }\n ':
2626
types.MainGalleryQueryDocument,
2727
"\n query AllGalleriesQuery($site: [String]) {\n galleriesEntries(site: $site) {\n ... on galleries_gallery_Entry {\n slug\n }\n }\n }\n ":
2828
types.AllGalleriesQueryDocument,
@@ -78,8 +78,8 @@ export function graphql(
7878
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
7979
*/
8080
export function graphql(
81-
source: '\n query MainGalleryQuery($site: [String]) {\n pagesEntries(site: $site, uri: "gallery") {\n ... on pages_galleryLandingPage_Entry {\n __typename\n isVisible\n galleryEntry {\n ... on galleries_gallery_Entry {\n __typename\n slug\n }\n }\n slideshowEntry {\n ... on slideshows_slideshow_Entry {\n id\n uri\n title\n richTextDescription\n }\n }\n }\n }\n }\n '
82-
): (typeof documents)['\n query MainGalleryQuery($site: [String]) {\n pagesEntries(site: $site, uri: "gallery") {\n ... on pages_galleryLandingPage_Entry {\n __typename\n isVisible\n galleryEntry {\n ... on galleries_gallery_Entry {\n __typename\n slug\n }\n }\n slideshowEntry {\n ... on slideshows_slideshow_Entry {\n id\n uri\n title\n richTextDescription\n }\n }\n }\n }\n }\n '];
81+
source: '\n query MainGalleryQuery($site: [String]) {\n pagesEntries(site: $site, uri: "gallery") {\n ... on pages_galleryLandingPage_Entry {\n __typename\n isVisible\n galleryEntry {\n ... on galleries_gallery_Entry {\n __typename\n slug\n }\n }\n slideshowEntry {\n ... on slideshows_slideshow_Entry {\n __typename\n id\n uri\n title\n richTextDescription\n images: representativeAssetVariant {\n ... on assetVariants_Asset {\n __typename\n altText\n width\n height\n url\n }\n }\n }\n }\n }\n }\n slideshowsMain: pagesEntries(site: $site, slug: "slideshows") {\n ... on pages_pages_Entry {\n __typename\n uri\n }\n }\n }\n '
82+
): (typeof documents)['\n query MainGalleryQuery($site: [String]) {\n pagesEntries(site: $site, uri: "gallery") {\n ... on pages_galleryLandingPage_Entry {\n __typename\n isVisible\n galleryEntry {\n ... on galleries_gallery_Entry {\n __typename\n slug\n }\n }\n slideshowEntry {\n ... on slideshows_slideshow_Entry {\n __typename\n id\n uri\n title\n richTextDescription\n images: representativeAssetVariant {\n ... on assetVariants_Asset {\n __typename\n altText\n width\n height\n url\n }\n }\n }\n }\n }\n }\n slideshowsMain: pagesEntries(site: $site, slug: "slideshows") {\n ... on pages_pages_Entry {\n __typename\n uri\n }\n }\n }\n '];
8383
/**
8484
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
8585
*/

0 commit comments

Comments
 (0)