@wandelbots/wandelbots-js-react-components 2.51.0 → 2.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Timer/TimerSmallVariant.d.ts +1 -1
- package/dist/components/Timer/TimerSmallVariant.d.ts.map +1 -1
- package/dist/components/Timer/utils.d.ts +10 -1
- package/dist/components/Timer/utils.d.ts.map +1 -1
- package/dist/index.cjs +36 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1525 -1515
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CycleTimer/SmallVariant.tsx +3 -3
- package/src/components/Timer/TimerSmallVariant.tsx +11 -11
- package/src/components/Timer/utils.ts +75 -3
package/package.json
CHANGED
|
@@ -202,19 +202,19 @@ export const SmallVariant = ({
|
|
|
202
202
|
</>
|
|
203
203
|
) : currentState === "measuring" ? (
|
|
204
204
|
compact ? (
|
|
205
|
-
|
|
205
|
+
formatTimeLocalized(remainingTime, i18n.language)
|
|
206
206
|
) : (
|
|
207
207
|
`${formatTimeLocalized(remainingTime, i18n.language)} / ${t("CycleTimer.Measuring.lb", "measuring...")}`
|
|
208
208
|
)
|
|
209
209
|
) : currentState === "measured" ? (
|
|
210
210
|
compact ? (
|
|
211
|
-
|
|
211
|
+
formatTimeLocalized(remainingTime, i18n.language)
|
|
212
212
|
) : (
|
|
213
213
|
`${formatTimeLocalized(remainingTime, i18n.language)} / ${t("CycleTimer.Determined.lb", "determined")}`
|
|
214
214
|
)
|
|
215
215
|
) : currentState === "countdown" && maxTime !== null ? (
|
|
216
216
|
compact ? (
|
|
217
|
-
|
|
217
|
+
formatTimeLocalized(remainingTime, i18n.language)
|
|
218
218
|
) : (
|
|
219
219
|
`${formatTimeLocalized(remainingTime, i18n.language)} / ${t("CycleTimer.Time.lb", { time: formatTimeLocalized(maxTime, i18n.language) })}`
|
|
220
220
|
)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Box, Typography } from "@mui/material"
|
|
2
2
|
import { useTheme } from "@mui/material/styles"
|
|
3
3
|
import { useTranslation } from "react-i18next"
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
4
|
+
import type { TimerAnimationState, TimerState } from "./types"
|
|
5
|
+
import { formatTimeLocalized } from "./utils"
|
|
6
6
|
|
|
7
7
|
interface TimerSmallVariantProps {
|
|
8
8
|
timerState: TimerState
|
|
@@ -19,7 +19,7 @@ export const TimerSmallVariant = ({
|
|
|
19
19
|
compact,
|
|
20
20
|
className,
|
|
21
21
|
}: TimerSmallVariantProps) => {
|
|
22
|
-
const { t } = useTranslation()
|
|
22
|
+
const { t, i18n } = useTranslation()
|
|
23
23
|
const theme = useTheme()
|
|
24
24
|
const { elapsedTime, currentProgress } = timerState
|
|
25
25
|
const { showErrorAnimation, showPauseAnimation } = animationState
|
|
@@ -45,7 +45,9 @@ export const TimerSmallVariant = ({
|
|
|
45
45
|
transition: "color 0.5s ease-out",
|
|
46
46
|
}}
|
|
47
47
|
>
|
|
48
|
-
{hasError
|
|
48
|
+
{hasError
|
|
49
|
+
? t("timer.error")
|
|
50
|
+
: formatTimeLocalized(elapsedTime, i18n.language)}
|
|
49
51
|
</Typography>
|
|
50
52
|
</Box>
|
|
51
53
|
)
|
|
@@ -87,9 +89,7 @@ export const TimerSmallVariant = ({
|
|
|
87
89
|
r="8"
|
|
88
90
|
fill="none"
|
|
89
91
|
stroke={
|
|
90
|
-
hasError
|
|
91
|
-
? theme.palette.error.light
|
|
92
|
-
: theme.palette.success.main
|
|
92
|
+
hasError ? theme.palette.error.light : theme.palette.success.main
|
|
93
93
|
}
|
|
94
94
|
strokeWidth="2"
|
|
95
95
|
opacity={0.3}
|
|
@@ -103,9 +103,7 @@ export const TimerSmallVariant = ({
|
|
|
103
103
|
r="8"
|
|
104
104
|
fill="none"
|
|
105
105
|
stroke={
|
|
106
|
-
hasError
|
|
107
|
-
? theme.palette.error.light
|
|
108
|
-
: theme.palette.success.main
|
|
106
|
+
hasError ? theme.palette.error.light : theme.palette.success.main
|
|
109
107
|
}
|
|
110
108
|
strokeWidth="2"
|
|
111
109
|
strokeLinecap="round"
|
|
@@ -133,7 +131,9 @@ export const TimerSmallVariant = ({
|
|
|
133
131
|
"color 0.8s ease-in-out, font-size 0.3s ease-out, opacity 2s ease-in-out",
|
|
134
132
|
}}
|
|
135
133
|
>
|
|
136
|
-
{hasError
|
|
134
|
+
{hasError
|
|
135
|
+
? t("timer.error")
|
|
136
|
+
: formatTimeLocalized(elapsedTime, i18n.language)}
|
|
137
137
|
</Typography>
|
|
138
138
|
</Box>
|
|
139
139
|
)
|
|
@@ -1,10 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Formats time in seconds to MM:SS format
|
|
2
|
+
* Formats time in seconds to D:HH:MM:SS, H:MM:SS or MM:SS format
|
|
3
|
+
* Used for the default (large) timer variant
|
|
4
|
+
* Automatically includes days and hours as needed for clarity
|
|
3
5
|
*/
|
|
4
6
|
export const formatTime = (seconds: number): string => {
|
|
5
|
-
const
|
|
7
|
+
const days = Math.floor(seconds / 86400)
|
|
8
|
+
const hours = Math.floor((seconds % 86400) / 3600)
|
|
9
|
+
const minutes = Math.floor((seconds % 3600) / 60)
|
|
6
10
|
const remainingSeconds = seconds % 60
|
|
7
|
-
|
|
11
|
+
|
|
12
|
+
// Build time parts array
|
|
13
|
+
const parts: string[] = []
|
|
14
|
+
|
|
15
|
+
if (days > 0) {
|
|
16
|
+
parts.push(days.toString())
|
|
17
|
+
parts.push(hours.toString().padStart(2, "0"))
|
|
18
|
+
parts.push(minutes.toString().padStart(2, "0"))
|
|
19
|
+
parts.push(remainingSeconds.toString().padStart(2, "0"))
|
|
20
|
+
} else if (hours > 0) {
|
|
21
|
+
parts.push(hours.toString())
|
|
22
|
+
parts.push(minutes.toString().padStart(2, "0"))
|
|
23
|
+
parts.push(remainingSeconds.toString().padStart(2, "0"))
|
|
24
|
+
} else {
|
|
25
|
+
parts.push(minutes.toString())
|
|
26
|
+
parts.push(remainingSeconds.toString().padStart(2, "0"))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return parts.join(":")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Formats time in seconds to a localized human-readable format
|
|
34
|
+
* Used for the small timer variant
|
|
35
|
+
* Examples: "2h 30m 15s", "45m 30s", "30s"
|
|
36
|
+
* Falls back to English units if Intl.DurationFormat is not available
|
|
37
|
+
*/
|
|
38
|
+
export const formatTimeLocalized = (
|
|
39
|
+
seconds: number,
|
|
40
|
+
locale?: string,
|
|
41
|
+
): string => {
|
|
42
|
+
const days = Math.floor(seconds / 86400)
|
|
43
|
+
const hours = Math.floor((seconds % 86400) / 3600)
|
|
44
|
+
const minutes = Math.floor((seconds % 3600) / 60)
|
|
45
|
+
const remainingSeconds = seconds % 60
|
|
46
|
+
|
|
47
|
+
// Try using Intl.DurationFormat if available (newer browsers)
|
|
48
|
+
if (typeof Intl !== "undefined" && "DurationFormat" in Intl) {
|
|
49
|
+
try {
|
|
50
|
+
const duration: Record<string, number> = {}
|
|
51
|
+
if (days > 0) duration.days = days
|
|
52
|
+
if (hours > 0) duration.hours = hours
|
|
53
|
+
if (minutes > 0) duration.minutes = minutes
|
|
54
|
+
if (remainingSeconds > 0 || Object.keys(duration).length === 0) {
|
|
55
|
+
duration.seconds = remainingSeconds
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// @ts-expect-error
|
|
59
|
+
// TODO: Remove suppression once Intl.DurationFormat is supported in TypeScript types.
|
|
60
|
+
// See: https://github.com/microsoft/TypeScript/issues/53971
|
|
61
|
+
// DurationFormat is a proposed API and not yet available in TypeScript's standard library types.
|
|
62
|
+
const formatter = new Intl.DurationFormat(locale, { style: "narrow" })
|
|
63
|
+
return formatter.format(duration)
|
|
64
|
+
} catch {
|
|
65
|
+
// Fall through to manual formatting
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Manual formatting with compact units
|
|
70
|
+
const parts: string[] = []
|
|
71
|
+
|
|
72
|
+
if (days > 0) parts.push(`${days}d`)
|
|
73
|
+
if (hours > 0) parts.push(`${hours}h`)
|
|
74
|
+
if (minutes > 0) parts.push(`${minutes}m`)
|
|
75
|
+
if (remainingSeconds > 0 || parts.length === 0) {
|
|
76
|
+
parts.push(`${remainingSeconds}s`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return parts.join(" ")
|
|
8
80
|
}
|
|
9
81
|
|
|
10
82
|
/**
|