@orsetra/shared-ui 1.0.60 → 1.0.61
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
|
+
}
|