mirror of
https://github.com/flarum/framework.git
synced 2025-04-26 06:34:06 +08:00

* Add focus trap util * Add focus trap to Modals Fixes #2663 * Split tab press into `onTab` handler * Remove deprecated code * Use requestAnimationFrame instead of setTimeout * Reduce code duplication * Implement focus trap in nav drawer Fixes #2665 * Hide drawer when window is resized to be bigger Fixes issue where focus trap would remain on the drawer when it is just the app header, if the drawer was opened then the window was made larger. * Simplify conditional function calls * Fix modal focus trap * Remove debug code * Simplify resize handler conditional statements * Add info about reasoning of resize handler * Prefer native JS methods over jQuery * Update conditional function call to handle `undefined` * Expose screen sizes as CSS custom properties * Use `window.matchMedia` rather than resize handler * Fix spelling error Co-authored-by: David Sevilla Martin <me@datitisev.me> * Remove breaking change Co-authored-by: David Sevilla Martin <me@datitisev.me>
120 lines
3.0 KiB
JavaScript
120 lines
3.0 KiB
JavaScript
import { createFocusTrap } from './focusTrap';
|
|
|
|
/**
|
|
* The `Drawer` class controls the page's drawer. The drawer is the area the
|
|
* slides out from the left on mobile devices; it contains the header and the
|
|
* footer.
|
|
*/
|
|
export default class Drawer {
|
|
/**
|
|
* @type {import('./focusTrap').FocusTrap}
|
|
*/
|
|
focusTrap;
|
|
|
|
/**
|
|
* @type {HTMLDivElement}
|
|
*/
|
|
appElement;
|
|
|
|
constructor() {
|
|
// Set up an event handler so that whenever the content area is tapped,
|
|
// the drawer will close.
|
|
document.getElementById('content').addEventListener('click', (e) => {
|
|
if (this.isOpen()) {
|
|
e.preventDefault();
|
|
this.hide();
|
|
}
|
|
});
|
|
|
|
this.appElement = document.getElementById('app');
|
|
this.focusTrap = createFocusTrap('#drawer', { allowOutsideClick: true });
|
|
this.drawerAvailableMediaQuery = window.matchMedia(
|
|
`(max-width: ${getComputedStyle(document.documentElement).getPropertyValue('--screen-phone-max')})`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handler for the `resize` event on `window`.
|
|
*
|
|
* This is used to close the drawer when the viewport is widened past the `phone` size.
|
|
* At this point, the drawer turns into the standard header that we see on desktop, but
|
|
* the drawer is still registered as 'open' internally.
|
|
*
|
|
* This causes issues with the focus trap, resulting in focus becoming trapped within
|
|
* the header on desktop viewports.
|
|
*
|
|
* @internal
|
|
*/
|
|
resizeHandler = ((e) => {
|
|
console.log(this, e);
|
|
if (!e.matches && this.isOpen()) {
|
|
// Drawer is open but we've made window bigger, so hide it.
|
|
this.hide();
|
|
}
|
|
}).bind(this);
|
|
|
|
/**
|
|
* @internal
|
|
* @type {MediaQueryList}
|
|
*/
|
|
drawerAvailableMediaQuery;
|
|
|
|
/**
|
|
* Check whether or not the drawer is currently open.
|
|
*
|
|
* @return {boolean}
|
|
* @public
|
|
*/
|
|
isOpen() {
|
|
return this.appElement.classList.contains('drawerOpen');
|
|
}
|
|
|
|
/**
|
|
* Hide the drawer.
|
|
*
|
|
* @public
|
|
*/
|
|
hide() {
|
|
/**
|
|
* As part of hiding the drawer, this function also ensures that the drawer
|
|
* correctly animates out, while ensuring it is not part of the navigation
|
|
* tree while off-screen.
|
|
*
|
|
* More info: https://github.com/flarum/core/pull/2666#discussion_r595381014
|
|
*/
|
|
|
|
this.focusTrap.deactivate();
|
|
this.drawerAvailableMediaQuery.removeListener(this.resizeHandler);
|
|
|
|
if (!this.isOpen()) return;
|
|
|
|
const $drawer = $('#drawer');
|
|
|
|
// Used to prevent `visibility: hidden` from breaking the exit animation
|
|
$drawer.css('visibility', 'visible').one('transitionend', () => $drawer.css('visibility', ''));
|
|
|
|
this.appElement.classList.remove('drawerOpen');
|
|
|
|
this.$backdrop?.remove?.();
|
|
}
|
|
|
|
/**
|
|
* Show the drawer.
|
|
*
|
|
* @public
|
|
*/
|
|
show() {
|
|
this.appElement.classList.add('drawerOpen');
|
|
|
|
this.drawerAvailableMediaQuery.addListener(this.resizeHandler);
|
|
|
|
this.$backdrop = $('<div/>').addClass('drawer-backdrop fade').appendTo('body').on('click', this.hide.bind(this));
|
|
|
|
requestAnimationFrame(() => {
|
|
this.$backdrop.addClass('in');
|
|
|
|
this.focusTrap.activate();
|
|
});
|
|
}
|
|
}
|