@wandelbots/wandelbots-js-react-components 2.34.0 → 2.34.1-pr.feature-robot-precondition-list.372.c1de8ff

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 (64) hide show
  1. package/dist/components/AppHeader.d.ts +34 -0
  2. package/dist/components/AppHeader.d.ts.map +1 -0
  3. package/dist/components/CycleTimer.d.ts.map +1 -1
  4. package/dist/components/DataGrid.d.ts +66 -0
  5. package/dist/components/DataGrid.d.ts.map +1 -0
  6. package/dist/components/LogPanel.d.ts +38 -0
  7. package/dist/components/LogPanel.d.ts.map +1 -0
  8. package/dist/components/LogStore.d.ts +12 -0
  9. package/dist/components/LogStore.d.ts.map +1 -0
  10. package/dist/components/LogViewer.d.ts +46 -0
  11. package/dist/components/LogViewer.d.ts.map +1 -0
  12. package/dist/components/ProgramControl.d.ts +8 -2
  13. package/dist/components/ProgramControl.d.ts.map +1 -1
  14. package/dist/components/ProgramStateIndicator.d.ts +1 -1
  15. package/dist/components/ProgramStateIndicator.d.ts.map +1 -1
  16. package/dist/components/RobotCard.d.ts +100 -0
  17. package/dist/components/RobotCard.d.ts.map +1 -0
  18. package/dist/components/RobotListItem.d.ts +34 -0
  19. package/dist/components/RobotListItem.d.ts.map +1 -0
  20. package/dist/components/RobotSetupReadinessIndicator.d.ts +31 -0
  21. package/dist/components/RobotSetupReadinessIndicator.d.ts.map +1 -0
  22. package/dist/components/RobotSetupReadinessIndicator.test.d.ts +2 -0
  23. package/dist/components/RobotSetupReadinessIndicator.test.d.ts.map +1 -0
  24. package/dist/components/TabBar.d.ts +30 -0
  25. package/dist/components/TabBar.d.ts.map +1 -0
  26. package/dist/components/robots/Robot.d.ts +3 -2
  27. package/dist/components/robots/Robot.d.ts.map +1 -1
  28. package/dist/components/robots/manufacturerHomePositions.d.ts +21 -0
  29. package/dist/components/robots/manufacturerHomePositions.d.ts.map +1 -0
  30. package/dist/icons/DropdownArrowIcon.d.ts +3 -0
  31. package/dist/icons/DropdownArrowIcon.d.ts.map +1 -0
  32. package/dist/icons/index.d.ts +1 -0
  33. package/dist/icons/index.d.ts.map +1 -1
  34. package/dist/index.cjs +49 -49
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +10 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +8772 -7044
  39. package/dist/index.js.map +1 -1
  40. package/dist/themes/createDarkTheme.d.ts.map +1 -1
  41. package/package.json +2 -1
  42. package/src/components/AppHeader.md +84 -0
  43. package/src/components/AppHeader.tsx +199 -0
  44. package/src/components/CycleTimer.tsx +43 -64
  45. package/src/components/DataGrid.tsx +659 -0
  46. package/src/components/LogPanel.tsx +69 -0
  47. package/src/components/LogStore.ts +44 -0
  48. package/src/components/LogViewer.tsx +370 -0
  49. package/src/components/ProgramControl.tsx +27 -12
  50. package/src/components/ProgramStateIndicator.tsx +25 -8
  51. package/src/components/RobotCard.tsx +559 -0
  52. package/src/components/RobotListItem.tsx +150 -0
  53. package/src/components/RobotSetupReadinessIndicator.test.tsx +60 -0
  54. package/src/components/RobotSetupReadinessIndicator.tsx +124 -0
  55. package/src/components/TabBar.tsx +144 -0
  56. package/src/components/robots/Robot.tsx +5 -2
  57. package/src/components/robots/manufacturerHomePositions.ts +76 -0
  58. package/src/i18n/locales/de/translations.json +7 -1
  59. package/src/i18n/locales/en/translations.json +7 -1
  60. package/src/icons/DropdownArrowIcon.tsx +13 -0
  61. package/src/icons/chevronDown.svg +3 -0
  62. package/src/icons/index.ts +1 -0
  63. package/src/index.ts +14 -0
  64. package/src/themes/createDarkTheme.ts +75 -1
