Skip to content

Commit

Permalink
fix: use native modal dialog and use its backdrop
Browse files Browse the repository at this point in the history
This uses the `<dialog>` element with `showModal()` method,
so that we can leverage the browser's built-in ways to make
this dialog modal, including keyboard handling (Esc), backdrop
and focus trap.

Fixes the issue that it was possible to tab outside the dialog
while it was open.

This removes the backdrop component, as the `<dialog>` element
comes with a backdrop built-in, that is styled via CSS instead.
  • Loading branch information
hidde committed Nov 21, 2024
1 parent 90e85e8 commit 0e38363
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import React, {type ComponentProps} from 'react';
import React from 'react';
import clsx from 'clsx';
import {useThemeConfig} from '@docusaurus/theme-common';
import {
Expand All @@ -18,16 +18,6 @@ import type {Props} from '@theme/Navbar/Layout';

import styles from './styles.module.css';

function NavbarBackdrop(props: ComponentProps<'div'>) {
return (
<div
role="presentation"
{...props}
className={clsx('navbar-sidebar__backdrop', props.className)}
/>
);
}

export default function NavbarLayout({children}: Props): JSX.Element {
const {
navbar: {hideOnScroll, style},
Expand Down Expand Up @@ -56,7 +46,6 @@ export default function NavbarLayout({children}: Props): JSX.Element {
},
)}>
{children}
<NavbarBackdrop onClick={mobileSidebar.toggle} />
<NavbarMobileSidebar />
</nav>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
* LICENSE file in the root directory of this source tree.
*/

.navbarHideable {
transition: transform var(--ifm-transition-fast) ease;
.navbar-sidebar {
inset: 0;
margin: 0;
padding: 0;
bottom: 0;
border: 0;
height: 100%;
max-height: 100%;
}

.navbarHidden {
transform: translate3d(0, calc(-100% - 2px), 0);
.navbar-sidebar::backdrop {
background-color: #0009;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import React, {useEffect, useRef} from 'react';
import clsx from 'clsx';
import {useNavbarSecondaryMenu} from '@docusaurus/theme-common/internal';
import {
useNavbarMobileSidebar,
useNavbarSecondaryMenu,
} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/Navbar/MobileSidebar/Layout';

export default function NavbarMobileSidebarLayout({
Expand All @@ -16,8 +19,40 @@ export default function NavbarMobileSidebarLayout({
secondaryMenu,
}: Props): JSX.Element {
const {shown: secondaryMenuShown} = useNavbarSecondaryMenu();
const navbarModalDialog = useRef<HTMLDialogElement | null>(null);
const {shown, toggle} = useNavbarMobileSidebar();

useEffect(() => {
const {current: dialogEl} = navbarModalDialog;

if (!dialogEl) {return;}
if (shown) {
dialogEl.showModal();
} else {
dialogEl.close();
}
});

useEffect(() => {
const {current: dialogEl} = navbarModalDialog;

function toggleOnEscape(e: {key: string}) {
if (e.key === 'Escape') {
if (shown) {
toggle();
}
}
}

dialogEl?.addEventListener('keydown', toggleOnEscape);

return () => {
dialogEl?.removeEventListener('keydown', toggleOnEscape);
};
}, [shown, toggle]);

return (
<div className="navbar-sidebar">
<dialog className="navbar-sidebar" ref={navbarModalDialog}>
{header}
<div
className={clsx('navbar-sidebar__items', {
Expand All @@ -26,6 +61,6 @@ export default function NavbarMobileSidebarLayout({
<div className="navbar-sidebar__item menu">{primaryMenu}</div>
<div className="navbar-sidebar__item menu">{secondaryMenu}</div>
</div>
</div>
</dialog>
);
}

0 comments on commit 0e38363

Please sign in to comment.