@wandelbots/wandelbots-js-react-components 2.53.0 → 2.54.0-pr.fix-always-show-full-badge.399.7adae47

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.53.0",
3
+ "version": "2.54.0-pr.fix-always-show-full-badge.399.7adae47",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -59,9 +59,9 @@
59
59
  "@rollup/plugin-node-resolve": "^16.0.0",
60
60
  "@rollup/plugin-terser": "^0.4.4",
61
61
  "@rollup/plugin-typescript": "^12.1.2",
62
- "@storybook/addon-docs": "^9.1.6",
63
- "@storybook/react-vite": "^9.1.6",
64
- "@storybook/test-runner": "^0.23.0",
62
+ "@storybook/addon-docs": "^10.0.1",
63
+ "@storybook/react-vite": "^10.0.1",
64
+ "@storybook/test-runner": "^0.24.1",
65
65
  "@svgr/rollup": "^8.1.0",
66
66
  "@testing-library/jest-dom": "^6.6.3",
67
67
  "@testing-library/react": "^16.3.0",
@@ -72,7 +72,7 @@
72
72
  "@vitejs/plugin-react": "^4.3.4",
73
73
  "@wandelbots/nova-js": "^2.1.0",
74
74
  "add": "^2.0.6",
75
- "eslint-plugin-storybook": "^9.1.6",
75
+ "eslint-plugin-storybook": "^10.0.1",
76
76
  "glob": "^11.0.1",
77
77
  "http-server": "^14.1.1",
78
78
  "husky": "^9.1.7",
@@ -95,7 +95,7 @@
95
95
  "rollup-plugin-peer-deps-external": "^2.2.4",
96
96
  "rollup-plugin-postcss": "^4.0.2",
97
97
  "semantic-release": "^24.2.3",
98
- "storybook": "^9.1.6",
98
+ "storybook": "^10.0.1",
99
99
  "storybook-preset-inline-svg": "^1.0.1",
100
100
  "three": "^0.174.0",
101
101
  "three-stdlib": "^2.35.14",
@@ -1,5 +1,5 @@
1
1
  import type { SxProps } from "@mui/material"
2
- import { Box, Tab, Tabs } from "@mui/material"
2
+ import { Badge, Box, Tab, Tabs } from "@mui/material"
3
3
  import { observer } from "mobx-react-lite"
4
4
  import { useEffect, useState } from "react"
5
5
  import { externalizeComponent } from "../externalizeComponent"
@@ -13,6 +13,24 @@ export interface TabItem {
13
13
  content: React.ReactNode
14
14
  /** Optional icon component to display with the tab */
15
15
  icon?: React.ReactElement
16
+ /** Optional badge configuration */
17
+ badge?: {
18
+ /** Badge content (number or string) */
19
+ content: React.ReactNode
20
+ /** Badge color variant */
21
+ color?:
22
+ | "default"
23
+ | "primary"
24
+ | "secondary"
25
+ | "error"
26
+ | "info"
27
+ | "success"
28
+ | "warning"
29
+ /** Maximum number to display (e.g., 99+) */
30
+ max?: number
31
+ /** Show badge even when content is zero */
32
+ showZero?: boolean
33
+ }
16
34
  }
17
35
 
