-
Notifications
You must be signed in to change notification settings - Fork 2
/
[slug].js
217 lines (191 loc) · 6.54 KB
/
[slug].js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import { useState } from 'react';
import Link from 'next/link';
import { splitArrayAlternating } from '../utils/arrays';
import { getAppConfig } from '../io/config';
import { getImages, getProjects, getPortfolios } from '../io/content';
import Image from '../components/Image';
import ColumnLayout from '../components/ColumnLayout';
import Lightbox from '../components/Lightbox';
const THUMBNAIL_COLUMNS = 2;
const THUMBNAIL_PADDING = 1;
const Thumbnail = ({ image, appConfig, imageProps }) => (
<div className="Thumbnail">
<Image
image={image}
appConfig={appConfig}
size="50vw"
className="w-100 db"
{...imageProps}
/>
</div>
);
const ThumbnailButton = ({ onClick, ...rest }) => (
<button
title="View Image"
type="button"
className="button-reset bn pa0 db w-100 pointer"
onClick={onClick}
>
<Thumbnail {...rest} />
</button>
);
const ThumbnailColumn = ({ slug, items, images, projects, appConfig, setLightboxImage }) => (
<div>
{items.map((item, index) => {
// lazy-load thumbnails below the fold
const thumbnailImageProps = index > 0 ? { loading: 'lazy' } : {};
switch(item.type) {
case 'image':
if (!item.filename) {
console.error(`Invalid data: item ${index} in column has type 'image' but does not have a filename property`);
return null;
}
const image = images.find(data => data.filename === item.filename);
if (!image) {
console.error(`Missing image data for ${item.filename}`);
return null;
}
return (
<div key={image.filename} className="mb2">
<ThumbnailButton
image={image}
appConfig={appConfig}
onClick={() => setLightboxImage(image)}
imageProps={thumbnailImageProps}
/>
</div>
);
case 'project':
if (!item.slug) {
console.error(`Invalid data: item ${index} in column has type 'project' but does not have a slug property`);
return null;
}
const project = projects.find(data => data.slug === item.slug);
if (!project) {
console.error(`Missing project data for ${item.slug}`);
return null;
}
if (!project.thumbnailImage) {
console.error(`Invalid data: project ${project.slug} does not have a thumbnailImage property`)
return null;
}
if (!project.thumbnailImage.filename) {
console.error(`Invalid data: project ${project.slug} thumbnailImage does not have a filename property`)
return null;
}
const thumbnailImage = images.find(data => data.filename === project.thumbnailImage.filename);
if (!thumbnailImage) {
console.error(`Missing image data for ${project.thumbnailImage.filename}`);
return null;
}
return (
<Link
key={project.slug}
href="/projects/[portfolioSlug]/[slug]"
as={`/projects/${slug}/${project.slug}`}
>
<a
className="mb4 pa0 db w-100 no-underline"
title="View Project"
>
<Thumbnail
image={thumbnailImage}
appConfig={appConfig}
imageProps={thumbnailImageProps}
/>
<div className="flex flex-column pa3 pb4-l bb bw1 b--moon-gray lh-title">
<h2 className="fw2 mt0 mb3 f4 f3-ns">{project.title}</h2>
</div>
</a>
</Link>
);
default:
console.error(`Invalid images data: item ${index} in column did not have a recognised type`);
return null;
}
})}
</div>
);
const Portfolio = ({ appConfig, data: { slug, items, images, projects } }) => {
const [lightboxImage, setLightboxImage] = useState(null);
const isLightboxOpen = !!lightboxImage;
const itemColumns = splitArrayAlternating(items, THUMBNAIL_COLUMNS);
return (
<div className="flex flex-wrap">
<nav className="ph4 pt2 w-100 w-20-l">
<div className="sticky-l top-2">
<ul className="flex flex-row flex-column-l justify-center justify-start-l list pa0 ma0 mr2 mr0-ns f4 fw2 ttl">
<li className={`Navitem mb3 mr4 mr0-l${slug === 'illustration' ? ' Navitem--current' : ''}`}>
<Link href="/[slug]" as="/illustration">
<a className="no-underline flex flex-row items-center">
<span className="Navitem__bullet f6 mr2">•</span>
illustration
</a>
</Link>
</li>
<li className={`Navitem mb3${slug === 'design' ? ' Navitem--current' : ''}`}>
<Link href="/[slug]" as="/design">
<a className="no-underline flex flew-row items-center">
<span className="Navitem__bullet f6 mr2">•</span>
design
</a>
</Link>
</li>
</ul>
</div>
</nav>
<main id="start-of-content" className="w-100 w-80-l">
<ColumnLayout
columns={THUMBNAIL_COLUMNS}
verticalPadding={THUMBNAIL_PADDING}
horizontalPadding={THUMBNAIL_PADDING}
>
{itemColumns.map((items, i) => (
<ThumbnailColumn
key={i}
slug={slug}
items={items}
images={images}
projects={projects}
appConfig={appConfig}
setLightboxImage={setLightboxImage}
/>
))}
</ColumnLayout>
<Lightbox
isOpen={isLightboxOpen}
image={lightboxImage}
appConfig={appConfig}
onClose={() => setLightboxImage(null)}
/>
</main>
</div>
);
};
export const getStaticProps = async ({ params }) => {
const appConfig = await getAppConfig();
const portfolios = await getPortfolios();
const projects = await getProjects();
const images = await getImages();
const { slug, items } = portfolios.find(data => data.slug === params.slug);
return {
props: {
appConfig,
data: {
slug,
items,
projects,
images,
},
},
};
};
export const getStaticPaths = async () => {
const portfolios = await getPortfolios();
const paths = portfolios.map(({ slug }) => ({ params: { slug } }));
return {
paths,
fallback: false,
};
};
export default Portfolio;