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