Skip to content

Commit

Permalink
Merge pull request #437 from dump-hr/ToniGrbic/schedule-improvments
Browse files Browse the repository at this point in the history
Toni grbic/schedule improvments
  • Loading branch information
lovretomic authored Dec 22, 2024
2 parents 6681c1e + 23e890c commit 9b9e85a
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 47 deletions.
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"devDependencies": {
"@ddays-app/tsconfig": "*",
"@ddays-app/types": "*",
"@ddays-app/types": "1.0.0",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@typescript-eslint/eslint-plugin": "^6.10.0",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
106 changes: 70 additions & 36 deletions apps/web/src/components/ScheduleSection/ScheduleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,70 @@ import PlusSvg from 'assets/icons/plus-black.svg';
import { useEffect, useState } from 'react';

import { useScreenSize } from '../../hooks/useScreenSize';
import ScheduleImageCard from './ScheduleImageCard';
import c from './ScheduleSection.module.scss';
import {
getEventTime,
getEventTypeTranslation,
getSpeakerCompanyStringForEvent,
getThemeShort,
} from './utils';

const getThemeShort = (theme: string) => {
switch (theme) {
case 'dev':
return 'DEV';
case 'design':
return 'DIZ';
case 'marketing':
return 'MARK';
case 'tech':
return 'TECH';
}
};

type ScheduleCardProps = {
event: EventWithSpeakerDto;
openCardId: number | null;
setOpenCardId: (id: number | null) => void;
lastClickedCardId: React.MutableRefObject<number | null>;
};

const ScheduleCard: React.FC<ScheduleCardProps> = ({
event,
openCardId,
setOpenCardId,
lastClickedCardId,
}) => {
const [currentImageIndex, setCurrentImageIndex] = useState(0);
const [currentImageIndex, setCurrentImageIndex] = useState<number>(1);
const [isImageShown, setIsImageShown] = useState<boolean>(false);

const { isMobile } = useScreenSize(1030);
const isOpenDescription = openCardId === event.id;

const images = event.speakers
const imagesList = event.speakers
?.filter((speaker) => speaker.photo !== null)
.map((speaker) => speaker.photo) || [''];
.map((speaker) => speaker.photo);

const images = imagesList?.length === 0 ? [''] : imagesList!;

useEffect(() => {
const intervalId = setInterval(() => {
setCurrentImageIndex((prev) => (prev + 1) % images.length);
setCurrentImageIndex((prev) => {
const next = (prev + 1) % images!.length;
if (next === 0) return 1;
return next;
});
}, 2000);

return () => clearInterval(intervalId);
}, []);

const cardAspectRatio = 401 / 320;
const { isMobile } = useScreenSize(930);

const isOpenDescription = openCardId === event.id;
const [isImageShown, setIsImageShown] = useState(false);

const handleCardClick = () => {
if (!event.description) return;

if (isOpenDescription) {
setOpenCardId(null);
lastClickedCardId.current = event.id;
} else {
setOpenCardId(event.id);
lastClickedCardId.current = -1;
}
};

const handleHover = () => {
if (!isOpenDescription) {
if (isOpenDescription) {
lastClickedCardId.current = event.id;
} else {
setIsImageShown(true);
lastClickedCardId.current = -1;
}
};

Expand All @@ -80,17 +82,49 @@ const ScheduleCard: React.FC<ScheduleCardProps> = ({
onClick={handleCardClick}
className={c.scheduleCardContainer}>
<div className={c.scheduleCard}>
{(isImageShown || isOpenDescription) &&
!isMobile &&
images[currentImageIndex] && (
<div className={c.speakerPhoto}>
<img
src={images[currentImageIndex]}
height={cardAspectRatio * 120}
width={120}
/>
</div>
)}
{(isImageShown || isOpenDescription) && !isMobile && (
<>
{event.type === 'panel' && images[currentImageIndex] && (
<>
<ScheduleImageCard
index={0}
src={images[0]}
event={event}
isOpenDescription={isOpenDescription}
isImageShown={isImageShown}
imagesLength={images.length}
lastClickedCardId={lastClickedCardId}
/>
<ScheduleImageCard
index={1}
src={images[currentImageIndex]}
event={event}
isOpenDescription={isOpenDescription}
isImageShown={isImageShown}
imagesLength={images.length}
lastClickedCardId={lastClickedCardId}
/>
</>
)}

{event.type !== 'panel' &&
images.map((image, index) => {
return (
<ScheduleImageCard
key={index}
index={index}
src={image}
event={event}
isOpenDescription={isOpenDescription}
isImageShown={isImageShown}
imagesLength={images.length}
lastClickedCardId={lastClickedCardId}
/>
);
})}
</>
)}

<div className={c.scheduleCardLeftWrapper}>
<div className={c.scheduleCardLeft}>
<p className={c.timeText}>
Expand Down Expand Up @@ -135,7 +169,7 @@ const ScheduleCard: React.FC<ScheduleCardProps> = ({
</div>
</div>
<div className={c.scheduleCardRight}>
{event.description !== '' && (
{event.description && (
<button className={c.plusButton}>
{isOpenDescription ? (
<img src={MinusSvg} alt='minus' />
Expand Down
102 changes: 102 additions & 0 deletions apps/web/src/components/ScheduleSection/ScheduleImageCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { useEffect, useRef } from 'react';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { EventWithSpeakerDto } from '@ddays-app/types';
import LunchImage from 'assets/images/events/schedule/pizza-lunch.png';
import CampfireTalks from 'assets/images/events/schedule/campfire-talks.png';
import { useScreenSize } from '../../hooks/useScreenSize';
import c from './ScheduleSection.module.scss';

gsap.registerPlugin(ScrollTrigger);

type ScheduleImageCardProps = {
index: number;
src: string | undefined;
event: EventWithSpeakerDto;
isOpenDescription: boolean;
isImageShown: boolean;
imagesLength?: number;
lastClickedCardId: React.MutableRefObject<number | null>;
};

const ScheduleImageCard: React.FC<ScheduleImageCardProps> = ({
index,
src,
event,
isOpenDescription,
isImageShown,
imagesLength = 1,
lastClickedCardId,
}) => {
const speakerPhoto = useRef<HTMLDivElement>(null);
const isSpeakerPhoto = useRef(false);
const cardAspectRatio = useRef(1);
const { isMobile } = useScreenSize(1030);

const getImageSrcAndSetType = (src?: string): string => {
if (event.type === 'campfireTalk') return CampfireTalks;
if (event.name === 'Ručak') return LunchImage;
if (!src) return '';

isSpeakerPhoto.current = true;
cardAspectRatio.current = 401 / 320;
return src;
};

const image = getImageSrcAndSetType(src);

const animateCards = (
translateY: number,
translateX: number,
rotateDeg: number,
) => {
if (
(lastClickedCardId.current !== event.id && !isOpenDescription) ||
(lastClickedCardId.current === -1 && !isOpenDescription)
) {
gsap.fromTo(
speakerPhoto.current,
{ scale: 0, x: 0, y: 0, rotation: -3 },
{
scale: 1,
x: translateX,
y: translateY,
rotation: rotateDeg,
duration: 0.7,
ease: 'power2.out',
},
);
} else {
const transform = `scale(1) translate(${translateX}px, ${translateY}px) rotate(${rotateDeg}deg)`;
speakerPhoto.current!.style.transform = transform;
}
};

useEffect(() => {
if (isMobile || !speakerPhoto) return;

const translateY = index !== 2 ? 6 * index : 20;
const rotateDeg = index !== 0 ? 6 * index : -3;
let translateX = 70;

if (isSpeakerPhoto.current && imagesLength > 1) translateX = 90 * index;

if (isSpeakerPhoto.current && imagesLength === 3) translateX -= 80;

const ctx = gsap.context(() =>
animateCards(translateY, translateX, rotateDeg),
);

return () => ctx.revert();
}, [isMobile, isImageShown, isOpenDescription, lastClickedCardId]);

return (
<div className={c.speakerPhoto} ref={speakerPhoto}>
{image && (
<img src={image} height={cardAspectRatio.current * 120} width={120} />
)}
</div>
);
};

export default ScheduleImageCard;
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@

&CenterWrapper {
max-width: 750px;

@media screen and (max-width: 1440px) {
max-width: 500px;
}
}

&Title {
Expand All @@ -75,6 +79,7 @@
margin: 0;
margin-right: 150px;
cursor: pointer;
word-break: break-word;
}

&Subtitle {
Expand Down Expand Up @@ -180,21 +185,47 @@
fill: black;
background-color: transparent;
border: none;
z-index: 10;
cursor: pointer;
}

.speakerPhoto {
position: absolute;
z-index: 2;
top: -26px;
right: 150px;
transform: rotate(-5deg);
top: -10px;
right: 250px;

@media screen and (max-width: 1180px) {
right: 200px;
}
}
.nonClickable{
.nonClickable {
cursor: text;
}

@media screen and (max-width: 1000px) {
@media screen and (max-width: 1440px) {
.scheduleCard {
padding: 35px 48px;
}

.scheduleCardLeft {
margin-right: 100px;
}

.scheduleCardTitle {
margin-right: 100px;
}

.scheduleCardSubtitle {
margin-right: 100px;
}

.scheduleButton {
padding: 16px 24px;
}
}

@media screen and (max-width: 1180px) {
.scheduleCardLeft {
margin-right: 80px;
}
Expand Down Expand Up @@ -224,6 +255,7 @@
}

.scheduleCard {
position: relative;
width: 100%;
display: flex;
justify-content: center;
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/components/ScheduleSection/ScheduleSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Theme } from '@ddays-app/types';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef } from 'react';

import { useEventGetAllWithSpeaker } from '../../api/event/useEventGetAllWithSpeaker';
import { useScreenSize } from '../../hooks/useScreenSize';
Expand All @@ -24,6 +24,7 @@ const ScheduleSection = () => {
const [date, setDate] = useState<ConferenceDay>(ConferenceDay.First);
const { isMobile } = useScreenSize(1000);
const [openedCardId, setOpenedCardId] = useState<number | null>(null);
const lastClickedCardId = useRef<number | null>(null);

useEffect(() => {
if (isMobile && theme !== null) {
Expand Down Expand Up @@ -105,12 +106,13 @@ const ScheduleSection = () => {
(theme === null || event.theme === theme) &&
getEventDay(event.startsAt) === date,
)
.map((event) => (
?.map((event) => (
<ScheduleCard
key={event.id}
event={event}
openCardId={openedCardId}
setOpenCardId={setOpenedCardId}
lastClickedCardId={lastClickedCardId}
/>
))}
{events.data?.length === 0 && 'Trenutno nema upisanih događaja'}
Expand Down
Loading

0 comments on commit 9b9e85a

Please sign in to comment.