foundry-component-library 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/CaseStudyTeaser/styles.module.scss +1 -1
- package/lib/components/ContactTeaser/index.tsx +52 -15
- package/lib/components/ContactTeaser/styles.module.scss +23 -0
- package/lib/components/Container/styles.module.scss +1 -1
- package/lib/components/Footer/index.tsx +9 -3
- package/lib/components/Footer/styles.module.scss +6 -3
- package/lib/components/Header/index.tsx +8 -3
- package/lib/components/Header/styles.module.scss +5 -4
- package/lib/components/Hero/Video.tsx +43 -0
- package/lib/components/Hero/index.tsx +28 -19
- package/lib/components/Hero/styles.module.scss +12 -0
- package/lib/components/HubsAccordion/Hub.tsx +98 -82
- package/lib/components/HubsAccordion/styles.module.scss +4 -10
- package/lib/components/QuoteSection/index.tsx +27 -7
- package/lib/components/QuoteSection/styles.module.scss +13 -2
- package/lib/components/ServiceHubsTeaser/Tile.tsx +3 -21
- package/lib/components/ServiceHubsTeaser/index.tsx +38 -9
- package/lib/components/ServiceHubsTeaser/styles.module.scss +6 -0
- package/lib/components/TeamPhotos/Item.tsx +2 -2
- package/lib/components/TeamPhotos/styles.module.scss +2 -1
- package/lib/components/TextSection/index.tsx +5 -9
- package/lib/components/Tiles/Tile.tsx +2 -19
- package/lib/components/VideoTeaser/index.tsx +31 -83
- package/lib/components/VideoTeaser/styles.module.scss +3 -11
- package/lib/components/case/Content/styles.module.scss +7 -7
- package/lib/components/cases/Pagination/index.tsx +3 -5
- package/lib/queries/getAboutPage.ts +6 -0
- package/lib/queries/getCaseById.ts +3 -0
- package/lib/queries/getCaseBySlug.ts +3 -0
- package/lib/queries/getCasesPage.ts +3 -0
- package/lib/queries/getContactPage.ts +6 -0
- package/lib/queries/getHomePage.ts +6 -0
- package/lib/queries/getHubBySlug.ts +6 -0
- package/lib/queries/getHubsPage.ts +3 -0
- package/lib/queries/getNewsPage.ts +3 -0
- package/lib/queries/getPeoplePage.ts +6 -0
- package/lib/queries/getPerformanceHubPage.ts +6 -0
- package/lib/queries/getPostBySlug.ts +3 -0
- package/lib/types/index.ts +3 -0
- package/package.json +1 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from "react";
|
|
1
3
|
import styles from "./styles.module.scss";
|
|
2
4
|
import Container from "../Container";
|
|
3
5
|
import { translate } from "../../utils";
|
|
4
6
|
import { NextLink } from "../../types";
|
|
5
7
|
import WavyText from "../TextAnimations/WavyText";
|
|
6
8
|
import FadeInText from "../TextAnimations/FadeInText";
|
|
9
|
+
import useClickOutside from "../../hooks/useClickOutside";
|
|
10
|
+
import Script from "next/script";
|
|
7
11
|
|
|
8
12
|
const ContactTeaser = ({
|
|
9
13
|
heading,
|
|
@@ -22,24 +26,57 @@ const ContactTeaser = ({
|
|
|
22
26
|
alternate?: boolean;
|
|
23
27
|
Link: NextLink;
|
|
24
28
|
}) => {
|
|
29
|
+
const [isTypeformOpen, setIsTypeformOpen] = useState(false);
|
|
30
|
+
const ref = useClickOutside<HTMLDivElement>(() => {
|
|
31
|
+
setIsTypeformOpen(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
25
34
|
return (
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
<>
|
|
36
|
+
<div className={`${styles.contactTeaser} ${styles[theme]}`}>
|
|
37
|
+
<Container>
|
|
38
|
+
<div className={styles.wrapper}>
|
|
39
|
+
{heading && (
|
|
40
|
+
<WavyText
|
|
41
|
+
className={`${styles.heading} ${!text ? styles.margin : ""}`}
|
|
42
|
+
text={heading}
|
|
43
|
+
alternate={alternate}
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
46
|
+
{text && <FadeInText className={styles.text} text={text} />}
|
|
47
|
+
{buttonHref !== "typeform" && (
|
|
48
|
+
<Link href={buttonHref} className={styles.button}>
|
|
49
|
+
{translate(buttonText)}
|
|
50
|
+
</Link>
|
|
51
|
+
)}
|
|
52
|
+
{buttonHref == "typeform" && (
|
|
53
|
+
<button
|
|
54
|
+
className={styles.button}
|
|
55
|
+
onClick={() => {
|
|
56
|
+
setIsTypeformOpen(true);
|
|
57
|
+
}}>
|
|
58
|
+
{translate(buttonText)}
|
|
59
|
+
</button>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
</Container>
|
|
63
|
+
</div>
|
|
64
|
+
{buttonHref === "typeform" && (
|
|
65
|
+
<div
|
|
66
|
+
className={styles.typeform}
|
|
67
|
+
style={{ display: isTypeformOpen ? "flex" : "none" }}>
|
|
68
|
+
<Script src="//embed.typeform.com/next/embed.js" />
|
|
69
|
+
<div ref={ref} className={styles.typeformWrapper}>
|
|
70
|
+
<div
|
|
71
|
+
data-tf-widget="qmv6Yk"
|
|
72
|
+
data-tf-iframe-props="title=Foundry Website Contact Form"
|
|
73
|
+
data-tf-medium="snippet"
|
|
74
|
+
style={{ width: "100%", height: "400px" }}
|
|
34
75
|
/>
|
|
35
|
-
|
|
36
|
-
{text && <FadeInText className={styles.text} text={text} />}
|
|
37
|
-
<Link href={buttonHref} className={styles.button}>
|
|
38
|
-
{translate(buttonText)}
|
|
39
|
-
</Link>
|
|
76
|
+
</div>
|
|
40
77
|
</div>
|
|
41
|
-
|
|
42
|
-
|
|
78
|
+
)}
|
|
79
|
+
</>
|
|
43
80
|
);
|
|
44
81
|
};
|
|
45
82
|
|
|
@@ -54,4 +54,27 @@
|
|
|
54
54
|
.button {
|
|
55
55
|
@extend .button--brown;
|
|
56
56
|
display: inline-block;
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.typeform {
|
|
61
|
+
position: fixed;
|
|
62
|
+
top: 0;
|
|
63
|
+
left: 0;
|
|
64
|
+
width: 100%;
|
|
65
|
+
height: 100%;
|
|
66
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
justify-content: center;
|
|
70
|
+
z-index: 9;
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.typeformWrapper {
|
|
75
|
+
background-color: $color-white;
|
|
76
|
+
width: 800px;
|
|
77
|
+
max-width: 100%;
|
|
78
|
+
border-radius: 8px;
|
|
79
|
+
overflow: hidden;
|
|
57
80
|
}
|
|
@@ -7,6 +7,9 @@ import { NextLink } from "../../types";
|
|
|
7
7
|
function Footer({
|
|
8
8
|
details,
|
|
9
9
|
Link,
|
|
10
|
+
facebook,
|
|
11
|
+
linkedin,
|
|
12
|
+
instagram,
|
|
10
13
|
}: {
|
|
11
14
|
details: {
|
|
12
15
|
berlinEmail: string;
|
|
@@ -14,6 +17,9 @@ function Footer({
|
|
|
14
17
|
newyorkEmail: string;
|
|
15
18
|
};
|
|
16
19
|
Link: NextLink;
|
|
20
|
+
facebook: string;
|
|
21
|
+
linkedin: string;
|
|
22
|
+
instagram: string;
|
|
17
23
|
}) {
|
|
18
24
|
const { berlinEmail, zurichEmail, newyorkEmail } = details;
|
|
19
25
|
const year = new Date().getFullYear();
|
|
@@ -81,13 +87,13 @@ function Footer({
|
|
|
81
87
|
<div className={styles.socialHeading}>Follow Us</div>
|
|
82
88
|
<ul className={styles.menuSocial}>
|
|
83
89
|
<li className={styles.menuItem}>
|
|
84
|
-
<Link href=
|
|
90
|
+
<Link href={instagram}>Instagram</Link>
|
|
85
91
|
</li>
|
|
86
92
|
<li className={styles.menuItem}>
|
|
87
|
-
<Link href=
|
|
93
|
+
<Link href={facebook}>Facebook</Link>
|
|
88
94
|
</li>
|
|
89
95
|
<li className={styles.menuItem}>
|
|
90
|
-
<Link href=
|
|
96
|
+
<Link href={linkedin}>LinkedIn</Link>
|
|
91
97
|
</li>
|
|
92
98
|
</ul>
|
|
93
99
|
</div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
max-width: 100%;
|
|
46
46
|
|
|
47
47
|
@media screen and (max-width: $screen-sm) {
|
|
48
|
-
margin-bottom:
|
|
48
|
+
margin-bottom: 40px;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
.menu {
|
|
119
119
|
font-size: 18px;
|
|
120
120
|
font-weight: 500;
|
|
121
|
+
font-family: $font-secondary;
|
|
121
122
|
|
|
122
123
|
@media #{$QUERY-sm} {
|
|
123
124
|
width: 100%;
|
|
@@ -147,7 +148,7 @@
|
|
|
147
148
|
@media #{$QUERY-sm} {
|
|
148
149
|
width: 100%;
|
|
149
150
|
display: flex;
|
|
150
|
-
justify-content:
|
|
151
|
+
justify-content: flex-start;
|
|
151
152
|
gap: 24px;
|
|
152
153
|
}
|
|
153
154
|
|
|
@@ -196,8 +197,10 @@
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
@media #{$QUERY-sm} {
|
|
199
|
-
margin-top:
|
|
200
|
+
margin-top: 20px;
|
|
200
201
|
width: 100%;
|
|
202
|
+
justify-content: flex-start;
|
|
203
|
+
gap: 40px;
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
a {
|
|
@@ -16,7 +16,13 @@ function Header({ Link }: { Link: NextLink }) {
|
|
|
16
16
|
<div className={styles.wrapper}>
|
|
17
17
|
<div className={styles.left}>
|
|
18
18
|
<Link href="/">
|
|
19
|
-
<video
|
|
19
|
+
<video
|
|
20
|
+
src="/logo.mp4"
|
|
21
|
+
autoPlay
|
|
22
|
+
muted
|
|
23
|
+
playsInline
|
|
24
|
+
{...{ "webkit-playsinline": "true" }}
|
|
25
|
+
/>
|
|
20
26
|
Foundry
|
|
21
27
|
</Link>
|
|
22
28
|
</div>
|
|
@@ -28,8 +34,7 @@ function Header({ Link }: { Link: NextLink }) {
|
|
|
28
34
|
onClick={() => {
|
|
29
35
|
setMenuOpen(!isMenuOpen);
|
|
30
36
|
}}
|
|
31
|
-
aria-label="Toggle menu"
|
|
32
|
-
>
|
|
37
|
+
aria-label="Toggle menu">
|
|
33
38
|
Menu
|
|
34
39
|
<div className={styles.hamburger}>
|
|
35
40
|
<span className={styles.line}></span>
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
.left {
|
|
21
21
|
font-size: 0;
|
|
22
|
-
margin-left: -
|
|
22
|
+
margin-left: -65px;
|
|
23
23
|
|
|
24
24
|
@media #{$QUERY-sm} {
|
|
25
25
|
margin-left: -68px;
|
|
@@ -98,9 +98,6 @@
|
|
|
98
98
|
display: flex;
|
|
99
99
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
100
100
|
|
|
101
|
-
@media #{$QUERY-sm} {
|
|
102
|
-
}
|
|
103
|
-
|
|
104
101
|
&.isMenuOpen {
|
|
105
102
|
// display: flex;
|
|
106
103
|
transform: translateX(0);
|
|
@@ -132,6 +129,10 @@
|
|
|
132
129
|
max-height: 600px;
|
|
133
130
|
margin: auto 0;
|
|
134
131
|
padding: 0 40px;
|
|
132
|
+
|
|
133
|
+
@media #{$QUERY-sm} {
|
|
134
|
+
padding-bottom: 60px;
|
|
135
|
+
}
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
.menuList {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
|
+
import { useOnScreen } from "../../hooks/useOnScreen";
|
|
4
|
+
import ReactPlayer from "react-player/lazy";
|
|
5
|
+
import styles from "./styles.module.scss";
|
|
6
|
+
|
|
7
|
+
const Video = ({ url }: { url: string }) => {
|
|
8
|
+
const sectionRef = useRef(null);
|
|
9
|
+
const onScreen = useOnScreen(sectionRef, "1000px");
|
|
10
|
+
// const [playing, setPlaying] = useState(false);
|
|
11
|
+
const [videoLoaded, setVideoLoaded] = useState(false);
|
|
12
|
+
const [hasWindow, setHasWindow] = useState(false);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (typeof window !== "undefined") {
|
|
16
|
+
setHasWindow(true);
|
|
17
|
+
}
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!videoLoaded && onScreen) {
|
|
22
|
+
setVideoLoaded(true);
|
|
23
|
+
}
|
|
24
|
+
}, [videoLoaded, onScreen]);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className={styles.videoWrapper} ref={sectionRef}>
|
|
28
|
+
{hasWindow && (
|
|
29
|
+
<ReactPlayer
|
|
30
|
+
playing
|
|
31
|
+
url={url}
|
|
32
|
+
width="100%"
|
|
33
|
+
height="100%"
|
|
34
|
+
loop
|
|
35
|
+
muted
|
|
36
|
+
playsinline
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default Video;
|
|
@@ -4,9 +4,11 @@ import styles from "./styles.module.scss";
|
|
|
4
4
|
import Container from "../Container";
|
|
5
5
|
import { NextImage, NextLink } from "../../types";
|
|
6
6
|
import { useOnScreen } from "../../hooks/useOnScreen";
|
|
7
|
+
import Video from "./Video";
|
|
7
8
|
|
|
8
9
|
const Hero = ({
|
|
9
10
|
image,
|
|
11
|
+
video,
|
|
10
12
|
text,
|
|
11
13
|
isFullWidth,
|
|
12
14
|
isFirst,
|
|
@@ -16,6 +18,7 @@ const Hero = ({
|
|
|
16
18
|
Image,
|
|
17
19
|
}: {
|
|
18
20
|
image: string;
|
|
21
|
+
video?: string;
|
|
19
22
|
text: string;
|
|
20
23
|
isFullWidth?: boolean;
|
|
21
24
|
isFirst?: boolean;
|
|
@@ -30,7 +33,9 @@ const Hero = ({
|
|
|
30
33
|
const sectionRef = useRef(null);
|
|
31
34
|
const onScreen = useOnScreen(sectionRef, "-50%");
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
console.log("pop", video);
|
|
37
|
+
|
|
38
|
+
if (!image && !video) return;
|
|
34
39
|
|
|
35
40
|
if (isFullWidth) {
|
|
36
41
|
return (
|
|
@@ -38,15 +43,17 @@ const Hero = ({
|
|
|
38
43
|
ref={sectionRef}
|
|
39
44
|
className={`${styles.hero} ${styles.isFullWidth} ${
|
|
40
45
|
noMarginBottom ? styles.noMarginBottom : ""
|
|
41
|
-
}`}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
}`}>
|
|
47
|
+
{image && !video && (
|
|
48
|
+
<Image
|
|
49
|
+
className={`${styles.background} ${onScreen ? styles.active : ""}`}
|
|
50
|
+
src={image}
|
|
51
|
+
width="1280"
|
|
52
|
+
height="600"
|
|
53
|
+
alt={text}
|
|
54
|
+
/>
|
|
55
|
+
)}
|
|
56
|
+
{video && <Video url={video} />}
|
|
50
57
|
<div className={styles.texts}>
|
|
51
58
|
{text && <div className={styles.heading}>{text}</div>}
|
|
52
59
|
{btn && (
|
|
@@ -64,15 +71,17 @@ const Hero = ({
|
|
|
64
71
|
<div
|
|
65
72
|
className={`${styles.hero} ${isFirst ? styles.first : ""} ${
|
|
66
73
|
noMarginBottom ? styles.noMarginBottom : ""
|
|
67
|
-
}`}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
}`}>
|
|
75
|
+
{image && !video && (
|
|
76
|
+
<Image
|
|
77
|
+
className={`${styles.background} ${onScreen ? styles.active : ""}`}
|
|
78
|
+
src={image}
|
|
79
|
+
width="1280"
|
|
80
|
+
height="600"
|
|
81
|
+
alt={text}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
{video && <Video url={video} />}
|
|
76
85
|
<div className={styles.texts}>
|
|
77
86
|
{text && <div className={styles.heading}>{text}</div>}
|
|
78
87
|
{btn && (
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useRef,
|
|
4
|
+
useState,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
Dispatch,
|
|
7
|
+
SetStateAction,
|
|
8
|
+
} from "react";
|
|
9
|
+
import { motion, AnimatePresence } from "framer-motion";
|
|
3
10
|
import { NextImage, NextLink, type Hub } from "../../types";
|
|
4
11
|
import styles from "./styles.module.scss";
|
|
5
12
|
import Arrow from "../../assets/svg/arrow.svg";
|
|
@@ -21,6 +28,8 @@ const Hub = ({
|
|
|
21
28
|
Image: NextImage;
|
|
22
29
|
}) => {
|
|
23
30
|
const casesRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
32
|
+
const [height, setHeight] = useState<number | "auto">(0);
|
|
24
33
|
|
|
25
34
|
const {
|
|
26
35
|
handleMouseDown,
|
|
@@ -33,6 +42,12 @@ const Hub = ({
|
|
|
33
42
|
const isActive = active === hub.slug;
|
|
34
43
|
const customFields = hub.customFieldsHub;
|
|
35
44
|
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
if (contentRef.current) {
|
|
47
|
+
setHeight(contentRef.current.scrollHeight);
|
|
48
|
+
}
|
|
49
|
+
}, [isActive, customFields]);
|
|
50
|
+
|
|
36
51
|
return (
|
|
37
52
|
<div className={`${styles.hub} ${isActive ? styles.active : ""}`}>
|
|
38
53
|
<div className={styles.top}>
|
|
@@ -40,94 +55,95 @@ const Hub = ({
|
|
|
40
55
|
<div className={styles.text}>{customFields.subheading}</div>
|
|
41
56
|
<button
|
|
42
57
|
className={styles.icon}
|
|
43
|
-
onClick={() =>
|
|
44
|
-
|
|
45
|
-
setActive("");
|
|
46
|
-
} else {
|
|
47
|
-
setActive(hub.slug);
|
|
48
|
-
}
|
|
49
|
-
}}
|
|
50
|
-
>
|
|
51
|
-
{isActive && <Minus />}
|
|
52
|
-
{!isActive && <Plus />}
|
|
58
|
+
onClick={() => setActive(isActive ? "" : hub.slug)}>
|
|
59
|
+
{isActive ? <Minus /> : <Plus />}
|
|
53
60
|
</button>
|
|
54
61
|
</div>
|
|
55
|
-
<div className={styles.rows}>
|
|
56
|
-
<div className={styles.row}>
|
|
57
|
-
<div className={styles.rowWrapper}>
|
|
58
|
-
<div className={styles.subheading}>SERVICE</div>
|
|
59
|
-
<div className={styles.right}>
|
|
60
|
-
{customFields.tags && (
|
|
61
|
-
<div className={styles.tags}>
|
|
62
|
-
{customFields.tags.map((tag) => `${tag.tag} // `)}
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
<div className={styles.row}>
|
|
69
|
-
<div className={styles.rowWrapper}>
|
|
70
|
-
<div className={styles.subheading}>APPROACH</div>
|
|
71
|
-
<div className={styles.right}>
|
|
72
|
-
<div className={styles.paragraph}>{customFields.approach}</div>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
<div className={styles.row}>
|
|
77
|
-
<div className={styles.rowWrapper}>
|
|
78
|
-
<div className={styles.subheading}>RELATED WORK</div>
|
|
79
|
-
<div className={styles.right}>
|
|
80
|
-
{customFields.relatedWork && (
|
|
81
|
-
<div
|
|
82
|
-
className={styles.cases}
|
|
83
|
-
ref={casesRef}
|
|
84
|
-
onMouseDown={handleMouseDown}
|
|
85
|
-
onMouseMove={handleMouseMove}
|
|
86
|
-
onMouseUp={handleMouseUp}
|
|
87
|
-
onMouseLeave={handleMouseUp}
|
|
88
|
-
style={{
|
|
89
|
-
...(dragStyle as React.CSSProperties),
|
|
90
|
-
}}
|
|
91
|
-
>
|
|
92
|
-
{customFields.relatedWork.map((item) => {
|
|
93
|
-
const { thumbnailImage, mainImage } = item.case;
|
|
94
62
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
63
|
+
<AnimatePresence initial={false}>
|
|
64
|
+
{(isActive || height !== 0) && (
|
|
65
|
+
<motion.div
|
|
66
|
+
key="content"
|
|
67
|
+
initial={{ height: 0, opacity: 0 }}
|
|
68
|
+
animate={{
|
|
69
|
+
height: isActive ? height : 0,
|
|
70
|
+
opacity: isActive ? 1 : 0,
|
|
71
|
+
}}
|
|
72
|
+
exit={{ height: 0, opacity: 0 }}
|
|
73
|
+
transition={{ duration: 0.4, ease: [0.4, 0, 0.2, 1] }}
|
|
74
|
+
style={{ overflow: "hidden" }}>
|
|
75
|
+
<div ref={contentRef}>
|
|
76
|
+
<div className={styles.rows}>
|
|
77
|
+
<div className={styles.row}>
|
|
78
|
+
<div className={styles.rowWrapper}>
|
|
79
|
+
<div className={styles.subheading}>SERVICE</div>
|
|
80
|
+
<div className={styles.right}>
|
|
81
|
+
{customFields.tags && (
|
|
82
|
+
<div className={styles.tags}>
|
|
83
|
+
{customFields.tags.map((tag) => `${tag.tag} // `)}
|
|
113
84
|
</div>
|
|
114
|
-
|
|
115
|
-
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className={styles.row}>
|
|
91
|
+
<div className={styles.rowWrapper}>
|
|
92
|
+
<div className={styles.subheading}>RELATED WORK</div>
|
|
93
|
+
<div className={styles.right}>
|
|
94
|
+
{customFields.relatedWork && (
|
|
95
|
+
<div
|
|
96
|
+
className={styles.cases}
|
|
97
|
+
ref={casesRef}
|
|
98
|
+
onMouseDown={handleMouseDown}
|
|
99
|
+
onMouseMove={handleMouseMove}
|
|
100
|
+
onMouseUp={handleMouseUp}
|
|
101
|
+
onMouseLeave={handleMouseUp}
|
|
102
|
+
style={{
|
|
103
|
+
...(dragStyle as React.CSSProperties),
|
|
104
|
+
}}>
|
|
105
|
+
{customFields.relatedWork.map((item) => {
|
|
106
|
+
const { thumbnailImage, mainImage } = item.case;
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Link
|
|
110
|
+
href={item.uri}
|
|
111
|
+
key={item.id}
|
|
112
|
+
className={styles.case}
|
|
113
|
+
draggable={false}
|
|
114
|
+
onClick={preventedClick}>
|
|
115
|
+
<div className={styles.caseImage}>
|
|
116
|
+
<Image
|
|
117
|
+
src={
|
|
118
|
+
thumbnailImage?.sourceUrl ||
|
|
119
|
+
mainImage?.sourceUrl ||
|
|
120
|
+
""
|
|
121
|
+
}
|
|
122
|
+
alt={item.title}
|
|
123
|
+
fill
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
<div className={styles.caseTitle}>
|
|
127
|
+
{item.title} <Arrow />
|
|
128
|
+
</div>
|
|
129
|
+
</Link>
|
|
130
|
+
);
|
|
131
|
+
})}
|
|
116
132
|
</div>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<div className={styles.more}>
|
|
137
|
+
<Link href={hub.uri}>
|
|
138
|
+
Learn More <Arrow />
|
|
139
|
+
</Link>
|
|
140
|
+
</div>
|
|
120
141
|
</div>
|
|
121
|
-
|
|
142
|
+
</div>
|
|
122
143
|
</div>
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Learn More <Arrow />
|
|
127
|
-
</Link>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
144
|
+
</motion.div>
|
|
145
|
+
)}
|
|
146
|
+
</AnimatePresence>
|
|
131
147
|
</div>
|
|
132
148
|
);
|
|
133
149
|
};
|
|
@@ -12,10 +12,8 @@
|
|
|
12
12
|
color: $color-brown;
|
|
13
13
|
position: relative;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
display: block;
|
|
18
|
-
}
|
|
15
|
+
@media #{$QUERY-sm} {
|
|
16
|
+
padding-bottom: 32px;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
&:before {
|
|
@@ -48,7 +46,7 @@
|
|
|
48
46
|
|
|
49
47
|
@media #{$QUERY-sm} {
|
|
50
48
|
flex-wrap: wrap;
|
|
51
|
-
padding: 16px
|
|
49
|
+
padding: 16px 40px;
|
|
52
50
|
}
|
|
53
51
|
}
|
|
54
52
|
|
|
@@ -105,10 +103,6 @@
|
|
|
105
103
|
}
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
.rows {
|
|
109
|
-
display: none;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
106
|
.row {
|
|
113
107
|
padding-bottom: 64px;
|
|
114
108
|
position: relative;
|
|
@@ -131,7 +125,7 @@
|
|
|
131
125
|
|
|
132
126
|
@media #{$QUERY-sm} {
|
|
133
127
|
padding-bottom: 0;
|
|
134
|
-
padding-left:
|
|
128
|
+
padding-left: 40px;
|
|
135
129
|
padding-right: 20px;
|
|
136
130
|
}
|
|
137
131
|
}
|