Раньше ConferenceFormCard всегда висела в сайдбаре двухколоночной сетки .layout-grid; теперь открывается в модалке (по образцу ConferenceOccurrenceDialog: modal-overlay/modal-panel, закрытие по клику на оверлей, Escape и возврат фокуса — новый общий хук useModalDismiss). .layout-grid стала одноколоночной — календарь занимает всю ширину.
33 lines
1.5 KiB
TypeScript
33 lines
1.5 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
||
|
||
/**
|
||
* Общее поведение модалок оболочки (`.modal-overlay`/`.modal-panel`):
|
||
* закрытие по Escape и возврат фокуса на элемент, который был активен до
|
||
* открытия модалки (обычно кнопка, её вызвавшая) — доступность по клавиатуре.
|
||
*/
|
||
export function useModalDismiss(onClose: () => void) {
|
||
const previouslyFocused = useRef<Element | null>(null)
|
||
// Всегда актуальный колбэк без пересоздания подписки на keydown при каждом
|
||
// рендере (onClose у вызывающих обычно инлайновая функция). Синхронизация —
|
||
// отдельным эффектом (не прямым присваиванием в теле рендера, см.
|
||
// react-hooks/refs — рефы нельзя трогать во время рендера).
|
||
const onCloseRef = useRef(onClose)
|
||
useEffect(() => {
|
||
onCloseRef.current = onClose
|
||
})
|
||
|
||
useEffect(() => {
|
||
previouslyFocused.current = document.activeElement
|
||
|
||
function handleKeyDown(e: KeyboardEvent) {
|
||
if (e.key === 'Escape') onCloseRef.current()
|
||
}
|
||
document.addEventListener('keydown', handleKeyDown)
|
||
|
||
return () => {
|
||
document.removeEventListener('keydown', handleKeyDown)
|
||
if (previouslyFocused.current instanceof HTMLElement) previouslyFocused.current.focus()
|
||
}
|
||
}, [])
|
||
}
|