@wandelbots/wandelbots-js-react-components 2.34.1-pr.feature-robot-precondition-list.372.224a5cd → 2.34.1-pr.fix-kr30-glb-model.375.36f4141

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