@wandelbots/wandelbots-js-react-components 2.38.1-pr.feature-remove-timer-from-robot-card.381.54703fe → 2.38.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wandelbots/wandelbots-js-react-components",
3
- "version": "2.38.1-pr.feature-remove-timer-from-robot-card.381.54703fe",
3
+ "version": "2.38.1",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -117,7 +117,7 @@ export const DefaultVariant = ({
117
117
  currentState !== "countup" &&
118
118
  currentState !== "success"
119
119
  }
120
- timeout={300}
120
+ timeout={200}
121
121
  >
122
122
  <Typography
123
123
  variant="body2"
@@ -237,7 +237,7 @@ export const DefaultVariant = ({
237
237
  currentState !== "idle" &&
238
238
  currentState !== "success"
239
239
  }
240
- timeout={300}
240
+ timeout={200}
241
241
  >
242
242
  <Typography
243
243
  variant="h1"
@@ -280,7 +280,7 @@ export const DefaultVariant = ({
280
280
  currentState !== "countup" &&
281
281
  currentState !== "success"
282
282
  }
283
- timeout={300}
283
+ timeout={200}
284
284
  >
285
285
  <Typography
286
286
  variant="body2"
@@ -53,10 +53,10 @@ export const useTimerLogic = ({
53
53
  ...prev,
54
54
  currentState: "idle",
55
55
  maxTime: null,
56
- // Don't reset remainingTime - keep the last value
56
+ remainingTime: 0,
57
57
  isRunning: false,
58
58
  isPausedState: false,
59
- currentProgress: 0, // Reset progress to 0 for gauge
59
+ currentProgress: 0, // Immediately reset progress to 0
60
60
  }))
61
61
  pausedTimeRef.current = 0
62
62
  startTimeRef.current = null
@@ -12,6 +12,7 @@ import { useTranslation } from "react-i18next"
12
12
  import type { Group } from "three"
13
13
  import { externalizeComponent } from "../externalizeComponent"
14
14
  import { PresetEnvironment } from "./3d-viewport/PresetEnvironment"
15
+ import { CycleTimer } from "./CycleTimer"
15
16
  import type { ProgramState } from "./ProgramControl"
16
17
  import { ProgramStateIndicator } from "./ProgramStateIndicator"
17
18
  import { Robot } from "./robots/Robot"
@@ -57,8 +58,34 @@ export interface RobotCardProps {
57
58
  transparentColor?: string
58
59
  getModel?: (modelFromController: string) => string
59
60
  }>
60
- /** Custom component to render in the content area (optional) */
61
- customContentComponent?: React.ComponentType<Record<string, unknown>>
61
+ /** Custom cycle timer component (optional, defaults to CycleTimer) */
62
+ cycleTimerComponent?: React.ComponentType<{
63
+ variant?: "default" | "small"
64
+ compact?: boolean
65
+ onCycleComplete: (controls: {
66
+ startNewCycle: (maxTimeSeconds?: number, elapsedSeconds?: number) => void
67
+ pause: () => void
68
+ resume: () => void
69
+ isPaused: () => boolean
70
+ }) => void
71
+ onCycleEnd?: () => void
72
+ autoStart?: boolean
73
+ hasError?: boolean
74
+ className?: string
75
+ }>
76
+ /** Callback to receive cycle timer controls for external timer management */
77
+ onCycleTimerReady?: (controls: {
78
+ startNewCycle: (maxTimeSeconds?: number, elapsedSeconds?: number) => void
79
+ pause: () => void
80
+ resume: () => void
81
+ isPaused: () => boolean
82
+ }) => void
83
+ /** Callback fired when a cycle completes (reaches zero) */
84
+ onCycleEnd?: () => void
85
+ /** Whether the cycle timer should auto-start when a new cycle is set */
86
+ cycleTimerAutoStart?: boolean
87
+ /** Whether the cycle timer is in an error state (pauses timer and shows error styling) */
88
+ cycleTimerHasError?: boolean
62
89
  /** Additional CSS class name */
63
90
  className?: string
64
91
  }
@@ -81,31 +108,11 @@ export interface RobotCardProps {
81
108
  * - Robot name displayed in Typography h6 at top-left
82
109
  * - Program state indicator below the name
83
110
  * - Auto-fitting 3D robot model that scales with container size
84
- * - Customizable content area for displaying custom React components
111
+ * - Compact cycle time component with small variant, error state, and count-up/count-down mode support
85
112
  * - Transparent gray divider line
86
113
  * - "Drive to Home" button with press-and-hold functionality
87
114
  * - Localization support via react-i18next
88
115
  * - Material-UI theming integration
89
- *
90
- * Usage with custom content:
91
- * ```tsx
92
- * // Example custom timer component
93
- * const CustomTimer = () => (
94
- * <Box>
95
- * <Typography variant="body1" sx={{ color: "text.secondary" }}>
96
- * Runtime
97
- * </Typography>
98
- * <Typography variant="h6">05:23</Typography>
99
- * </Box>
100
- * )
101
- *
102
- * <RobotCard
103
- * robotName="UR5e Robot"
104
- * programState={ProgramState.RUNNING}
105
- * customContentComponent={CustomTimer}
106
- * // ... other props
107
- * />
108
- * ```
109
116
  */
110
117
  export const RobotCard = externalizeComponent(
111
118
  observer(
@@ -119,7 +126,11 @@ export const RobotCard = externalizeComponent(
119
126
  onDriveToHomeRelease,
120
127
  connectedMotionGroup,
121
128
  robotComponent: RobotComponent = Robot,
122
- customContentComponent: CustomContentComponent,
129
+ cycleTimerComponent: CycleTimerComponent = CycleTimer,
130
+ onCycleTimerReady,
131
+ onCycleEnd,
132
+ cycleTimerAutoStart = true,
133
+ cycleTimerHasError = false,
123
134
  className,
124
135
  }: RobotCardProps) => {
125
136
  const theme = useTheme()
@@ -134,6 +145,17 @@ export const RobotCard = externalizeComponent(
134
145
  }>({ width: 400, height: 600 })
135
146
  const [modelRenderTrigger, setModelRenderTrigger] = useState(0)
136
147
 
148
+ // Store cycle timer controls for external control
149
+ const cycleControlsRef = useRef<{
150
+ startNewCycle: (
151
+ maxTimeSeconds?: number,
152
+ elapsedSeconds?: number,
153
+ ) => void
154
+ pause: () => void
155
+ resume: () => void
156
+ isPaused: () => boolean
157
+ } | null>(null)
158
+
137
159
  // Hook to detect aspect ratio and size changes
138
160
  useEffect(() => {
139
161
  const checkDimensions = () => {
@@ -182,16 +204,38 @@ export const RobotCard = externalizeComponent(
182
204
  }
183
205
  }, [isDriveToHomePressed, onDriveToHomeRelease])
184
206
 
207
+ // Store and provide cycle timer controls for external use
208
+ const handleCycleComplete = useCallback(
209
+ (controls: {
210
+ startNewCycle: (
211
+ maxTimeSeconds?: number,
212
+ elapsedSeconds?: number,
213
+ ) => void
214
+ pause: () => void
215
+ resume: () => void
216
+ isPaused: () => boolean
217
+ }) => {
218
+ // Store the controls for potential future use
219
+ cycleControlsRef.current = controls
220
+
221
+ // Notify parent component that timer controls are ready
222
+ if (onCycleTimerReady) {
223
+ onCycleTimerReady(controls)
224
+ }
225
+ },
226
+ [onCycleTimerReady],
227
+ )
228
+
185
229
  // Determine if robot should be hidden at small sizes to save space
186
230
  const shouldHideRobot = isLandscape
187
231
  ? cardSize.width < 350
188
232
  : cardSize.height < 200 // Hide robot at height < 200px in portrait
189
233
 
190
- // Determine if custom content should be hidden when height is too low
191
- // Custom content should be hidden BEFORE the robot (at higher threshold)
192
- const shouldHideCustomContent = isLandscape
193
- ? cardSize.height < 310 // Landscape: hide custom content at height < 310px
194
- : cardSize.height < 450 // Portrait: hide custom content at height < 450px
234
+ // Determine if runtime view should be hidden when height is too low
235
+ // Runtime should be hidden BEFORE the robot (at higher threshold)
236
+ const shouldHideRuntime = isLandscape
237
+ ? cardSize.height < 310 // Landscape: hide runtime at height < 350px
238
+ : cardSize.height < 450 // Portrait: hide runtime at height < 450px
195
239
 
196
240
  return (
197
241
  <Card
@@ -293,7 +337,7 @@ export const RobotCard = externalizeComponent(
293
337
  />
294
338
  </Box>
295
339
 
296
- {/* Bottom section with custom content and button */}
340
+ {/* Bottom section with runtime, cycle time, and button */}
297
341
  <Box
298
342
  sx={{
299
343
  p: { xs: 1.5, sm: 2, md: 3 },
@@ -304,10 +348,32 @@ export const RobotCard = externalizeComponent(
304
348
  justifyContent: "space-between",
305
349
  }}
306
350
  >
307
- {/* Custom content section - hidden if height is too low in landscape mode */}
308
- {!shouldHideCustomContent && CustomContentComponent && (
351
+ {/* Runtime view - hidden if height is too low in landscape mode */}
352
+ {!shouldHideRuntime && (
309
353
  <Box>
310
- <CustomContentComponent />
354
+ {/* Runtime display */}
355
+ <Typography
356
+ variant="body1"
357
+ sx={{
358
+ mb: 0,
359
+ color: theme.palette.text.secondary,
360
+ textAlign: "left",
361
+ }}
362
+ >
363
+ {t("RobotCard.Runtime.lb")}
364
+ </Typography>
365
+
366
+ {/* Compact cycle time component directly below runtime */}
367
+ <Box sx={{ textAlign: "left" }}>
368
+ <CycleTimerComponent
369
+ variant="small"
370
+ compact
371
+ onCycleComplete={handleCycleComplete}
372
+ onCycleEnd={onCycleEnd}
373
+ autoStart={cycleTimerAutoStart}
374
+ hasError={cycleTimerHasError}
375
+ />
376
+ </Box>
311
377
 
312
378
  {/* Divider */}
313
379
  <Divider
@@ -321,14 +387,7 @@ export const RobotCard = externalizeComponent(
321
387
  </Box>
322
388
  )}
323
389
 
324
- <Box
325
- sx={{
326
- mt:
327
- !shouldHideCustomContent && CustomContentComponent
328
- ? "auto"
329
- : 0,
330
- }}
331
- >
390
+ <Box sx={{ mt: !shouldHideRuntime ? "auto" : 0 }}>
332
391
  {/* Drive to Home button with some space */}
333
392
  <Box
334
393
  sx={{
@@ -429,12 +488,31 @@ export const RobotCard = externalizeComponent(
429
488
  )}
430
489
  </Box>
431
490
 
432
- {/* Bottom section with custom content and button */}
491
+ {/* Bottom section with runtime, cycle time, and button */}
433
492
  <Box>
434
- {/* Custom content section - hidden if height is too low */}
435
- {!shouldHideCustomContent && CustomContentComponent && (
493
+ {/* Runtime view - hidden if height is too low */}
494
+ {!shouldHideRuntime && (
436
495
  <>
437
- <CustomContentComponent />
496
+ {/* Runtime display */}
497
+ <Typography
498
+ variant="body1"
499
+ sx={{
500
+ mb: 0,
501
+ color: theme.palette.text.secondary,
502
+ }}
503
+ >
504
+ {t("RobotCard.Runtime.lb")}
505
+ </Typography>
506
+
507
+ {/* Compact cycle time component directly below runtime */}
508
+ <CycleTimerComponent
509
+ variant="small"
510
+ compact
511
+ onCycleComplete={handleCycleComplete}
512
+ onCycleEnd={onCycleEnd}
513
+ autoStart={cycleTimerAutoStart}
514
+ hasError={cycleTimerHasError}
515
+ />
438
516
 
439
517
  {/* Divider */}
440
518
  <Divider
@@ -453,10 +531,9 @@ export const RobotCard = externalizeComponent(
453
531
  sx={{
454
532
  display: "flex",
455
533
  justifyContent: "flex-start",
456
- mt:
457
- !shouldHideCustomContent && CustomContentComponent
458
- ? { xs: 1, sm: 2, md: 5 }
459
- : { xs: 0.5, sm: 1, md: 2 },
534
+ mt: !shouldHideRuntime
535
+ ? { xs: 1, sm: 2, md: 5 }
536
+ : { xs: 0.5, sm: 1, md: 2 },
460
537
  mb: { xs: 0.5, sm: 0.75, md: 1 },
461
538
  }}
462
539
  >