@wandelbots/wandelbots-js-react-components 2.34.2-pr.feature-robot-precondition-list.372.9dfd57e → 2.34.2

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 +50 -50
  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 +9576 -11435
  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 +159 -384
  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 -8
  24. package/src/i18n/locales/en/translations.json +1 -8
  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 -568
  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,568 +0,0 @@
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
- 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
89
- /** Additional CSS class name */
90
- className?: string
91
- }
92
-
93
- /**
94
- * A responsive card component that displays a 3D robot with states and controls.
95
- * The card automatically adapts to its container's size and aspect ratio.
96
- *
97
- * Features:
98
- * - Fully responsive Material-UI Card that adapts to container dimensions
99
- * - Automatic layout switching based on aspect ratio:
100
- * - Portrait mode: Vertical layout with robot in center
101
- * - Landscape mode: Horizontal layout with robot on left, content on right (left-aligned)
102
- * - Responsive 3D robot rendering:
103
- * - Scales dynamically with container size
104
- * - Hides at very small sizes to preserve usability
105
- * - Adaptive margin based on available space
106
- * - Smart spacing and padding that reduces at smaller sizes
107
- * - Minimum size constraints for usability while maximizing content density
108
- * - Robot name displayed in Typography h6 at top-left
109
- * - Program state indicator below the name
110
- * - Auto-fitting 3D robot model that scales with container size
111
- * - Compact cycle time component with small variant, error state, and count-up/count-down mode support
112
- * - Transparent gray divider line
113
- * - "Drive to Home" button with press-and-hold functionality
114
- * - Localization support via react-i18next
115
- * - Material-UI theming integration
116
- */
117
- export const RobotCard = externalizeComponent(
118
- observer(
119
- ({
120
- robotName,
121
- programState,
122
- safetyState,
123
- operationMode,
124
- driveToHomeEnabled = false,
125
- onDriveToHomePress,
126
- onDriveToHomeRelease,
127
- connectedMotionGroup,
128
- robotComponent: RobotComponent = Robot,
129
- cycleTimerComponent: CycleTimerComponent = CycleTimer,
130
- onCycleTimerReady,
131
- onCycleEnd,
132
- cycleTimerAutoStart = true,
133
- cycleTimerHasError = false,
134
- className,
135
- }: RobotCardProps) => {
136
- const theme = useTheme()
137
- const { t } = useTranslation()
138
- const [isDriveToHomePressed, setIsDriveToHomePressed] = useState(false)
139
- const driveButtonRef = useRef<HTMLButtonElement>(null)
140
- const cardRef = useRef<HTMLDivElement>(null)
141
- const [isLandscape, setIsLandscape] = useState(false)
142
- const [cardSize, setCardSize] = useState<{
143
- width: number
144
- height: number
145
- }>({ width: 400, height: 600 })
146
- const [modelRenderTrigger, setModelRenderTrigger] = useState(0)
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
-
159
- // Hook to detect aspect ratio and size changes
160
- useEffect(() => {
161
- const checkDimensions = () => {
162
- if (cardRef.current) {
163
- const { offsetWidth, offsetHeight } = cardRef.current
164
- setIsLandscape(offsetWidth > offsetHeight)
165
- setCardSize({ width: offsetWidth, height: offsetHeight })
166
- }
167
- }
168
-
169
- // Initial check
170
- checkDimensions()
171
-
172
- // Set up ResizeObserver to watch for size changes
173
- const resizeObserver = new ResizeObserver(checkDimensions)
174
- if (cardRef.current) {
175
- resizeObserver.observe(cardRef.current)
176
- }
177
-
178
- return () => {
179
- resizeObserver.disconnect()
180
- }
181
- }, [])
182
-
183
- const handleModelRender = useCallback(() => {
184
- // Trigger bounds refresh when model renders
185
- setModelRenderTrigger((prev) => prev + 1)
186
- }, [])
187
-
188
- const handleDriveToHomeMouseDown = useCallback(() => {
189
- if (!driveToHomeEnabled || !onDriveToHomePress) return
190
- setIsDriveToHomePressed(true)
191
- onDriveToHomePress()
192
- }, [driveToHomeEnabled, onDriveToHomePress])
193
-
194
- const handleDriveToHomeMouseUp = useCallback(() => {
195
- if (!driveToHomeEnabled || !onDriveToHomeRelease) return
196
- setIsDriveToHomePressed(false)
197
- onDriveToHomeRelease()
198
- }, [driveToHomeEnabled, onDriveToHomeRelease])
199
-
200
- const handleDriveToHomeMouseLeave = useCallback(() => {
201
- if (isDriveToHomePressed && onDriveToHomeRelease) {
202
- setIsDriveToHomePressed(false)
203
- onDriveToHomeRelease()
204
- }
205
- }, [isDriveToHomePressed, onDriveToHomeRelease])
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
-
229
- // Determine if robot should be hidden at small sizes to save space
230
- const shouldHideRobot = isLandscape
231
- ? cardSize.width < 350
232
- : cardSize.height < 200 // Hide robot at height < 200px in portrait
233
-
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
239
-
240
- return (
241
- <Card
242
- ref={cardRef}
243
- className={className}
244
- sx={{
245
- width: "100%",
246
- height: "100%",
247
- display: "flex",
248
- flexDirection: isLandscape ? "row" : "column",
249
- position: "relative",
250
- overflow: "hidden",
251
- minWidth: { xs: 180, sm: 220, md: 250 },
252
- minHeight: isLandscape
253
- ? { xs: 200, sm: 240, md: 260 } // Allow runtime hiding at < 283px
254
- : { xs: 150, sm: 180, md: 220 }, // Allow progressive hiding in portrait mode
255
- border: `1px solid ${theme.palette.divider}`,
256
- borderRadius: "18px",
257
- boxShadow: "none",
258
- backgroundColor:
259
- theme.palette.backgroundPaperElevation?.[8] || "#2A2A3F",
260
- backgroundImage: "none", // Override any gradient from elevation
261
- }}
262
- >
263
- {isLandscape ? (
264
- <>
265
- {/* Landscape Layout: Robot on left, content on right */}
266
- <Box
267
- sx={{
268
- flex: "0 0 50%",
269
- position: "relative",
270
- height: "100%",
271
- minHeight: "100%",
272
- maxHeight: "100%",
273
- borderRadius: 1,
274
- m: { xs: 1.5, sm: 2, md: 3 },
275
- mr: { xs: 0.75, sm: 1, md: 1.5 },
276
- overflow: "hidden", // Prevent content from affecting container size
277
- display: shouldHideRobot ? "none" : "block",
278
- }}
279
- >
280
- {!shouldHideRobot && (
281
- <Canvas
282
- orthographic
283
- camera={{
284
- position: [3, 2, 3],
285
- zoom: 1,
286
- }}
287
- shadows
288
- frameloop="demand"
289
- style={{
290
- borderRadius: theme.shape.borderRadius,
291
- width: "100%",
292
- height: "100%",
293
- background: "transparent",
294
- position: "absolute",
295
- top: 0,
296
- left: 0,
297
- }}
298
- dpr={[1, 2]}
299
- gl={{ alpha: true, antialias: true }}
300
- >
301
- <PresetEnvironment />
302
- <Bounds fit observe margin={1} maxDuration={1}>
303
- <RobotComponent
304
- connectedMotionGroup={connectedMotionGroup}
305
- postModelRender={handleModelRender}
306
- />
307
- </Bounds>
308
- </Canvas>
309
- )}
310
- </Box>
311
-
312
- {/* Content container on right */}
313
- <Box
314
- sx={{
315
- flex: shouldHideRobot ? "1" : "1",
316
- display: "flex",
317
- flexDirection: "column",
318
- justifyContent: "flex-start",
319
- width: shouldHideRobot ? "100%" : "50%",
320
- }}
321
- >
322
- {/* Header section with robot name and program state */}
323
- <Box
324
- sx={{
325
- p: { xs: 1.5, sm: 2, md: 3 },
326
- pb: { xs: 1, sm: 1.5, md: 2 },
327
- textAlign: "left",
328
- }}
329
- >
330
- <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
331
- {robotName}
332
- </Typography>
333
- <ProgramStateIndicator
334
- programState={programState}
335
- safetyState={safetyState}
336
- operationMode={operationMode}
337
- />
338
- </Box>
339
-
340
- {/* Bottom section with runtime, cycle time, and button */}
341
- <Box
342
- sx={{
343
- p: { xs: 1.5, sm: 2, md: 3 },
344
- pt: 0,
345
- flex: "1",
346
- display: "flex",
347
- flexDirection: "column",
348
- justifyContent: "space-between",
349
- }}
350
- >
351
- {/* Runtime view - hidden if height is too low in landscape mode */}
352
- {!shouldHideRuntime && (
353
- <Box>
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>
377
-
378
- {/* Divider */}
379
- <Divider
380
- sx={{
381
- mt: 1,
382
- mb: 0,
383
- borderColor: theme.palette.divider,
384
- opacity: 0.5,
385
- }}
386
- />
387
- </Box>
388
- )}
389
-
390
- <Box sx={{ mt: !shouldHideRuntime ? "auto" : 0 }}>
391
- {/* Drive to Home button with some space */}
392
- <Box
393
- sx={{
394
- display: "flex",
395
- justifyContent: "flex-start",
396
- mt: { xs: 1, sm: 1.5, md: 2 },
397
- mb: { xs: 0.5, sm: 0.75, md: 1 },
398
- }}
399
- >
400
- <Button
401
- ref={driveButtonRef}
402
- variant="contained"
403
- color="secondary"
404
- size="small"
405
- disabled={!driveToHomeEnabled}
406
- onMouseDown={handleDriveToHomeMouseDown}
407
- onMouseUp={handleDriveToHomeMouseUp}
408
- onMouseLeave={handleDriveToHomeMouseLeave}
409
- onTouchStart={handleDriveToHomeMouseDown}
410
- onTouchEnd={handleDriveToHomeMouseUp}
411
- sx={{
412
- textTransform: "none",
413
- px: 1.5,
414
- py: 0.5,
415
- }}
416
- >
417
- {t("RobotCard.DriveToHome.bt")}
418
- </Button>
419
- </Box>
420
- </Box>
421
- </Box>
422
- </Box>
423
- </>
424
- ) : (
425
- <>
426
- {/* Portrait Layout: Header, Robot, Footer */}
427
- <Box
428
- sx={{
429
- p: 3,
430
- height: "100%",
431
- display: "flex",
432
- flexDirection: "column",
433
- }}
434
- >
435
- {/* Header section with robot name and program state */}
436
- <Box>
437
- <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
438
- {robotName}
439
- </Typography>
440
- <ProgramStateIndicator
441
- programState={programState}
442
- safetyState={safetyState}
443
- operationMode={operationMode}
444
- />
445
- </Box>
446
-
447
- {/* 3D Robot viewport in center */}
448
- <Box
449
- sx={{
450
- flex: shouldHideRobot ? 0 : 1,
451
- position: "relative",
452
- minHeight: shouldHideRobot
453
- ? 0
454
- : { xs: 120, sm: 150, md: 200 },
455
- height: shouldHideRobot ? 0 : "auto",
456
- borderRadius: 1,
457
- overflow: "hidden",
458
- display: shouldHideRobot ? "none" : "block",
459
- }}
460
- >
461
- {!shouldHideRobot && (
462
- <Canvas
463
- orthographic
464
- camera={{
465
- position: [3, 2, 3],
466
- zoom: 1,
467
- }}
468
- shadows
469
- frameloop="demand"
470
- style={{
471
- borderRadius: theme.shape.borderRadius,
472
- width: "100%",
473
- height: "100%",
474
- background: "transparent",
475
- position: "absolute",
476
- }}
477
- dpr={[1, 2]}
478
- gl={{ alpha: true, antialias: true }}
479
- >
480
- <PresetEnvironment />
481
- <Bounds fit clip observe margin={1} maxDuration={1}>
482
- <RobotComponent
483
- connectedMotionGroup={connectedMotionGroup}
484
- postModelRender={handleModelRender}
485
- />
486
- </Bounds>
487
- </Canvas>
488
- )}
489
- </Box>
490
-
491
- {/* Bottom section with runtime, cycle time, and button */}
492
- <Box>
493
- {/* Runtime view - hidden if height is too low */}
494
- {!shouldHideRuntime && (
495
- <>
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
- />
516
-
517
- {/* Divider */}
518
- <Divider
519
- sx={{
520
- mt: 1,
521
- mb: 0,
522
- borderColor: theme.palette.divider,
523
- opacity: 0.5,
524
- }}
525
- />
526
- </>
527
- )}
528
-
529
- {/* Drive to Home button with some space */}
530
- <Box
531
- sx={{
532
- display: "flex",
533
- justifyContent: "flex-start",
534
- mt: !shouldHideRuntime
535
- ? { xs: 1, sm: 2, md: 5 }
536
- : { xs: 0.5, sm: 1, md: 2 },
537
- mb: { xs: 0.5, sm: 0.75, md: 1 },
538
- }}
539
- >
540
- <Button
541
- ref={driveButtonRef}
542
- variant="contained"
543
- color="secondary"
544
- size="small"
545
- disabled={!driveToHomeEnabled}
546
- onMouseDown={handleDriveToHomeMouseDown}
547
- onMouseUp={handleDriveToHomeMouseUp}
548
- onMouseLeave={handleDriveToHomeMouseLeave}
549
- onTouchStart={handleDriveToHomeMouseDown}
550
- onTouchEnd={handleDriveToHomeMouseUp}
551
- sx={{
552
- textTransform: "none",
553
- px: 1.5,
554
- py: 0.5,
555
- }}
556
- >
557
- {t("RobotCard.DriveToHome.bt")}
558
- </Button>
559
- </Box>
560
- </Box>
561
- </Box>
562
- </>
563
- )}
564
- </Card>
565
- )
566
- },
567
- ),
568
- )