@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/dist/components/AppTopBar.d.ts +34 -0
- package/dist/components/AppTopBar.d.ts.map +1 -0
- package/dist/components/DataGrid.d.ts.map +1 -1
- package/dist/components/RobotCard.d.ts.map +1 -1
- package/dist/components/TabBar.d.ts +30 -0
- package/dist/components/TabBar.d.ts.map +1 -0
- package/dist/icons/DropdownArrowIcon.d.ts +3 -0
- package/dist/icons/DropdownArrowIcon.d.ts.map +1 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/index.cjs +46 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4724 -4505
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AppTopBar.md +84 -0
- package/src/components/AppTopBar.tsx +188 -0
- package/src/components/DataGrid.tsx +8 -4
- package/src/components/RobotCard.tsx +6 -5
- package/src/components/RobotListItem.tsx +5 -5
- package/src/components/RobotSetupReadinessIndicator.tsx +3 -3
- package/src/components/TabBar.tsx +144 -0
- package/src/icons/DropdownArrowIcon.tsx +13 -0
- package/src/icons/chevronDown.svg +3 -0
- package/src/icons/index.ts +1 -0
- package/src/index.ts +2 -0
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.
|
|
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:
|
|
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:
|
|
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: "
|
|
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:
|
|
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:
|
|
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
|
-
|
|
63
|
-
|
|
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:
|
|
93
|
+
fill: `${theme.palette.primary.main} !important`,
|
|
94
94
|
},
|
|
95
95
|
"& svg path": {
|
|
96
|
-
fill:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
+
}
|
package/src/icons/index.ts
CHANGED
|
@@ -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"
|