Files
router-lists-ui/frontend/src/hooks/useModal.js
T

127 lines
3.1 KiB
JavaScript

import { useState, useCallback, useEffect } from 'react';
/**
* Хук для управления состоянием модального окна
* Упрощает работу с открытием/закрытием модалок
*/
export function useModal(initialState = false) {
const [isOpen, setIsOpen] = useState(initialState);
const open = useCallback(() => {
setIsOpen(true);
}, []);
const close = useCallback(() => {
setIsOpen(false);
}, []);
const toggle = useCallback(() => {
setIsOpen(prev => !prev);
}, []);
return {
isOpen,
open,
close,
toggle,
setIsOpen
};
}
/**
* Хук для управления ESC и клавиатурной навигацией в модалках
*/
export function useModalKeyboard(isOpen, onClose) {
useEffect(() => {
if (!isOpen) return;
const handleEscape = (e) => {
if (e.key === 'Escape') {
onClose?.();
}
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [isOpen, onClose]);
}
/**
* Хук для управления классом modal-open на body
*/
export function useModalBodyClass(isOpen) {
useEffect(() => {
if (isOpen) {
document.body.classList.add('modal-open');
} else {
document.body.classList.remove('modal-open');
}
return () => {
document.body.classList.remove('modal-open');
};
}, [isOpen]);
}
/**
* Хук для управления focus trap внутри модалки
*/
export function useFocusTrap(isOpen, containerRef) {
useEffect(() => {
if (!isOpen || !containerRef.current) return;
const container = containerRef.current;
const focusableElements = container.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length === 0) return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
// Фокус на первый элемент при открытии
firstElement?.focus();
const handleTabKey = (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
// Shift + Tab
if (document.activeElement === firstElement) {
e.preventDefault();
lastElement?.focus();
}
} else {
// Tab
if (document.activeElement === lastElement) {
e.preventDefault();
firstElement?.focus();
}
}
};
container.addEventListener('keydown', handleTabKey);
return () => container.removeEventListener('keydown', handleTabKey);
}, [isOpen, containerRef]);
}
/**
* Комплексный хук, объединяющий все хуки для модалок
*/
export function useModalManager(initialState = false) {
const modal = useModal(initialState);
return {
...modal,
// Дополнительные утилиты
openWithData: (data) => {
modal.setData?.(data);
modal.open();
},
};
}
export default useModal;