Files
ds-chat/frontend/src/hooks/useResizableWidth.ts
T
ksmith 91589ee647 Add resizable room panel, searchable user picker, and direct room membership
Room info panel is now user-resizable (fixing a layout clip at narrow
widths), and every user-selection spot (room membership, admin ownership
transfer) uses a new searchable UserPicker instead of raw text input or
prompt(). Member rows fold role + actions into a single inline dropdown
instead of a row of buttons, so the member list stays usable as rooms grow.

Room invites (the accept/decline flow) are replaced by adding a user to a
room directly -- an admin/owner picks someone and they're a member
immediately, with a "you've been added" notification email instead of an
invite email. Drops the now-unused room_invites table.
2026-08-14 20:40:37 -06:00

48 lines
1.7 KiB
TypeScript

import { useCallback, useEffect, useRef, useState, type PointerEvent } from 'react'
interface UseResizableWidthOptions {
storageKey: string
defaultWidth: number
min: number
max: number
}
// Right-anchored resizable panel: width is the distance from the cursor to
// the viewport's right edge, so a handle on the panel's left edge drags
// naturally. Persists to localStorage so it survives a reload.
export function useResizableWidth({ storageKey, defaultWidth, min, max }: UseResizableWidthOptions) {
const [width, setWidth] = useState(() => {
const stored = Number(localStorage.getItem(storageKey))
return stored >= min && stored <= max ? stored : defaultWidth
})
const widthRef = useRef(width)
widthRef.current = width
const draggingRef = useRef(false)
useEffect(() => {
function handleMove(e: PointerEvent<Window> | globalThis.PointerEvent) {
if (!draggingRef.current) return
const next = Math.min(max, Math.max(min, window.innerWidth - e.clientX))
setWidth(next)
}
function handleUp() {
if (!draggingRef.current) return
draggingRef.current = false
localStorage.setItem(storageKey, String(widthRef.current))
}
window.addEventListener('pointermove', handleMove as (e: globalThis.PointerEvent) => void)
window.addEventListener('pointerup', handleUp)
return () => {
window.removeEventListener('pointermove', handleMove as (e: globalThis.PointerEvent) => void)
window.removeEventListener('pointerup', handleUp)
}
}, [max, min, storageKey])
const startResize = useCallback((e: PointerEvent) => {
e.preventDefault()
draggingRef.current = true
}, [])
return { width, startResize }
}