Skip to content

Commit

Permalink
Create countdown component (#697)
Browse files Browse the repository at this point in the history
* Create countdown component
  • Loading branch information
Mathias-a authored Oct 24, 2023
1 parent 52d13c5 commit e6b3666
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 3 deletions.
38 changes: 38 additions & 0 deletions frontend/src/Components/Countdown/Countdown.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.countdown {
position: relative;
display: inline-block;
}

.countdown_button {
background-color: #4caf50;
color: white;
border: none;
padding: 15px 32px;
text-align: center;
font-size: 16px;
cursor: pointer;
}

.countdown_blur {
filter: blur(5px);
background-color: rgba(33, 33, 33, 0.6);
text-align: center;
font-size: 20px;
display: inline-block;
z-index: 1000;
}

.countdown_time {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: calc(6px + 30%);
z-index: 5;
color: ivory;
}
27 changes: 27 additions & 0 deletions frontend/src/Components/Countdown/Countdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ComponentMeta, ComponentStory } from '@storybook/react';

import { Button } from '~/Components/Button';
import { Countdown } from './Countdown';

export default {
title: 'Components/Countdown',
component: Countdown,
} as ComponentMeta<typeof Countdown>;

const Template: ComponentStory<typeof Countdown> = function (args) {
return (
<Countdown {...args}>
<Button theme="green">he he he haw, Guilty</Button>
</Countdown>
);
};

export const Basic = Template.bind({});
Basic.args = {
targetDate: new Date(new Date(new Date().getTime() + 60 * 60 * 24 * 1000)),
};

export const Short = Template.bind({});
Short.args = {
targetDate: new Date(new Date(new Date().getTime() + 60 * 1000 * 5)),
};
36 changes: 36 additions & 0 deletions frontend/src/Components/Countdown/Countdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useEffect, useState } from 'react';

import classNames from 'classnames';
import { SECOND_MILLIS } from '~/constants';
import { Children } from '~/types';
import styles from './Countdown.module.scss';
import { calculateTimeLeft, hasReachedTargetDate } from './utils';

type CountdownProps = {
targetDate: Date;
children?: Children;
};

export function Countdown({ targetDate, children }: CountdownProps) {
const [timeLeft, setTimeLeft] = useState<string>('');
const [isTimeOut, setIsTimeOut] = useState<boolean>(false);

useEffect(() => {
const updateTimer = setInterval(() => {
const newTimeLeft = calculateTimeLeft(targetDate);
setTimeLeft(newTimeLeft);
if (hasReachedTargetDate(targetDate)) setIsTimeOut(true);
}, SECOND_MILLIS);

return () => clearInterval(updateTimer);
}, [targetDate]);

if (isTimeOut) return <>{children}</>;

return (
<div className={classNames(styles.countdown)}>
<div className={styles.countdown_time}>{timeLeft}</div>
<div className={styles.countdown_blur}>{children}</div>
</div>
);
}
1 change: 1 addition & 0 deletions frontend/src/Components/Countdown/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Countdown } from './Countdown';
24 changes: 24 additions & 0 deletions frontend/src/Components/Countdown/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DAY_MILLIS, HOUR_MILLIS, MINUTE_MILLIS, SECOND_MILLIS } from '~/constants';

export function calculateTimeLeft(targetDate: Date): string {
const now = new Date();
const difference = targetDate.getTime() - now.getTime();

if (difference <= 0) return '0m 0s';

const days = Math.floor(difference / DAY_MILLIS);
const hours = Math.floor((difference % DAY_MILLIS) / HOUR_MILLIS);
const minutes = Math.floor((difference % HOUR_MILLIS) / MINUTE_MILLIS);
const seconds = Math.floor((difference % MINUTE_MILLIS) / SECOND_MILLIS);

if (days > 0) return `${days}d ${hours}h`;
if (difference < 60 * SECOND_MILLIS) return `${seconds}s`;
if (difference < 10 * MINUTE_MILLIS) return `${minutes}m ${seconds}s`;
if (difference < 60 * MINUTE_MILLIS) return `${minutes}m`;
return `${hours}h ${minutes}m`;
}

export function hasReachedTargetDate(targetDate: Date): boolean {
const now = new Date();
return now.getTime() >= targetDate.getTime();
}
5 changes: 3 additions & 2 deletions frontend/src/Components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ export { Carousel } from './Carousel';
export { Checkbox } from './Checkbox';
export { ColorDisplay } from './ColorDisplay';
export { ContentCard } from './ContentCard';
export { Countdown } from './Countdown';
export { CrudButtons } from './CrudButtons';
export { Dropdown } from './Dropdown';
export { Error } from './Error';
export { EventCard } from './EventCard';
export { EventQuery } from './EventQuery';
export { ExpandableHeader } from './ExpandableHeader';
export { ExpandableList } from './ExpandableList';
export { Footer } from './Footer';
export { IconButton } from './IconButton';
export { Image } from './Image';
export { ImageCard } from './ImageCard';
Expand All @@ -28,6 +30,7 @@ export { OpeningHours } from './OpeningHours';
export { Page } from './Page';
export { PermissionRoute } from './PermissionRoute';
export { ProgressBar } from './ProgressBar';
export { ProtectedRoute } from './ProtectedRoute';
export { PulseEffect } from './PulseEffect';
export { RadioButton } from './RadioButton';
export { SamfOutlet } from './SamfOutlet';
Expand All @@ -48,5 +51,3 @@ export { TimeDisplay } from './TimeDisplay';
export { TimeDuration } from './TimeDuration';
export { ToggleSwitch } from './ToggleSwitch';
export { Video } from './Video';
export { Footer } from './Footer';
export { ProtectedRoute } from './ProtectedRoute';
11 changes: 10 additions & 1 deletion frontend/src/Pages/ComponentPage/ComponentPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Button, InputField, ProgressBar, RadioButton } from '~/Components';
import { Button, Countdown, InputField, ProgressBar, RadioButton } from '~/Components';
import { Checkbox } from '~/Components/Checkbox';
import { Link } from '~/Components/Link';
import { List } from '~/Components/List';
import { norwegianFlag } from '~/assets';
import { HOUR_MILLIS } from '~/constants';
import styles from './ComponentPage.module.scss';

/**
Expand Down Expand Up @@ -54,6 +56,13 @@ export function ComponentPage() {
<h2>ProgressBar:</h2>
<ProgressBar value={75} max={100} />
</div>
<div>
<h2>
<Countdown targetDate={new Date(new Date().getTime() + HOUR_MILLIS)}>
<img src={norwegianFlag}></img>
</Countdown>
</h2>
</div>
</div>
);
}
5 changes: 5 additions & 0 deletions frontend/src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,8 @@ export const CYPRESS_BACKEND_DOMAIN = import.meta.env.VITE_CYPRESS_BACKEND_DOMAI
export const VENUE = {
LYCHE: 'lyche',
} as const;

export const SECOND_MILLIS = 1000;
export const MINUTE_MILLIS = 60 * SECOND_MILLIS;
export const HOUR_MILLIS = 60 * MINUTE_MILLIS;
export const DAY_MILLIS = 24 * HOUR_MILLIS;

0 comments on commit e6b3666

Please sign in to comment.