@wandelbots/wandelbots-js-react-components 2.33.0-pr.feature-robot-precondition-list.372.cb78a22 → 2.33.0

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 (45) hide show
  1. package/dist/components/CycleTimer.d.ts.map +1 -1
  2. package/dist/components/ProgramControl.d.ts +1 -6
  3. package/dist/components/ProgramControl.d.ts.map +1 -1
  4. package/dist/components/ProgramStateIndicator.d.ts +1 -1
  5. package/dist/components/ProgramStateIndicator.d.ts.map +1 -1
  6. package/dist/components/robots/Robot.d.ts +2 -3
  7. package/dist/components/robots/Robot.d.ts.map +1 -1
  8. package/dist/index.cjs +48 -48
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.ts +0 -7
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7196 -8357
  13. package/dist/index.js.map +1 -1
  14. package/package.json +1 -2
  15. package/src/components/CycleTimer.tsx +64 -43
  16. package/src/components/ProgramControl.tsx +10 -20
  17. package/src/components/ProgramStateIndicator.tsx +8 -20
  18. package/src/components/robots/Robot.tsx +2 -5
  19. package/src/i18n/locales/de/translations.json +1 -6
  20. package/src/i18n/locales/en/translations.json +1 -6
  21. package/src/index.ts +0 -7
  22. package/dist/components/DataGrid.d.ts +0 -61
  23. package/dist/components/DataGrid.d.ts.map +0 -1
  24. package/dist/components/LogPanel.d.ts +0 -54
  25. package/dist/components/LogPanel.d.ts.map +0 -1
  26. package/dist/components/LogStore.d.ts +0 -11
  27. package/dist/components/LogStore.d.ts.map +0 -1
  28. package/dist/components/LogViewer.d.ts +0 -26
  29. package/dist/components/LogViewer.d.ts.map +0 -1
  30. package/dist/components/RobotCard.d.ts +0 -84
  31. package/dist/components/RobotCard.d.ts.map +0 -1
  32. package/dist/components/RobotListItem.d.ts +0 -34
  33. package/dist/components/RobotListItem.d.ts.map +0 -1
  34. package/dist/components/RobotSetupReadinessIndicator.d.ts +0 -31
  35. package/dist/components/RobotSetupReadinessIndicator.d.ts.map +0 -1
  36. package/dist/components/RobotSetupReadinessIndicator.test.d.ts +0 -2
  37. package/dist/components/RobotSetupReadinessIndicator.test.d.ts.map +0 -1
  38. package/src/components/DataGrid.tsx +0 -454
  39. package/src/components/LogPanel.tsx +0 -85
  40. package/src/components/LogStore.ts +0 -40
  41. package/src/components/LogViewer.tsx +0 -297
  42. package/src/components/RobotCard.tsx +0 -576
  43. package/src/components/RobotListItem.tsx +0 -152
  44. package/src/components/RobotSetupReadinessIndicator.test.tsx +0 -60
  45. package/src/components/RobotSetupReadinessIndicator.tsx +0 -124