18
36
  export interface TabBarProps {
@@ -71,6 +89,8 @@ export const TabBar = externalizeComponent(
71
89
  const [uncontrolledActiveTab, setUncontrolledActiveTab] =
72
90
  useState(defaultActiveTab)
73
91
 
92
+ const currentValue = isControlled ? activeTab! : uncontrolledActiveTab
93
+
74
94
  // Keep uncontrolled state in range when items change
75
95
  useEffect(() => {
76
96
  if (isControlled) return
@@ -83,8 +103,6 @@ export const TabBar = externalizeComponent(
83
103
  }
84
104
  }, [items.length, isControlled, uncontrolledActiveTab])
85
105
 
86
- const currentValue = isControlled ? activeTab! : uncontrolledActiveTab
87
-
88
106
  const handleTabChange = (
89
107
  _event: React.SyntheticEvent,
90
108
  newValue: number,
@@ -101,62 +119,161 @@ export const TabBar = externalizeComponent(
101
119
  sx={{ height: "100%", display: "flex", flexDirection: "column", ...sx }}
102
120
  >
103
121
  {/* Tabs */}
104
- <Box sx={{ px: 0, py: 0 }}>
122
+ <Box
123
+ sx={{
124
+ px: 0,
125
+ py: 0,
126
+ overflow: "visible",
127
+ position: "relative",
128
+ // Extra padding to prevent badge clipping
129
+ // Top: accommodates badge positioned at top: -6px with 20px height
130
+ // Right: accommodates badge positioned at right: -8px with 20px width
131
+ paddingTop: (theme) => theme.spacing(2), // 16px
132
+ paddingRight: (theme) => theme.spacing(2.5), // 20px
133
+ }}
134
+ >
105
135
  <Tabs
106
136
  value={currentValue}
107
137
  onChange={handleTabChange}
108
138
  sx={{
109
139
  minHeight: "32px",
110
140
  backgroundColor: "transparent",
141
+ overflow: "visible",
111
142
  "& .MuiTabs-indicator": {
112
143
  display: "none", // Hide the default indicator
113
144
  },
114
145
  "& .MuiTabs-flexContainer": {
115
146
  gap: 2,
147
+ overflow: "visible",
148
+ paddingTop: 0,
149
+ paddingBottom: 0,
150
+ },
151
+ "& .MuiTabs-scroller": {
152
+ overflow: "visible !important",
153
+ },
154
+ "& .MuiTab-root": {
155
+ overflow: "visible",
116
156
  },
117
157
  }}
118
158
  >
119
- {items.map((item, index) => (
120
- <Tab
121
- key={item.id}
122
- label={item.label}
123
- icon={item.icon}
124
- iconPosition="start"
125
- disableRipple
126
- sx={{
127
- minHeight: "32px",
128
- height: "32px",
129
- padding: "0px 10px",
130
- borderRadius: "12px",
131
- backgroundColor: (theme) =>
132
- theme.palette.backgroundPaperElevation?.[11] || "#32344B",
133
- color: "text.primary",
134
- opacity: currentValue === index ? 1 : 0.38,
135
- fontSize: "13px",
136
- transition: "all 0.2s ease-in-out",
137
- "&:hover": {
138
- opacity: currentValue === index ? 1 : 0.6,
139
- },
140
- "&.Mui-selected": {
141
- opacity: 1,
159
+ {items.map((item, index) => {
160
+ // Helper functions for badge logic
161
+ const getBadgeContent = () => {
162
+ if (!item.badge) return undefined
163
+ const content = item.badge.content
164
+ const max = item.badge.max
165
+
166
+ // Handle numeric content with max limit
167
+ if (typeof content === "number" && max && content > max) {
168
+ return `${max}+`
169
+ }
170
+
171
+ return content
172
+ }
173
+
174
+ const shouldShowBadge = () => {
175
+ if (!item.badge) return false
176
+ const content = item.badge.content
177
+ const showZero = item.badge.showZero
178
+
179
+ // If content is 0 and showZero is false, hide badge
180
+ if (content === 0 && !showZero) return false
181
+
182
+ return true
183
+ }
184
+
185
+ const badgeContent = getBadgeContent()
186
+ const showBadge = shouldShowBadge()
187
+
188
+ const handleTabClick = () => {
189
+ if (!isControlled) {
190
+ setUncontrolledActiveTab(index)
191
+ }
192
+ onTabChange?.(index)
193
+ }
194
+
195
+ const tab = (
196
+ <Tab
197
+ label={item.label}
198
+ icon={item.icon}
199
+ iconPosition="start"
200
+ disableRipple
201
+ onClick={handleTabClick}
202
+ sx={{
203
+ minHeight: "32px",
204
+ height: "32px",
205
+ padding: "0px 10px",
206
+ borderRadius: "12px",
142
207
  backgroundColor: (theme) =>
143
208
  theme.palette.backgroundPaperElevation?.[11] || "#32344B",
144
209
  color: "text.primary",
145
- },
146
- "&:focus": {
147
- outline: "none",
148
- },
149
- "&:active": {
150
- transform: "none",
151
- },
152
- }}
153
- />
154
- ))}
210
+ opacity: currentValue === index ? 1 : 0.38,
211
+ fontSize: "13px",
212
+ transition: "all 0.2s ease-in-out",
213
+ position: "relative",
214
+ overflow: "visible",
215
+ "&:hover": {
216
+ opacity: currentValue === index ? 1 : 0.6,
217
+ },
218
+ "&.Mui-selected": {
219
+ opacity: 1,
220
+ backgroundColor: (theme) =>
221
+ theme.palette.backgroundPaperElevation?.[11] ||
222
+ "#32344B",
223
+ color: "text.primary",
224
+ },
225
+ "&:focus": {
226
+ outline: "none",
227
+ },
228
+ "&:active": {
229
+ transform: "none",
230
+ },
231
+ }}
232
+ />
233
+ )
234
+
235
+ if (!showBadge) {
236
+ return (
237
+ <Box key={item.id} sx={{ display: "inline-flex" }}>
238
+ {tab}
239
+ </Box>
240
+ )
241
+ }
242
+
243
+ return (
244
+ <Badge
245
+ key={item.id}
246
+ badgeContent={badgeContent}
247
+ color={item.badge?.color || "error"}
248
+ max={item.badge?.max}
249
+ showZero={item.badge?.showZero}
250
+ anchorOrigin={{
251
+ vertical: "top",
252
+ horizontal: "right",
253
+ }}
254
+ overlap="rectangular"
255
+ sx={{
256
+ "& .MuiBadge-badge": {
257
+ // Ensure badge doesn't inherit tab opacity
258
+ opacity: "1 !important",
259
+ },
260
+ }}
261
+ >
262
+ {tab}
263
+ </Badge>
264
+ )
265
+ })}
155
266
  </Tabs>
156
267
  </Box>
157
268
 
158
269
  {/* Border line */}
159
- <Box sx={{ mt: "32px", borderBottom: 1, borderColor: "divider" }} />
270
+ <Box
271
+ sx={{
272
+ mt: (theme) => theme.spacing(2),
273
+ borderBottom: 1,
274
+ borderColor: "divider",
275
+ }}
276
+ />
160
277
 
161
278
  {/* Tab Content */}
162
279
  <Box sx={{ flex: 1, overflow: "auto" }}>