|
1 |
| -import React, { useEffect, useState } from 'react'; |
| 1 | +import React, { useEffect, useMemo, useState } from 'react'; |
2 | 2 | import classNames from 'classnames';
|
3 | 3 | import SbEditable from 'storyblok-react';
|
4 | 4 |
|
5 | 5 | import CountdownPie from './CountdownPie';
|
6 | 6 | import UseCountdown from '../../hooks/useCountdown';
|
7 | 7 |
|
| 8 | +// use useId when it becomes available |
| 9 | +const generateClassName = (key) => |
| 10 | + `${key}-${Math.floor(Math.random() * 100000, 5)}`; |
| 11 | + |
8 | 12 | const getDescriptorString = (descriptor, time) => {
|
9 | 13 | return time !== 1 ? `${descriptor}s` : descriptor;
|
10 | 14 | };
|
11 | 15 |
|
12 | 16 | const convertDaysToHours = (days) => (days ? days * 24 : 0);
|
13 | 17 |
|
14 | 18 | const Countdown = ({ blok }) => {
|
15 |
| - const { date, hasDays, hourPieRange, useNew } = blok; |
| 19 | + const { date, dayPieRange, hasDays, hourPieRange, isDST } = blok; |
16 | 20 | const [countdownDate, setCountdownDate] = useState(null);
|
17 | 21 | const [days, hours, minutes, seconds] = UseCountdown(countdownDate) || [];
|
18 | 22 | const displayHours = hasDays ? hours : convertDaysToHours(days) + hours;
|
19 | 23 | const displayHourPieRange = hasDays ? 24 : hourPieRange;
|
| 24 | + const noTime = days + hours + minutes + seconds <= 0; |
| 25 | + const daysClassName = useMemo(() => generateClassName('days'), []); |
| 26 | + const hoursClassName = useMemo(() => generateClassName('hours'), []); |
| 27 | + const minutesClassName = useMemo(() => generateClassName('minutes'), []); |
| 28 | + const secondsClassName = useMemo(() => generateClassName('seconds'), []); |
| 29 | + const pacificTimeOffset = useMemo(() => { |
| 30 | + const pacificTime = isDST ? 420 : 480; |
| 31 | + return pacificTime * 60 * 1000; |
| 32 | + }, [isDST]); |
20 | 33 |
|
21 | 34 | useEffect(() => {
|
22 | 35 | if (!countdownDate) {
|
23 |
| - if (!date) { |
24 |
| - if (useNew) { |
25 |
| - /** |
26 |
| - * manually set date to 04/05/2024 7PM w/ a UTC offset (no daylight savings) |
27 |
| - * this should only stay for the current campaign, can remove this and make |
28 |
| - * component purely storyblok configurable after campaign |
29 |
| - */ |
30 |
| - setCountdownDate(new Date(Date.UTC(2024, 3, 6, 2, 0, 0))); |
31 |
| - } else { |
32 |
| - /** |
33 |
| - * manually set date to 04/04/2024 7AM w/ a UTC offset (no daylight savings) |
34 |
| - * this should only stay for the current campaign, can remove this and make |
35 |
| - * component purely storyblok configurable after campaign |
36 |
| - */ |
37 |
| - setCountdownDate(new Date(Date.UTC(2024, 3, 4, 14, 0, 0))); |
38 |
| - } |
39 |
| - } else { |
40 |
| - /** |
41 |
| - * the date prop returns in the following format: "2023-11-21 23:56" |
42 |
| - * we have convert it to be usable for the js Date object |
43 |
| - */ |
44 |
| - const dateArray = date.split(' '); |
45 |
| - |
46 |
| - setCountdownDate(new Date(`${dateArray[0]}T${dateArray[1]}`)); |
47 |
| - } |
| 36 | + /** |
| 37 | + * the date prop returns in the following format: "2023-11-21 23:56" |
| 38 | + * we have convert it to be a usable for the js Date object |
| 39 | + */ |
| 40 | + const blokDateArray = date?.split(' '); |
| 41 | + const dateArray = blokDateArray?.[0]?.split('-'); |
| 42 | + const timeArray = blokDateArray?.[1]?.split(':'); |
| 43 | + const blokDateObj = new Date( |
| 44 | + `${dateArray?.[0]}-${dateArray?.[1]}-${dateArray?.[2]} ${timeArray?.[0]}:${timeArray?.[1]}` |
| 45 | + ); |
| 46 | + const utcOffset = blokDateObj?.getTimezoneOffset() * 60 * 1000; |
| 47 | + const timezoneDifference = utcOffset - pacificTimeOffset; |
| 48 | + const utcDateObj = new Date( |
| 49 | + blokDateObj?.getTime() + utcOffset - timezoneDifference |
| 50 | + ); |
| 51 | + setCountdownDate( |
| 52 | + new Date( |
| 53 | + Date.UTC( |
| 54 | + utcDateObj?.getFullYear(), |
| 55 | + utcDateObj?.getMonth(), |
| 56 | + utcDateObj?.getDate(), |
| 57 | + utcDateObj?.getHours(), |
| 58 | + utcDateObj?.getMinutes() |
| 59 | + ) |
| 60 | + ) |
| 61 | + ); |
48 | 62 | }
|
49 |
| - }, [countdownDate, setCountdownDate]); |
| 63 | + }, [date, countdownDate, setCountdownDate]); |
50 | 64 |
|
51 | 65 | return (
|
52 | 66 | <SbEditable content={blok}>
|
53 |
| - {days + hours + minutes + seconds <= 0 ? ( |
54 |
| - <div |
55 |
| - className={classNames('countdown-wrapper', { |
56 |
| - ['has-days']: days > 0, |
57 |
| - })} |
58 |
| - > |
59 |
| - {days > 0 && hasDays && ( |
60 |
| - <CountdownPie className="pie-days" descriptor="days" percent={0}> |
61 |
| - 0 |
62 |
| - </CountdownPie> |
63 |
| - )} |
64 |
| - <CountdownPie clasName="pie-hours" descriptor="hours" percent={0}> |
65 |
| - 0 |
66 |
| - </CountdownPie> |
67 |
| - <CountdownPie clasName="pie-minutes" descriptor="minutes" percent={0}> |
68 |
| - 0 |
69 |
| - </CountdownPie> |
| 67 | + <div |
| 68 | + aria-atomic="true" |
| 69 | + className={classNames('countdown-wrapper', { |
| 70 | + ['has-days']: days > 0 && hasDays, |
| 71 | + })} |
| 72 | + role="timer" |
| 73 | + > |
| 74 | + {days > 0 && hasDays && ( |
70 | 75 | <CountdownPie
|
71 |
| - className="pie-seconds" |
72 |
| - descriptor="seconds" |
73 |
| - percent={0} |
| 76 | + className={daysClassName} |
| 77 | + descriptor={getDescriptorString('day', days)} |
| 78 | + percent={noTime ? 0 : (days / dayPieRange) * 100} |
74 | 79 | >
|
75 |
| - 0 |
| 80 | + {noTime ? 0 : days} |
76 | 81 | </CountdownPie>
|
77 |
| - </div> |
78 |
| - ) : ( |
79 |
| - <div |
80 |
| - aria-atomic="true" |
81 |
| - className={classNames('countdown-wrapper', { |
82 |
| - ['has-days']: days > 0, |
83 |
| - })} |
84 |
| - role="timer" |
| 82 | + )} |
| 83 | + <CountdownPie |
| 84 | + className={hoursClassName} |
| 85 | + descriptor={getDescriptorString('hour', displayHours)} |
| 86 | + percent={noTime ? 0 : (displayHours / displayHourPieRange) * 100} |
85 | 87 | >
|
86 |
| - {days > 0 && hasDays && ( |
87 |
| - <CountdownPie |
88 |
| - className="pie-days" |
89 |
| - descriptor={getDescriptorString('day', days)} |
90 |
| - percent={(days / 29) * 100} |
91 |
| - > |
92 |
| - {days} |
93 |
| - </CountdownPie> |
94 |
| - )} |
95 |
| - <CountdownPie |
96 |
| - className="pie-hours" |
97 |
| - descriptor={getDescriptorString('hour', displayHours)} |
98 |
| - percent={(displayHours / displayHourPieRange) * 100} |
99 |
| - > |
100 |
| - {displayHours} |
101 |
| - </CountdownPie> |
102 |
| - <CountdownPie |
103 |
| - className="pie-minutes" |
104 |
| - descriptor={getDescriptorString('minute', minutes)} |
105 |
| - percent={(minutes / 60) * 100} |
106 |
| - > |
107 |
| - {minutes} |
108 |
| - </CountdownPie> |
109 |
| - <CountdownPie |
110 |
| - className="pie-seconds" |
111 |
| - descriptor={getDescriptorString('second', seconds)} |
112 |
| - percent={(seconds / 60) * 100} |
113 |
| - > |
114 |
| - {seconds} |
115 |
| - </CountdownPie> |
116 |
| - </div> |
117 |
| - )} |
| 88 | + {noTime ? 0 : displayHours} |
| 89 | + </CountdownPie> |
| 90 | + <CountdownPie |
| 91 | + className={minutesClassName} |
| 92 | + descriptor={getDescriptorString('minute', minutes)} |
| 93 | + percent={noTime ? 0 : (minutes / 60) * 100} |
| 94 | + > |
| 95 | + {noTime ? 0 : minutes} |
| 96 | + </CountdownPie> |
| 97 | + <CountdownPie |
| 98 | + className={secondsClassName} |
| 99 | + descriptor={getDescriptorString('second', seconds)} |
| 100 | + percent={noTime ? 0 : (seconds / 60) * 100} |
| 101 | + > |
| 102 | + {noTime ? 0 : seconds} |
| 103 | + </CountdownPie> |
| 104 | + </div> |
118 | 105 | </SbEditable>
|
119 | 106 | );
|
120 | 107 | };
|
|
0 commit comments