- time === 0 ? (
- Time's up
- ) : (
-
- {time}s
- remaining
-
- )
- }
- />
- )
-}
+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 (
- <>
+
{
- setExpired(true)
+ expiresAt={time}
+ onUpdate={(value) => {
+ setNumber(value)
}}
/>
- Function executed: {expired ? 'yes' : 'no'}
- >
+ Number through onUpdate: {number}
+
+ )
+}
+
+export const Styled = () => {
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 40)
+
+ return (
+
)
}
diff --git a/src/Countdown.tsx b/src/Countdown.tsx
index 2a8114b..837a4d9 100644
--- a/src/Countdown.tsx
+++ b/src/Countdown.tsx
@@ -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 (
-
- 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
- }}
-
-
+ (
+
+ {formatter
+ ? formatter(Math.round(props.total / 1000))
+ : Math.round(props.total / 1000)}
+
+ )}
+ onComplete={onExpire}
+ onTick={(timeDelta) => onUpdate?.(timeDelta.total / 1000)}
+ />
)
}
diff --git a/src/CountdownNew.stories.tsx b/src/CountdownNew.stories.tsx
deleted file mode 100644
index 2a0cc2a..0000000
--- a/src/CountdownNew.stories.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React, { useMemo, useState } from 'react'
-import CountdownNew from './CountdownNew'
-
-export const Default = () => {
- let time = new Date()
- time.setSeconds(time.getSeconds() + 40)
-
- return
-}
-
-export const Static = () => {
- let time = new Date()
- time.setSeconds(time.getSeconds() + 40)
-
- return
-}
-
-export const Formatter = () => {
- let time = new Date()
- time.setSeconds(time.getSeconds() + 40)
-
- return (
- {
- return `${value} seconds`
- }}
- />
- )
-}
-
-export const onExpire = () => {
- let time = new Date()
- time.setSeconds(time.getSeconds() + 10)
-
- return (
- {
- alert('expired')
- }}
- />
- )
-}
-
-export const UpdateFunction = () => {
- const time = useMemo(() => {
- let t = new Date()
- t.setSeconds(t.getSeconds() + 40)
- return t
- }, [])
-
- const [number, setNumber] = useState(0)
-
- return (
-
- {
- setNumber(value)
- }}
- />
- Number through onUpdate: {number}
-
- )
-}
-
-export const Styled = () => {
- let time = new Date()
- time.setSeconds(time.getSeconds() + 40)
-
- return (
-
- )
-}
diff --git a/src/CountdownNew.tsx b/src/CountdownNew.tsx
deleted file mode 100644
index 18ab605..0000000
--- a/src/CountdownNew.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react'
-import Countdown from 'react-countdown'
-
-interface CountdownTextProps {
- isStatic?: boolean
- expiresAt: Date
- formatter?: (value: any) => any
- onExpire?: () => void
- onUpdate?: (timeRemaining: number) => void
- data?: {
- cy?: string
- test?: string
- }
- className?: {
- root?: string
- }
-}
-
-function CountdownText({
- isStatic,
- expiresAt,
- formatter,
- onExpire,
- onUpdate,
- data,
- className,
-}: CountdownTextProps) {
- return (
- (
-
- {formatter
- ? formatter(Math.round(props.total / 1000))
- : Math.round(props.total / 1000)}
-
- )}
- onComplete={onExpire}
- onTick={(timeDelta) => onUpdate?.(timeDelta.total / 1000)}
- />
- )
-}
-
-export default CountdownText
diff --git a/src/CycleCountdown.stories.tsx b/src/CycleCountdown.stories.tsx
new file mode 100644
index 0000000..b2f034c
--- /dev/null
+++ b/src/CycleCountdown.stories.tsx
@@ -0,0 +1,107 @@
+import React, { useMemo, useState } from 'react'
+import CycleCountdown from './CycleCountdown'
+
+export const Default = () => {
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 10)
+
+ return
+}
+
+export const Static = () => {
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 10)
+
+ return
+}
+
+export const Small = () => {
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 10)
+
+ return (
+
+ )
+}
+
+export const Formatter = () => {
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 10)
+
+ return (
+ {
+ return `${value} s`
+ }}
+ />
+ )
+}
+
+export const Expiration = () => {
+ const [expired, setExpired] = useState(false)
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 10)
+
+ return (
+
+ {
+ setExpired(true)
+ }}
+ />
+ onExpire function executed: {expired ? 'yes' : 'no'}
+
+ )
+}
+
+export const Updating = () => {
+ const time = useMemo(() => {
+ let t = new Date()
+ t.setSeconds(t.getSeconds() + 10)
+ return t
+ }, [])
+
+ const [number, setNumber] = useState(0)
+
+ return (
+
+ This countdown uses the onUpdate function to update a state value. Note
+ that we need to use a useMemo hook for the memoization of the time
+ variable, otherwise the countdown will be re-rendered every second. Also,
+ the time will not be updated on expiration and will therefore stay at 1 in
+ the end.
+ {
+ setNumber(value)
+ }}
+ />
+ Updated value: {number}
+
+ )
+}
+
+export const Styled = () => {
+ let time = new Date()
+ time.setSeconds(time.getSeconds() + 10)
+
+ return (
+
+ )
+}
diff --git a/src/CycleCountdown.tsx b/src/CycleCountdown.tsx
new file mode 100644
index 0000000..e3fcd98
--- /dev/null
+++ b/src/CycleCountdown.tsx
@@ -0,0 +1,90 @@
+import dayjs from 'dayjs'
+import React, { useState } from 'react'
+import Countdown from './Countdown'
+import CycleProgress from './CycleProgress'
+
+export interface CycleCountdownProps {
+ expiresAt: Date
+ totalDuration: number
+ size?: 'sm' | 'md'
+ color?: string
+ strokeWidthRem?: number
+ isStatic?: boolean
+ formatter?: (value: any) => any
+ onExpire?: () => void
+ onUpdate?: (timeRemaining: number) => void
+ data?: {
+ cy?: string
+ test?: string
+ }
+ className?: {
+ root?: string
+ countdownWrapper?: string
+ countdown?: string
+ }
+}
+
+/**
+ * This function combines the CycleProgress and Countdown components to create a circular progress bar with a countdown in the middle
+ *
+ * @param expiresAt - Date when the countdown should expire
+ * @param totalDuration - Total duration of the countdown in seconds, which is needed to compute the progress in percent
+ * @param size - Size of the progress bar, can be 'sm' or 'md'
+ * @param color - Color of the progress bar (static for the moment)
+ * @param strokeWidthRem - Width of the progress bar. For small size, a smaller value is recommended
+ * @param isStatic - If true, the countdown will not be running, but instead show the initial value. However, as the end value is given by a date, reloading can modify the displayed countdown value
+ * @param formatter - Function to format the countdown value
+ * @param onExpire - Function that is executed when the countdown expires
+ * @param onUpdate - Function that is executed when the countdown is updated (not when it expires)
+ * @param data - Optional data object that can be used for testing (e.g. data-test or data-cy)
+ * @param className - Optional className object allows you to override the default styling
+ * @returns A circular progress bar with a countdown in the middle
+ */
+export function CycleCountdown({
+ expiresAt,
+ totalDuration,
+ size = 'md',
+ color = '#00A321',
+ strokeWidthRem = 0.35,
+ isStatic,
+ formatter,
+ onExpire,
+ onUpdate,
+ data,
+ className,
+}: CycleCountdownProps) {
+ const [percentage, setPercentage] = useState(
+ (dayjs(expiresAt).diff(dayjs(), 'second') / totalDuration) * 100
+ )
+
+ return (
+
+ {
+ onExpire?.()
+ setPercentage(0)
+ }}
+ onUpdate={(remainingSeconds) => {
+ onUpdate?.(remainingSeconds)
+ setPercentage((remainingSeconds / totalDuration) * 100)
+ }}
+ className={{ root: className?.countdown }}
+ />
+
+ )
+}
+
+export default CycleCountdown
diff --git a/src/index.ts b/src/index.ts
index 61691af..a810d33 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -5,7 +5,7 @@ export * from './Checkbox'
export * from './Collapsible'
export * from './ColorPicker'
export * from './Countdown'
-export * from './CountdownNew'
+export * from './CycleCountdown'
export * from './CycleProgress'
export * from './DateChanger'
export * from './Dropdown'