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

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wandelbots/wandelbots-js-react-components",
3
- "version": "2.33.0-pr.feature-robot-precondition-list.372.3ad6c62",
3
+ "version": "2.33.0-pr.feature-robot-precondition-list.372.cb3db34",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -0,0 +1,84 @@
1
+ # AppTopBar Component
2
+
3
+ A navigation bar component that displays the current application's icon and name, with an optional dropdown menu for switching between different applications.
4
+
5
+ ## Features
6
+
7
+ - **App Branding**: Displays the current app's icon and name with custom typography
8
+ - **App Switching**: Dropdown menu with blur backdrop for navigating to other apps
9
+ - **Responsive Design**: Uses MUI AppBar for consistent styling
10
+ - **Accessibility**: Proper ARIA labels and keyboard navigation
11
+ - **Flexible Navigation**: Supports both URL navigation and custom click handlers
12
+
13
+ ## Usage
14
+
15
+ ```tsx
16
+ import {
17
+ AppTopBar,
18
+ type AppItem,
19
+ } from "@wandelbots/wandelbots-js-react-components"
20
+ import { Home, Settings, Person } from "@mui/icons-material"
21
+
22
+ const apps: AppItem[] = [
23
+ {
24
+ id: "dashboard",
25
+ name: "Dashboard",
26
+ icon: <Home />,
27
+ href: "/dashboard",
28
+ },
29
+ {
30
+ id: "settings",
31
+ name: "Settings",
32
+ icon: <Settings />,
33
+ onClick: () => navigateToSettings(),
34
+ },
35
+ ]
36
+
37
+ function MyApp() {
38
+ return (
39
+ <AppTopBar
40
+ appIcon={<Person />}
41
+ appName="Robot Control Studio"
42
+ apps={apps}
43
+ onAppSelect={(app) => console.log("Selected:", app.name)}
44
+ />
45
+ )
46
+ }
47
+ ```
48
+
49
+ ## Props
50
+
51
+ ### AppTopBarProps
52
+
53
+ - **appIcon** (`ReactNode`): Icon component for the current application
54
+ - **appName** (`string`): Display name of the current application
55
+ - **apps** (`AppItem[]`, optional): Array of available apps for the dropdown menu
56
+ - **onAppSelect** (`(app: AppItem) => void`, optional): Callback when an app is selected
57
+ - **sx** (`SxProps`, optional): Additional MUI styling
58
+
59
+ ### AppItem
60
+
61
+ - **id** (`string`): Unique identifier for the app
62
+ - **name** (`string`): Display name of the app
63
+ - **icon** (`ReactNode`): Icon component to display
64
+ - **href** (`string`, optional): URL to navigate to
65
+ - **onClick** (`() => void`, optional): Custom click handler
66
+
67
+ ## Styling
68
+
69
+ The component uses the specified typography:
70
+
71
+ - Font weight: 700 (Bold)
72
+ - Font size: 20px
73
+ - Line height: 24px
74
+ - Letter spacing: 0px
75
+
76
+ The dropdown arrow uses a custom SVG icon with 56% opacity.
77
+
78
+ ## Behavior
79
+
80
+ - When apps are provided, a dropdown arrow appears on the right side
81
+ - Clicking the arrow opens a menu with available apps
82
+ - The background blurs when the menu is open to focus attention
83
+ - Apps can navigate via URL (`href`) or custom handler (`onClick`)
84
+ - The menu closes automatically when an app is selected or when clicking outside
@@ -0,0 +1,188 @@
1
+ import {
2
+ alpha,
3
+ AppBar,
4
+ Backdrop,
5
+ Box,
6
+ IconButton,
7
+ Menu,
8
+ MenuItem,
9
+ type SxProps,
10
+ Toolbar,
11
+ Typography,
12
+ } from "@mui/material"
13
+ import { observer } from "mobx-react-lite"
14
+ import { type MouseEvent, type ReactNode, useState } from "react"
15
+ import { externalizeComponent } from "../externalizeComponent"
16
+ import { DropdownArrowIcon } from "../icons/DropdownArrowIcon"
17
+
18
+ export type AppItem = {
19
+ /** Unique identifier for the app */
20
+ id: string
21
+ /** Display name of the app */
22
+ name: string
23
+ /** Icon component to display */
24
+ icon: ReactNode
25
+ /** URL or callback to navigate to the app */
26
+ href?: string
27
+ /** Click handler for the app */
28
+ onClick?: () => void
29
+ }
30
+
31
+ export type AppTopBarProps = {
32
+ /** Current app icon */
33
+ appIcon: ReactNode
34
+ /** Current app name */
35
+ appName: string
36
+ /** List of other available apps */
37
+ apps?: AppItem[]
38
+ /** Callback when an app is selected */
39
+ onAppSelect?: (app: AppItem) => void
40
+ /** Additional styling */
41
+ sx?: SxProps
42
+ }
43
+
44
+ /**
45
+ * A top navigation bar component that displays the current app and provides
46
+ * a dropdown menu to navigate to other apps.
47
+ */
48
+ export const AppTopBar = externalizeComponent(
49
+ observer((props: AppTopBarProps) => {
50
+ const { appIcon, appName, apps = [], onAppSelect, sx } = props
51
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
52
+ const isMenuOpen = Boolean(anchorEl)
53
+
54
+ const handleMenuOpen = (event: MouseEvent<HTMLElement>) => {
55
+ setAnchorEl(event.currentTarget)
56
+ }
57
+
58
+ const handleMenuClose = () => {
59
+ setAnchorEl(null)
60
+ }
61
+
62
+ const handleAppSelect = (app: AppItem) => {
63
+ handleMenuClose()
64
+
65
+ if (app.onClick) {
66
+ app.onClick()
67
+ } else if (app.href) {
68
+ window.location.href = app.href
69
+ }
70
+
71
+ onAppSelect?.(app)
72
+ }
73
+
74
+ return (
75
+ <>
76
+ <AppBar position="static" sx={sx}>
77
+ <Toolbar sx={{ minHeight: "64px !important" }}>
78
+ {/* App Icon */}
79
+ <Box sx={{ mr: 2, display: "flex", alignItems: "center" }}>
80
+ {appIcon}
81
+ </Box>
82
+
83
+ {/* App Name and Dropdown */}
84
+ <Box sx={{ display: "flex", alignItems: "center", flexGrow: 1 }}>
85
+ <Typography
86
+ variant="h6"
87
+ component="div"
88
+ sx={{
89
+ fontWeight: 700,
90
+ fontSize: "20px",
91
+ lineHeight: "24px",
92
+ letterSpacing: 0,
93
+ }}
94
+ >
95
+ {appName}
96
+ </Typography>
97
+
98
+ {/* Dropdown Button */}
99
+ {apps.length > 0 && (
100
+ <IconButton
101
+ color="inherit"
102
+ aria-label="switch app"
103
+ aria-controls="app-menu"
104
+ aria-haspopup="true"
105
+ onClick={handleMenuOpen}
106
+ sx={{
107
+ ml: 2,
108
+ width: 30,
109
+ height: 30,
110
+ borderRadius: "8px",
111
+ padding: "5px",
112
+ backgroundColor: (theme) =>
113
+ alpha(theme.palette.common.white, 0.1),
114
+ opacity: 1,
115
+ "&:hover": {
116
+ backgroundColor: (theme) =>
117
+ alpha(theme.palette.common.white, 0.16),
118
+ },
119
+ "& .MuiSvgIcon-root": {
120
+ fontSize: "10px",
121
+ width: "10px",
122
+ height: "8px",
123
+ },
124
+ }}
125
+ >
126
+ <DropdownArrowIcon />
127
+ </IconButton>
128
+ )}
129
+ </Box>
130
+ </Toolbar>
131
+ </AppBar>
132
+
133
+ {/* Backdrop for blur effect */}
134
+ <Backdrop
135
+ open={isMenuOpen}
136
+ onClick={handleMenuClose}
137
+ sx={{
138
+ backdropFilter: "blur(4px)",
139
+ backgroundColor: "rgba(0, 0, 0, 0.3)",
140
+ zIndex: (theme) => theme.zIndex.modal - 1,
141
+ }}
142
+ />
143
+
144
+ {/* Dropdown Menu */}
145
+ <Menu
146
+ id="app-menu"
147
+ anchorEl={anchorEl}
148
+ open={isMenuOpen}
149
+ onClose={handleMenuClose}
150
+ anchorOrigin={{
151
+ vertical: "bottom",
152
+ horizontal: "left",
153
+ }}
154
+ transformOrigin={{
155
+ vertical: "top",
156
+ horizontal: "left",
157
+ }}
158
+ sx={{
159
+ zIndex: (theme) => theme.zIndex.modal,
160
+ "& .MuiPaper-root": {
161
+ minWidth: 200,
162
+ mt: 1,
163
+ },
164
+ }}
165
+ >
166
+ {apps.map((app) => (
167
+ <MenuItem
168
+ key={app.id}
169
+ onClick={() => handleAppSelect(app)}
170
+ sx={{
171
+ display: "flex",
172
+ alignItems: "center",
173
+ gap: 2,
174
+ py: 1.5,
175
+ px: 2,
176
+ }}
177
+ >
178
+ <Box sx={{ display: "flex", alignItems: "center" }}>
179
+ {app.icon}
180
+ </Box>
181
+ <Typography variant="body1">{app.name}</Typography>
182
+ </MenuItem>
183
+ ))}
184
+ </Menu>
185
+ </>
186
+ )
187
+ }),
188
+ )
@@ -1,7 +1,7 @@
1
1
  import ClearIcon from "@mui/icons-material/Clear"
