Files
new-api/web/src/hooks/use-dialog.ts
T
Calcium-Ion 31d70fca39 refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)
* refactor(auth): replace dashboard sessions with stateless tokens

* feat(auth): harden session issuance and distributed enforcement

* fix(proxy): preserve trusted proxy compatibility defaults

* refactor: address dashboard auth review feedback

* refactor: remove classic frontend and flatten web app
2026-07-20 16:48:43 +08:00

187 lines
4.8 KiB
TypeScript

/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import {
useState,
useCallback,
useMemo,
type Dispatch,
type SetStateAction,
} from 'react'
// ============================================================================
// Types
// ============================================================================
export interface DialogHandlers {
open: () => void
close: () => void
toggle: () => void
}
export interface DialogStateHandlers {
reset: () => void
isOpen: boolean
}
export interface DialogsHandlers<T extends string> {
open: (key: T) => void
close: (key: T) => void
toggle: (key: T) => void
isOpen: (key: T) => boolean
closeAll: () => void
hasAnyOpen: boolean
}
// ============================================================================
// Dialog State Management Hooks
// ============================================================================
/**
* Simple hook for managing a single dialog state with boolean value
* @param initialOpen Initial dialog open state (default: false)
* @returns Tuple of [isOpen, handlers]
* @example
* const [isOpen, handlers] = useDialog()
* handlers.open()
* handlers.close()
* handlers.toggle()
*/
export function useDialog(
initialOpen = false
): readonly [boolean, DialogHandlers] {
const [isOpen, setIsOpen] = useState(initialOpen)
const handlers: DialogHandlers = useMemo(
() => ({
open: () => setIsOpen(true),
close: () => setIsOpen(false),
toggle: () => setIsOpen((prev) => !prev),
}),
[]
)
return [isOpen, handlers] as const
}
/**
* Hook for managing dialog state with custom value types
* Useful for dialogs that need to track which item is being edited/viewed
* @param initialState Initial dialog state (default: null)
* @returns Tuple of [state, setState, handlers]
* @example
* const [status, setStatus, handlers] = useDialogState<"approve" | "reject">()
* setStatus('approve')
* if (handlers.isOpen) {}
* handlers.reset()
*
* // Or with objects:
* const [user, setUser, handlers] = useDialogState<User>()
* setUser({ id: 1, name: 'John' })
*/
export function useDialogState<T = unknown>(
initialState: T | null = null
): readonly [
T | null,
Dispatch<SetStateAction<T | null>>,
DialogStateHandlers,
] {
const [state, setState] = useState<T | null>(initialState)
const reset = useCallback(() => setState(null), [])
const handlers: DialogStateHandlers = useMemo(
() => ({
reset,
isOpen: state !== null,
}),
[state, reset]
)
return [state, setState, handlers] as const
}
/**
* Hook for managing multiple independent dialog states
* Useful when you have multiple dialogs that can be open simultaneously
* @returns Object with methods to manage multiple dialogs
* @example
* const dialogs = useDialogs<'create' | 'edit' | 'delete'>()
* dialogs.open('create')
* dialogs.close('edit')
* dialogs.toggle('delete')
* dialogs.isOpen('create')
* dialogs.closeAll()
*/
export function useDialogs<T extends string>(): DialogsHandlers<T> {
const [openDialogs, setOpenDialogs] = useState<Set<T>>(new Set())
const open = useCallback((key: T) => {
setOpenDialogs((prev) => {
if (prev.has(key)) return prev
const next = new Set(prev)
next.add(key)
return next
})
}, [])
const close = useCallback((key: T) => {
setOpenDialogs((prev) => {
if (!prev.has(key)) return prev
const next = new Set(prev)
next.delete(key)
return next
})
}, [])
const toggle = useCallback((key: T) => {
setOpenDialogs((prev) => {
const next = new Set(prev)
if (next.has(key)) {
next.delete(key)
} else {
next.add(key)
}
return next
})
}, [])
const closeAll = useCallback(() => {
setOpenDialogs((prev) => (prev.size === 0 ? prev : new Set()))
}, [])
const hasAnyOpen = useMemo(() => openDialogs.size > 0, [openDialogs])
return useMemo(
() => ({
open,
close,
toggle,
isOpen: (key: T) => openDialogs.has(key),
closeAll,
hasAnyOpen,
}),
[open, close, toggle, openDialogs, closeAll, hasAnyOpen]
)
}
/**
* @deprecated Use named import useDialogState instead
*/
export default useDialogState