Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CycleCountdown): add cycle countdown component #73

Merged
merged 4 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 50 additions & 48 deletions src/Countdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,80 @@
import React, { useState } from 'react'
import React, { useMemo, useState } from 'react'
import Countdown from './Countdown'

export const Default = () => {
return <Countdown countdownDuration={20} />
let time = new Date()
time.setSeconds(time.getSeconds() + 40)

return <Countdown expiresAt={time} />
}

export const Static = () => {
return (
<Countdown
isStatic
countdownDuration={5}
onExpire={() => {
return { shouldRepeat: true, delay: 1.5 }
}}
/>
)
let time = new Date()
time.setSeconds(time.getSeconds() + 40)

return <Countdown expiresAt={time} isStatic />
}

export const Repeating = () => {
export const Formatter = () => {
let time = new Date()
time.setSeconds(time.getSeconds() + 40)

return (
<Countdown
countdownDuration={5}
onExpire={() => {
return { shouldRepeat: true, delay: 1.5 }
expiresAt={time}
formatter={(value) => {
return `${value} seconds`
}}
/>
)
}

export const Coloring = () => {
export const onExpire = () => {
let time = new Date()
time.setSeconds(time.getSeconds() + 10)

return (
<Countdown
size={70}
strokeWidth={10}
countdownDuration={15}
colors={['#4fda22', '#1c17bf', '#edff18', '#A30000']}
colorTimes={[15, 10, 2, 0]}
expiresAt={time}
onExpire={() => {
return { shouldRepeat: true, delay: 1.5 }
alert('expired')
}}
/>
)
}

export const Formatted = () => {
return (
<Countdown
size={100}
countdownDuration={15}
formatter={(time) =>
time === 0 ? (
<div>Time's up</div>
) : (
<div className="text-center">
{time}s<br />
remaining
</div>
)
}
/>
)
}
export const UpdateFunction = () => {
const time = useMemo(() => {
let t = new Date()
t.setSeconds(t.getSeconds() + 40)
return t
}, [])

const [number, setNumber] = useState(0)

export const Expiration = () => {
const [expired, setExpired] = useState(false)
return (
<>
<div>
<Countdown
countdownDuration={5}
onExpire={() => {
setExpired(true)
expiresAt={time}
onUpdate={(value) => {
setNumber(value)
}}
/>
Function executed: {expired ? 'yes' : 'no'}
</>
Number through onUpdate: {number}
</div>
)
}

export const Styled = () => {
let time = new Date()
time.setSeconds(time.getSeconds() + 40)

return (
<Countdown
expiresAt={time}
className={{
root: 'w-max bg-red-300 text-blue-500',
}}
/>
)
}
97 changes: 31 additions & 66 deletions src/Countdown.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,49 @@
import React from 'react'
import { CountdownCircleTimer } from 'react-countdown-circle-timer'
import ReactCountdown from 'react-countdown'

export interface CountdownProps {
interface CountdownProps {
isStatic?: boolean
expiresAt: Date
formatter?: (value: any) => any
onExpire?: () => void
onUpdate?: (timeRemaining: number) => void
data?: {
cy?: string
test?: string
}
countdownDuration: number
size?: number
strokeWidth?: number
colors?: [`#${string}`, `#${string}`, ...`#${string}`[]]
colorTimes?: [number, number, ...number[]]
className?: {
root?: string
}
formatter?: (value: any) => any
onExpire?: () => void
onUpdate?: (timeRemaining: number) => void
isStatic?: boolean
}

/**
* This function returnes a pre-styled Countdown component based on the react-countdown-circle-timer component.
*
* @param data - The object of data attributes that can be used for testing (e.g. data-test or data-cy)
* @param countdownDuration - The duration of the countdown in seconds.
* @param size - The size of the countdown in pixels.
* @param strokeWidth - The width of the countdown stroke in pixels.
* @param colors - The colors that are shown in the countdown (from the start to the end). The length of this array needs to be consistent with the colorTimes array.
* @param colorTimes - The times at which the colors change (automatic interpolation). The length of this array needs to be consistent with the colors array.
* @param className - The optional className object allows you to override the default styling.
* @param formatter - The function that is called to format the countdown value.
* @param onExpire - The function that is called when the countdown expires.
* @param onUpdate - The function that is called when the remaining time is updated.
* @param isStatic - Indicate whether the countdown is static (does not run) or not.
* @returns Countdown component
*/
export function Countdown({
data,
countdownDuration,
colors,
colorTimes,
size,
strokeWidth,
className,
function Countdown({
isStatic,
expiresAt,
formatter,
onExpire,
onUpdate,
isStatic = false,
}: CountdownProps): React.ReactElement {
data,
className,
}: CountdownProps) {
return (
<div className={className?.root}>
<CountdownCircleTimer
data-cy={data?.cy}
data-test={data?.test}
isPlaying={!isStatic && countdownDuration > 0}
duration={countdownDuration > 0 ? countdownDuration : 0}
colors={colors || ['#00A321', '#00A321', '#F7B801', '#A30000']}
colorsTime={
colorTimes || [
countdownDuration,
(countdownDuration / 2) >> 0,
(countdownDuration / 4) >> 0,
0,
]
}
size={size || 45}
strokeWidth={strokeWidth || 7}
onComplete={onExpire}
onUpdate={onUpdate}
>
{({ remainingTime }: any) => {
return formatter
? formatter(remainingTime)
: remainingTime > 0
? remainingTime
: 0
}}
</CountdownCircleTimer>
</div>
<ReactCountdown
autoStart={!isStatic}
date={expiresAt}
intervalDelay={0}
renderer={(props) => (
<div
className={className?.root}
data-cy={data?.cy}
data-test={data?.test}
>
{formatter
? formatter(Math.round(props.total / 1000))
: Math.round(props.total / 1000)}
</div>
)}
onComplete={onExpire}
onTick={(timeDelta) => onUpdate?.(timeDelta.total / 1000)}
/>
)
}

Expand Down
80 changes: 0 additions & 80 deletions src/CountdownNew.stories.tsx

This file was deleted.

50 changes: 0 additions & 50 deletions src/CountdownNew.tsx

This file was deleted.

Loading
Loading