diff --git a/package.json b/package.json index c5c8eebb..410bc8f4 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 254f77cb..c5e5fb1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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'} diff --git a/src/components/overlays/OverlayDisplay.tsx b/src/components/overlays/OverlayDisplay.tsx index a9a09d74..b737b1aa 100644 --- a/src/components/overlays/OverlayDisplay.tsx +++ b/src/components/overlays/OverlayDisplay.tsx @@ -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(null); const ref = useRef(null); - const target = useRef(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( -
- -
- - - {props.children} - -
+ +
+ +
+ + + {props.children} + +
+
, 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 (