@wandelbots/wandelbots-js-react-components 2.36.0 → 2.37.0-pr.feature-states-for-cycle-timer.379.b99a9af

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.
Files changed (44) hide show
  1. package/dist/components/CycleTimer/CycleTimer.d.ts +3 -0
  2. package/dist/components/CycleTimer/CycleTimer.d.ts.map +1 -0
  3. package/dist/components/CycleTimer/DefaultVariant.d.ts +10 -0
  4. package/dist/components/CycleTimer/DefaultVariant.d.ts.map +1 -0
  5. package/dist/components/CycleTimer/SmallVariant.d.ts +11 -0
  6. package/dist/components/CycleTimer/SmallVariant.d.ts.map +1 -0
  7. package/dist/components/CycleTimer/index.d.ts +28 -0
  8. package/dist/components/CycleTimer/index.d.ts.map +1 -0
  9. package/dist/components/CycleTimer/types.d.ts +51 -0
  10. package/dist/components/CycleTimer/types.d.ts.map +1 -0
  11. package/dist/components/CycleTimer/useAnimations.d.ts +15 -0
  12. package/dist/components/CycleTimer/useAnimations.d.ts.map +1 -0
  13. package/dist/components/CycleTimer/useTimerLogic.d.ts +26 -0
  14. package/dist/components/CycleTimer/useTimerLogic.d.ts.map +1 -0
  15. package/dist/components/CycleTimer/utils.d.ts +13 -0
  16. package/dist/components/CycleTimer/utils.d.ts.map +1 -0
  17. package/dist/components/CycleTimer.d.ts +2 -96
  18. package/dist/components/CycleTimer.d.ts.map +1 -1
  19. package/dist/components/TabBar.d.ts.map +1 -1
  20. package/dist/components/jogging/PoseCartesianValues.d.ts +8 -4
  21. package/dist/components/jogging/PoseCartesianValues.d.ts.map +1 -1
  22. package/dist/components/jogging/PoseJointValues.d.ts +8 -4
  23. package/dist/components/jogging/PoseJointValues.d.ts.map +1 -1
  24. package/dist/index.cjs +50 -50
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +9290 -8813
  27. package/dist/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/components/AppHeader.tsx +1 -1
  30. package/src/components/CycleTimer/CycleTimer.ts +6 -0
  31. package/src/components/CycleTimer/DefaultVariant.tsx +327 -0
  32. package/src/components/CycleTimer/SmallVariant.tsx +230 -0
  33. package/src/components/CycleTimer/index.tsx +157 -0
  34. package/src/components/CycleTimer/types.ts +60 -0
  35. package/src/components/CycleTimer/useAnimations.ts +202 -0
  36. package/src/components/CycleTimer/useTimerLogic.ts +386 -0
  37. package/src/components/CycleTimer/utils.ts +53 -0
  38. package/src/components/CycleTimer.tsx +6 -715
  39. package/src/components/ProgramControl.tsx +4 -4
  40. package/src/components/TabBar.tsx +8 -10
  41. package/src/components/jogging/PoseCartesianValues.tsx +67 -7
  42. package/src/components/jogging/PoseJointValues.tsx +68 -8
  43. package/src/i18n/locales/de/translations.json +4 -0
  44. package/src/i18n/locales/en/translations.json +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wandelbots/wandelbots-js-react-components",
3
- "version": "2.36.0",
3
+ "version": "2.37.0-pr.feature-states-for-cycle-timer.379.b99a9af",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -85,7 +85,7 @@ export const AppHeader = externalizeComponent(
85
85
  ...sx,
86
86
  }}
87
87
  >
88
- <Toolbar sx={{ minHeight: "64px !important" }}>
88
+ <Toolbar sx={{ minHeight: "62px !important" }}>
89
89
  {/* App Icon */}
90
90
  <Box sx={{ mr: 2, display: "flex", alignItems: "center" }}>
91
91
  {appIcon}
@@ -0,0 +1,6 @@
1
+ export { CycleTimer } from "./index"
2
+ export type {
3
+ CycleTimerControls,
4
+ CycleTimerProps,
5
+ CycleTimerState,
6
+ } from "./types"
@@ -0,0 +1,327 @@
1
+ import { Box, Fade, Typography, useTheme } from "@mui/material"
2
+ import { Gauge } from "@mui/x-charts/Gauge"
3
+ import { useTranslation } from "react-i18next"
4
+ import type { AnimationState, TimerState } from "./types"
5
+ import { formatTime } from "./utils"
6
+
7
+ interface DefaultVariantProps {
8
+ timerState: TimerState
9
+ animationState: AnimationState
10
+ hasError: boolean
11
+ className?: string
12
+ }
13
+
14
+ export const DefaultVariant = ({
15
+ timerState,
16
+ animationState,
17
+ hasError,
18
+ className,
19
+ }: DefaultVariantProps) => {
20
+ const { t } = useTranslation()
21
+ const theme = useTheme()
22
+ const { currentState, remainingTime, maxTime, currentProgress } = timerState
23
+ const {
24
+ showErrorAnimation,
25
+ showPauseAnimation,
26
+ showPulsatingText,
27
+ pulsatingFinished,
28
+ showLabels,
29
+ showMainText,
30
+ showIdlePulsating,
31
+ idleDotsCount,
32
+ } = animationState
33
+
34
+ return (
35
+ <Box
36
+ className={className}
37
+ sx={{
38
+ position: "relative",
39
+ width: 264,
40
+ height: 264,
41
+ display: "flex",
42
+ alignItems: "center",
43
+ justifyContent: "center",
44
+ }}
45
+ >
46
+ <Gauge
47
+ width={264}
48
+ height={264}
49
+ value={currentState === "idle" ? 0 : currentProgress}
50
+ valueMin={0}
51
+ valueMax={100}
52
+ innerRadius="85%"
53
+ outerRadius="100%"
54
+ margin={0}
55
+ skipAnimation={true}
56
+ text={() => ""}
57
+ sx={{
58
+ opacity: showPauseAnimation || showErrorAnimation ? 0.6 : 1,
59
+ transition: "opacity 0.5s ease-out",
60
+ [`& .MuiGauge-valueArc`]: {
61
+ fill: hasError
62
+ ? theme.palette.error.light
63
+ : theme.palette.success.main,
64
+ transition: "fill 0.5s ease-out",
65
+ },
66
+ [`& .MuiGauge-referenceArc`]: {
67
+ fill: currentState === "idle" ? "#171927" : "#171927",
68
+ stroke: "transparent",
69
+ strokeWidth: 0,
70
+ transition:
71
+ "fill 0.5s ease-out, stroke 0.5s ease-out, stroke-width 0.5s ease-out",
72
+ },
73
+ [`& .MuiGauge-valueText`]: {
74
+ display: "none",
75
+ },
76
+ [`& .MuiGauge-text`]: {
77
+ display: "none",
78
+ },
79
+ }}
80
+ />
81
+
82
+ {/* Center content overlay */}
83
+ <Box
84
+ sx={{
85
+ position: "absolute",
86
+ top: "50%",
87
+ left: "50%",
88
+ transform: "translate(-50%, -50%)",
89
+ borderRadius: "50%",
90
+ display: "flex",
91
+ flexDirection: "column",
92
+ alignItems: "center",
93
+ justifyContent: "center",
94
+ textAlign: "center",
95
+ gap: 1,
96
+ transition: "background-color 0.5s ease-out",
97
+ }}
98
+ >
99
+ {/* Top label */}
100
+ <Box
101
+ sx={{
102
+ height: "16px",
103
+ display: "flex",
104
+ alignItems: "center",
105
+ justifyContent: "center",
106
+ marginBottom: 1,
107
+ }}
108
+ >
109
+ <Fade
110
+ in={
111
+ showLabels &&
112
+ !hasError &&
113
+ currentState !== "idle" &&
114
+ currentState !== "countup" &&
115
+ currentState !== "success"
116
+ }
117
+ timeout={200}
118
+ >
119
+ <Typography
120
+ variant="body2"
121
+ sx={{
122
+ fontSize: "12px",
123
+ color:
124
+ currentState === "measured"
125
+ ? showPulsatingText || pulsatingFinished
126
+ ? theme.palette.success.main
127
+ : theme.palette.text.secondary
128
+ : theme.palette.text.secondary,
129
+ transition: "color 0.8s ease-in-out",
130
+ }}
131
+ >
132
+ <span
133
+ style={{
134
+ opacity:
135
+ currentState === "measured" && pulsatingFinished
136
+ ? showPulsatingText
137
+ ? 1
138
+ : 0.6
139
+ : 1,
140
+ transition: "opacity 2s ease-in-out",
141
+ }}
142
+ >
143
+ {currentState === "measuring"
144
+ ? t("CycleTimer.CycleTime.lb", "Cycle Time")
145
+ : currentState === "measured"
146
+ ? t("CycleTimer.CycleTime.lb", "Cycle Time")
147
+ : currentState === "countdown"
148
+ ? t("CycleTimer.RemainingTime.lb", "Remaining Time")
149
+ : ""}
150
+ </span>
151
+ </Typography>
152
+ </Fade>
153
+ </Box>
154
+
155
+ {/* Main display */}
156
+ <Box
157
+ sx={{
158
+ position: "relative",
159
+ height: "48px",
160
+ display: "flex",
161
+ alignItems: "center",
162
+ justifyContent: "center",
163
+ marginBottom: 0.5,
164
+ }}
165
+ >
166
+ {/* Idle state text */}
167
+ <Fade
168
+ in={showMainText && currentState === "idle" && !hasError}
169
+ timeout={200}
170
+ >
171
+ <Typography
172
+ variant="body2"
173
+ sx={{
174
+ position: "absolute",
175
+ fontSize: "12px",
176
+ fontWeight: 400,
177
+ color: "rgba(255, 255, 255, 0.7)",
178
+ lineHeight: "166%",
179
+ letterSpacing: "0.17px",
180
+ textAlign: "center",
181
+ width: "200px",
182
+ height: "20px",
183
+ display: "flex",
184
+ alignItems: "center",
185
+ justifyContent: "center",
186
+ }}
187
+ >
188
+ <span
189
+ style={{
190
+ opacity: showIdlePulsating ? 1 : 0.6,
191
+ transition: "opacity 2s ease-in-out",
192
+ }}
193
+ >
194
+ {t(
195
+ "CycleTimer.WaitingForCycle.lb",
196
+ "Waiting for program cycle",
197
+ )}
198
+ </span>
199
+ <span
200
+ style={{
201
+ display: "inline-block",
202
+ width: "18px",
203
+ textAlign: "left",
204
+ opacity: showIdlePulsating ? 1 : 0.6,
205
+ transition: "opacity 2s ease-in-out",
206
+ }}
207
+ >
208
+ {".".repeat(idleDotsCount)}
209
+ </span>
210
+ </Typography>
211
+ </Fade>
212
+
213
+ {/* Error text */}
214
+ <Fade in={showMainText && hasError} timeout={200}>
215
+ <Typography
216
+ variant="h3"
217
+ sx={{
218
+ position: "absolute",
219
+ fontSize: "40px",
220
+ fontWeight: 400,
221
+ color: "#FFFFFF",
222
+ lineHeight: "116.7%",
223
+ }}
224
+ >
225
+ {t("CycleTimer.Error.lb", "Error")}
226
+ </Typography>
227
+ </Fade>
228
+
229
+ {/* Normal timer text */}
230
+ <Fade
231
+ in={
232
+ showMainText &&
233
+ !hasError &&
234
+ currentState !== "idle" &&
235
+ currentState !== "success"
236
+ }
237
+ timeout={200}
238
+ >
239
+ <Typography
240
+ variant="h1"
241
+ sx={{
242
+ position: "absolute",
243
+ fontSize: "48px",
244
+ fontWeight: 500,
245
+ color: theme.palette.text.primary,
246
+ lineHeight: 1,
247
+ transition: "color 0.8s ease-in-out",
248
+ }}
249
+ >
250
+ <span
251
+ style={{
252
+ opacity:
253
+ currentState === "measured" && pulsatingFinished
254
+ ? showPulsatingText
255
+ ? 1
256
+ : 0.6
257
+ : 1,
258
+ transition: "opacity 2s ease-in-out",
259
+ }}
260
+ >
261
+ {formatTime(remainingTime)}
262
+ </span>
263
+ </Typography>
264
+ </Fade>
265
+ </Box>
266
+
267
+ {/* Bottom label */}
268
+ <Box
269
+ sx={{
270
+ height: "16px",
271
+ display: "flex",
272
+ marginTop: 0.5,
273
+ alignItems: "center",
274
+ justifyContent: "center",
275
+ }}
276
+ >
277
+ <Fade
278
+ in={
279
+ showLabels &&
280
+ !hasError &&
281
+ currentState !== "idle" &&
282
+ currentState !== "countup" &&
283
+ currentState !== "success"
284
+ }
285
+ timeout={200}
286
+ >
287
+ <Typography
288
+ variant="body2"
289
+ sx={{
290
+ fontSize: "12px",
291
+ color:
292
+ currentState === "measured"
293
+ ? showPulsatingText || pulsatingFinished
294
+ ? theme.palette.success.main
295
+ : theme.palette.text.secondary
296
+ : theme.palette.text.secondary,
297
+ transition: "color 0.8s ease-in-out",
298
+ }}
299
+ >
300
+ <span
301
+ style={{
302
+ opacity:
303
+ currentState === "measured" && pulsatingFinished
304
+ ? showPulsatingText
305
+ ? 1
306
+ : 0.6
307
+ : 1,
308
+ transition: "opacity 2s ease-in-out",
309
+ }}
310
+ >
311
+ {currentState === "measuring"
312
+ ? t("CycleTimer.Measuring.lb", "measuring...")
313
+ : currentState === "measured"
314
+ ? t("CycleTimer.Determined.lb", "determined")
315
+ : currentState === "countdown" && maxTime !== null
316
+ ? t("CycleTimer.OfTime.lb", {
317
+ time: formatTime(maxTime),
318
+ })
319
+ : ""}
320
+ </span>
321
+ </Typography>
322
+ </Fade>
323
+ </Box>
324
+ </Box>
325
+ </Box>
326
+ )
327
+ }
@@ -0,0 +1,230 @@
1
+ import { Box, Typography, useTheme } from "@mui/material"
2
+ import { useTranslation } from "react-i18next"
3
+ import type { AnimationState, TimerState } from "./types"
4
+ import { formatTime } from "./utils"
5
+
6
+ interface SmallVariantProps {
7
+ timerState: TimerState
8
+ animationState: AnimationState
9
+ hasError: boolean
10
+ compact: boolean
11
+ className?: string
12
+ }
13
+
14
+ export const SmallVariant = ({
15
+ timerState,
16
+ animationState,
17
+ hasError,
18
+ compact,
19
+ className,
20
+ }: SmallVariantProps) => {
21
+ const { t } = useTranslation()
22
+ const theme = useTheme()
23
+ const { currentState, remainingTime, maxTime } = timerState
24
+ const {
25
+ showErrorAnimation,
26
+ showPauseAnimation,
27
+ showPulsatingText,
28
+ pulsatingFinished,
29
+ showIdlePulsating,
30
+ idleDotsCount,
31
+ } = animationState
32
+
33
+ // Simple text-only mode for compact variant in certain states
34
+ if (compact && (currentState === "countup" || currentState === "idle")) {
35
+ return (
36
+ <Box
37
+ className={className}
38
+ sx={{
39
+ display: "flex",
40
+ alignItems: "center",
41
+ m: 0,
42
+ }}
43
+ >
44
+ <Typography
45
+ variant="body2"
46
+ sx={{
47
+ color: hasError
48
+ ? theme.palette.error.light
49
+ : theme.palette.text.primary,
50
+ fontSize: "14px",
51
+ transition: "color 0.5s ease-out",
52
+ }}
53
+ >
54
+ {hasError
55
+ ? t("CycleTimer.Error.lb", "Error")
56
+ : currentState === "idle"
57
+ ? "0:00"
58
+ : formatTime(remainingTime)}
59
+ </Typography>
60
+ </Box>
61
+ )
62
+ }
63
+
64
+ return (
65
+ <Box
66
+ className={className}
67
+ sx={{
68
+ display: "flex",
69
+ alignItems: "center",
70
+ m: 0,
71
+ gap: 1,
72
+ }}
73
+ >
74
+ {/* Animated progress ring icon */}
75
+ {!(
76
+ currentState === "countup" ||
77
+ (currentState === "idle" && compact)
78
+ ) && (
79
+ <Box
80
+ sx={{
81
+ width: 20,
82
+ height: 20,
83
+ display: "flex",
84
+ alignItems: "center",
85
+ justifyContent: "center",
86
+ opacity: showPauseAnimation || showErrorAnimation ? 0.6 : 1,
87
+ transition: "opacity 0.5s ease-out",
88
+ }}
89
+ >
90
+ <svg
91
+ width="20"
92
+ height="20"
93
+ viewBox="0 0 20 20"
94
+ style={{ transform: "rotate(-90deg)" }}
95
+ role="img"
96
+ aria-label="Timer progress"
97
+ >
98
+ {/* Background ring */}
99
+ <circle
100
+ cx="10"
101
+ cy="10"
102
+ r="8"
103
+ fill="none"
104
+ stroke={
105
+ hasError
106
+ ? theme.palette.error.light
107
+ : currentState === "measured"
108
+ ? showPulsatingText || pulsatingFinished
109
+ ? theme.palette.success.main
110
+ : theme.palette.text.secondary
111
+ : theme.palette.success.main
112
+ }
113
+ strokeWidth="2"
114
+ opacity={
115
+ currentState === "measured" && pulsatingFinished
116
+ ? showPulsatingText
117
+ ? 1
118
+ : 0.6
119
+ : 0.3
120
+ }
121
+ style={{
122
+ transition: "stroke 0.8s ease-in-out, opacity 2s ease-in-out",
123
+ }}
124
+ />
125
+ {/* Progress ring */}
126
+ <circle
127
+ cx="10"
128
+ cy="10"
129
+ r="8"
130
+ fill="none"
131
+ stroke={
132
+ hasError
133
+ ? theme.palette.error.light
134
+ : currentState === "measured"
135
+ ? showPulsatingText || pulsatingFinished
136
+ ? theme.palette.success.main
137
+ : theme.palette.text.secondary
138
+ : theme.palette.success.main
139
+ }
140
+ strokeWidth="2"
141
+ strokeLinecap="round"
142
+ strokeDasharray={`${2 * Math.PI * 8}`}
143
+ strokeDashoffset={`${2 * Math.PI * 8 * (1 - (currentState === "idle" ? 0 : timerState.currentProgress) / 100)}`}
144
+ style={{
145
+ opacity:
146
+ currentState === "measured" && pulsatingFinished
147
+ ? showPulsatingText
148
+ ? 1
149
+ : 0.6
150
+ : 1,
151
+ transition:
152
+ "stroke-dashoffset 0.1s ease-out, stroke 0.8s ease-in-out, opacity 2s ease-in-out",
153
+ }}
154
+ />
155
+ </svg>
156
+ </Box>
157
+ )}
158
+
159
+ {/* Timer text display */}
160
+ <Typography
161
+ variant="body2"
162
+ sx={{
163
+ color: hasError
164
+ ? theme.palette.error.light
165
+ : currentState === "idle"
166
+ ? "rgba(255, 255, 255, 0.7)"
167
+ : currentState === "measured"
168
+ ? showPulsatingText || pulsatingFinished
169
+ ? theme.palette.success.main
170
+ : theme.palette.text.secondary
171
+ : theme.palette.text.primary,
172
+ fontSize: "14px",
173
+ lineHeight: "normal",
174
+ letterSpacing: "normal",
175
+ opacity:
176
+ currentState === "idle"
177
+ ? showIdlePulsating
178
+ ? 1
179
+ : 0.6
180
+ : currentState === "measured" && pulsatingFinished
181
+ ? showPulsatingText
182
+ ? 1
183
+ : 0.6
184
+ : 1,
185
+ transition:
186
+ "color 0.8s ease-in-out, font-size 0.3s ease-out, opacity 2s ease-in-out",
187
+ }}
188
+ >
189
+ {hasError ? (
190
+ t("CycleTimer.Error.lb", "Error")
191
+ ) : currentState === "idle" ? (
192
+ <>
193
+ <span>
194
+ {t("CycleTimer.WaitingForCycle.lb", "Waiting for program cycle")}
195
+ </span>
196
+ <span
197
+ style={{
198
+ display: "inline-block",
199
+ width: "18px",
200
+ textAlign: "left",
201
+ }}
202
+ >
203
+ {".".repeat(idleDotsCount)}
204
+ </span>
205
+ </>
206
+ ) : currentState === "measuring" ? (
207
+ compact ? (
208
+ `${formatTime(remainingTime)} ${t("CycleTimer.Time.lb", { time: "" }).replace(/\s*$/, "")}`
209
+ ) : (
210
+ `${formatTime(remainingTime)} / ${t("CycleTimer.Measuring.lb", "measuring...")}`
211
+ )
212
+ ) : currentState === "measured" ? (
213
+ compact ? (
214
+ `${formatTime(remainingTime)} ${t("CycleTimer.Time.lb", { time: "" }).replace(/\s*$/, "")}`
215
+ ) : (
216
+ `${formatTime(remainingTime)} / ${t("CycleTimer.Determined.lb", "determined")}`
217
+ )
218
+ ) : currentState === "countdown" && maxTime !== null ? (
219
+ compact ? (
220
+ `${formatTime(remainingTime)} ${t("CycleTimer.Time.lb", { time: "" }).replace(/\s*$/, "")}`
221
+ ) : (
222
+ `${formatTime(remainingTime)} / ${t("CycleTimer.Time.lb", { time: formatTime(maxTime) })}`
223
+ )
224
+ ) : (
225
+ formatTime(remainingTime)
226
+ )}
227
+ </Typography>
228
+ </Box>
229
+ )
230
+ }