@@ -0,0 +1,559 @@
1
+ import { Box, Button, Card, Divider, Typography, useTheme } from "@mui/material"
2
+ import { Bounds } from "@react-three/drei"
3
+ import { Canvas } from "@react-three/fiber"
4
+ import type {
5
+ ConnectedMotionGroup,
6
+ RobotControllerStateOperationModeEnum,
7
+ RobotControllerStateSafetyStateEnum,
8
+ } from "@wandelbots/nova-js/v1"
9
+ import { observer } from "mobx-react-lite"
10
+ import { useCallback, useEffect, useRef, useState } from "react"
11
+ import { useTranslation } from "react-i18next"
12
+ import type { Group } from "three"
13
+ import { externalizeComponent } from "../externalizeComponent"
14
+ import { PresetEnvironment } from "./3d-viewport/PresetEnvironment"
15
+ import { CycleTimer } from "./CycleTimer"
16
+ import type { ProgramState } from "./ProgramControl"
17
+ import { ProgramStateIndicator } from "./ProgramStateIndicator"
18
+ import { Robot } from "./robots/Robot"
19
+
20
+ export interface RobotCardProps {
21
+ /** Name of the robot displayed at the top */
22
+ robotName: string
23
+ /** Current program state */
24
+ programState: ProgramState
25
+ /** Current safety state of the robot controller */
26
+ safetyState: RobotControllerStateSafetyStateEnum
27
+ /** Current operation mode of the robot controller */
28
+ operationMode: RobotControllerStateOperationModeEnum
29
+ /** Whether the "Drive to Home" button should be enabled */
30
+ driveToHomeEnabled?: boolean
31
+ /** Callback fired when "Drive to Home" button is pressed */
32
+ onDriveToHomePress?: () => void
33
+ /** Callback fired when "Drive to Home" button is released */
34
+ onDriveToHomeRelease?: () => void
35
+ /**
36
+ * Callback fired when "Drive to Home" button is pressed, with the default home position.
37
+ * If provided, this will be called instead of onDriveToHomePress, providing the recommended
38
+ * home position joint configuration based on the robot manufacturer.
39
+ */
40
+ onDriveToHomePressWithConfig?: (homePosition: number[]) => void
41
+ /**
42
+ * Callback fired when "Drive to Home" button is released after using onDriveToHomePressWithConfig.
43
+ * If provided, this will be called instead of onDriveToHomeRelease.
44
+ */
45
+ onDriveToHomeReleaseWithConfig?: () => void
46
+ /**
47
+ * Custom default joint configuration to use if manufacturer-based defaults are not available.
48
+ * Joint values should be in radians.
49
+ */
50
+ defaultJointConfig?: number[]
51
+ /** Connected motion group for the robot */
52
+ connectedMotionGroup: ConnectedMotionGroup
53
+ /** Custom robot component to render (optional, defaults to Robot) */
54
+ robotComponent?: React.ComponentType<{
55
+ connectedMotionGroup: ConnectedMotionGroup
56
+ flangeRef?: React.Ref<Group>
57
+ postModelRender?: () => void
58
+ transparentColor?: string
59
+ getModel?: (modelFromController: string) => string
60
+ }>
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
+ className?: string
74
+ }>
75
+ /** Callback to receive cycle timer controls for external timer management */
76
+ onCycleTimerReady?: (controls: {
77
+ startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
78
+ pause: () => void
79
+ resume: () => void
80
+ isPaused: () => boolean
81
+ }) => void
82
+ /** Callback fired when a cycle completes (reaches zero) */
83
+ onCycleEnd?: () => void
84
+ /** Whether the cycle timer should auto-start when a new cycle is set */
85
+ cycleTimerAutoStart?: boolean
86
+ /** Additional CSS class name */
87
+ className?: string
88
+ }
89
+
90
+ /**
91
+ * A responsive card component that displays a 3D robot with states and controls.
92
+ * The card automatically adapts to its container's size and aspect ratio.
93
+ *
94
+ * Features:
95
+ * - Fully responsive Material-UI Card that adapts to container dimensions
96
+ * - Automatic layout switching based on aspect ratio:
97
+ * - Portrait mode: Vertical layout with robot in center
98
+ * - Landscape mode: Horizontal layout with robot on left, content on right (left-aligned)
99
+ * - Responsive 3D robot rendering:
100
+ * - Scales dynamically with container size
101
+ * - Hides at very small sizes to preserve usability
102
+ * - Adaptive margin based on available space
103
+ * - Smart spacing and padding that reduces at smaller sizes
104
+ * - Minimum size constraints for usability while maximizing content density
105
+ * - Robot name displayed in Typography h6 at top-left
106
+ * - Program state indicator below the name
107
+ * - Auto-fitting 3D robot model that scales with container size
108
+ * - Compact cycle time component with small variant
109
+ * - Transparent gray divider line
110
+ * - "Drive to Home" button with press-and-hold functionality
111
+ * - Localization support via react-i18next
112
+ * - Material-UI theming integration
113
+ */
114
+ export const RobotCard = externalizeComponent(
115
+ observer(
116
+ ({
117
+ robotName,
118
+ programState,
119
+ safetyState,
120
+ operationMode,
121
+ driveToHomeEnabled = false,
122
+ onDriveToHomePress,
123
+ onDriveToHomeRelease,
124
+ connectedMotionGroup,
125
+ robotComponent: RobotComponent = Robot,
126
+ cycleTimerComponent: CycleTimerComponent = CycleTimer,
127
+ onCycleTimerReady,
128
+ onCycleEnd,
129
+ cycleTimerAutoStart = true,
130
+ className,
131
+ }: RobotCardProps) => {
132
+ const theme = useTheme()
133
+ const { t } = useTranslation()
134
+ const [isDriveToHomePressed, setIsDriveToHomePressed] = useState(false)
135
+ const driveButtonRef = useRef<HTMLButtonElement>(null)
136
+ const cardRef = useRef<HTMLDivElement>(null)
137
+ const [isLandscape, setIsLandscape] = useState(false)
138
+ const [cardSize, setCardSize] = useState<{
139
+ width: number
140
+ height: number
141
+ }>({ width: 400, height: 600 })
142
+ const [modelRenderTrigger, setModelRenderTrigger] = useState(0)
143
+
144
+ // Store cycle timer controls for external control
145
+ const cycleControlsRef = useRef<{
146
+ startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
147
+ pause: () => void
148
+ resume: () => void
149
+ isPaused: () => boolean
150
+ } | null>(null)
151
+
152
+ // Hook to detect aspect ratio and size changes
153
+ useEffect(() => {
154
+ const checkDimensions = () => {
155
+ if (cardRef.current) {
156
+ const { offsetWidth, offsetHeight } = cardRef.current
157
+ setIsLandscape(offsetWidth > offsetHeight)
158
+ setCardSize({ width: offsetWidth, height: offsetHeight })
159
+ }
160
+ }
161
+
162
+ // Initial check
163
+ checkDimensions()
164
+
165
+ // Set up ResizeObserver to watch for size changes
166
+ const resizeObserver = new ResizeObserver(checkDimensions)
167
+ if (cardRef.current) {
168
+ resizeObserver.observe(cardRef.current)
169
+ }
170
+
171
+ return () => {
172
+ resizeObserver.disconnect()
173
+ }
174
+ }, [])
175
+
176
+ const handleModelRender = useCallback(() => {
177
+ // Trigger bounds refresh when model renders
178
+ setModelRenderTrigger((prev) => prev + 1)
179
+ }, [])
180
+
181
+ const handleDriveToHomeMouseDown = useCallback(() => {
182
+ if (!driveToHomeEnabled || !onDriveToHomePress) return
183
+ setIsDriveToHomePressed(true)
184
+ onDriveToHomePress()
185
+ }, [driveToHomeEnabled, onDriveToHomePress])
186
+
187
+ const handleDriveToHomeMouseUp = useCallback(() => {
188
+ if (!driveToHomeEnabled || !onDriveToHomeRelease) return
189
+ setIsDriveToHomePressed(false)
190
+ onDriveToHomeRelease()
191
+ }, [driveToHomeEnabled, onDriveToHomeRelease])
192
+
193
+ const handleDriveToHomeMouseLeave = useCallback(() => {
194
+ if (isDriveToHomePressed && onDriveToHomeRelease) {
195
+ setIsDriveToHomePressed(false)
196
+ onDriveToHomeRelease()
197
+ }
198
+ }, [isDriveToHomePressed, onDriveToHomeRelease])
199
+
200
+ // Store and provide cycle timer controls for external use
201
+ const handleCycleComplete = useCallback(
202
+ (controls: {
203
+ startNewCycle: (
204
+ maxTimeSeconds: number,
205
+ elapsedSeconds?: number,
206
+ ) => void
207
+ pause: () => void
208
+ resume: () => void
209
+ isPaused: () => boolean
210
+ }) => {
211
+ // Store the controls for potential future use
212
+ cycleControlsRef.current = controls
213
+
214
+ // Notify parent component that timer controls are ready
215
+ if (onCycleTimerReady) {
216
+ onCycleTimerReady(controls)
217
+ }
218
+ },
219
+ [onCycleTimerReady],
220
+ )
221
+
222
+ // Determine if robot should be hidden at small sizes to save space
223
+ const shouldHideRobot = isLandscape
224
+ ? cardSize.width < 350
225
+ : cardSize.height < 200 // Hide robot at height < 200px in portrait
226
+
227
+ // Determine if runtime view should be hidden when height is too low
228
+ // Runtime should be hidden BEFORE the robot (at higher threshold)
229
+ const shouldHideRuntime = isLandscape
230
+ ? cardSize.height < 310 // Landscape: hide runtime at height < 350px
231
+ : cardSize.height < 450 // Portrait: hide runtime at height < 450px
232
+
233
+ return (
234
+ <Card
235
+ ref={cardRef}
236
+ className={className}
237
+ sx={{
238
+ width: "100%",
239
+ height: "100%",
240
+ display: "flex",
241
+ flexDirection: isLandscape ? "row" : "column",
242
+ position: "relative",
243
+ overflow: "hidden",
244
+ minWidth: { xs: 180, sm: 220, md: 250 },
245
+ minHeight: isLandscape
246
+ ? { xs: 200, sm: 240, md: 260 } // Allow runtime hiding at < 283px
247
+ : { xs: 150, sm: 180, md: 220 }, // Allow progressive hiding in portrait mode
248
+ border: `1px solid ${theme.palette.divider}`,
249
+ borderRadius: "18px",
250
+ boxShadow: "none",
251
+ backgroundColor:
252
+ theme.palette.backgroundPaperElevation?.[8] || "#2A2A3F",
253
+ backgroundImage: "none", // Override any gradient from elevation
254
+ }}
255
+ >
256
+ {isLandscape ? (
257
+ <>
258
+ {/* Landscape Layout: Robot on left, content on right */}
259
+ <Box
260
+ sx={{
261
+ flex: "0 0 50%",
262
+ position: "relative",
263
+ height: "100%",
264
+ minHeight: "100%",
265
+ maxHeight: "100%",
266
+ borderRadius: 1,
267
+ m: { xs: 1.5, sm: 2, md: 3 },
268
+ mr: { xs: 0.75, sm: 1, md: 1.5 },
269
+ overflow: "hidden", // Prevent content from affecting container size
270
+ display: shouldHideRobot ? "none" : "block",
271
+ }}
272
+ >
273
+ {!shouldHideRobot && (
274
+ <Canvas
275
+ orthographic
276
+ camera={{
277
+ position: [3, 2, 3],
278
+ zoom: 1,
279
+ }}
280
+ shadows
281
+ frameloop="demand"
282
+ style={{
283
+ borderRadius: theme.shape.borderRadius,
284
+ width: "100%",
285
+ height: "100%",
286
+ background: "transparent",
287
+ position: "absolute",
288
+ top: 0,
289
+ left: 0,
290
+ }}
291
+ dpr={[1, 2]}
292
+ gl={{ alpha: true, antialias: true }}
293
+ >
294
+ <PresetEnvironment />
295
+ <Bounds fit observe margin={1} maxDuration={1}>
296
+ <RobotComponent
297
+ connectedMotionGroup={connectedMotionGroup}
298
+ postModelRender={handleModelRender}
299
+ />
300
+ </Bounds>
301
+ </Canvas>
302
+ )}
303
+ </Box>
304
+
305
+ {/* Content container on right */}
306
+ <Box
307
+ sx={{
308
+ flex: shouldHideRobot ? "1" : "1",
309
+ display: "flex",
310
+ flexDirection: "column",
311
+ justifyContent: "flex-start",
312
+ width: shouldHideRobot ? "100%" : "50%",
313
+ }}
314
+ >
315
+ {/* Header section with robot name and program state */}
316
+ <Box
317
+ sx={{
318
+ p: { xs: 1.5, sm: 2, md: 3 },
319
+ pb: { xs: 1, sm: 1.5, md: 2 },
320
+ textAlign: "left",
321
+ }}
322
+ >
323
+ <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
324
+ {robotName}
325
+ </Typography>
326
+ <ProgramStateIndicator
327
+ programState={programState}
328
+ safetyState={safetyState}
329
+ operationMode={operationMode}
330
+ />
331
+ </Box>
332
+
333
+ {/* Bottom section with runtime, cycle time, and button */}
334
+ <Box
335
+ sx={{
336
+ p: { xs: 1.5, sm: 2, md: 3 },
337
+ pt: 0,
338
+ flex: "1",
339
+ display: "flex",
340
+ flexDirection: "column",
341
+ justifyContent: "space-between",
342
+ }}
343
+ >
344
+ {/* Runtime view - hidden if height is too low in landscape mode */}
345
+ {!shouldHideRuntime && (
346
+ <Box>
347
+ {/* Runtime display */}
348
+ <Typography
349
+ variant="body1"
350
+ sx={{
351
+ mb: 0,
352
+ color: theme.palette.text.secondary,
353
+ textAlign: "left",
354
+ }}
355
+ >
356
+ {t("RobotCard.Runtime.lb")}
357
+ </Typography>
358
+
359
+ {/* Compact cycle time component directly below runtime */}
360
+ <Box sx={{ textAlign: "left" }}>
361
+ <CycleTimerComponent
362
+ variant="small"
363
+ compact
364
+ onCycleComplete={handleCycleComplete}
365
+ onCycleEnd={onCycleEnd}
366
+ autoStart={cycleTimerAutoStart}
367
+ />
368
+ </Box>
369
+
370
+ {/* Divider */}
371
+ <Divider
372
+ sx={{
373
+ mt: 1,
374
+ mb: 0,
375
+ borderColor: theme.palette.divider,
376
+ opacity: 0.5,
377
+ }}
378
+ />
379
+ </Box>
380
+ )}
381
+
382
+ <Box sx={{ mt: !shouldHideRuntime ? "auto" : 0 }}>
383
+ {/* Drive to Home button with some space */}
384
+ <Box
385
+ sx={{
386
+ display: "flex",
387
+ justifyContent: "flex-start",
388
+ mt: { xs: 1, sm: 1.5, md: 2 },
389
+ mb: { xs: 0.5, sm: 0.75, md: 1 },
390
+ }}
391
+ >
392
+ <Button
393
+ ref={driveButtonRef}
394
+ variant="contained"
395
+ color="secondary"
396
+ size="small"
397
+ disabled={!driveToHomeEnabled}
398
+ onMouseDown={handleDriveToHomeMouseDown}
399
+ onMouseUp={handleDriveToHomeMouseUp}
400
+ onMouseLeave={handleDriveToHomeMouseLeave}
401
+ onTouchStart={handleDriveToHomeMouseDown}
402
+ onTouchEnd={handleDriveToHomeMouseUp}
403
+ sx={{
404
+ textTransform: "none",
405
+ px: 1.5,
406
+ py: 0.5,
407
+ }}
408
+ >
409
+ {t("RobotCard.DriveToHome.bt")}
410
+ </Button>
411
+ </Box>
412
+ </Box>
413
+ </Box>
414
+ </Box>
415
+ </>
416
+ ) : (
417
+ <>
418
+ {/* Portrait Layout: Header, Robot, Footer */}
419
+ <Box
420
+ sx={{
421
+ p: 3,
422
+ height: "100%",
423
+ display: "flex",
424
+ flexDirection: "column",
425
+ }}
426
+ >
427
+ {/* Header section with robot name and program state */}
428
+ <Box>
429
+ <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
430
+ {robotName}
431
+ </Typography>
432
+ <ProgramStateIndicator
433
+ programState={programState}
434
+ safetyState={safetyState}
435
+ operationMode={operationMode}
436
+ />
437
+ </Box>
438
+
439
+ {/* 3D Robot viewport in center */}
440
+ <Box
441
+ sx={{
442
+ flex: shouldHideRobot ? 0 : 1,
443
+ position: "relative",
444
+ minHeight: shouldHideRobot
445
+ ? 0
446
+ : { xs: 120, sm: 150, md: 200 },
447
+ height: shouldHideRobot ? 0 : "auto",
448
+ borderRadius: 1,
449
+ overflow: "hidden",
450
+ display: shouldHideRobot ? "none" : "block",
451
+ }}
452
+ >
453
+ {!shouldHideRobot && (
454
+ <Canvas
455
+ orthographic
456
+ camera={{
457
+ position: [3, 2, 3],
458
+ zoom: 1,
459
+ }}
460
+ shadows
461
+ frameloop="demand"
462
+ style={{
463
+ borderRadius: theme.shape.borderRadius,
464
+ width: "100%",
465
+ height: "100%",
466
+ background: "transparent",
467
+ position: "absolute",
468
+ }}
469
+ dpr={[1, 2]}
470
+ gl={{ alpha: true, antialias: true }}
471
+ >
472
+ <PresetEnvironment />
473
+ <Bounds fit clip observe margin={1} maxDuration={1}>
474
+ <RobotComponent
475
+ connectedMotionGroup={connectedMotionGroup}
476
+ postModelRender={handleModelRender}
477
+ />
478
+ </Bounds>
479
+ </Canvas>
480
+ )}
481
+ </Box>
482
+
483
+ {/* Bottom section with runtime, cycle time, and button */}
484
+ <Box>
485
+ {/* Runtime view - hidden if height is too low */}
486
+ {!shouldHideRuntime && (
487
+ <>
488
+ {/* Runtime display */}
489
+ <Typography
490
+ variant="body1"
491
+ sx={{
492
+ mb: 0,
493
+ color: theme.palette.text.secondary,
494
+ }}
495
+ >
496
+ {t("RobotCard.Runtime.lb")}
497
+ </Typography>
498
+
499
+ {/* Compact cycle time component directly below runtime */}
500
+ <CycleTimerComponent
501
+ variant="small"
502
+ compact
503
+ onCycleComplete={handleCycleComplete}
504
+ onCycleEnd={onCycleEnd}
505
+ autoStart={cycleTimerAutoStart}
506
+ />
507
+
508
+ {/* Divider */}
509
+ <Divider
510
+ sx={{
511
+ mt: 1,
512
+ mb: 0,
513
+ borderColor: theme.palette.divider,
514
+ opacity: 0.5,
515
+ }}
516
+ />
517
+ </>
518
+ )}
519
+
520
+ {/* Drive to Home button with some space */}
521
+ <Box
522
+ sx={{
523
+ display: "flex",
524
+ justifyContent: "flex-start",
525
+ mt: !shouldHideRuntime
526
+ ? { xs: 1, sm: 2, md: 5 }
527
+ : { xs: 0.5, sm: 1, md: 2 },
528
+ mb: { xs: 0.5, sm: 0.75, md: 1 },
529
+ }}
530
+ >
531
+ <Button
532
+ ref={driveButtonRef}
533
+ variant="contained"
534
+ color="secondary"
535
+ size="small"
536
+ disabled={!driveToHomeEnabled}
537
+ onMouseDown={handleDriveToHomeMouseDown}
538
+ onMouseUp={handleDriveToHomeMouseUp}
539
+ onMouseLeave={handleDriveToHomeMouseLeave}
540
+ onTouchStart={handleDriveToHomeMouseDown}
541
+ onTouchEnd={handleDriveToHomeMouseUp}
542
+ sx={{
543
+ textTransform: "none",
544
+ px: 1.5,
545
+ py: 0.5,
546
+ }}
547
+ >
548
+ {t("RobotCard.DriveToHome.bt")}
549
+ </Button>
550
+ </Box>
551
+ </Box>
552
+ </Box>
553
+ </>
554
+ )}
555
+ </Card>
556
+ )
557
+ },
558
+ ),
559
+ )