Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m34s
127 lines
3.1 KiB
JavaScript
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;
|
|
|