mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2024-11-14 17:35:35 +01:00
Compare commits
4 Commits
29a7c491a4
...
2562306a4a
Author | SHA1 | Date | |
---|---|---|---|
|
2562306a4a | ||
|
9e2278e016 | ||
|
83a6ff75f6 | ||
|
02f3803265 |
|
@ -5,10 +5,12 @@
|
||||||
* See /LICENSE for more information.
|
* See /LICENSE for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { useRef } from "react";
|
||||||
|
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
import { useFocusTrap } from "../utils/hooks";
|
||||||
|
|
||||||
export const ALERT_TYPES = Object.freeze({
|
export const ALERT_TYPES = Object.freeze({
|
||||||
PRIMARY: "primary",
|
PRIMARY: "primary",
|
||||||
SECONDARY: "secondary",
|
SECONDARY: "secondary",
|
||||||
|
@ -37,11 +39,15 @@ Alert.defaultProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function Alert({ type, onDismiss, children }) {
|
function Alert({ type, onDismiss, children }) {
|
||||||
|
const alertRef = useRef();
|
||||||
|
useFocusTrap(alertRef, !!onDismiss);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={alertRef}
|
||||||
className={`alert alert-${type} ${
|
className={`alert alert-${type} ${
|
||||||
onDismiss ? "alert-dismissible" : ""
|
onDismiss ? "alert-dismissible" : ""
|
||||||
}`.trim()}
|
}`.trim()}
|
||||||
|
role="alert"
|
||||||
>
|
>
|
||||||
{onDismiss && (
|
{onDismiss && (
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -33,7 +33,7 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
|
||||||
let modalSize = "modal-";
|
let modalSize = "modal-";
|
||||||
|
|
||||||
useClickOutside(modalRef, () => setShown(false));
|
useClickOutside(modalRef, () => setShown(false));
|
||||||
useFocusTrap(modalRef);
|
useFocusTrap(modalRef, shown);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleEsc = (event) => {
|
const handleEsc = (event) => {
|
||||||
|
@ -46,7 +46,7 @@ export function Modal({ shown, setShown, scrollable, size, children }) {
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", handleEsc);
|
window.removeEventListener("keydown", handleEsc);
|
||||||
};
|
};
|
||||||
}, [shown]);
|
}, [setShown]);
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case "sm":
|
case "sm":
|
||||||
|
|
|
@ -6,6 +6,8 @@ exports[`<RebootButton/> Render modal. 1`] = `
|
||||||
id="modal-container"
|
id="modal-container"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
aria-labelledby="modal-title"
|
||||||
|
aria-modal="true"
|
||||||
class="modal fade show"
|
class="modal fade show"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
>
|
>
|
||||||
|
@ -19,11 +21,11 @@ exports[`<RebootButton/> Render modal. 1`] = `
|
||||||
<div
|
<div
|
||||||
class="modal-header"
|
class="modal-header"
|
||||||
>
|
>
|
||||||
<h5
|
<h1
|
||||||
class="modal-title"
|
class="modal-title fs-5"
|
||||||
>
|
>
|
||||||
Warning!
|
Warning!
|
||||||
</h5>
|
</h1>
|
||||||
<button
|
<button
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
class="btn-close"
|
class="btn-close"
|
||||||
|
|
|
@ -7,6 +7,7 @@ exports[`AlertContext should render alert 1`] = `
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="alert alert-danger alert-dismissible"
|
class="alert alert-danger alert-dismissible"
|
||||||
|
role="alert"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
|
|
|
@ -41,40 +41,39 @@ export function useClickOutside(element, callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Trap focus inside modal. */
|
/* Trap focus inside element. */
|
||||||
export function useFocusTrap(modalRef) {
|
export function useFocusTrap(elementRef, condition = true) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (shown) {
|
if (!condition) {
|
||||||
const modal = modalRef.current;
|
return;
|
||||||
const focusableElements = modal.querySelectorAll(
|
|
||||||
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
||||||
);
|
|
||||||
const firstElement = focusableElements[0];
|
|
||||||
const lastElement = focusableElements[focusableElements.length - 1];
|
|
||||||
|
|
||||||
const handleTab = (event) => {
|
|
||||||
if (event.key === "Tab") {
|
|
||||||
if (event.shiftKey) {
|
|
||||||
if (document.activeElement === firstElement) {
|
|
||||||
lastElement.focus();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (document.activeElement === lastElement) {
|
|
||||||
firstElement.focus();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
modal.addEventListener("keydown", handleTab);
|
|
||||||
|
|
||||||
firstElement.focus();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
modal.removeEventListener("keydown", handleTab);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}, [shown]);
|
const currentElement = elementRef.current;
|
||||||
|
const focusableElements = currentElement.querySelectorAll(
|
||||||
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
||||||
|
);
|
||||||
|
const firstElement = focusableElements[0];
|
||||||
|
const lastElement = focusableElements[focusableElements.length - 1];
|
||||||
|
|
||||||
|
const handleTab = (event) => {
|
||||||
|
if (event.key === "Tab") {
|
||||||
|
if (event.shiftKey) {
|
||||||
|
if (document.activeElement === firstElement) {
|
||||||
|
lastElement.focus();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
} else if (document.activeElement === lastElement) {
|
||||||
|
firstElement.focus();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
currentElement.addEventListener("keydown", handleTab);
|
||||||
|
|
||||||
|
firstElement.focus();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
currentElement.removeEventListener("keydown", handleTab);
|
||||||
|
};
|
||||||
|
}, [elementRef, condition]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user