mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
add focus traps to overlays
This commit is contained in:
parent
2def74cb32
commit
ce6b6ef88b
3 changed files with 59 additions and 42 deletions
|
@ -16,6 +16,7 @@
|
|||
"core-js": "^3.29.1",
|
||||
"dompurify": "^3.0.1",
|
||||
"flag-icons": "^6.11.1",
|
||||
"focus-trap-react": "^10.2.3",
|
||||
"fscreen": "^1.2.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"hls.js": "^1.0.7",
|
||||
|
|
|
@ -47,6 +47,9 @@ dependencies:
|
|||
flag-icons:
|
||||
specifier: ^6.11.1
|
||||
version: 6.11.1
|
||||
focus-trap-react:
|
||||
specifier: ^10.2.3
|
||||
version: 10.2.3(prop-types@15.8.1)(react-dom@17.0.2)(react@17.0.2)
|
||||
fscreen:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
|
@ -3735,6 +3738,26 @@ packages:
|
|||
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
|
||||
dev: true
|
||||
|
||||
/focus-trap-react@10.2.3(prop-types@15.8.1)(react-dom@17.0.2)(react@17.0.2):
|
||||
resolution: {integrity: sha512-YXBpFu/hIeSu6NnmV2xlXzOYxuWkoOtar9jzgp3lOmjWLWY59C/b8DtDHEAV4SPU07Nd/t+nS/SBNGkhUBFmEw==}
|
||||
peerDependencies:
|
||||
prop-types: ^15.8.1
|
||||
react: '>=16.3.0'
|
||||
react-dom: '>=16.3.0'
|
||||
dependencies:
|
||||
focus-trap: 7.5.4
|
||||
prop-types: 15.8.1
|
||||
react: 17.0.2
|
||||
react-dom: 17.0.2(react@17.0.2)
|
||||
tabbable: 6.2.0
|
||||
dev: false
|
||||
|
||||
/focus-trap@7.5.4:
|
||||
resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
|
||||
dependencies:
|
||||
tabbable: 6.2.0
|
||||
dev: false
|
||||
|
||||
/for-each@0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
dependencies:
|
||||
|
@ -5807,6 +5830,10 @@ packages:
|
|||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
dev: true
|
||||
|
||||
/tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
dev: false
|
||||
|
||||
/tailwind-scrollbar@2.1.0(tailwindcss@3.3.3):
|
||||
resolution: {integrity: sha512-zpvY5mDs0130YzYjZKBiDaw32rygxk5RyJ4KmeHjGnwkvbjm/PszON1m4Bbt2DkMRIXlXsfNevykAESgURN4KA==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import classNames from "classnames";
|
||||
import FocusTrap from "focus-trap-react";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
|
@ -37,31 +38,8 @@ export function OverlayPortal(props: {
|
|||
}) {
|
||||
const [portalElement, setPortalElement] = useState<Element | null>(null);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const target = useRef<Element | null>(null);
|
||||
const close = props.close;
|
||||
|
||||
useEffect(() => {
|
||||
function listen(e: MouseEvent) {
|
||||
target.current = e.target as Element;
|
||||
}
|
||||
document.addEventListener("mousedown", listen);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", listen);
|
||||
};
|
||||
});
|
||||
|
||||
const click = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
const startedTarget = target.current;
|
||||
target.current = null;
|
||||
if (e.currentTarget !== e.target) return;
|
||||
if (!startedTarget) return;
|
||||
if (!startedTarget.isEqualNode(e.currentTarget as Element)) return;
|
||||
close?.();
|
||||
},
|
||||
[close]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current?.closest(".popout-location");
|
||||
setPortalElement(element ?? document.body);
|
||||
|
@ -72,24 +50,30 @@ export function OverlayPortal(props: {
|
|||
{portalElement
|
||||
? createPortal(
|
||||
<Transition show={props.show} animation="none">
|
||||
<div className="popout-wrapper fixed overflow-hidden pointer-events-auto inset-0 z-[999] select-none">
|
||||
<Transition animation="fade" isChild>
|
||||
<div
|
||||
onClick={click}
|
||||
className={classNames({
|
||||
"absolute inset-0": true,
|
||||
"bg-black opacity-90": props.darken,
|
||||
})}
|
||||
/>
|
||||
</Transition>
|
||||
<Transition
|
||||
animation="slide-up"
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
isChild
|
||||
>
|
||||
{props.children}
|
||||
</Transition>
|
||||
</div>
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
onDeactivate: close,
|
||||
}}
|
||||
>
|
||||
<div className="popout-wrapper absolute overflow-hidden pointer-events-auto inset-0 z-[999] select-none">
|
||||
<Transition animation="fade" isChild>
|
||||
<div
|
||||
onClick={close}
|
||||
className={classNames({
|
||||
"absolute inset-0": true,
|
||||
"bg-black opacity-90": props.darken,
|
||||
})}
|
||||
/>
|
||||
</Transition>
|
||||
<Transition
|
||||
animation="slide-up"
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
isChild
|
||||
>
|
||||
{props.children}
|
||||
</Transition>
|
||||
</div>
|
||||
</FocusTrap>
|
||||
</Transition>,
|
||||
portalElement
|
||||
)
|
||||
|
@ -100,13 +84,18 @@ export function OverlayPortal(props: {
|
|||
|
||||
export function Overlay(props: OverlayProps) {
|
||||
const router = useInternalOverlayRouter(props.id);
|
||||
const realClose = router.close;
|
||||
|
||||
// listen for anchor updates
|
||||
useRouterAnchorUpdate(props.id);
|
||||
|
||||
const close = useCallback(() => {
|
||||
realClose();
|
||||
}, [realClose]);
|
||||
|
||||
return (
|
||||
<OverlayPortal
|
||||
close={router.close}
|
||||
close={close}
|
||||
show={router.isOverlayActive()}
|
||||
darken={props.darken}
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue