@orsetra/shared-ui 1.9.1 → 1.9.2
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.
|
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react"
|
|
|
4
4
|
import { usePathname, useSearchParams, useParams } from "next/navigation"
|
|
5
5
|
import { MainSidebar, type SidebarMode } from "./sidebar/main-sidebar"
|
|
6
6
|
import { Sidebar, SidebarProvider, useSidebar } from "./sidebar/sidebar"
|
|
7
|
-
import { type SidebarMenus, type MainMenuItem, resolveIcon } from "./sidebar/data"
|
|
7
|
+
import { type SidebarMenus, type MainMenuItem, type SidebarMenuMeta, resolveIcon } from "./sidebar/data"
|
|
8
8
|
import { Button } from "../ui"
|
|
9
9
|
import { Logo } from "../ui/logo"
|
|
10
10
|
import { getMenuFromPath } from "../../lib/menu-utils"
|
|
@@ -50,6 +50,7 @@ function LayoutContent({
|
|
|
50
50
|
const [mainMenuItems, setMainMenuItems] = useState<MainMenuItem[]>([])
|
|
51
51
|
const [fetchedSidebarMenus, setFetchedSidebarMenus] = useState<SidebarMenus>({})
|
|
52
52
|
const [sectionLabels, setSectionLabels] = useState<Record<string, string>>({})
|
|
53
|
+
const [sidebarMenuMeta, setSidebarMenuMeta] = useState<Record<string, SidebarMenuMeta>>({})
|
|
53
54
|
|
|
54
55
|
useEffect(() => {
|
|
55
56
|
if (!fetchMenus) return
|
|
@@ -69,6 +70,7 @@ function LayoutContent({
|
|
|
69
70
|
|
|
70
71
|
const menus: SidebarMenus = {}
|
|
71
72
|
const labels: Record<string, string> = {}
|
|
73
|
+
const meta: Record<string, SidebarMenuMeta> = {}
|
|
72
74
|
Object.entries(subMenus).forEach(([key, subMenu]) => {
|
|
73
75
|
menus[key] = subMenu.items.map((item) => ({
|
|
74
76
|
id: item.id,
|
|
@@ -78,9 +80,11 @@ function LayoutContent({
|
|
|
78
80
|
icon: resolveIcon(item.icon),
|
|
79
81
|
}))
|
|
80
82
|
labels[key] = subMenu.header
|
|
83
|
+
meta[key] = { header: subMenu.header, description: subMenu.description }
|
|
81
84
|
})
|
|
82
85
|
setFetchedSidebarMenus(menus)
|
|
83
86
|
setSectionLabels(labels)
|
|
87
|
+
setSidebarMenuMeta(meta)
|
|
84
88
|
})
|
|
85
89
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
90
|
}, [])
|
|
@@ -148,6 +152,7 @@ function LayoutContent({
|
|
|
148
152
|
mode={isHidden ? "expanded" : mode}
|
|
149
153
|
sidebarMenus={effectiveSidebarMenus}
|
|
150
154
|
mainMenuItems={mainMenuItems}
|
|
155
|
+
sidebarMenuMeta={sidebarMenuMeta}
|
|
151
156
|
/>
|
|
152
157
|
|
|
153
158
|
<main className="flex-1 bg-ibm-gray-10 min-h-0 overflow-auto">
|
|
@@ -88,9 +88,15 @@ export interface ApiSubMenuItem {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export interface ApiSubMenu {
|
|
91
|
-
id:
|
|
92
|
-
header:
|
|
93
|
-
|
|
91
|
+
id: string
|
|
92
|
+
header: string
|
|
93
|
+
description?: string
|
|
94
|
+
items: ApiSubMenuItem[]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface SidebarMenuMeta {
|
|
98
|
+
header?: string
|
|
99
|
+
description?: string
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
export interface ApiMenuData {
|
|
@@ -13,14 +13,14 @@ export interface FlyoutDefaultLink {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
interface MainSidebarFlyoutProps {
|
|
16
|
-
item:
|
|
17
|
-
subItems:
|
|
18
|
-
onMouseEnter:
|
|
19
|
-
onMouseLeave:
|
|
20
|
-
main_base_url?:
|
|
21
|
-
defaultTitle?:
|
|
22
|
-
defaultDescription?:
|
|
23
|
-
defaultLinks?:
|
|
16
|
+
item: MainMenuItem | null
|
|
17
|
+
subItems: SubMenuItem[]
|
|
18
|
+
onMouseEnter: () => void
|
|
19
|
+
onMouseLeave: () => void
|
|
20
|
+
main_base_url?: string
|
|
21
|
+
defaultTitle?: string
|
|
22
|
+
defaultDescription?: string
|
|
23
|
+
defaultLinks?: FlyoutDefaultLink[]
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function MainSidebarFlyout({
|
|
@@ -92,35 +92,26 @@ export function MainSidebarFlyout({
|
|
|
92
92
|
</nav>
|
|
93
93
|
</>
|
|
94
94
|
) : (
|
|
95
|
-
/* ── Default state:
|
|
96
|
-
<div className="flex flex-col h-full">
|
|
97
|
-
{/*
|
|
98
|
-
<div className="
|
|
99
|
-
<
|
|
100
|
-
<div className="h-8 w-8 rounded-md bg-interactive flex items-center justify-center flex-shrink-0">
|
|
101
|
-
<Home className="h-4 w-4 text-white" />
|
|
102
|
-
</div>
|
|
103
|
-
<span className="text-sm font-semibold text-text-primary truncate">{defaultTitle}</span>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
95
|
+
/* ── Default state: centré verticalement ────────────────────────── */
|
|
96
|
+
<div className="flex flex-col h-full items-center justify-center px-6 gap-6">
|
|
97
|
+
{/* Titre + texte + CTA */}
|
|
98
|
+
<div className="flex flex-col items-center gap-3 w-full">
|
|
99
|
+
<span className="text-sm font-semibold text-text-primary text-center">{defaultTitle}</span>
|
|
106
100
|
{defaultDescription && (
|
|
107
|
-
<p className="text-xs text-text-secondary leading-relaxed
|
|
108
|
-
{defaultDescription}
|
|
109
|
-
</p>
|
|
101
|
+
<p className="text-xs text-text-secondary text-center leading-relaxed">{defaultDescription}</p>
|
|
110
102
|
)}
|
|
111
|
-
|
|
112
103
|
<a
|
|
113
104
|
href={homePath}
|
|
114
|
-
className="flex items-center justify-center gap-2 w-full px-3 py-2
|
|
105
|
+
className="flex items-center justify-center gap-2 w-full px-3 py-2 border border-ui-border text-text-secondary hover:text-text-primary hover:bg-ui-background text-sm font-medium transition-colors"
|
|
115
106
|
>
|
|
116
107
|
<Home className="h-4 w-4 flex-shrink-0" />
|
|
117
108
|
Tableau de bord
|
|
118
109
|
</a>
|
|
119
110
|
</div>
|
|
120
111
|
|
|
121
|
-
{/*
|
|
112
|
+
{/* Liens ressources */}
|
|
122
113
|
{defaultLinks.length > 0 && (
|
|
123
|
-
<nav className="
|
|
114
|
+
<nav className="w-full space-y-0.5">
|
|
124
115
|
<p className="px-2 pb-1 text-[10px] font-semibold text-text-secondary uppercase tracking-widest">
|
|
125
116
|
Ressources
|
|
126
117
|
</p>
|
|
@@ -132,7 +123,7 @@ export function MainSidebarFlyout({
|
|
|
132
123
|
key={i}
|
|
133
124
|
href={link.href}
|
|
134
125
|
{...(isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
|
135
|
-
className="flex items-start gap-x-3 px-2
|
|
126
|
+
className="flex items-start gap-x-3 px-2 py-2 border-l-4 border-transparent hover:bg-ui-background hover:border-interactive transition-colors"
|
|
136
127
|
>
|
|
137
128
|
<LinkIcon className="h-4 w-4 flex-shrink-0 mt-0.5 text-text-secondary" />
|
|
138
129
|
<div className="min-w-0 flex-1">
|
|
@@ -4,7 +4,9 @@ import * as React from "react"
|
|
|
4
4
|
import { cn } from "../../../lib/utils"
|
|
5
5
|
import { Button } from "../../ui/button"
|
|
6
6
|
import { Logo } from "../../ui/logo"
|
|
7
|
-
import { type MainMenuItem, type SidebarMenus } from "./data"
|
|
7
|
+
import { type MainMenuItem, type SidebarMenus, type SidebarMenuMeta } from "./data"
|
|
8
|
+
|
|
9
|
+
export type { SidebarMenuMeta }
|
|
8
10
|
import { X, Menu, ChevronDown, ChevronRight } from "lucide-react"
|
|
9
11
|
import { useIsMobile } from "../../../hooks/use-mobile"
|
|
10
12
|
import { MainSidebarFlyout, type FlyoutDefaultLink } from "./main-sidebar-flyout"
|
|
@@ -23,9 +25,8 @@ interface MainSidebarProps {
|
|
|
23
25
|
onSecondarySidebarOpen?: () => void
|
|
24
26
|
sidebarMenus?: SidebarMenus
|
|
25
27
|
mainMenuItems?: MainMenuItem[]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
flyoutDefaultLinks?: FlyoutDefaultLink[]
|
|
28
|
+
/** Per-submenu metadata (header + description) derived from the API menu response. */
|
|
29
|
+
sidebarMenuMeta?: Record<string, SidebarMenuMeta>
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export function MainSidebar({
|
|
@@ -38,9 +39,7 @@ export function MainSidebar({
|
|
|
38
39
|
onSecondarySidebarOpen,
|
|
39
40
|
sidebarMenus = {},
|
|
40
41
|
mainMenuItems = [],
|
|
41
|
-
|
|
42
|
-
flyoutDefaultDescription,
|
|
43
|
-
flyoutDefaultLinks = [],
|
|
42
|
+
sidebarMenuMeta = {},
|
|
44
43
|
}: MainSidebarProps) {
|
|
45
44
|
const isMinimized = mode === 'minimized'
|
|
46
45
|
const isMobile = useIsMobile()
|
|
@@ -153,6 +152,17 @@ export function MainSidebar({
|
|
|
153
152
|
}
|
|
154
153
|
}
|
|
155
154
|
|
|
155
|
+
// Default flyout content derived from the "main" submenu in the loaded menu data
|
|
156
|
+
const mainMeta = sidebarMenuMeta["main"] ?? {}
|
|
157
|
+
const defaultTitle = mainMeta.header ?? "Camel X Platform"
|
|
158
|
+
const defaultDesc = mainMeta.description ?? undefined
|
|
159
|
+
const defaultLinks: FlyoutDefaultLink[] = (sidebarMenus["main"] ?? []).map(sub => ({
|
|
160
|
+
label: sub.name,
|
|
161
|
+
href: sub.href,
|
|
162
|
+
description: sub.description,
|
|
163
|
+
icon: sub.icon,
|
|
164
|
+
}))
|
|
165
|
+
|
|
156
166
|
const handleSubMenuClick = (e: React.MouseEvent, menuId: string, href: string) => {
|
|
157
167
|
e.preventDefault()
|
|
158
168
|
window.location.href = buildSubItemHref(menuId, href)
|
|
@@ -302,9 +312,9 @@ export function MainSidebar({
|
|
|
302
312
|
onMouseEnter={cancelCloseTimer}
|
|
303
313
|
onMouseLeave={startCloseTimer}
|
|
304
314
|
main_base_url={main_base_url}
|
|
305
|
-
defaultTitle={
|
|
306
|
-
defaultDescription={
|
|
307
|
-
defaultLinks={
|
|
315
|
+
defaultTitle={defaultTitle}
|
|
316
|
+
defaultDescription={defaultDesc}
|
|
317
|
+
defaultLinks={defaultLinks}
|
|
308
318
|
/>
|
|
309
319
|
)}
|
|
310
320
|
|