@orsetra/shared-ui 1.5.2 → 1.5.4

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.
@@ -106,6 +106,7 @@ function LayoutContent({
106
106
  main_base_url={main_base_url}
107
107
  sectionLabels={sectionLabels}
108
108
  getCurrentMenuItem={getCurrentMenuItem}
109
+ mainMenuItems={mainMenuItems}
109
110
  />
110
111
  )}
111
112
 
@@ -20,7 +20,7 @@ import {
20
20
  } from "../../ui/tooltip"
21
21
  import { Menu } from "lucide-react"
22
22
  import { Logo } from "../../ui/logo"
23
- import { type SidebarMenus, type SubMenuItem } from "./data"
23
+ import { type SidebarMenus, type SubMenuItem, type MainMenuItem } from "./data"
24
24
  import { Skeleton } from "../skeleton"
25
25
 
26
26
  const SIDEBAR_STORAGE_KEY = "sidebar:state"
@@ -179,11 +179,12 @@ interface SidebarProps {
179
179
  main_base_url?: string
180
180
  sectionLabels?: Record<string, string>
181
181
  getCurrentMenuItem?: (pathname: string, searchParams: URLSearchParams) => string
182
+ mainMenuItems?: MainMenuItem[]
182
183
  }
183
184
 
184
185
 
185
186
 
186
- function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_url = "", sectionLabels = {}, getCurrentMenuItem }: SidebarProps = {}) {
187
+ function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_url = "", sectionLabels = {}, getCurrentMenuItem, mainMenuItems = [] }: SidebarProps = {}) {
187
188
  const pathname = usePathname()
188
189
  const searchParams = useSearchParams()
189
190
  const { state, toggleSidebar } = useSidebar()
@@ -238,6 +239,37 @@ function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_u
238
239
  "flex-1 overflow-y-auto",
239
240
  isCollapsed ? "px-1 py-3 space-y-0.5" : "px-3 py-4 space-y-1"
240
241
  )}>
242
+ {/* Main menu items — only shown in collapsed mode, excluding the current secondary menu */}
243
+ {isCollapsed && mainMenuItems.length > 0 && (
244
+ <>
245
+ {mainMenuItems.filter((item) => item.id !== currentMenu).map((item) => {
246
+ const href = `${main_base_url}/${item.id}`
247
+ const isActive = currentMenu === item.id
248
+ const linkEl = (
249
+ <Link
250
+ key={item.id}
251
+ href={href.startsWith('http') ? new URL(href).pathname : href}
252
+ className={cn(
253
+ "flex items-center justify-center p-2 transition-colors border-l-4",
254
+ isActive
255
+ ? "bg-interactive/10 text-interactive border-interactive"
256
+ : "text-text-secondary hover:bg-ui-background hover:text-text-primary border-transparent"
257
+ )}
258
+ >
259
+ <item.icon className={cn("h-4 w-4 flex-shrink-0", isActive ? "text-interactive" : "text-text-secondary")} />
260
+ </Link>
261
+ )
262
+ return (
263
+ <Tooltip key={item.id}>
264
+ <TooltipTrigger asChild>{linkEl}</TooltipTrigger>
265
+ <TooltipContent side="right" align="center">{item.label}</TooltipContent>
266
+ </Tooltip>
267
+ )
268
+ })}
269
+ <div className="border-t border-ui-border mx-1 my-1" />
270
+ </>
271
+ )}
272
+
241
273
  {currentNavigation.map((item) => {
242
274
  const activeItemId = getCurrentMenuItem ? getCurrentMenuItem(pathname, searchParams) : null
243
275
  const isActive = activeItemId
@@ -265,7 +297,7 @@ function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_u
265
297
  : "text-text-secondary hover:bg-ui-background hover:text-text-primary border-transparent"
266
298
  )}
267
299
  >
268
- <item.icon className={cn("h-4 w-4 flex-shrink-0", isActive ? "text-interactive" : "text-text-secondary")} />
300
+ <item.icon className={cn("h-4 w-4 flex-shrink-0", isActive ? "text-interactive" : "text-ibm-blue-50")} />
269
301
  {!isCollapsed && item.name}
270
302
  </Link>
271
303
  )
@@ -14,7 +14,10 @@ export interface DetailPageHeaderTab {
14
14
  id: string
15
15
  label: string
16
16
  icon?: ElementType
17
- href: string
17
+ /** Navigate to a URL when the tab is clicked */
18
+ href?: string
19
+ /** Call a callback instead of navigating (for state-based tab switching) */
20
+ action?: () => void
18
21
  }
19
22
 
20
23
  export interface DetailPageHeaderProps {
@@ -104,16 +107,20 @@ export function DetailPageHeader({
104
107
  {tabBar?.map((tab) => {
105
108
  const Icon = tab.icon
106
109
  const isActive = activeTab === tab.id
110
+ const tabClassName = `inline-flex items-center gap-2 px-5 py-2.5 text-sm font-medium transition-colors whitespace-nowrap ${
111
+ isActive ? TAB_ACTIVE : TAB_INACTIVE
112
+ }`
113
+ const content = <>{Icon && <Icon className="h-4 w-4" />}{tab.label}</>
114
+ if (tab.action) {
115
+ return (
116
+ <button key={tab.id} type="button" onClick={tab.action} className={tabClassName}>
117
+ {content}
118
+ </button>
119
+ )
120
+ }
107
121
  return (
108
- <Link
109
- key={tab.id}
110
- href={tab.href}
111
- className={`inline-flex items-center gap-2 px-5 py-2.5 text-sm font-medium transition-colors whitespace-nowrap ${
112
- isActive ? TAB_ACTIVE : TAB_INACTIVE
113
- }`}
114
- >
115
- {Icon && <Icon className="h-4 w-4" />}
116
- {tab.label}
122
+ <Link key={tab.id} href={tab.href!} className={tabClassName}>
123
+ {content}
117
124
  </Link>
118
125
  )
119
126
  })}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orsetra/shared-ui",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Shared UI components for Orsetra platform",
5
5
  "main": "./index.ts",
6
6
  "types": "./index.ts",