-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add shared components CardsSlider, CollapsibleContent, LottieCarousel…
…, Motions, Text
- Loading branch information
Showing
9 changed files
with
969 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
@import "~@/scss/solutions/_variables.scss"; | ||
|
||
.Title { | ||
font-size: 40px; | ||
font-weight: 700; | ||
line-height: 1.04; | ||
letter-spacing: -0.01em; | ||
text-align: center; | ||
color: var(--white); | ||
text-transform: capitalize; | ||
margin: 0; | ||
padding: 64px 24px 0; | ||
|
||
strong { | ||
background: var(--gradient-2); | ||
-webkit-background-clip: text; | ||
background-clip: text; | ||
-webkit-text-fill-color: transparent; | ||
} | ||
|
||
@include breakpoint(md) { | ||
font-size: 56px; | ||
padding-top: 80px; | ||
} | ||
|
||
@include breakpoint(lg) { | ||
font-size: 64px; | ||
padding-top: 128px; | ||
} | ||
} | ||
|
||
.Carousel { | ||
position: relative; | ||
width: 100%; | ||
} | ||
|
||
.CarouselContainer { | ||
display: flex; | ||
width: 100%; | ||
padding: { | ||
top: 64px; | ||
bottom: 64px; | ||
} | ||
|
||
overflow-x: scroll; | ||
overscroll-behavior-x: auto; | ||
scroll-behavior: smooth; | ||
scrollbar-width: none; /*FireFox*/ | ||
-ms-overflow-style: -ms-autohiding-scrollbar; /*IE10+*/ | ||
|
||
/*Chrome, Safari, Edge*/ | ||
&::-webkit-scrollbar { | ||
display: none; | ||
} | ||
|
||
@include breakpoint(md) { | ||
padding: { | ||
top: 80px; | ||
bottom: 40px; | ||
} | ||
} | ||
|
||
@include breakpoint(lg) { | ||
padding: { | ||
top: 96px; | ||
} | ||
} | ||
} | ||
|
||
.Cards { | ||
display: flex; | ||
justify-content: start; | ||
gap: 16px; | ||
padding-left: 16px; | ||
max-width: 80rem; | ||
margin: 0 auto; | ||
} | ||
|
||
.CardWrapper { | ||
border-radius: 1.5rem; | ||
box-sizing: content-box; | ||
|
||
&:last-child { | ||
padding-right: 3%; | ||
|
||
@include breakpoint(md) { | ||
padding-right: 10%; | ||
} | ||
|
||
@include breakpoint(lg) { | ||
padding-right: 33%; | ||
} | ||
} | ||
|
||
@include breakpoint(md) { | ||
> * { | ||
width: 22rem; | ||
height: 100%; | ||
} | ||
} | ||
} | ||
|
||
.Arrows { | ||
display: flex; | ||
justify-content: center; | ||
gap: 16px; | ||
padding-bottom: 64px; | ||
|
||
button { | ||
position: relative; | ||
z-index: 40; | ||
height: 40px; | ||
width: 40px; | ||
border-radius: 50%; | ||
background-color: var(--grey-300); | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
transition: background-color 0.1s ease; | ||
transition: | ||
background 0.3s ease-in-out, | ||
transform 0.15s $easeInOutQuart; | ||
|
||
&:disabled { | ||
background-color: var(--grey-500); | ||
|
||
svg path { | ||
stroke: var(--grey-300); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { useEffect, useState, useRef, ReactNode } from "react"; | ||
import { motion } from "framer-motion"; | ||
import { Trans } from "next-i18next"; | ||
import { useInView } from "react-intersection-observer"; | ||
import classNames from "classnames"; | ||
|
||
import CaretIcon from "@/components/icons/Caret"; | ||
import { AnimatedText } from "@/components/shared/Text"; | ||
|
||
import styles from "./CardsSlider.module.scss"; | ||
|
||
interface CarouselProps { | ||
items: Element[] | ReactNode[]; | ||
titleKey?: string; | ||
initialScroll?: number; | ||
id?: string; | ||
className?: string; | ||
carouselClassName?: string; | ||
cardsClassName?: string; | ||
cardWrapperClassName?: string; | ||
} | ||
|
||
const CardsSlider = ({ | ||
items, | ||
titleKey, | ||
initialScroll = 0, | ||
id = "", | ||
className = "", | ||
carouselClassName = "", | ||
cardsClassName = "", | ||
cardWrapperClassName = "", | ||
}: CarouselProps) => { | ||
const carouselRef = useRef<HTMLDivElement>(null); | ||
const [canScrollLeft, setCanScrollLeft] = useState(false); | ||
const [canScrollRight, setCanScrollRight] = useState(true); | ||
|
||
useEffect(() => { | ||
if (carouselRef.current) { | ||
carouselRef.current.scrollLeft = initialScroll; | ||
checkScrollability(); | ||
} | ||
}, [initialScroll]); | ||
|
||
const checkScrollability = () => { | ||
if (carouselRef.current) { | ||
const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current; | ||
setCanScrollLeft(scrollLeft > 0); | ||
setCanScrollRight(scrollLeft < scrollWidth - clientWidth); | ||
} | ||
}; | ||
|
||
const scrollLeft = () => { | ||
if (carouselRef.current) { | ||
carouselRef.current.scrollBy({ left: -304, behavior: "smooth" }); | ||
} | ||
}; | ||
|
||
const scrollRight = () => { | ||
if (carouselRef.current) { | ||
carouselRef.current.scrollBy({ left: 304, behavior: "smooth" }); | ||
} | ||
}; | ||
|
||
const { ref, inView } = useInView({ | ||
triggerOnce: true, | ||
threshold: 0.5, | ||
}); | ||
|
||
return ( | ||
<div ref={ref} id={id} className={className}> | ||
{titleKey && ( | ||
<AnimatedText element="h2" as="heading" className={styles.Title}> | ||
<Trans i18nKey={titleKey} /> | ||
</AnimatedText> | ||
)} | ||
|
||
<div className={styles.Carousel}> | ||
<div | ||
className={classNames(styles.CarouselContainer, carouselClassName)} | ||
ref={carouselRef} | ||
onScroll={checkScrollability} | ||
> | ||
<div className={classNames(styles.Cards, cardsClassName)}> | ||
{items.map((item: any, index: number) => ( | ||
<motion.div | ||
initial={{ | ||
opacity: 0, | ||
y: 20, | ||
}} | ||
animate={ | ||
inView | ||
? { | ||
opacity: 1, | ||
y: 0, | ||
transition: { | ||
duration: 0.5, | ||
delay: 0.2 * index, | ||
ease: "easeOut", | ||
once: true, | ||
}, | ||
} | ||
: {} | ||
} | ||
key={"card" + index} | ||
className={classNames(styles.CardWrapper, cardWrapperClassName)} | ||
> | ||
{item} | ||
</motion.div> | ||
))} | ||
</div> | ||
</div> | ||
|
||
<div className={styles.Arrows}> | ||
<button onClick={scrollLeft} disabled={!canScrollLeft}> | ||
<CaretIcon direction="left" /> | ||
</button> | ||
<button onClick={scrollRight} disabled={!canScrollRight}> | ||
<CaretIcon /> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default CardsSlider; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
@import "../../scss/solutions/_variables.scss"; | ||
|
||
.CollapsibleTrigger { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
width: 100%; | ||
gap: 16px; | ||
font-size: 18px; | ||
font-weight: 700; | ||
letter-spacing: -0.01em; | ||
line-height: 1.16; | ||
color: var(--grey-200); | ||
text-align: left; | ||
margin-bottom: 0; | ||
|
||
p { | ||
margin: 0; | ||
} | ||
} | ||
|
||
.IconArrowDown { | ||
transform: rotate(180deg); | ||
} | ||
|
||
@include breakpoint(md) { | ||
.CollapsibleTrigger { | ||
justify-content: flex-start; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { useState, ReactNode } from "react"; | ||
import { motion, AnimatePresence } from "framer-motion"; | ||
import * as Collapsible from "@radix-ui/react-collapsible"; | ||
import styles from "./CollapsibleContent.module.scss"; | ||
import CaretIcon from "@/components/icons/Caret"; | ||
|
||
interface CollapsibleContentProps { | ||
label: string; | ||
defaultOpen?: boolean; | ||
className?: string; | ||
triggerClassName?: string; | ||
onOpenChange?: () => void; | ||
previewContent?: ReactNode; | ||
children: ReactNode; | ||
} | ||
|
||
const CollapsibleContent: React.FC<CollapsibleContentProps> = ({ | ||
label, | ||
defaultOpen, | ||
className, | ||
triggerClassName, | ||
onOpenChange, | ||
previewContent, | ||
children, | ||
}) => { | ||
const [open, setOpen] = useState(defaultOpen || false); | ||
|
||
const handleOpenChange = () => { | ||
setOpen(!open); | ||
onOpenChange && onOpenChange(); | ||
}; | ||
|
||
return ( | ||
<Collapsible.Root | ||
className={className} | ||
open={open} | ||
onOpenChange={handleOpenChange} | ||
> | ||
<Collapsible.Trigger asChild className={triggerClassName}> | ||
<button className={styles.CollapsibleTrigger}> | ||
{open ? ( | ||
<> | ||
<span className={styles.Label}>{label}</span> | ||
<CaretIcon color="#D0D0DC" direction="down" /> | ||
</> | ||
) : ( | ||
<> | ||
<p className={styles.Label}>{label}</p> | ||
<CaretIcon color="#D0D0DC" direction="up" /> | ||
</> | ||
)} | ||
</button> | ||
</Collapsible.Trigger> | ||
|
||
{previewContent ? previewContent : null} | ||
|
||
<AnimatePresence> | ||
{open && ( | ||
<motion.div | ||
initial={{ | ||
height: 0, | ||
opacity: 0, | ||
}} | ||
animate={{ | ||
height: "auto", | ||
opacity: 1, | ||
transition: { | ||
ease: "circOut", | ||
}, | ||
}} | ||
exit={{ | ||
height: 0, | ||
opacity: 0, | ||
}} | ||
> | ||
{children} | ||
</motion.div> | ||
)} | ||
</AnimatePresence> | ||
</Collapsible.Root> | ||
); | ||
}; | ||
|
||
export default CollapsibleContent; |
Oops, something went wrong.