@orsetra/shared-ui 1.0.60 → 1.0.62
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.
|
@@ -30,3 +30,4 @@ export {
|
|
|
30
30
|
// Skeleton is exported from ../ui to avoid duplicate exports
|
|
31
31
|
export { RootLayoutWrapper, ibmPlexSans, ibmPlexMono } from './root-layout-wrapper'
|
|
32
32
|
export { LayoutContainer } from './layout-container'
|
|
33
|
+
export { PageWithSidePanel } from './page-with-side-panel'
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { ReactNode, useState } from "react"
|
|
4
|
+
import { X, ChevronLeft } from "lucide-react"
|
|
5
|
+
import { Button } from "../ui/button"
|
|
6
|
+
|
|
7
|
+
interface PageWithSidePanelProps {
|
|
8
|
+
children: ReactNode
|
|
9
|
+
sidePanel?: ReactNode
|
|
10
|
+
sidePanelHeader?: ReactNode
|
|
11
|
+
sidePanelWidth?: "sm" | "md" | "lg"
|
|
12
|
+
closable?: boolean
|
|
13
|
+
defaultOpen?: boolean
|
|
14
|
+
showBorder?: boolean
|
|
15
|
+
onClose?: () => void
|
|
16
|
+
onOpen?: () => void
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const PANEL_WIDTHS = {
|
|
20
|
+
sm: "w-64", // 256px
|
|
21
|
+
md: "w-72", // 288px
|
|
22
|
+
lg: "w-80", // 320px
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const PANEL_PADDING = {
|
|
26
|
+
sm: "lg:pr-64",
|
|
27
|
+
md: "lg:pr-72",
|
|
28
|
+
lg: "lg:pr-80",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function PageWithSidePanel({
|
|
32
|
+
children,
|
|
33
|
+
sidePanel,
|
|
34
|
+
sidePanelHeader,
|
|
35
|
+
sidePanelWidth = "md",
|
|
36
|
+
closable = false,
|
|
37
|
+
defaultOpen = true,
|
|
38
|
+
showBorder = true,
|
|
39
|
+
onClose,
|
|
40
|
+
onOpen
|
|
41
|
+
}: PageWithSidePanelProps) {
|
|
42
|
+
const [isOpen, setIsOpen] = useState(defaultOpen)
|
|
43
|
+
const panelWidthClass = PANEL_WIDTHS[sidePanelWidth]
|
|
44
|
+
const panelPaddingClass = PANEL_PADDING[sidePanelWidth]
|
|
45
|
+
|
|
46
|
+
const handleClose = () => {
|
|
47
|
+
setIsOpen(false)
|
|
48
|
+
onClose?.()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const handleOpen = () => {
|
|
52
|
+
setIsOpen(true)
|
|
53
|
+
onOpen?.()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div className="min-h-screen bg-white overflow-hidden">
|
|
58
|
+
{/* Main content */}
|
|
59
|
+
<div className={`w-full px-6 py-8 transition-all duration-300 ${sidePanel && isOpen ? panelPaddingClass : ''}`}>
|
|
60
|
+
{children}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{/* Side panel - fixed */}
|
|
64
|
+
{sidePanel && (
|
|
65
|
+
<>
|
|
66
|
+
<div
|
|
67
|
+
className={`hidden lg:block ${panelWidthClass} flex-shrink-0 fixed top-14 bottom-0 right-0 bg-white flex flex-col ${showBorder ? 'border-l border-ibm-gray-40' : ''} transition-transform duration-300 ease-in-out ${
|
|
68
|
+
isOpen ? 'translate-x-0' : 'translate-x-full'
|
|
69
|
+
}`}
|
|
70
|
+
>
|
|
71
|
+
{/* Header */}
|
|
72
|
+
{(sidePanelHeader || closable) && (
|
|
73
|
+
<div className="flex items-center justify-between p-4 border-b border-ibm-gray-20 flex-shrink-0">
|
|
74
|
+
<div className="flex-1">
|
|
75
|
+
{sidePanelHeader}
|
|
76
|
+
</div>
|
|
77
|
+
{closable && (
|
|
78
|
+
<Button
|
|
79
|
+
variant="ghost"
|
|
80
|
+
size="xs"
|
|
81
|
+
onClick={handleClose}
|
|
82
|
+
className="h-8 w-8 p-0 ml-2"
|
|
83
|
+
>
|
|
84
|
+
<X className="h-4 w-4" />
|
|
85
|
+
</Button>
|
|
86
|
+
)}
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
|
|
90
|
+
{/* Content */}
|
|
91
|
+
<div className="flex-1 overflow-y-auto">
|
|
92
|
+
{sidePanel}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
{/* Toggle button - shown when panel is closed */}
|
|
97
|
+
{closable && !isOpen && (
|
|
98
|
+
<button
|
|
99
|
+
onClick={handleOpen}
|
|
100
|
+
className="hidden lg:flex fixed top-1/2 right-0 -translate-y-1/2 items-center justify-center w-8 h-16 bg-white border border-ibm-gray-40 border-r-0 rounded-l-lg shadow-md hover:bg-ibm-gray-10 transition-colors duration-200"
|
|
101
|
+
aria-label="Open side panel"
|
|
102
|
+
>
|
|
103
|
+
<ChevronLeft className="h-5 w-5 text-ibm-gray-70" />
|
|
104
|
+
</button>
|
|
105
|
+
)}
|
|
106
|
+
</>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
@@ -145,8 +145,8 @@ export function MainSidebar({
|
|
|
145
145
|
</button>
|
|
146
146
|
|
|
147
147
|
{isMinimized && hoveredMenu === item.id && sidebarMenus[item.id] && sidebarMenus[item.id].length > 0 && (
|
|
148
|
-
<div className="absolute left-full top-0 ml-2 bg-
|
|
149
|
-
<div className="px-4 py-2
|
|
148
|
+
<div className="absolute left-full top-0 ml-2 bg-white rounded-lg shadow-xl z-50 min-w-[200px] py-2">
|
|
149
|
+
<div className="px-4 py-2">
|
|
150
150
|
<h3 className="text-sm font-semibold text-text-primary">{item.label}</h3>
|
|
151
151
|
</div>
|
|
152
152
|
<div className="py-1">
|
|
@@ -272,7 +272,7 @@ function Sidebar({ currentMenu, onMainMenuToggle, sidebarMenus = {}, main_base_u
|
|
|
272
272
|
</nav>
|
|
273
273
|
|
|
274
274
|
{/* Footer - Menu Paramètres */}
|
|
275
|
-
<div className="relative
|
|
275
|
+
<div className="relative border-ui-border px-3 py-3" ref={settingsRef}>
|
|
276
276
|
{/* Dropdown vers le haut */}
|
|
277
277
|
{settingsOpen && (
|
|
278
278
|
<div className="absolute bottom-full left-2 right-2 mb-1 bg-white border border-ui-border rounded-none shadow-lg z-50">
|
|
@@ -4,32 +4,28 @@ import type { ReactNode } from "react"
|
|
|
4
4
|
|
|
5
5
|
interface PageHeaderProps {
|
|
6
6
|
title: string
|
|
7
|
-
description?: string
|
|
8
7
|
backLink?: string
|
|
9
8
|
actions?: ReactNode
|
|
10
9
|
statusNode?: ReactNode
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
export function PageHeader({ title,
|
|
12
|
+
export function PageHeader({ title, backLink, actions, statusNode}: PageHeaderProps) {
|
|
14
13
|
return (
|
|
15
|
-
<div className="
|
|
16
|
-
<div className="flex
|
|
14
|
+
<div className="sticky top-0 z-10 bg-white border-b border-gray-200 -mx-6 px-6 py-3 mb-6">
|
|
15
|
+
<div className="flex items-center justify-between gap-4">
|
|
17
16
|
<div className="flex items-center space-x-3 min-w-0">
|
|
18
17
|
{backLink && (
|
|
19
18
|
<Link href={backLink} className="flex-shrink-0">
|
|
20
|
-
<ArrowLeft className="h-
|
|
19
|
+
<ArrowLeft className="h-4 w-4 text-text-secondary" />
|
|
21
20
|
</Link>
|
|
22
21
|
)}
|
|
23
|
-
<div className="min-w-0">
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
{statusNode && <div className="mt-2 flex-shrink-0">{statusNode}</div>}
|
|
27
|
-
</div>
|
|
28
|
-
{description && <p className="text-sm sm:text-base text-text-secondary line-clamp-2">{description}</p>}
|
|
22
|
+
<div className="flex items-center space-x-3 min-w-0">
|
|
23
|
+
<h2 className="text-base font-semibold text-text-primary truncate">{title}</h2>
|
|
24
|
+
{statusNode && <div className="flex-shrink-0">{statusNode}</div>}
|
|
29
25
|
</div>
|
|
30
26
|
</div>
|
|
31
|
-
{actions && <div className="flex items-center space-x-2
|
|
27
|
+
{actions && <div className="flex items-center space-x-2 flex-shrink-0">{actions}</div>}
|
|
32
28
|
</div>
|
|
33
29
|
</div>
|
|
34
30
|
)
|
|
35
|
-
}
|
|
31
|
+
}
|