@@ -1,576 +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 type { ProgramState } from "./ProgramControl"
17
- import { ProgramStateIndicator } from "./ProgramStateIndicator"
18
- import { Robot } from "./robots/Robot"
19
-
20
- // Component to refresh bounds when model renders
21
- function BoundsRefresher({
22
- modelRenderTrigger,
23
- children,
24
- }: {
25
- modelRenderTrigger: number
26
- children: React.ReactNode
27
- }) {
28
- const bounds = useBounds()
29
-
30
- useEffect(() => {
31
- if (modelRenderTrigger > 0) {
32
- bounds.refresh().clip().fit()
33
- }
34
- }, [modelRenderTrigger, bounds])
35
-
36
- return <>{children}</>
37
- }
38
-
39
- export interface RobotCardProps {
40
- /** Name of the robot displayed at the top */
41
- robotName: string
42
- /** Current program state */
43
- programState: ProgramState
44
- /** Current safety state of the robot controller */
45
- safetyState: RobotControllerStateSafetyStateEnum
46
- /** Current operation mode of the robot controller */
47
- operationMode: RobotControllerStateOperationModeEnum
48
- /** Whether the "Drive to Home" button should be enabled */
49
- driveToHomeEnabled?: boolean
50
- /** Callback fired when "Drive to Home" button is pressed */
51
- onDriveToHomePress?: () => void
52
- /** Callback fired when "Drive to Home" button is released */
53
- onDriveToHomeRelease?: () => void
54
- /** Connected motion group for the robot */
55
- connectedMotionGroup: ConnectedMotionGroup
56
- /** Custom robot component to render (optional, defaults to Robot) */
57
- robotComponent?: React.ComponentType<{
58
- connectedMotionGroup: ConnectedMotionGroup
59
- flangeRef?: React.Ref<Group>
60
- postModelRender?: () => void
61
- transparentColor?: string
62
- getModel?: (modelFromController: string) => string
63
- }>
64
- /** Custom cycle timer component (optional, defaults to CycleTimer) */
65
- cycleTimerComponent?: React.ComponentType<{
66
- variant?: "default" | "small"
67
- compact?: boolean
68
- onCycleComplete: (controls: {
69
- startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
70
- pause: () => void
71
- resume: () => void
72
- isPaused: () => boolean
73
- }) => void
74
- onCycleEnd?: () => void
75
- autoStart?: boolean
76
- className?: string
77
- }>
78
- /** Callback to receive cycle timer controls for external timer management */
79
- onCycleTimerReady?: (controls: {
80
- startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
81
- pause: () => void
82
- resume: () => void
83
- isPaused: () => boolean
84
- }) => void
85
- /** Callback fired when a cycle completes (reaches zero) */
86
- onCycleEnd?: () => void
87
- /** Whether the cycle timer should auto-start when a new cycle is set */
88
- cycleTimerAutoStart?: 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
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
- className,
134
- }: RobotCardProps) => {
135
- const theme = useTheme()
136
- const { t } = useTranslation()
137
- const [isDriveToHomePressed, setIsDriveToHomePressed] = useState(false)
138
- const driveButtonRef = useRef<HTMLButtonElement>(null)
139
- const robotRef = useRef<Group>(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: (maxTimeSeconds: number, elapsedSeconds?: number) => void
151
- pause: () => void
152
- resume: () => void
153
- isPaused: () => boolean
154
- } | null>(null)
155
-
156
- // Hook to detect aspect ratio and size changes
157
- useEffect(() => {
158
- const checkDimensions = () => {
159
- if (cardRef.current) {
160
- const { offsetWidth, offsetHeight } = cardRef.current
161
- setIsLandscape(offsetWidth > offsetHeight)
162
- setCardSize({ width: offsetWidth, height: offsetHeight })
163
- }
164
- }
165
-
166
- // Initial check
167
- checkDimensions()
168
-
169
- // Set up ResizeObserver to watch for size changes
170
- const resizeObserver = new ResizeObserver(checkDimensions)
171
- if (cardRef.current) {
172
- resizeObserver.observe(cardRef.current)
173
- }
174
-
175
- return () => {
176
- resizeObserver.disconnect()
177
- }
178
- }, [])
179
-
180
- const handleModelRender = useCallback(() => {
181
- // Trigger bounds refresh when model renders
182
- setModelRenderTrigger((prev) => prev + 1)
183
- }, [])
184
-
185
- const handleDriveToHomeMouseDown = useCallback(() => {
186
- if (!driveToHomeEnabled || !onDriveToHomePress) return
187
- setIsDriveToHomePressed(true)
188
- onDriveToHomePress()
189
- }, [driveToHomeEnabled, onDriveToHomePress])
190
-
191
- const handleDriveToHomeMouseUp = useCallback(() => {
192
- if (!driveToHomeEnabled || !onDriveToHomeRelease) return
193
- setIsDriveToHomePressed(false)
194
- onDriveToHomeRelease()
195
- }, [driveToHomeEnabled, onDriveToHomeRelease])
196
-
197
- const handleDriveToHomeMouseLeave = useCallback(() => {
198
- if (isDriveToHomePressed && onDriveToHomeRelease) {
199
- setIsDriveToHomePressed(false)
200
- onDriveToHomeRelease()
201
- }
202
- }, [isDriveToHomePressed, onDriveToHomeRelease])
203
-
204
- // Store and provide cycle timer controls for external use
205
- const handleCycleComplete = useCallback(
206
- (controls: {
207
- startNewCycle: (
208
- maxTimeSeconds: number,
209
- elapsedSeconds?: number,
210
- ) => void
211
- pause: () => void
212
- resume: () => void
213
- isPaused: () => boolean
214
- }) => {
215
- // Store the controls for potential future use
216
- cycleControlsRef.current = controls
217
-
218
- // Notify parent component that timer controls are ready
219
- if (onCycleTimerReady) {
220
- onCycleTimerReady(controls)
221
- }
222
- },
223
- [onCycleTimerReady],
224
- )
225
-
226
- // Determine if robot should be hidden at small sizes to save space
227
- // Less aggressive hiding for portrait mode since robot is in center
228
- const shouldHideRobot = isLandscape
229
- ? cardSize.width < 350 || cardSize.height < 250 // Aggressive for landscape
230
- : cardSize.width < 250 || cardSize.height < 180 // Less aggressive for portrait
231
-
232
- // Calculate responsive robot scale based on card size and orientation
233
- const getRobotScale = () => {
234
- if (shouldHideRobot) return 0
235
-
236
- if (isLandscape) {
237
- // More aggressive scaling for landscape since robot is on side
238
- if (cardSize.width < 450) return 0.8
239
- if (cardSize.width < 550) return 0.9
240
- return 1
241
- } else {
242
- // Less aggressive scaling for portrait since robot is central
243
- if (cardSize.width < 300) return 0.8
244
- if (cardSize.width < 400) return 0.9
245
- return 1
246
- }
247
- }
248
-
249
- const robotScale = getRobotScale()
250
-
251
- return (
252
- <Card
253
- ref={cardRef}
254
- className={className}
255
- elevation={5}
256
- sx={{
257
- width: "100%",
258
- height: "100%",
259
- display: "flex",
260
- flexDirection: isLandscape ? "row" : "column",
261
- position: "relative",
262
- overflow: "hidden",
263
- minWidth: { xs: 180, sm: 220, md: 250 },
264
- minHeight: isLandscape
265
- ? { xs: 160, sm: 200, md: 250 }
266
- : { xs: 200, sm: 280, md: 350 },
267
- border:
268
- "1px solid var(--secondary-_states-outlinedBorder, #FFFFFF1F)",
269
- borderRadius: "18px",
270
- boxShadow: "none",
271
- }}
272
- >
273
- {isLandscape ? (
274
- <>
275
- {/* Landscape Layout: Robot on left, content on right */}
276
- <Box
277
- sx={{
278
- flex: "0 0 50%",
279
- position: "relative",
280
- height: "100%",
281
- minHeight: "100%",
282
- maxHeight: "100%",
283
- borderRadius: 1,
284
- m: { xs: 1.5, sm: 2, md: 3 },
285
- mr: { xs: 0.75, sm: 1, md: 1.5 },
286
- overflow: "hidden", // Prevent content from affecting container size
287
- display: shouldHideRobot ? "none" : "block",
288
- }}
289
- >
290
- {!shouldHideRobot && (
291
- <Canvas
292
- camera={{
293
- position: [4, 4, 4], // Move camera further back for orthographic view
294
- fov: 15, // Low FOV for near-orthographic projection
295
- }}
296
- shadows
297
- frameloop="demand"
298
- style={{
299
- borderRadius: theme.shape.borderRadius,
300
- width: "100%",
301
- height: "100%",
302
- background: "transparent",
303
- position: "absolute",
304
- top: 0,
305
- left: 0,
306
- }}
307
- dpr={[1, 2]}
308
- gl={{ alpha: true, antialias: true }}
309
- >
310
- <PresetEnvironment />
311
- <Bounds fit clip observe margin={1}>
312
- <BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
313
- <group ref={robotRef} scale={robotScale}>
314
- <RobotComponent
315
- connectedMotionGroup={connectedMotionGroup}
316
- postModelRender={handleModelRender}
317
- />
318
- </group>
319
- </BoundsRefresher>
320
- </Bounds>
321
- </Canvas>
322
- )}
323
- </Box>
324
-
325
- {/* Content container on right */}
326
- <Box
327
- sx={{
328
- flex: shouldHideRobot ? "1" : "1",
329
- display: "flex",
330
- flexDirection: "column",
331
- justifyContent: "flex-start",
332
- width: shouldHideRobot ? "100%" : "50%",
333
- }}
334
- >
335
- {/* Header section with robot name and program state */}
336
- <Box
337
- sx={{
338
- p: { xs: 1.5, sm: 2, md: 3 },
339
- pb: { xs: 1, sm: 1.5, md: 2 },
340
- textAlign: "left",
341
- }}
342
- >
343
- <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
344
- {robotName}
345
- </Typography>
346
- <ProgramStateIndicator
347
- programState={programState}
348
- safetyState={safetyState}
349
- operationMode={operationMode}
350
- />
351
- </Box>
352
-
353
- {/* Bottom section with runtime, cycle time, and button */}
354
- <Box
355
- sx={{
356
- p: { xs: 1.5, sm: 2, md: 3 },
357
- pt: 0,
358
- flex: "1",
359
- display: "flex",
360
- flexDirection: "column",
361
- justifyContent: "space-between",
362
- }}
363
- >
364
- <Box>
365
- {/* Runtime display */}
366
- <Typography
367
- variant="body1"
368
- sx={{
369
- mb: 0,
370
- color: "var(--text-secondary, #FFFFFFB2)",
371
- textAlign: "left",
372
- }}
373
- >
374
- {t("RobotCard.Runtime.lb")}
375
- </Typography>
376
-
377
- {/* Compact cycle time component directly below runtime */}
378
- <Box sx={{ textAlign: "left" }}>
379
- <CycleTimerComponent
380
- variant="small"
381
- compact
382
- onCycleComplete={handleCycleComplete}
383
- onCycleEnd={onCycleEnd}
384
- autoStart={cycleTimerAutoStart}
385
- />
386
- </Box>
387
-
388
- {/* Divider */}
389
- <Divider
390
- sx={{
391
- mt: 1,
392
- mb: 0,
393
- borderColor: theme.palette.divider,
394
- opacity: 0.5,
395
- }}
396
- />
397
- </Box>
398
-
399
- <Box sx={{ mt: "auto" }}>
400
- {/* Drive to Home button with some space */}
401
- <Box
402
- sx={{
403
- display: "flex",
404
- justifyContent: "flex-start",
405
- mt: { xs: 1, sm: 1.5, md: 2 },
406
- mb: { xs: 0.5, sm: 0.75, md: 1 },
407
- }}
408
- >
409
- <Button
410
- ref={driveButtonRef}
411
- variant="contained"
412
- color="secondary"
413
- size="small"
414
- disabled={!driveToHomeEnabled}
415
- onMouseDown={handleDriveToHomeMouseDown}
416
- onMouseUp={handleDriveToHomeMouseUp}
417
- onMouseLeave={handleDriveToHomeMouseLeave}
418
- onTouchStart={handleDriveToHomeMouseDown}
419
- onTouchEnd={handleDriveToHomeMouseUp}
420
- sx={{
421
- textTransform: "none",
422
- px: 1.5,
423
- py: 0.5,
424
- }}
425
- >
426
- {t("RobotCard.DriveToHome.bt")}
427
- </Button>
428
- </Box>
429
- </Box>
430
- </Box>
431
- </Box>
432
- </>
433
- ) : (
434
- <>
435
- {/* Portrait Layout: Header, Robot, Footer */}
436
- <Box
437
- sx={{
438
- p: 3,
439
- height: "100%",
440
- display: "flex",
441
- flexDirection: "column",
442
- }}
443
- >
444
- {/* Header section with robot name and program state */}
445
- <Box>
446
- <Typography variant="h6" component="h2" sx={{ mb: 1 }}>
447
- {robotName}
448
- </Typography>
449
- <ProgramStateIndicator
450
- programState={programState}
451
- safetyState={safetyState}
452
- operationMode={operationMode}
453
- />
454
- </Box>
455
-
456
- {/* 3D Robot viewport in center */}
457
- <Box
458
- sx={{
459
- flex: shouldHideRobot ? 0 : 1,
460
- position: "relative",
461
- minHeight: shouldHideRobot
462
- ? 0
463
- : { xs: 120, sm: 150, md: 200 },
464
- height: shouldHideRobot ? 0 : "auto",
465
- borderRadius: 1,
466
- overflow: "hidden",
467
- display: shouldHideRobot ? "none" : "block",
468
- }}
469
- >
470
- {!shouldHideRobot && (
471
- <Canvas
472
- camera={{
473
- position: [3, 3, 3], // Closer camera position for portrait to make robot larger
474
- fov: 20, // Slightly higher FOV for portrait to fill better
475
- }}
476
- shadows
477
- frameloop="demand"
478
- style={{
479
- borderRadius: theme.shape.borderRadius,
480
- width: "100%",
481
- height: "100%",
482
- background: "transparent",
483
- position: "absolute",
484
- top: 0,
485
- left: 0,
486
- }}
487
- dpr={[1, 2]}
488
- gl={{ alpha: true, antialias: true }}
489
- >
490
- <PresetEnvironment />
491
- <Bounds fit observe margin={1}>
492
- <BoundsRefresher
493
- modelRenderTrigger={modelRenderTrigger}
494
- >
495
- <group ref={robotRef} scale={robotScale}>
496
- <RobotComponent
497
- connectedMotionGroup={connectedMotionGroup}
498
- postModelRender={handleModelRender}
499
- />
500
- </group>
501
- </BoundsRefresher>
502
- </Bounds>
503
- </Canvas>
504
- )}
505
- </Box>
506
-
507
- {/* Bottom section with runtime, cycle time, and button */}
508
- <Box>
509
- {/* Runtime display */}
510
- <Typography
511
- variant="body1"
512
- sx={{
513
- mb: 0,
514
- color: "var(--text-secondary, #FFFFFFB2)",
515
- }}
516
- >
517
- {t("RobotCard.Runtime.lb")}
518
- </Typography>
519
-
520
- {/* Compact cycle time component directly below runtime */}
521
- <CycleTimerComponent
522
- variant="small"
523
- compact
524
- onCycleComplete={handleCycleComplete}
525
- onCycleEnd={onCycleEnd}
526
- autoStart={cycleTimerAutoStart}
527
- />
528
-
529
- {/* Divider */}
530
- <Divider
531
- sx={{
532
- mt: 1,
533
- mb: 0,
534
- borderColor: theme.palette.divider,
535
- opacity: 0.5,
536
- }}
537
- />
538
-
539
- {/* Drive to Home button with some space */}
540
- <Box
541
- sx={{
542
- display: "flex",
543
- justifyContent: "flex-start",
544
- mt: { xs: 1, sm: 2, md: 5 },
545
- mb: { xs: 0.5, sm: 0.75, md: 1 },
546
- }}
547
- >
548
- <Button
549
- ref={driveButtonRef}
550
- variant="contained"
551
- color="secondary"
552
- size="small"
553
- disabled={!driveToHomeEnabled}
554
- onMouseDown={handleDriveToHomeMouseDown}
555
- onMouseUp={handleDriveToHomeMouseUp}
556
- onMouseLeave={handleDriveToHomeMouseLeave}
557
- onTouchStart={handleDriveToHomeMouseDown}
558
- onTouchEnd={handleDriveToHomeMouseUp}
559
- sx={{
560
- textTransform: "none",
561
- px: 1.5,
562
- py: 0.5,
563
- }}
564
- >
565
- {t("RobotCard.DriveToHome.bt")}
566
- </Button>
567
- </Box>
568
- </Box>
569
- </Box>
570
- </>
571
- )}
572
- </Card>
573
- )
574
- },
575
- ),
576
- )