diff --git a/apps/web/src/App.scss b/apps/web/src/App.scss index 1be150e0..8c962fe1 100644 --- a/apps/web/src/App.scss +++ b/apps/web/src/App.scss @@ -27,3 +27,7 @@ body { src: url('./assets/fonts/PPNeueMontrealMono-Medium.woff2') format('woff2'); font-display: fallback; } + +body:has(dialog[open]) { + overflow: hidden; +} diff --git a/apps/web/src/components/FooterSection/FooterSection.module.scss b/apps/web/src/components/FooterSection/FooterSection.module.scss index 5b53501a..d45d735f 100644 --- a/apps/web/src/components/FooterSection/FooterSection.module.scss +++ b/apps/web/src/components/FooterSection/FooterSection.module.scss @@ -108,7 +108,7 @@ z-index: 0; transition: 300ms; } - + &:hover { overflow: hidden; @@ -288,7 +288,7 @@ } .dottedRuler { - @include mixins.dottedBreak(rgba(255, 255, 255, 0.20)); + @include mixins.dottedBreak(rgba(255, 255, 255, 0.2)); margin-top: 10px; } diff --git a/apps/web/src/components/SpeakersSection/SpeakerCard.tsx b/apps/web/src/components/SpeakersSection/OldSpeakerCard/SpeakerCard.tsx similarity index 97% rename from apps/web/src/components/SpeakersSection/SpeakerCard.tsx rename to apps/web/src/components/SpeakersSection/OldSpeakerCard/SpeakerCard.tsx index 2238db12..b1f8c0dc 100644 --- a/apps/web/src/components/SpeakersSection/SpeakerCard.tsx +++ b/apps/web/src/components/SpeakersSection/OldSpeakerCard/SpeakerCard.tsx @@ -2,7 +2,7 @@ import { SpeakerWithCompanyDto } from '@ddays-app/types'; import { motion } from 'framer-motion'; import { useState } from 'react'; -import SpeakerModal from './SpeakerModal'; +import SpeakerModal from '../SpeakerModal'; import c from './SpeakersSection.module.scss'; type SpeakerCardProps = { diff --git a/apps/web/src/components/SpeakersSection/SpeakerModal.tsx b/apps/web/src/components/SpeakersSection/OldSpeakerModal.tsx similarity index 100% rename from apps/web/src/components/SpeakersSection/SpeakerModal.tsx rename to apps/web/src/components/SpeakersSection/OldSpeakerModal.tsx diff --git a/apps/web/src/components/SpeakersSection/SpeakerCard/SpeakerCard.module.scss b/apps/web/src/components/SpeakersSection/SpeakerCard/SpeakerCard.module.scss new file mode 100644 index 00000000..c94ce0a3 --- /dev/null +++ b/apps/web/src/components/SpeakersSection/SpeakerCard/SpeakerCard.module.scss @@ -0,0 +1,50 @@ +.speakerCard { + display: flex; + align-items: flex-start; + flex-direction: column; + color: white; + text-align: center; + font-family: 'Times New Roman', Times, serif; + text-transform: uppercase; + + .image { + aspect-ratio: 320/400; + } + + .fullName { + color: #fff; + font-family: 'PP Neue Machina'; + font-size: 24px; + font-style: normal; + font-weight: 500; + line-height: 32px; + letter-spacing: -0.5px; + + margin-top: 24px; + } + + .position { + color: #fff; + font-family: 'PP Neue Montreal Mono'; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 1px; + } +} + +@media screen and (max-width: 768px) { + .speakerCard { + .fullName { + font-size: 18px; + line-height: 22px; + margin-top: 8px; + } + + .position { + font-size: 14px; + line-height: 16px; + } + } +} diff --git a/apps/web/src/components/SpeakersSection/SpeakerCard/SpeakerCard.tsx b/apps/web/src/components/SpeakersSection/SpeakerCard/SpeakerCard.tsx new file mode 100644 index 00000000..ebe5b010 --- /dev/null +++ b/apps/web/src/components/SpeakersSection/SpeakerCard/SpeakerCard.tsx @@ -0,0 +1,58 @@ +import { SpeakerWithCompanyDto } from '@ddays-app/types'; +import clsx from 'clsx'; +import { motion } from 'framer-motion'; +import { useState } from 'react'; + +import SpeakerModal from '../SpeakerModal'; +import c from './SpeakerCard.module.scss'; + +type SpeakerCardProps = { + speaker: SpeakerWithCompanyDto; +} & React.HTMLAttributes; + +const SpeakerCard = ({ + speaker, + className /*, ...handlers*/, +}: SpeakerCardProps) => { + const classes = clsx(c.speakerCard, className); + + const [isModalOpen, setIsModalOpen] = useState(false); + + const openModal = () => { + setIsModalOpen(true); + }; + + const closeModal = () => { + setIsModalOpen(false); + }; + + return ( + <> + openModal()} + //{...handlers} <-- Zbog ovoga ne radi modion.div + > + +

+ {speaker.firstName} {speaker.lastName} +

+ {speaker.title && speaker.company?.name && ( +

+ {speaker.title} @ {speaker.company?.name} +

+ )} + closeModal()} + /> +
+ + ); +}; + +export default SpeakerCard; diff --git a/apps/web/src/components/SpeakersSection/SpeakerCard/index.ts b/apps/web/src/components/SpeakersSection/SpeakerCard/index.ts new file mode 100644 index 00000000..2923fbe2 --- /dev/null +++ b/apps/web/src/components/SpeakersSection/SpeakerCard/index.ts @@ -0,0 +1,3 @@ +import SpeakerCard from './SpeakerCard'; + +export default SpeakerCard; diff --git a/apps/web/src/components/SpeakersSection/SpeakerModal/SpeakerModal.module.scss b/apps/web/src/components/SpeakersSection/SpeakerModal/SpeakerModal.module.scss new file mode 100644 index 00000000..0c5a2a9e --- /dev/null +++ b/apps/web/src/components/SpeakersSection/SpeakerModal/SpeakerModal.module.scss @@ -0,0 +1,143 @@ +.backdrop { + position: fixed; + top: 0; + left: 0; + + height: 100vh; + width: 100%; + + background: rgba(23, 22, 21, 0.8); + + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + padding: 32px; + + .speakerModal { + background: #181716; + display: flex; + flex-direction: row; + position: relative; + //width: 100%; + box-sizing: border-box; + padding: 64px 56px; + + .image { + max-height: 500px; + max-width: 400px; + + height: auto; + object-fit: cover; + flex-shrink: 0; + flex-grow: 0; + } + + .contentWrapper { + width: 536px; + display: flex; + flex-direction: column; + align-items: flex-start; + text-align: start; + box-sizing: border-box; + padding-left: 80px; + + .fullName { + color: #eaeaea; + font-family: 'PP Neue Machina'; + font-size: 48px; + line-height: 48px; + letter-spacing: -2px; + margin-bottom: 16px; + width: 100%; + } + + .titleAndCompany { + width: 100%; + color: #ff482f; + font-family: 'PP Neue Machina'; + font-size: 24px; + line-height: 32px; + letter-spacing: -1px; + text-transform: uppercase; + } + + .socialMediaButtons { + display: flex; + gap: 24px; + margin: 32px 0; + } + + .description { + color: #fff; + font-family: 'PP Neue Machina'; + font-size: 18px; + line-height: 26px; + text-transform: none; + width: 100%; + } + + .button { + width: fit-content; + color: #fff; + + font-feature-settings: + 'ss06' on, + 'ss01' on; + font-family: 'PP Neue Montreal Mono', sans-serif; + font-size: 14px; + line-height: 16px; + letter-spacing: 1px; + text-transform: uppercase; + background-color: transparent; + border: none; + cursor: pointer; + text-decoration: none; + position: relative; + transition: 300ms; + padding: 14px 16px; + } + + .grainyButton { + &:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.4' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E"); + background-size: cover; + opacity: 0; + z-index: 0; + transition: 300ms; + } + + &:hover { + overflow: hidden; + + .text { + color: white; + z-index: 5; + } + + &:before { + opacity: 0.2; + } + } + + &:active { + &::before { + opacity: 0.85; + } + } + } + } + + .closeButton { + position: absolute; + top: 0; + right: 0; + } + } +} diff --git a/apps/web/src/components/SpeakersSection/SpeakerModal/SpeakerModal.tsx b/apps/web/src/components/SpeakersSection/SpeakerModal/SpeakerModal.tsx new file mode 100644 index 00000000..477e5e6d --- /dev/null +++ b/apps/web/src/components/SpeakersSection/SpeakerModal/SpeakerModal.tsx @@ -0,0 +1,98 @@ +import { SpeakerWithCompanyDto } from '@ddays-app/types'; +import { useEffect } from 'react'; +import clsx from 'clsx'; + +import c from './SpeakerModal.module.scss'; + +type SpeakerModalProps = { + close: () => void; + isOpen: boolean; + speaker: SpeakerWithCompanyDto; +}; + +const SpeakerModal = ({ close, isOpen, speaker }: SpeakerModalProps) => { + const stopPropagation = (event: React.MouseEvent) => { + event.stopPropagation(); // Prevent clicks inside the modal from bubbling to the parent + }; + + useEffect(() => { + const disableScroll = (e: Event) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const disableKeyboardScroll = (e: KeyboardEvent) => { + if ( + ['ArrowUp', 'ArrowDown', 'Space', 'PageUp', 'PageDown'].includes(e.code) + ) { + e.preventDefault(); + } + }; + + if (isOpen) { + document.addEventListener('wheel', disableScroll, { passive: false }); + document.addEventListener('touchmove', disableScroll, { passive: false }); + document.addEventListener('keydown', disableKeyboardScroll); + } else { + document.removeEventListener('wheel', disableScroll); + document.removeEventListener('touchmove', disableScroll); + document.removeEventListener('keydown', disableKeyboardScroll); + } + + return () => { + document.removeEventListener('wheel', disableScroll); + document.removeEventListener('touchmove', disableScroll); + document.removeEventListener('keydown', disableKeyboardScroll); + }; + }, [isOpen]); + + if (!isOpen) return null; + + const openLink = (href: string | undefined) => { + if (!href) return; + window.open(href, '_blank'); + }; + + return ( +
+
+ +
+

+ {speaker.firstName} {speaker.lastName} +

+

+ {speaker.title} {speaker.title && speaker.company?.name && '@'}{' '} + {speaker.company?.name} +

+
+ {speaker.linkedin && ( + + )} + {speaker.instagram && ( + + )} +
+

{speaker.description}

+
+ +
+
+ ); +}; + +export default SpeakerModal; diff --git a/apps/web/src/components/SpeakersSection/SpeakerModal/index.ts b/apps/web/src/components/SpeakersSection/SpeakerModal/index.ts new file mode 100644 index 00000000..d0cb2c52 --- /dev/null +++ b/apps/web/src/components/SpeakersSection/SpeakerModal/index.ts @@ -0,0 +1,3 @@ +import SpeakerModal from './SpeakerModal'; + +export default SpeakerModal; diff --git a/apps/web/src/components/SpeakersSection/SpeakersSection.module.scss b/apps/web/src/components/SpeakersSection/SpeakersSection.module.scss index 4bed964f..1d3cd0cd 100644 --- a/apps/web/src/components/SpeakersSection/SpeakersSection.module.scss +++ b/apps/web/src/components/SpeakersSection/SpeakersSection.module.scss @@ -1,410 +1,163 @@ -.section { - position: relative; - width: 100%; -} - -.background { +.speakersSection { background-color: #ff482f; + width: 100%; position: relative; - overflow: hidden; -} -.background:before { - content: ''; - background-image: url(../../assets/images/noise.webp); - opacity: 0.15; - top: 0; - left: 0; - position: absolute; - width: 100%; - height: 100%; - pointer-events: none; -} + display: flex; + flex-direction: column; + align-items: center; -.wrapper { - background-color: #ff482f; - padding: 32px; + box-sizing: border-box; padding-top: 200px; - padding-bottom: 0; - max-width: 1440px; - margin: auto; -} -.header { - &Container { - text-align: center; - margin-bottom: 122px; - } - - &Title { - color: #eaeaea; - font-family: 'PP Neue Machina', sans-serif; - font-size: 120px; - font-style: normal; - font-weight: 500; - line-height: 144px; - text-transform: uppercase; + @media screen and (max-width: 1100px) { + padding-top: 100px; } - &SmallText { - color: rgba(255, 255, 255, 0.7); - font-family: 'PP Neue Montreal Mono', sans-serif; - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: 16px; - letter-spacing: 1px; - text-transform: uppercase; - margin-bottom: 24px; + @media screen and (max-width: 500px) { + padding-top: 35px; } -} -.speakers { - &WrapperContainer { - height: 1750px; - overflow: hidden; - - &.expanded { - height: auto; - } - - &:not(.expanded) { - .speakersColumn:nth-child(3) { - .card:nth-child(2) { - opacity: 0.2 !important; - } - } - - @media screen and (max-width: 950px) { - .speakersColumn { - .card:nth-child(4) { - opacity: 0.2 !important; - } - } - } - } + &:before { + content: ''; + background-image: url(../../assets/images/noise.webp); + opacity: 0.15; + top: 0; + left: 0; + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; } - &Wrapper { + .titleWrapper { display: flex; - flex-wrap: wrap; - justify-content: center; - margin-bottom: 48px; - } - - &Column:not(:last-child) { - margin-right: 32px; - } - - &Column:nth-child(2) { - margin-top: 647px; - } - - &Column:nth-child(3) { - margin-top: 112px; - } - - &Column:nth-child(4) { - margin-top: 779px; - } -} - -.speaker { - &Frame { - height: 401px; - width: 310px; - object-fit: contain; - } - - &Image { - height: 401px; - width: 300px; - } -} - -.card { - margin-bottom: 732px; - cursor: pointer; - - &InfoWrapper { + flex-direction: column; text-align: center; - margin-top: 22px; - } - - &Name { - color: #fff; - font-family: 'PP Neue Machina', sans-serif; - font-size: 24px; - line-height: 32px; - text-transform: uppercase; - margin: 0; - margin-bottom: 8px; - font-weight: 500; - } - - &Title { - color: #fff; - font-family: 'PP Neue Montreal Mono', sans-serif; - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 16px; - letter-spacing: 1px; - text-transform: uppercase; - margin: 0; - } -} - -.card:last-child { - margin-bottom: 0px; -} - -.modal { - position: fixed; - top: 0; - left: 50%; - - z-index: 10001; - transform: translate(-50%, 40px); -} - -.container { - position: relative; - background: black; - color: white; - padding: 40px 80px 55px 80px; - width: 1200px; - display: flex; - max-height: 500px; - - @media screen and (max-width: 600px) { - padding: 40px; - } -} - -.container:before { - content: ''; - background-image: url(../../assets/images/noise.webp); - opacity: 0.15; - top: 0; - left: 0; - position: absolute; - width: 100%; - height: 100%; - pointer-events: none; -} -.container::-webkit-scrollbar { - display: none; -} - -.modalBackground { - position: fixed; - top: 0; - left: 0; - z-index: 10000; + gap: 24px; - width: 100%; - height: 100%; - backdrop-filter: blur(5px); -} + @media screen and (max-width: 500px) { + gap: 0; + } -.close { - position: absolute; - top: 32px; - right: 32px; + .subtitle { + color: rgba(255, 255, 255, 0.7); + font-family: 'PP Neue Montreal Mono'; + font-size: 14px; + line-height: 16px; + letter-spacing: 1px; + text-transform: uppercase; - width: 16px; - height: 16px; + @media screen and (max-width: 1100px) { + font-size: 12px; + } + } - cursor: pointer; -} + .title { + color: #eaeaea; + font-family: 'PP Neue Machina'; + font-size: 120px; + letter-spacing: -5px; + text-transform: uppercase; -.modalImage { - margin-right: 80px; + @media screen and (max-width: 1100px) { + font-size: 80px; + } - @media screen and (max-width: 950px) { - margin-right: 40px; + @media screen and (max-width: 500px) { + font-size: 48px; + line-height: 80px; + letter-spacing: -3px; + } + } } -} - -.modalRight { - width: 536px; - display: flex; - flex-direction: column; - overflow-y: hidden; -} - -.modalSpeakerName { - color: #eaeaea; - font-family: 'PP Neue Machina', sans-serif; - font-size: 48px; - font-style: normal; - font-weight: 500; - line-height: 48px; - letter-spacing: -2px; - text-transform: uppercase; - margin-bottom: 16px; -} - -.modalTitle { - color: #ff482f; - font-family: 'PP Neue Machina', sans-serif; - font-size: 24px; - font-style: normal; - font-weight: 500; - line-height: 32px; - letter-spacing: -1px; - text-transform: uppercase; - margin-bottom: 32px; -} -.socialsWrapper { - display: flex; - margin-bottom: 32px; + .speakerCardsWrapper { + display: grid; + width: 100vw; + grid-template-columns: repeat(4, 1fr); + grid-template-rows: 1fr; + box-sizing: border-box; + padding: 0 32px; - @media screen and (max-width: 400px) { - flex-direction: column; - } -} + column-gap: 32px; + row-gap: 100px; -.socialsLink { - color: #fff; - font-feature-settings: - 'ss06' on, - 'ss01' on; - font-family: 'PP Neue Montreal Mono', sans-serif; - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 16px; - letter-spacing: 1px; - text-decoration: none; - padding: 8px; -} + margin-top: 120px; + margin-bottom: 50px; -.modalDescription { - color: #fff; - font-family: 'PP Neue Machina', sans-serif; - font-size: 18px; - font-style: normal; - font-weight: 500; - line-height: 26px; - overflow-y: scroll; -} + .speakerCard { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + object-fit: contain; -.modalDescription::-webkit-scrollbar { - display: none; -} + img { + width: 100%; + } -@media screen and (max-width: 1250px) { - .container { - width: 90vw; - } -} + &:nth-child(4n + 2) { + grid-row: span 1; + margin-top: 647px; + } -@media screen and (max-width: 980px) { - .wrapper { - padding: 16px; - } - .header { - &Title { - font-size: 48px; - line-height: 52px; - letter-spacing: -3px; - } + &:nth-child(4n + 3) { + grid-row: span 1; + margin-top: 112px; + } - &SmallText { - font-size: 14px; - margin-bottom: 12px; + &:nth-child(4n + 4) { + grid-row: span 1; + margin-top: 779px; + } } } } -@media screen and (max-width: 950px) { - .headerContainer { - margin-bottom: 40px; - } - .card { - margin-bottom: 180px; - - &Name { - font-size: 18px; - margin-bottom: 0px; - } - } - - .speakersColumn:nth-child(2) { - margin-top: 140px; - } -} +@media screen and (max-width: 1150px) { + .speakersSection { + .speakerCardsWrapper { + grid-template-columns: repeat(3, 1fr); -@media screen and (max-width: 870px) { - .modalImage { - display: none; - } -} + .speakerCard { + &:nth-child(n) { + grid-row: span 0; + margin-top: 0; + } -.expandOption { - display: flex; - justify-content: center; - position: absolute; - width: 100%; - height: 575px; - bottom: 0; - left: 50%; - padding-top: 64px; - transform: translateX(-50%); - cursor: pointer; - color: #171615; - font-feature-settings: - 'ss06' on, - 'ss01' on; - font-family: 'PP Neue Montreal Mono'; - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: 16px; /* 114.286% */ - letter-spacing: 1px; - text-transform: uppercase; + &:nth-child(3n) { + grid-row: span 1; + margin-top: 200px; + } - @media screen and (max-width: 950px) { - height: 200px; + &:nth-child(3n + 2) { + grid-row: span 1; + margin-top: 500px; + } + } + } } } -.sectionBreaker { - position: absolute; - left: 0; - bottom: -1px; - width: 100%; - height: calc(0px + 31px); - z-index: 3; - - .breakerPadding { - width: 100%; - height: 0; - position: relative; - background-color: #f5ede1; - - &:before { - content: ''; - background-image: url(../../assets/images/noise.webp); - opacity: 0.15; - top: 0; - left: 0; - position: absolute; - width: 100%; - height: 100%; - pointer-events: none; - } - } +@media screen and (max-width: 768px) { + .speakersSection { + .speakerCardsWrapper { + grid-template-columns: repeat(2, 1fr); + margin-top: 50px; - @media (max-width: 992px) { - height: 30px; + .speakerCard { + &:nth-child(n) { + grid-row: span 0; + margin-top: 0; + } - .breakerPadding { - height: 0; + &:nth-child(2n) { + grid-row: span 1; + margin-top: 140px; + } + } } } } diff --git a/apps/web/src/components/SpeakersSection/SpeakersSection.tsx b/apps/web/src/components/SpeakersSection/SpeakersSection.tsx index fd47844b..2c124fda 100644 --- a/apps/web/src/components/SpeakersSection/SpeakersSection.tsx +++ b/apps/web/src/components/SpeakersSection/SpeakersSection.tsx @@ -1,71 +1,35 @@ -import clsx from 'clsx'; import { SectionBreaker } from 'components/SectionBreaker'; -import { useState } from 'react'; import { useSpeakerWithCompanyGetAll } from '../../api/speaker/useSpeakerWithCompanyGetAll'; -import { useScreenSize } from '../../hooks/useScreenSize'; import SpeakerCard from './SpeakerCard'; import c from './SpeakersSection.module.scss'; -import { getCardWidth, getColumns } from './utils'; const SpeakersSection = () => { - const { screenWidth, isMobile } = useScreenSize(950); - const cardAspectRatio = 401 / 320; - const [isExpanded, setIsExpanded] = useState(false); + const fetchedSpeakers = useSpeakerWithCompanyGetAll(); - const speakers = useSpeakerWithCompanyGetAll(); - - const handleSetExpanded = () => { - setIsExpanded((prev) => !prev); - }; - - if (speakers.isLoading || !speakers.data) { + if (fetchedSpeakers.isLoading || !fetchedSpeakers.data) { return null; } - const balancedArray = getColumns(speakers.data, isMobile); - - const cardWidth = getCardWidth(screenWidth); + const speakers = fetchedSpeakers.data; return ( -
-
-
-

- Digitalni inovatori pod istim krovom -

-

SPEAKERI ({speakers.data.length})

-
-
-
- {balancedArray?.map((subArray, index) => ( -
- {subArray.map((speaker) => ( - - ))} -
- ))} -
-
- {!isExpanded && ( -
- [ Pogledajte sve predavače ] -
- )} +
+
+

Digitalni inovatori pod istim krovom

+

Speakeri

-
- -
+
+ {speakers?.map((speaker, index) => ( + + ))}
-
+ +
); };