- sidebar-resize.tsx: hook useSidebarResize (localStorage persistence, 280-560px) - sidebar-resize-client.tsx: wrapper layout qui applique la largeur - layout.tsx: remplace div statique par SidebarResizeClient - Resize handle: barre 1px→3px au hover, brand-accent pendant drag - pointer events (pas mouse) pour support tactile - cursor: col-resize + userSelect:none pendant le drag L'utilisateur peut maintenant réduire ou élargir la zone de carnet en glissant la barre entre le sidebar et le contenu principal.
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
'use client'
|
|
|
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
|
const STORAGE_KEY = 'memento-sidebar-width'
|
|
const MIN = 280
|
|
const MAX = 560
|
|
|
|
export function useSidebarResize() {
|
|
const [width, setWidth] = useState(320)
|
|
const [isDragging, setIsDragging] = useState(false)
|
|
const startXRef = useRef(0)
|
|
const startWidthRef = useRef(320)
|
|
|
|
useEffect(() => {
|
|
const stored = localStorage.getItem(STORAGE_KEY)
|
|
if (stored) {
|
|
const parsed = parseInt(stored, 10)
|
|
if (!isNaN(parsed) && parsed >= MIN && parsed <= MAX) {
|
|
setWidth(parsed)
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
const onPointerDown = useCallback((e: React.PointerEvent) => {
|
|
e.preventDefault()
|
|
setIsDragging(true)
|
|
startXRef.current = e.clientX
|
|
startWidthRef.current = width
|
|
document.body.style.cursor = 'col-resize'
|
|
document.body.style.userSelect = 'none'
|
|
}, [width])
|
|
|
|
useEffect(() => {
|
|
if (!isDragging) return
|
|
|
|
const onPointerMove = (e: PointerEvent) => {
|
|
const delta = e.clientX - startXRef.current
|
|
const newWidth = Math.max(MIN, Math.min(MAX, startWidthRef.current + delta))
|
|
setWidth(newWidth)
|
|
}
|
|
|
|
const onPointerUp = () => {
|
|
setIsDragging(false)
|
|
document.body.style.cursor = ''
|
|
document.body.style.userSelect = ''
|
|
setWidth(prev => {
|
|
localStorage.setItem(STORAGE_KEY, String(prev))
|
|
return prev
|
|
})
|
|
}
|
|
|
|
document.addEventListener('pointermove', onPointerMove)
|
|
document.addEventListener('pointerup', onPointerUp)
|
|
return () => {
|
|
document.removeEventListener('pointermove', onPointerMove)
|
|
document.removeEventListener('pointerup', onPointerUp)
|
|
}
|
|
}, [isDragging])
|
|
|
|
return { width, isDragging, onPointerDown }
|
|
}
|
|
|
|
export function SidebarResizeHandle({ onPointerDown, isDragging }: { onPointerDown: (e: React.PointerEvent) => void; isDragging: boolean }) {
|
|
return (
|
|
<div
|
|
onPointerDown={onPointerDown}
|
|
className={`hidden md:flex items-center justify-center w-1 shrink-0 cursor-col-resize transition-colors z-50 ${
|
|
isDragging
|
|
? 'bg-brand-accent w-[3px]'
|
|
: 'bg-border/40 hover:bg-brand-accent/50 hover:w-[3px]'
|
|
}`}
|
|
style={{ touchAction: 'none' }}
|
|
aria-hidden="true"
|
|
>
|
|
<div className={`w-[2px] h-12 rounded-full transition-colors ${isDragging ? 'bg-brand-accent' : 'bg-border/60'}`} />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export { MIN as SIDEBAR_MIN_WIDTH, MAX as SIDEBAR_MAX_WIDTH }
|