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

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.
@@ -1,5 +1,5 @@
1
1
  import { Box, Button, Card, Divider, Typography, useTheme } from "@mui/material"
2
- import { Bounds, useBounds } from "@react-three/drei"
2
+ import { Bounds } from "@react-three/drei"
3
3
  import { Canvas } from "@react-three/fiber"
4
4
  import type {
5
5
  ConnectedMotionGroup,
@@ -17,25 +17,6 @@ import type { ProgramState } from "./ProgramControl"
17
17
  import { ProgramStateIndicator } from "./ProgramStateIndicator"
18
18
  import { Robot } from "./robots/Robot"
19
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
20
  export interface RobotCardProps {
40
21
  /** Name of the robot displayed at the top */
41
22
  robotName: string
@@ -51,6 +32,22 @@ export interface RobotCardProps {
51
32
  onDriveToHomePress?: () => void
52
33
  /** Callback fired when "Drive to Home" button is released */
53
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[]
54
51
  /** Connected motion group for the robot */
55
52
  connectedMotionGroup: ConnectedMotionGroup
56
53
  /** Custom robot component to render (optional, defaults to Robot) */
@@ -136,7 +133,6 @@ export const RobotCard = externalizeComponent(
136
133
  const { t } = useTranslation()
137
134
  const [isDriveToHomePressed, setIsDriveToHomePressed] = useState(false)
138
135
  const driveButtonRef = useRef<HTMLButtonElement>(null)
139
- const robotRef = useRef<Group>(null)
140
136
  const cardRef = useRef<HTMLDivElement>(null)
141
137
  const [isLandscape, setIsLandscape] = useState(false)
142
138
  const [cardSize, setCardSize] = useState<{
@@ -224,29 +220,15 @@ export const RobotCard = externalizeComponent(
224
220
  )
225
221
 
226
222
  // 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
223
  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
- }
224
+ ? cardSize.width < 390 || cardSize.height < 250 // Hide robot when width < 390px in landscape
225
+ : cardSize.height < 350 // Hide robot at height < 200px in portrait
248
226
 
249
- const robotScale = getRobotScale()
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 < 283 // Landscape: hide runtime at height < 283px
231
+ : cardSize.height < 450 // Portrait: hide runtime much earlier at height < 450px
250
232
 
251
233
  return (
252
234
  <Card
@@ -261,8 +243,8 @@ export const RobotCard = externalizeComponent(
261
243
  overflow: "hidden",
262
244
  minWidth: { xs: 180, sm: 220, md: 250 },
263
245
  minHeight: isLandscape
264
- ? { xs: 160, sm: 200, md: 250 }
265
- : { xs: 200, sm: 280, md: 350 },
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
266
248
  border: `1px solid ${theme.palette.divider}`,
267
249
  borderRadius: "18px",
268
250
  boxShadow: "none",
@@ -290,9 +272,10 @@ export const RobotCard = externalizeComponent(
290
272
  >
291
273
  {!shouldHideRobot && (
292
274
  <Canvas
275
+ orthographic
293
276
  camera={{
294
- position: [4, 4, 4], // Move camera further back for orthographic view
295
- fov: 15, // Low FOV for near-orthographic projection
277
+ position: [3, 2, 3],
278
+ zoom: 1,
296
279
  }}
297
280
  shadows
298
281
  frameloop="demand"
@@ -310,14 +293,10 @@ export const RobotCard = externalizeComponent(
310
293
  >
311
294
  <PresetEnvironment />
312
295
  <Bounds fit clip observe margin={1}>
313
- <BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
314
- <group ref={robotRef} scale={robotScale}>
315
- <RobotComponent
316
- connectedMotionGroup={connectedMotionGroup}
317
- postModelRender={handleModelRender}
318
- />
319
- </group>
320
- </BoundsRefresher>
296
+ <RobotComponent
297
+ connectedMotionGroup={connectedMotionGroup}
298
+ postModelRender={handleModelRender}
299
+ />
321
300
  </Bounds>
322
301
  </Canvas>
323
302
  )}
@@ -362,42 +341,45 @@ export const RobotCard = externalizeComponent(
362
341
  justifyContent: "space-between",
363
342
  }}
364
343
  >
365
- <Box>
366
- {/* Runtime display */}
367
- <Typography
368
- variant="body1"
369
- sx={{
370
- mb: 0,
371
- color: theme.palette.text.secondary,
372
- textAlign: "left",
373
- }}
374
- >
375
- {t("RobotCard.Runtime.lb")}
376
- </Typography>
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>
377
358
 
378
- {/* Compact cycle time component directly below runtime */}
379
- <Box sx={{ textAlign: "left" }}>
380
- <CycleTimerComponent
381
- variant="small"
382
- compact
383
- onCycleComplete={handleCycleComplete}
384
- onCycleEnd={onCycleEnd}
385
- autoStart={cycleTimerAutoStart}
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
+ }}
386
378
  />
387
379
  </Box>
380
+ )}
388
381
 
389
- {/* Divider */}
390
- <Divider
391
- sx={{
392
- mt: 1,
393
- mb: 0,
394
- borderColor: theme.palette.divider,
395
- opacity: 0.5,
396
- }}
397
- />
398
- </Box>
399
-
400
- <Box sx={{ mt: "auto" }}>
382
+ <Box sx={{ mt: !shouldHideRuntime ? "auto" : 0 }}>
401
383
  {/* Drive to Home button with some space */}
402
384
  <Box
403
385
  sx={{
@@ -470,9 +452,10 @@ export const RobotCard = externalizeComponent(
470
452
  >
471
453
  {!shouldHideRobot && (
472
454
  <Canvas
455
+ orthographic
473
456
  camera={{
474
- position: [3, 3, 3], // Closer camera position for portrait to make robot larger
475
- fov: 20, // Slightly higher FOV for portrait to fill better
457
+ position: [3, 2, 3],
458
+ zoom: 1,
476
459
  }}
477
460
  shadows
478
461
  frameloop="demand"
@@ -482,24 +465,16 @@ export const RobotCard = externalizeComponent(
482
465
  height: "100%",
483
466
  background: "transparent",
484
467
  position: "absolute",
485
- top: 0,
486
- left: 0,
487
468
  }}
488
469
  dpr={[1, 2]}
489
470
  gl={{ alpha: true, antialias: true }}
490
471
  >
491
472
  <PresetEnvironment />
492
- <Bounds fit observe margin={1}>
493
- <BoundsRefresher
494
- modelRenderTrigger={modelRenderTrigger}
495
- >
496
- <group ref={robotRef} scale={robotScale}>
497
- <RobotComponent
498
- connectedMotionGroup={connectedMotionGroup}
499
- postModelRender={handleModelRender}
500
- />
501
- </group>
502
- </BoundsRefresher>
473
+ <Bounds fit clip observe margin={1}>
474
+ <RobotComponent
475
+ connectedMotionGroup={connectedMotionGroup}
476
+ postModelRender={handleModelRender}
477
+ />
503
478
  </Bounds>
504
479
  </Canvas>
505
480
  )}
@@ -507,42 +482,49 @@ export const RobotCard = externalizeComponent(
507
482
 
508
483
  {/* Bottom section with runtime, cycle time, and button */}
509
484
  <Box>
510
- {/* Runtime display */}
511
- <Typography
512
- variant="body1"
513
- sx={{
514
- mb: 0,
515
- color: theme.palette.text.secondary,
516
- }}
517
- >
518
- {t("RobotCard.Runtime.lb")}
519
- </Typography>
485
+ {/* Runtime view - hidden based on shouldHideRuntime */}
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>
520
498
 
521
- {/* Compact cycle time component directly below runtime */}
522
- <CycleTimerComponent
523
- variant="small"
524
- compact
525
- onCycleComplete={handleCycleComplete}
526
- onCycleEnd={onCycleEnd}
527
- autoStart={cycleTimerAutoStart}
528
- />
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
+ />
529
507
 
530
- {/* Divider */}
531
- <Divider
532
- sx={{
533
- mt: 1,
534
- mb: 0,
535
- borderColor: theme.palette.divider,
536
- opacity: 0.5,
537
- }}
538
- />
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
+ )}
539
519
 
540
520
  {/* Drive to Home button with some space */}
541
521
  <Box
542
522
  sx={{
543
523
  display: "flex",
544
524
  justifyContent: "flex-start",
545
- mt: { xs: 1, sm: 2, md: 5 },
525
+ mt: !shouldHideRuntime
526
+ ? { xs: 1, sm: 2, md: 5 }
527
+ : { xs: 2, sm: 3, md: 4 }, // More margin when runtime is hidden
546
528
  mb: { xs: 0.5, sm: 0.75, md: 1 },
547
529
  }}
548
530
  >
@@ -0,0 +1,76 @@
1
+ import { Manufacturer } from "@wandelbots/nova-api/v1"
2
+
3
+ /**
4
+ * Default home configs for different robot manufacturers.
5
+ * These joint configurations represent safe home configs for each manufacturer's robots.
6
+ * All angles are in radians.
7
+ */
8
+ export const MANUFACTURER_HOME_CONFIGS: Record<Manufacturer, number[]> = {
9
+ [Manufacturer.Abb]: [0.0, 0.0, 0.0, 0.0, Math.PI / 2, 0.0, 0.0],
10
+ [Manufacturer.Fanuc]: [0.0, 0.0, 0.0, 0.0, -Math.PI / 2, 0.0, 0.0],
11
+ [Manufacturer.Yaskawa]: [0.0, 0.0, 0.0, 0.0, -Math.PI / 2, 0.0, 0.0],
12
+ [Manufacturer.Kuka]: [
13
+ 0.0,
14
+ -Math.PI / 2,
15
+ Math.PI / 2,
16
+ 0.0,
17
+ Math.PI / 2,
18
+ 0.0,
19
+ 0.0,
20
+ ],
21
+ [Manufacturer.Universalrobots]: [
22
+ 0.0,
23
+ -Math.PI / 2,
24
+ -Math.PI / 2,
25
+ -Math.PI / 2,
26
+ Math.PI / 2,
27
+ -Math.PI / 2,
28
+ 0.0,
29
+ ],
30
+ }
31
+
32
+ /**
33
+ * Extracts manufacturer from modelFromController string.
34
+ * @param modelFromController - String in format "Manufacturer_ModelName"
35
+ * @returns Manufacturer enum value or null if not recognized
36
+ */
37
+ export function extractManufacturer(
38
+ modelFromController: string,
39
+ ): Manufacturer | null {
40
+ const [manufacturerString] = modelFromController.split("_")
41
+
42
+ // Handle the mapping from string to enum
43
+ switch (manufacturerString) {
44
+ case "ABB":
45
+ return Manufacturer.Abb
46
+ case "FANUC":
47
+ return Manufacturer.Fanuc
48
+ case "YASKAWA":
49
+ return Manufacturer.Yaskawa
50
+ case "KUKA":
51
+ return Manufacturer.Kuka
52
+ case "UniversalRobots":
53
+ return Manufacturer.Universalrobots
54
+ default:
55
+ return null
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Gets the default home config for a robot based on its model identifier.
61
+ * @param modelFromController - String in format "Manufacturer_ModelName"
62
+ * @param defaultJointConfig - Optional custom default configuration to use if manufacturer not found
63
+ * @returns Array of joint positions in radians, or null if no configuration available
64
+ */
65
+ export function getDefaultHomeConfig(
66
+ modelFromController: string,
67
+ defaultJointConfig?: number[],
68
+ ): number[] | null {
69
+ const manufacturer = extractManufacturer(modelFromController)
70
+
71
+ if (manufacturer && manufacturer in MANUFACTURER_HOME_CONFIGS) {
72
+ return MANUFACTURER_HOME_CONFIGS[manufacturer]
73
+ }
74
+
75
+ return defaultJointConfig || null
76
+ }
@@ -49,6 +49,7 @@
49
49
  "CycleTimer.Time.lb": "{{time}} min.",
50
50
  "ProgramControl.Start.bt": "Start",
51
51
  "ProgramControl.Resume.bt": "Weiter",
52
+ "ProgramControl.Retry.bt": "Wiederholen",
52
53
  "ProgramControl.Pause.bt": "Pause",
53
54
  "ProgramControl.Stop.bt": "Stopp",
54
55
  "ProgramStateIndicator.Running.lb": "In Betrieb",
@@ -50,6 +50,7 @@
50
50
  "CycleTimer.Time.lb": "{{time}} min.",
51
51
  "ProgramControl.Start.bt": "Start",
52
52
  "ProgramControl.Resume.bt": "Resume",
53
+ "ProgramControl.Retry.bt": "Retry",
53
54
  "ProgramControl.Pause.bt": "Pause",
54
55
  "ProgramControl.Stop.bt": "Stop",
55
56
  "ProgramStateIndicator.Running.lb": "Running",
package/src/index.ts CHANGED
@@ -20,6 +20,11 @@ export * from "./components/ProgramStateIndicator"
20
20
  export * from "./components/RobotCard"
21
21
  export * from "./components/RobotListItem"
22
22
  export * from "./components/robots/AxisConfig"
23
+ export {
24
+ extractManufacturer,
25
+ getDefaultHomeConfig,
26
+ MANUFACTURER_HOME_CONFIGS,
27
+ } from "./components/robots/manufacturerHomePositions"
23
28
  export * from "./components/robots/Robot"
24
29
  export { defaultGetModel } from "./components/robots/robotModelLogic"
25
30
  export * from "./components/robots/SupportedRobot"
@@ -109,6 +109,80 @@ export function createDarkTheme(): Theme {
109
109
  scrollbarWidth: "thin",
110
110
  scrollbarColor: `${baseTheme.palette.divider} transparent`,
111
111
  },
112
+
113
+ // Global styles for DataGrid filter popup
114
+ ".MuiDataGrid-panelContent": {
115
+ borderRadius: "16px !important",
116
+ "& .MuiPaper-root": {
117
+ borderRadius: "16px !important",
118
+ },
119
+ },
120
+ ".MuiDataGrid-filterForm": {
121
+ borderRadius: "16px !important",
122
+ "& .MuiInputBase-root": {
123
+ borderRadius: "16px !important",
124
+ "& fieldset": {
125
+ borderRadius: "16px !important",
126
+ },
127
+ "& .MuiOutlinedInput-notchedOutline": {
128
+ borderRadius: "16px !important",
129
+ },
130
+ },
131
+ "& .MuiTextField-root": {
132
+ "& .MuiInputBase-root": {
133
+ borderRadius: "16px !important",
134
+ "& fieldset": {
135
+ borderRadius: "16px !important",
136
+ },
137
+ "& .MuiOutlinedInput-notchedOutline": {
138
+ borderRadius: "16px !important",
139
+ },
140
+ },
141
+ },
142
+ },
143
+ ".MuiDataGrid-filterFormValueInput": {
144
+ "& .MuiInputBase-root": {
145
+ borderRadius: "16px !important",
146
+ "& fieldset": {
147
+ borderRadius: "16px !important",
148
+ },
149
+ "& .MuiOutlinedInput-notchedOutline": {
150
+ borderRadius: "16px !important",
151
+ },
152
+ },
153
+ },
154
+ },
155
+ },
156
+ MuiDataGrid: {
157
+ styleOverrides: {
158
+ root: {
159
+ // Main DataGrid styling can go here if needed
160
+ },
161
+ panelContent: {
162
+ borderRadius: "16px !important",
163
+ },
164
+ filterForm: {
165
+ "& .MuiInputBase-root": {
166
+ borderRadius: "10px !important",
167
+ "& fieldset": {
168
+ borderRadius: "10px !important",
169
+ },
170
+ "& .MuiOutlinedInput-notchedOutline": {
171
+ borderRadius: "10px !important",
172
+ },
173
+ },
174
+ },
175
+ filterFormValueInput: {
176
+ "& .MuiInputBase-root": {
177
+ borderRadius: "10px !important",
178
+ "& fieldset": {
179
+ borderRadius: "10px !important",
180
+ },
181
+ "& .MuiOutlinedInput-notchedOutline": {
182
+ borderRadius: "10px !important",
183
+ },
184
+ },
185
+ },
112
186
  },
113
187
  },
114
188
  MuiButton: {
@@ -287,5 +361,5 @@ export function createDarkTheme(): Theme {
287
361
  },
288
362
  },
289
363
  },
290
- }
364
+ } as Theme
291
365
  }