-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSlidePanel.tsx
More file actions
105 lines (92 loc) · 2.97 KB
/
SlidePanel.tsx
File metadata and controls
105 lines (92 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import React, { ReactNode, useCallback, useEffect, useState } from 'react'
export interface SlidePanelConfig {
slidePanel?: {
minWidth?: number
defaultWidth?: number
}
}
interface SlidePanelProps {
mainContent: ReactNode
panelContent: ReactNode
isPanelOpen: boolean
config?: SlidePanelConfig
}
const WIDTH = {
MIN: 100,
DEFAULT: 400,
} as const
/**
* Slide out panel component with resizing.
*/
export default function SlidePanel({
mainContent, panelContent, isPanelOpen, config,
}: SlidePanelProps) {
const minWidth = config?.slidePanel?.minWidth && config.slidePanel.minWidth > 0 ? config.slidePanel.minWidth : WIDTH.MIN
function validWidth(width?: number): number | undefined {
if (width && minWidth <= width) {
return width
}
return undefined
}
const defaultWidth = validWidth(config?.slidePanel?.defaultWidth) ?? WIDTH.DEFAULT
const [resizingClientX, setResizingClientX] = useState(-1)
const panelRef = React.createRef<HTMLDivElement>()
// Load initial panel width from localStorage if available
const [panelWidth, setPanelWidth] = useState<number>(() => {
const savedWidth = typeof window !== 'undefined' ? localStorage.getItem('panelWidth') : null
const parsedWidth = savedWidth ? parseInt(savedWidth, 10) : NaN
return !isNaN(parsedWidth) ? parsedWidth : defaultWidth
})
useEffect(() => {
// Persist panelWidth to localStorage
localStorage.setItem('panelWidth', panelWidth.toString())
}, [panelWidth])
useEffect(() => {
function handleMouseMove(e: MouseEvent) {
if (resizingClientX === -1) return
// Calculate new width based on mouse position
setPanelWidth(Math.max(minWidth, resizingClientX - e.clientX))
}
function handleMouseUp() {
if (resizingClientX !== -1) {
setResizingClientX(-1)
}
}
if (resizingClientX !== -1) {
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}
return () => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
}, [resizingClientX, minWidth])
const handleMouseDown = useCallback((e: React.MouseEvent) => {
if (panelRef.current && panelRef.current.offsetWidth < panelWidth) {
setPanelWidth(panelRef.current.offsetWidth)
setResizingClientX(e.clientX + panelRef.current.offsetWidth)
} else {
setResizingClientX(e.clientX + panelWidth)
}
}, [panelRef, panelWidth])
return (
<div className="slideContainer">
<div className="slideMain">
{mainContent}
</div>
{isPanelOpen &&
<div
className="resizer"
onMouseDown={handleMouseDown}
/>
}
<div
className={resizingClientX === -1 ? 'slidePanel' : 'slidePanel slideDragging'}
ref={panelRef}
style={isPanelOpen ? { width: panelWidth } : undefined}
>
{panelContent}
</div>
</div>
)
}