2
2
  import FilterListIcon from "@mui/icons-material/FilterList"
3
3
  import SearchIcon from "@mui/icons-material/Search"
4
- import { Box, Divider, Typography } from "@mui/material"
4
+ import { Box, Divider, Typography, useTheme } from "@mui/material"
5
5
  import {
6
6
  DataGrid,
7
7
  type DataGridProps,
@@ -101,6 +101,8 @@ export const WandelbotsDataGrid = externalizeComponent(
101
101
  CustomToolbar,
102
102
  selectFirstByDefault = false,
103
103
  }: WandelbotsDataGridProps<T>) => {
104
+ const theme = useTheme()
105
+
104
106
  // Internal state for selection when not controlled
105
107
  const [internalSelectedItem, setInternalSelectedItem] =
106
108
  useState<T | null>(null)
@@ -276,7 +278,8 @@ export const WandelbotsDataGrid = externalizeComponent(
276
278
  height: "100%",
277
279
  display: "flex",
278
280
  flexDirection: "column",
279
- background: "var(--background-paper-elevation-5, #202233)",
281
+ background:
282
+ theme.palette.backgroundPaperElevation?.[5] || "#202233",
280
283
  }}
281
284
  >
282
285
  <DataGrid
@@ -331,12 +334,13 @@ export const WandelbotsDataGrid = externalizeComponent(
331
334
  "& .MuiDataGrid-columnHeaders": {
332
335
  border: "none",
333
336
  borderBottom: "none !important",
334
- backgroundColor: "var(--background-paper-elevation-5, #202233)",
337
+ backgroundColor:
338
+ theme.palette.backgroundPaperElevation?.[5] || "#202233",
335
339
  },
336
340
  "& .MuiDataGrid-row": {
337
341
  cursor: onRowClick ? "pointer" : "default",
338
342
  border: "none",
339
- margin: "0px 0",
343
+ margin: "1px 0",
340
344
  position: "relative",
341
345
  "&:hover": {
342
346
  backgroundColor: "transparent !important",
@@ -252,7 +252,6 @@ export const RobotCard = externalizeComponent(
252
252
  <Card
253
253
  ref={cardRef}
254
254
  className={className}
255
- elevation={5}
256
255
  sx={{
257
256
  width: "100%",
258
257
  height: "100%",
@@ -264,10 +263,12 @@ export const RobotCard = externalizeComponent(
264
263
  minHeight: isLandscape
265
264
  ? { xs: 160, sm: 200, md: 250 }
266
265
  : { xs: 200, sm: 280, md: 350 },
267
- border:
268
- "1px solid var(--secondary-_states-outlinedBorder, #FFFFFF1F)",
266
+ border: `1px solid ${theme.palette.divider}`,
269
267
  borderRadius: "18px",
270
268
  boxShadow: "none",
269
+ backgroundColor:
270
+ theme.palette.backgroundPaperElevation?.[8] || "#2A2A3F",
271
+ backgroundImage: "none", // Override any gradient from elevation
271
272
  }}
272
273
  >
273
274
  {isLandscape ? (
@@ -367,7 +368,7 @@ export const RobotCard = externalizeComponent(
367
368
  variant="body1"
368
369
  sx={{
369
370
  mb: 0,
370
- color: "var(--text-secondary, #FFFFFFB2)",
371
+ color: theme.palette.text.secondary,
371
372
  textAlign: "left",
372
373
  }}
373
374
  >
@@ -511,7 +512,7 @@ export const RobotCard = externalizeComponent(
511
512
  variant="body1"
512
513
  sx={{
513
514
  mb: 0,
514
- color: "var(--text-secondary, #FFFFFFB2)",
515
+ color: theme.palette.text.secondary,
515
516
  }}
516
517
  >
517
518
  {t("RobotCard.Runtime.lb")}
@@ -58,9 +58,9 @@ export const RobotListItem = externalizeComponent(
58
58
  <Box
59
59
  className={className}
60
60
  sx={{
61
- border:
62
- "1px solid var(--secondary-_states-outlinedBorder, #FFFFFF1F)",
63
- background: "var(--background-paper-elevation-8, #292B3E)",
61
+ border: `1px solid ${theme.palette.divider}`,
62
+ background:
63
+ theme.palette.backgroundPaperElevation?.[8] || "#292B3F",
64
64
  height: 80,
65
65
  minHeight: "80px",
66
66
  borderRadius: "8px",
@@ -90,10 +90,10 @@ export const RobotListItem = externalizeComponent(
90
90
  width: 24,
91
91
  height: 24,
92
92
  "& svg": {
93
- fill: "var(--primary-main, #8E56FC) !important",
93
+ fill: `${theme.palette.primary.main} !important`,
94
94
  },
95
95
  "& svg path": {
96
- fill: "var(--primary-main, #8E56FC) !important",
96
+ fill: `${theme.palette.primary.main} !important`,
97
97
  },
98
98
  }}
99
99
  >
@@ -47,7 +47,7 @@ export const RobotSetupReadinessIndicator = externalizeComponent(
47
47
  backgroundColor:
48
48
  theme.palette.backgroundPaperElevation?.[11] ||
49
49
  theme.palette.background.paper,
50
- textColor: "var(--secondary-contrast, #FFFFFFDE)",
50
+ textColor: theme.palette.secondary.contrastText,
51
51
  }
52
52
  case RobotSetupReadinessState.ROBOT_DISCONNECTED:
53
53
  return {
@@ -56,7 +56,7 @@ export const RobotSetupReadinessIndicator = externalizeComponent(
56
56
  backgroundColor:
57
57
  theme.palette.backgroundPaperElevation?.[11] ||
58
58
  theme.palette.background.paper,
59
- textColor: "var(--secondary-contrast, #FFFFFFDE)",
59
+ textColor: theme.palette.secondary.contrastText,
60
60
  }
61
61
  case RobotSetupReadinessState.PRECONDITION_NOT_FULFILLED:
62
62
  default:
@@ -68,7 +68,7 @@ export const RobotSetupReadinessIndicator = externalizeComponent(
68
68
  backgroundColor:
69
69
  theme.palette.backgroundPaperElevation?.[11] ||
70
70
  theme.palette.background.paper,
71
- textColor: "var(--secondary-contrast, #FFFFFFDE)",
71
+ textColor: theme.palette.secondary.contrastText,
72
72
  }
73
73
  }
74
74
  }
@@ -0,0 +1,144 @@
1
+ import type { SxProps } from "@mui/material"
2
+ import { Box, Tab, Tabs } from "@mui/material"
3
+ import { observer } from "mobx-react-lite"
4
+ import { useState } from "react"
5
+ import { externalizeComponent } from "../externalizeComponent"
6
+
7
+ export interface TabItem {
8
+ /** Unique identifier for the tab */
9
+ id: string
10
+ /** Label text for the tab */
11
+ label: string
12
+ /** Content to display when tab is active */
13
+ content: React.ReactNode
14
+ /** Optional icon component to display with the tab */
15
+ icon?: React.ReactElement
16
+ }
17
+
18
+ export interface TabBarProps {
19
+ /** Array of tab items to display */
20
+ items: TabItem[]
21
+ /** Default active tab index */
22
+ defaultActiveTab?: number
23
+ /** Callback when tab changes */
24
+ onTabChange?: (index: number) => void
25
+ /** Additional styling */
26
+ sx?: SxProps
27
+ }
28
+
29
+ interface TabPanelProps {
30
+ children?: React.ReactNode
31
+ index: number
32
+ value: number
33
+ }
34
+
35
+ function TabPanel(props: TabPanelProps) {
36
+ const { children, value, index, ...other } = props
37
+
38
+ return (
39
+ <div
40
+ role="tabpanel"
41
+ hidden={value !== index}
42
+ id={`tabpanel-${index}`}
43
+ aria-labelledby={`tab-${index}`}
44
+ {...other}
45
+ >
46
+ {value === index && <Box>{children}</Box>}
47
+ </div>
48
+ )
49
+ }
50
+
51
+ /**
52
+ * A styled tab bar component with configurable tabs and content.
53
+ * Features the same styling as the Wandelbots design system with rounded tabs
54
+ * and smooth transitions.
55
+ */
56
+ export const TabBar = externalizeComponent(
57
+ observer((props: TabBarProps) => {
58
+ const { items, defaultActiveTab = 0, onTabChange, sx } = props
59
+ const [activeTab, setActiveTab] = useState(defaultActiveTab)
60
+
61
+ const handleTabChange = (
62
+ _event: React.SyntheticEvent,
63
+ newValue: number,
64
+ ) => {
65
+ setActiveTab(newValue)
66
+ onTabChange?.(newValue)
67
+ }
68
+
69
+ return (
70
+ <Box
71
+ sx={{ height: "100%", display: "flex", flexDirection: "column", ...sx }}
72
+ >
73
+ {/* Tabs */}
74
+ <Box sx={{ px: 3, pt: 3, pb: 3 }}>
75
+ <Tabs
76
+ value={activeTab}
77
+ onChange={handleTabChange}
78
+ sx={{
79
+ minHeight: "auto",
80
+ backgroundColor: "transparent",
81
+ mt: 3,
82
+ "& .MuiTabs-indicator": {
83
+ display: "none", // Hide the default indicator
84
+ },
85
+ "& .MuiTabs-flexContainer": {
86
+ gap: 4,
87
+ },
88
+ }}
89
+ >
90
+ {items.map((item, index) => (
91
+ <Tab
92
+ key={item.id}
93
+ label={item.label}
94
+ icon={item.icon}
95
+ iconPosition="start"
96
+ disableRipple
97
+ sx={{
98
+ minHeight: "auto",
99
+ minWidth: "auto",
100
+ padding: "4px 8px",
101
+ borderRadius: "10px",
102
+ backgroundColor: (theme) =>
103
+ theme.palette.backgroundPaperElevation?.[11] || "#32344B",
104
+ color: "text.primary",
105
+ opacity: activeTab === index ? 1 : 0.38,
106
+ textTransform: "none",
107
+ fontWeight: 500,
108
+ transition: "all 0.2s ease-in-out",
109
+ "&:hover": {
110
+ opacity: activeTab === index ? 1 : 0.6,
111
+ },
112
+ "&.Mui-selected": {
113
+ opacity: 1,
114
+ backgroundColor: (theme) =>
115
+ theme.palette.backgroundPaperElevation?.[11] || "#32344B",
116
+ color: "text.primary",
117
+ },
118
+ "&:focus": {
119
+ outline: "none",
120
+ },
121
+ "&:active": {
122
+ transform: "none",
123
+ },
124
+ }}
125
+ />
126
+ ))}
127
+ </Tabs>
128
+ </Box>
129
+
130
+ {/* Border line */}
131
+ <Box sx={{ mx: 3, borderBottom: 1, borderColor: "divider" }} />
132
+
133
+ {/* Tab Content */}
134
+ <Box sx={{ flex: 1, overflow: "auto" }}>
135
+ {items.map((item, index) => (
136
+ <TabPanel key={item.id} value={activeTab} index={index}>
137
+ {item.content}
138
+ </TabPanel>
139
+ ))}
140
+ </Box>
141
+ </Box>
142
+ )
143
+ }),
144
+ )
@@ -0,0 +1,13 @@
1
+ import { SvgIcon, type SvgIconProps } from "@mui/material"
2
+
3
+ export const DropdownArrowIcon = (props: SvgIconProps) => {
4
+ return (
5
+ <SvgIcon {...props} viewBox="0 0 10 8">
6
+ <path
7
+ d="M8.825 0.9125L5 4.72917L1.175 0.9125L0 2.0875L5 7.0875L10 2.0875L8.825 0.9125Z"
8
+ fill="currentColor"
9
+ fillOpacity="0.56"
10
+ />
11
+ </SvgIcon>
12
+ )
13
+ }
@@ -0,0 +1,3 @@
1
+ <svg width="10" height="8" viewBox="0 0 10 8" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M8.825 0.9125L5 4.72917L1.175 0.9125L0 2.0875L5 7.0875L10 2.0875L8.825 0.9125Z" fill="currentColor"/>
3
+ </svg>
@@ -1,4 +1,5 @@
1
1
  export { default as ArrowForwardFilledIcon } from "./arrowForwardFilled.svg"
2
+ export { default as ChevronDownIcon } from "./chevronDown.svg"
2
3
  export { default as ExpandFilledIcon } from "./expandFilled.svg"
3
4
  export { default as HomeIcon } from "./home.svg"
4
5
  export { default as InfoOutlinedIcon } from "./infoOutlined.svg"
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./components/3d-viewport/PresetEnvironment"
2
2
  export * from "./components/3d-viewport/SafetyZonesRenderer"
3
3
  export * from "./components/3d-viewport/TrajectoryRenderer"
4
+ export * from "./components/AppTopBar"
4
5
  export * from "./components/CycleTimer"
5
6
  export * from "./components/DataGrid"
6
7
  export * from "./components/jogging/JoggingCartesianAxisControl"
@@ -25,6 +26,7 @@ export * from "./components/robots/SupportedRobot"
25
26
  export * from "./components/RobotSetupReadinessIndicator"
26
27
  export * from "./components/safetyBar/SafetyBar"
27
28
  export * from "./components/SelectableFab"
29
+ export * from "./components/TabBar"
28
30
  export * from "./components/utils/hooks"
29
31
  export * from "./components/utils/interpolation"
30
32
  export * from "./components/VelocitySlider"