@wandelbots/wandelbots-js-react-components 2.34.1-pr.feature-robot-precondition-list.372.d8a5663 → 2.34.1-pr.fix-kr30-glb-model.375.6739762
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/CycleTimer.d.ts +16 -33
- package/dist/components/CycleTimer.d.ts.map +1 -1
- package/dist/components/ProgramControl.d.ts +2 -8
- package/dist/components/ProgramControl.d.ts.map +1 -1
- package/dist/components/ProgramStateIndicator.d.ts +1 -1
- package/dist/components/ProgramStateIndicator.d.ts.map +1 -1
- package/dist/components/robots/Robot.d.ts +2 -3
- package/dist/components/robots/Robot.d.ts.map +1 -1
- package/dist/icons/index.d.ts +0 -1
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/index.cjs +49 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7512 -9338
- package/dist/index.js.map +1 -1
- package/dist/themes/createDarkTheme.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/components/CycleTimer.tsx +148 -277
- package/src/components/ProgramControl.tsx +12 -27
- package/src/components/ProgramStateIndicator.tsx +8 -25
- package/src/components/robots/Robot.tsx +2 -5
- package/src/i18n/locales/de/translations.json +1 -7
- package/src/i18n/locales/en/translations.json +1 -7
- package/src/icons/index.ts +0 -1
- package/src/index.ts +0 -14
- package/src/themes/createDarkTheme.ts +1 -75
- package/dist/components/AppHeader.d.ts +0 -34
- package/dist/components/AppHeader.d.ts.map +0 -1
- package/dist/components/DataGrid.d.ts +0 -66
- package/dist/components/DataGrid.d.ts.map +0 -1
- package/dist/components/LogPanel.d.ts +0 -38
- package/dist/components/LogPanel.d.ts.map +0 -1
- package/dist/components/LogStore.d.ts +0 -12
- package/dist/components/LogStore.d.ts.map +0 -1
- package/dist/components/LogViewer.d.ts +0 -46
- package/dist/components/LogViewer.d.ts.map +0 -1
- package/dist/components/RobotCard.d.ts +0 -103
- package/dist/components/RobotCard.d.ts.map +0 -1
- package/dist/components/RobotListItem.d.ts +0 -34
- package/dist/components/RobotListItem.d.ts.map +0 -1
- package/dist/components/RobotSetupReadinessIndicator.d.ts +0 -31
- package/dist/components/RobotSetupReadinessIndicator.d.ts.map +0 -1
- package/dist/components/RobotSetupReadinessIndicator.test.d.ts +0 -2
- package/dist/components/RobotSetupReadinessIndicator.test.d.ts.map +0 -1
- package/dist/components/TabBar.d.ts +0 -30
- package/dist/components/TabBar.d.ts.map +0 -1
- package/dist/components/robots/manufacturerHomePositions.d.ts +0 -21
- package/dist/components/robots/manufacturerHomePositions.d.ts.map +0 -1
- package/dist/icons/DropdownArrowIcon.d.ts +0 -3
- package/dist/icons/DropdownArrowIcon.d.ts.map +0 -1
- package/src/components/AppHeader.md +0 -84
- package/src/components/AppHeader.tsx +0 -199
- package/src/components/DataGrid.tsx +0 -659
- package/src/components/LogPanel.tsx +0 -69
- package/src/components/LogStore.ts +0 -44
- package/src/components/LogViewer.tsx +0 -370
- package/src/components/RobotCard.tsx +0 -596
- package/src/components/RobotListItem.tsx +0 -150
- package/src/components/RobotSetupReadinessIndicator.test.tsx +0 -60
- package/src/components/RobotSetupReadinessIndicator.tsx +0 -124
- package/src/components/TabBar.tsx +0 -144
- package/src/components/robots/manufacturerHomePositions.ts +0 -76
- package/src/icons/DropdownArrowIcon.tsx +0 -13
- package/src/icons/chevronDown.svg +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createDarkTheme.d.ts","sourceRoot":"","sources":["../../src/themes/createDarkTheme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,eAAe,CAAA;AAEjE,wBAAgB,eAAe,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"createDarkTheme.d.ts","sourceRoot":"","sources":["../../src/themes/createDarkTheme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,KAAK,EAAE,MAAM,eAAe,CAAA;AAEjE,wBAAgB,eAAe,IAAI,KAAK,CAgSvC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wandelbots/wandelbots-js-react-components",
|
|
3
|
-
"version": "2.34.1-pr.
|
|
3
|
+
"version": "2.34.1-pr.fix-kr30-glb-model.375.6739762",
|
|
4
4
|
"description": "React UI toolkit for building applications on top of the Wandelbots platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -139,7 +139,6 @@
|
|
|
139
139
|
"dependencies": {
|
|
140
140
|
"@monaco-editor/react": "^4.7.0",
|
|
141
141
|
"@mui/x-charts": "^8.9.0",
|
|
142
|
-
"@mui/x-data-grid": "^8.10.1",
|
|
143
142
|
"@shikijs/monaco": "^3.1.0",
|
|
144
143
|
"i18next-browser-languagedetector": "^8.0.4",
|
|
145
144
|
"lodash-es": "^4.17.21",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box,
|
|
1
|
+
import { Box, Typography, useTheme } from "@mui/material"
|
|
2
2
|
import { Gauge } from "@mui/x-charts/Gauge"
|
|
3
3
|
import { observer } from "mobx-react-lite"
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
@@ -9,13 +9,13 @@ import { useInterpolation } from "./utils/interpolation"
|
|
|
9
9
|
export interface CycleTimerProps {
|
|
10
10
|
/**
|
|
11
11
|
* Callback that receives the timer control functions:
|
|
12
|
-
* - `startNewCycle(maxTimeSeconds
|
|
12
|
+
* - `startNewCycle(maxTimeSeconds, elapsedSeconds?)` - Start a new timer cycle
|
|
13
13
|
* - `pause()` - Pause the countdown while preserving remaining time
|
|
14
14
|
* - `resume()` - Resume countdown from where it was paused
|
|
15
15
|
* - `isPaused()` - Check current pause state
|
|
16
16
|
*/
|
|
17
17
|
onCycleComplete: (controls: {
|
|
18
|
-
startNewCycle: (maxTimeSeconds
|
|
18
|
+
startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
|
|
19
19
|
pause: () => void
|
|
20
20
|
resume: () => void
|
|
21
21
|
isPaused: () => boolean
|
|
@@ -30,23 +30,18 @@ export interface CycleTimerProps {
|
|
|
30
30
|
compact?: boolean
|
|
31
31
|
/** Additional CSS classes */
|
|
32
32
|
className?: string
|
|
33
|
-
/** Whether the timer is in an error state (pauses timer and shows error styling) */
|
|
34
|
-
hasError?: boolean
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
/**
|
|
38
|
-
* A circular gauge timer component that shows the remaining time of a cycle
|
|
36
|
+
* A circular gauge timer component that shows the remaining time of a cycle
|
|
39
37
|
*
|
|
40
38
|
* Features:
|
|
41
39
|
* - Circular gauge with 264px diameter and 40px thickness
|
|
42
|
-
* -
|
|
43
|
-
* -
|
|
44
|
-
* -
|
|
45
|
-
* - Displays appropriate labels based on mode
|
|
46
|
-
* - Automatically counts down/up and triggers callback when reaching zero (count-down only)
|
|
40
|
+
* - Shows remaining time prominently in the center (60px font)
|
|
41
|
+
* - Displays "remaining time" label at top and total time at bottom
|
|
42
|
+
* - Automatically counts down and triggers callback when reaching zero
|
|
47
43
|
* - Full timer control: start, pause, resume functionality
|
|
48
44
|
* - Support for starting with elapsed time (resume mid-cycle)
|
|
49
|
-
* - Error state support: pauses timer and shows error styling (red color)
|
|
50
45
|
* - Smooth spring-based progress animations for all state transitions
|
|
51
46
|
* - Fully localized with i18next
|
|
52
47
|
* - Material-UI theming integration
|
|
@@ -58,44 +53,32 @@ export interface CycleTimerProps {
|
|
|
58
53
|
* @param variant - Visual variant: "default" (large gauge) or "small" (animated icon with text)
|
|
59
54
|
* @param compact - For small variant: whether to hide remaining time details
|
|
60
55
|
* @param className - Additional CSS classes
|
|
61
|
-
* @param hasError - Whether the timer is in an error state (pauses timer and shows error styling)
|
|
62
56
|
*
|
|
63
57
|
* Usage:
|
|
64
58
|
* ```tsx
|
|
65
|
-
* // Count-down timer (with max time)
|
|
66
59
|
* <CycleTimer
|
|
67
60
|
* onCycleComplete={(controls) => {
|
|
68
|
-
* // Start a 5-minute
|
|
61
|
+
* // Start a 5-minute cycle
|
|
69
62
|
* controls.startNewCycle(300)
|
|
70
63
|
*
|
|
71
64
|
* // Or start a 5-minute cycle with 2 minutes already elapsed
|
|
72
65
|
* controls.startNewCycle(300, 120)
|
|
73
|
-
* }}
|
|
74
|
-
* onCycleEnd={() => console.log('Cycle completed!')}
|
|
75
|
-
* />
|
|
76
66
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* onCycleComplete={(controls) => {
|
|
80
|
-
* // Start count-up timer
|
|
81
|
-
* controls.startNewCycle()
|
|
67
|
+
* // Pause the timer
|
|
68
|
+
* controls.pause()
|
|
82
69
|
*
|
|
83
|
-
* //
|
|
84
|
-
* controls.
|
|
85
|
-
* }}
|
|
86
|
-
* />
|
|
70
|
+
* // Resume the timer
|
|
71
|
+
* controls.resume()
|
|
87
72
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* hasError={errorCondition}
|
|
91
|
-
* onCycleComplete={(controls) => {
|
|
92
|
-
* controls.startNewCycle(300)
|
|
73
|
+
* // Check if paused
|
|
74
|
+
* const paused = controls.isPaused()
|
|
93
75
|
* }}
|
|
76
|
+
* onCycleEnd={() => console.log('Cycle completed!')}
|
|
94
77
|
* />
|
|
95
78
|
* ```
|
|
96
79
|
*
|
|
97
80
|
* Control Functions:
|
|
98
|
-
* - `startNewCycle(maxTimeSeconds
|
|
81
|
+
* - `startNewCycle(maxTimeSeconds, elapsedSeconds?)` - Start a new timer cycle
|
|
99
82
|
* - `pause()` - Pause the countdown while preserving remaining time
|
|
100
83
|
* - `resume()` - Resume countdown from where it was paused
|
|
101
84
|
* - `isPaused()` - Check current pause state
|
|
@@ -109,23 +92,17 @@ export const CycleTimer = externalizeComponent(
|
|
|
109
92
|
variant = "default",
|
|
110
93
|
compact = false,
|
|
111
94
|
className,
|
|
112
|
-
hasError = false,
|
|
113
95
|
}: CycleTimerProps) => {
|
|
114
96
|
const theme = useTheme()
|
|
115
97
|
const { t } = useTranslation()
|
|
116
98
|
const [remainingTime, setRemainingTime] = useState(0)
|
|
117
|
-
const [maxTime, setMaxTime] = useState
|
|
99
|
+
const [maxTime, setMaxTime] = useState(0)
|
|
118
100
|
const [isRunning, setIsRunning] = useState(false)
|
|
119
101
|
const [isPausedState, setIsPausedState] = useState(false)
|
|
120
102
|
const [currentProgress, setCurrentProgress] = useState(0)
|
|
121
103
|
const animationRef = useRef<number | null>(null)
|
|
122
104
|
const startTimeRef = useRef<number | null>(null)
|
|
123
105
|
const pausedTimeRef = useRef<number>(0)
|
|
124
|
-
const [wasRunningBeforeError, setWasRunningBeforeError] = useState(false)
|
|
125
|
-
|
|
126
|
-
// Track mode changes for fade transitions
|
|
127
|
-
const [showLabels, setShowLabels] = useState(true)
|
|
128
|
-
const prevMaxTimeRef = useRef<number | null | undefined>(undefined)
|
|
129
106
|
|
|
130
107
|
// Spring-based interpolator for smooth gauge progress animations
|
|
131
108
|
// Uses physics simulation to create natural, smooth transitions between progress values
|
|
@@ -137,78 +114,37 @@ export const CycleTimer = externalizeComponent(
|
|
|
137
114
|
},
|
|
138
115
|
})
|
|
139
116
|
|
|
140
|
-
// Handle mode changes with fade transitions for labels only
|
|
141
|
-
useEffect(() => {
|
|
142
|
-
const currentIsCountUp = maxTime === null
|
|
143
|
-
const prevMaxTime = prevMaxTimeRef.current
|
|
144
|
-
const prevIsCountUp = prevMaxTime === null
|
|
145
|
-
|
|
146
|
-
// Check if mode actually changed (not just first render)
|
|
147
|
-
if (
|
|
148
|
-
prevMaxTimeRef.current !== undefined &&
|
|
149
|
-
prevIsCountUp !== currentIsCountUp
|
|
150
|
-
) {
|
|
151
|
-
// Mode changed - labels will fade based on the Fade component conditions
|
|
152
|
-
// We just need to ensure showLabels is true so Fade can control visibility
|
|
153
|
-
setShowLabels(true)
|
|
154
|
-
} else {
|
|
155
|
-
// No mode change or first time - set initial state
|
|
156
|
-
setShowLabels(true)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
prevMaxTimeRef.current = maxTime
|
|
160
|
-
}, [maxTime])
|
|
161
|
-
|
|
162
117
|
const startNewCycle = useCallback(
|
|
163
|
-
(maxTimeSeconds
|
|
164
|
-
setMaxTime(maxTimeSeconds
|
|
118
|
+
(maxTimeSeconds: number, elapsedSeconds: number = 0) => {
|
|
119
|
+
setMaxTime(maxTimeSeconds)
|
|
120
|
+
const remainingSeconds = Math.max(0, maxTimeSeconds - elapsedSeconds)
|
|
121
|
+
setRemainingTime(remainingSeconds)
|
|
165
122
|
setIsPausedState(false)
|
|
166
123
|
pausedTimeRef.current = 0
|
|
167
124
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// Animate progress smoothly to starting position
|
|
177
|
-
const initialProgress =
|
|
178
|
-
elapsedSeconds > 0 ? (elapsedSeconds / maxTimeSeconds) * 100 : 0
|
|
179
|
-
if (elapsedSeconds === 0) {
|
|
180
|
-
progressInterpolator.setTarget([0])
|
|
181
|
-
} else {
|
|
182
|
-
progressInterpolator.setTarget([initialProgress])
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (remainingSeconds === 0) {
|
|
186
|
-
setIsRunning(false)
|
|
187
|
-
startTimeRef.current = null
|
|
188
|
-
// Trigger completion callback immediately if time is already up
|
|
189
|
-
if (onCycleEnd) {
|
|
190
|
-
setTimeout(() => onCycleEnd(), 0)
|
|
191
|
-
}
|
|
192
|
-
} else if (autoStart) {
|
|
193
|
-
startTimeRef.current = Date.now() - elapsedSeconds * 1000
|
|
194
|
-
setIsRunning(true)
|
|
195
|
-
} else {
|
|
196
|
-
startTimeRef.current = null
|
|
197
|
-
}
|
|
125
|
+
// Animate progress smoothly to starting position
|
|
126
|
+
// For new cycles (no elapsed time), animate from current position to 0%
|
|
127
|
+
// For resumed cycles, animate to the appropriate progress percentage
|
|
128
|
+
const initialProgress =
|
|
129
|
+
elapsedSeconds > 0 ? (elapsedSeconds / maxTimeSeconds) * 100 : 0
|
|
130
|
+
if (elapsedSeconds === 0) {
|
|
131
|
+
progressInterpolator.setTarget([0])
|
|
198
132
|
} else {
|
|
199
|
-
// Count-up mode: start from elapsed time
|
|
200
|
-
setRemainingTime(elapsedSeconds)
|
|
201
|
-
|
|
202
|
-
// For count-up mode, progress is based on minute steps
|
|
203
|
-
const initialProgress = ((elapsedSeconds / 60) % 1) * 100
|
|
204
133
|
progressInterpolator.setTarget([initialProgress])
|
|
134
|
+
}
|
|
205
135
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
136
|
+
if (remainingSeconds === 0) {
|
|
137
|
+
setIsRunning(false)
|
|
138
|
+
startTimeRef.current = null
|
|
139
|
+
// Trigger completion callback immediately if time is already up
|
|
140
|
+
if (onCycleEnd) {
|
|
141
|
+
setTimeout(() => onCycleEnd(), 0)
|
|
211
142
|
}
|
|
143
|
+
} else if (autoStart) {
|
|
144
|
+
startTimeRef.current = Date.now() - elapsedSeconds * 1000
|
|
145
|
+
setIsRunning(true)
|
|
146
|
+
} else {
|
|
147
|
+
startTimeRef.current = null
|
|
212
148
|
}
|
|
213
149
|
},
|
|
214
150
|
[autoStart, onCycleEnd, progressInterpolator],
|
|
@@ -223,16 +159,8 @@ export const CycleTimer = externalizeComponent(
|
|
|
223
159
|
// Calculate exact progress position and smoothly animate to it when pausing
|
|
224
160
|
// This ensures the visual progress matches the actual elapsed time
|
|
225
161
|
const totalElapsed = pausedTimeRef.current / 1000
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// Count-down mode
|
|
229
|
-
const exactProgress = Math.min(100, (totalElapsed / maxTime) * 100)
|
|
230
|
-
progressInterpolator.setTarget([exactProgress])
|
|
231
|
-
} else {
|
|
232
|
-
// Count-up mode: progress based on minute steps
|
|
233
|
-
const exactProgress = ((totalElapsed / 60) % 1) * 100
|
|
234
|
-
progressInterpolator.setTarget([exactProgress])
|
|
235
|
-
}
|
|
162
|
+
const exactProgress = Math.min(100, (totalElapsed / maxTime) * 100)
|
|
163
|
+
progressInterpolator.setTarget([exactProgress])
|
|
236
164
|
}
|
|
237
165
|
setIsRunning(false)
|
|
238
166
|
setIsPausedState(true)
|
|
@@ -250,30 +178,6 @@ export const CycleTimer = externalizeComponent(
|
|
|
250
178
|
return isPausedState
|
|
251
179
|
}, [isPausedState])
|
|
252
180
|
|
|
253
|
-
// Handle error state changes
|
|
254
|
-
useEffect(() => {
|
|
255
|
-
if (hasError) {
|
|
256
|
-
// Error occurred - pause timer if running and remember state
|
|
257
|
-
if (isRunning && !isPausedState) {
|
|
258
|
-
setWasRunningBeforeError(true)
|
|
259
|
-
pause()
|
|
260
|
-
}
|
|
261
|
-
} else {
|
|
262
|
-
// Error resolved - resume if was running before error
|
|
263
|
-
if (wasRunningBeforeError && isPausedState) {
|
|
264
|
-
setWasRunningBeforeError(false)
|
|
265
|
-
resume()
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}, [
|
|
269
|
-
hasError,
|
|
270
|
-
isRunning,
|
|
271
|
-
isPausedState,
|
|
272
|
-
wasRunningBeforeError,
|
|
273
|
-
pause,
|
|
274
|
-
resume,
|
|
275
|
-
])
|
|
276
|
-
|
|
277
181
|
// Call onCycleComplete immediately to provide the timer control functions
|
|
278
182
|
useEffect(() => {
|
|
279
183
|
let isMounted = true
|
|
@@ -298,39 +202,30 @@ export const CycleTimer = externalizeComponent(
|
|
|
298
202
|
if (isRunning) {
|
|
299
203
|
// Single animation frame loop that handles both time updates and progress
|
|
300
204
|
const updateTimer = () => {
|
|
301
|
-
if (startTimeRef.current) {
|
|
205
|
+
if (startTimeRef.current && maxTime > 0) {
|
|
302
206
|
const now = Date.now()
|
|
303
207
|
const elapsed =
|
|
304
208
|
(now - startTimeRef.current + pausedTimeRef.current) / 1000
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
setTimeout(() => onCycleEnd(), 0)
|
|
324
|
-
}
|
|
325
|
-
return
|
|
209
|
+
const remaining = Math.max(0, maxTime - elapsed)
|
|
210
|
+
|
|
211
|
+
// Update remaining time based on timestamp calculation
|
|
212
|
+
setRemainingTime(Math.ceil(remaining))
|
|
213
|
+
|
|
214
|
+
// Smoothly animate progress based on elapsed time for fluid visual feedback
|
|
215
|
+
const progress = Math.min(100, (elapsed / maxTime) * 100)
|
|
216
|
+
progressInterpolator.setTarget([progress])
|
|
217
|
+
|
|
218
|
+
if (remaining <= 0) {
|
|
219
|
+
setIsRunning(false)
|
|
220
|
+
startTimeRef.current = null
|
|
221
|
+
setRemainingTime(0)
|
|
222
|
+
// Animate to 100% completion with smooth spring transition
|
|
223
|
+
progressInterpolator.setTarget([100])
|
|
224
|
+
// Call onCycleEnd when timer reaches zero to notify about completion
|
|
225
|
+
if (onCycleEnd) {
|
|
226
|
+
setTimeout(() => onCycleEnd(), 0)
|
|
326
227
|
}
|
|
327
|
-
|
|
328
|
-
// Count-up mode
|
|
329
|
-
setRemainingTime(Math.floor(elapsed))
|
|
330
|
-
|
|
331
|
-
// For count-up mode, progress completes every minute (0-100% per minute)
|
|
332
|
-
const progress = ((elapsed / 60) % 1) * 100
|
|
333
|
-
progressInterpolator.setTarget([progress])
|
|
228
|
+
return
|
|
334
229
|
}
|
|
335
230
|
|
|
336
231
|
// Continue animation loop while running
|
|
@@ -377,16 +272,9 @@ export const CycleTimer = externalizeComponent(
|
|
|
377
272
|
// Keep interpolator synchronized with static progress when timer is stopped
|
|
378
273
|
// Ensures correct visual state when component initializes or timer stops
|
|
379
274
|
useEffect(() => {
|
|
380
|
-
if (!isRunning && !isPausedState) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const staticProgress = ((maxTime - remainingTime) / maxTime) * 100
|
|
384
|
-
progressInterpolator.setTarget([staticProgress])
|
|
385
|
-
} else if (maxTime === null) {
|
|
386
|
-
// Count-up mode
|
|
387
|
-
const staticProgress = ((remainingTime / 60) % 1) * 100
|
|
388
|
-
progressInterpolator.setTarget([staticProgress])
|
|
389
|
-
}
|
|
275
|
+
if (!isRunning && !isPausedState && maxTime > 0) {
|
|
276
|
+
const staticProgress = ((maxTime - remainingTime) / maxTime) * 100
|
|
277
|
+
progressInterpolator.setTarget([staticProgress])
|
|
390
278
|
}
|
|
391
279
|
}, [
|
|
392
280
|
isRunning,
|
|
@@ -413,64 +301,77 @@ export const CycleTimer = externalizeComponent(
|
|
|
413
301
|
sx={{
|
|
414
302
|
display: "flex",
|
|
415
303
|
alignItems: "center",
|
|
416
|
-
|
|
417
|
-
gap: 1, // 8px gap between circle and text
|
|
304
|
+
gap: 0.125, // Minimal gap - 1px
|
|
418
305
|
}}
|
|
419
306
|
>
|
|
420
|
-
{/* Animated progress
|
|
307
|
+
{/* Animated progress gauge icon */}
|
|
421
308
|
<Box
|
|
422
309
|
sx={{
|
|
423
|
-
|
|
424
|
-
|
|
310
|
+
position: "relative",
|
|
311
|
+
width: 40,
|
|
312
|
+
height: 40,
|
|
425
313
|
display: "flex",
|
|
426
314
|
alignItems: "center",
|
|
427
315
|
justifyContent: "center",
|
|
428
|
-
|
|
429
|
-
|
|
316
|
+
borderRadius: "50%",
|
|
317
|
+
overflow: "visible",
|
|
430
318
|
}}
|
|
431
319
|
>
|
|
432
|
-
<
|
|
433
|
-
width=
|
|
434
|
-
height=
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
{
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
320
|
+
<Gauge
|
|
321
|
+
width={40}
|
|
322
|
+
height={40}
|
|
323
|
+
value={progressValue}
|
|
324
|
+
valueMin={0}
|
|
325
|
+
valueMax={100}
|
|
326
|
+
innerRadius="70%"
|
|
327
|
+
outerRadius="95%"
|
|
328
|
+
skipAnimation={true}
|
|
329
|
+
sx={{
|
|
330
|
+
opacity: isPausedState ? 0.6 : 1,
|
|
331
|
+
transition: "opacity 0.2s ease",
|
|
332
|
+
[`& .MuiGauge-valueArc`]: {
|
|
333
|
+
fill: theme.palette.success.main,
|
|
334
|
+
},
|
|
335
|
+
[`& .MuiGauge-referenceArc`]: {
|
|
336
|
+
fill: theme.palette.success.main,
|
|
337
|
+
opacity: 0.3,
|
|
338
|
+
},
|
|
339
|
+
[`& .MuiGauge-valueText`]: {
|
|
340
|
+
display: "none",
|
|
341
|
+
},
|
|
342
|
+
[`& .MuiGauge-text`]: {
|
|
343
|
+
display: "none",
|
|
344
|
+
},
|
|
345
|
+
[`& text`]: {
|
|
346
|
+
display: "none",
|
|
347
|
+
},
|
|
348
|
+
// Hide any inner circle elements that might flash
|
|
349
|
+
[`& .MuiGauge-referenceArcBackground`]: {
|
|
350
|
+
display: "none",
|
|
351
|
+
},
|
|
352
|
+
[`& .MuiGauge-valueArcBackground`]: {
|
|
353
|
+
display: "none",
|
|
354
|
+
},
|
|
355
|
+
[`& circle`]: {
|
|
356
|
+
display: "none",
|
|
357
|
+
},
|
|
358
|
+
}}
|
|
359
|
+
/>
|
|
360
|
+
|
|
361
|
+
{/* Inner circle overlay to prevent flashing */}
|
|
362
|
+
<Box
|
|
363
|
+
sx={{
|
|
364
|
+
position: "absolute",
|
|
365
|
+
top: "50%",
|
|
366
|
+
left: "50%",
|
|
367
|
+
transform: "translate(-50%, -50%)",
|
|
368
|
+
width: 13,
|
|
369
|
+
height: 13,
|
|
370
|
+
borderRadius: "50%",
|
|
371
|
+
backgroundColor: theme.palette.background?.paper || "white",
|
|
372
|
+
pointerEvents: "none",
|
|
373
|
+
}}
|
|
374
|
+
/>
|
|
474
375
|
</Box>
|
|
475
376
|
|
|
476
377
|
{/* Timer text display */}
|
|
@@ -481,15 +382,11 @@ export const CycleTimer = externalizeComponent(
|
|
|
481
382
|
fontSize: "14px",
|
|
482
383
|
}}
|
|
483
384
|
>
|
|
484
|
-
{
|
|
485
|
-
? //
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
: // Full mode: show "remaining / of total min." format
|
|
490
|
-
`${formatTime(remainingTime)} / ${t("CycleTimer.Time.lb", { time: formatTime(maxTime) })}`
|
|
491
|
-
: // Count-up mode: show elapsed time only
|
|
492
|
-
formatTime(remainingTime)}
|
|
385
|
+
{compact
|
|
386
|
+
? // Compact mode: show only remaining time
|
|
387
|
+
formatTime(remainingTime)
|
|
388
|
+
: // Full mode: show "remaining / of total min." format
|
|
389
|
+
`${formatTime(remainingTime)} / ${t("CycleTimer.Time.lb", { time: formatTime(maxTime) })}`}
|
|
493
390
|
</Typography>
|
|
494
391
|
</Box>
|
|
495
392
|
)
|
|
@@ -521,9 +418,7 @@ export const CycleTimer = externalizeComponent(
|
|
|
521
418
|
opacity: isPausedState ? 0.6 : 1,
|
|
522
419
|
transition: "opacity 0.2s ease",
|
|
523
420
|
[`& .MuiGauge-valueArc`]: {
|
|
524
|
-
fill:
|
|
525
|
-
? theme.palette.error.light
|
|
526
|
-
: theme.palette.success.main,
|
|
421
|
+
fill: theme.palette.success.main,
|
|
527
422
|
},
|
|
528
423
|
[`& .MuiGauge-referenceArc`]: {
|
|
529
424
|
fill: "white",
|
|
@@ -551,30 +446,19 @@ export const CycleTimer = externalizeComponent(
|
|
|
551
446
|
gap: 1,
|
|
552
447
|
}}
|
|
553
448
|
>
|
|
554
|
-
{/* "remaining time" label
|
|
555
|
-
<
|
|
449
|
+
{/* "remaining time" label */}
|
|
450
|
+
<Typography
|
|
451
|
+
variant="body2"
|
|
556
452
|
sx={{
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
alignItems: "center",
|
|
560
|
-
justifyContent: "center",
|
|
453
|
+
fontSize: "12px",
|
|
454
|
+
color: theme.palette.text.secondary,
|
|
561
455
|
marginBottom: 0.5,
|
|
562
456
|
}}
|
|
563
457
|
>
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
variant="body2"
|
|
567
|
-
sx={{
|
|
568
|
-
fontSize: "12px",
|
|
569
|
-
color: theme.palette.text.secondary,
|
|
570
|
-
}}
|
|
571
|
-
>
|
|
572
|
-
{t("CycleTimer.RemainingTime.lb")}
|
|
573
|
-
</Typography>
|
|
574
|
-
</Fade>
|
|
575
|
-
</Box>
|
|
458
|
+
{t("CycleTimer.RemainingTime.lb")}
|
|
459
|
+
</Typography>
|
|
576
460
|
|
|
577
|
-
{/* Main timer display
|
|
461
|
+
{/* Main timer display */}
|
|
578
462
|
<Typography
|
|
579
463
|
variant="h1"
|
|
580
464
|
sx={{
|
|
@@ -588,29 +472,16 @@ export const CycleTimer = externalizeComponent(
|
|
|
588
472
|
{formatTime(remainingTime)}
|
|
589
473
|
</Typography>
|
|
590
474
|
|
|
591
|
-
{/* Total time display
|
|
592
|
-
<
|
|
475
|
+
{/* Total time display */}
|
|
476
|
+
<Typography
|
|
477
|
+
variant="body2"
|
|
593
478
|
sx={{
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
alignItems: "center",
|
|
597
|
-
justifyContent: "center",
|
|
479
|
+
fontSize: "12px",
|
|
480
|
+
color: theme.palette.text.secondary,
|
|
598
481
|
}}
|
|
599
482
|
>
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
variant="body2"
|
|
603
|
-
sx={{
|
|
604
|
-
fontSize: "12px",
|
|
605
|
-
color: theme.palette.text.secondary,
|
|
606
|
-
}}
|
|
607
|
-
>
|
|
608
|
-
{maxTime !== null
|
|
609
|
-
? t("CycleTimer.OfTime.lb", { time: formatTime(maxTime) })
|
|
610
|
-
: ""}
|
|
611
|
-
</Typography>
|
|
612
|
-
</Fade>
|
|
613
|
-
</Box>
|
|
483
|
+
{t("CycleTimer.OfTime.lb", { time: formatTime(maxTime) })}
|
|
484
|
+
</Typography>
|
|
614
485
|
</Box>
|
|
615
486
|
</Box>
|
|
616
487
|
)
|