From 27274eeab19160fffada3a4e0d9950295da2df83 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Thu, 14 Sep 2023 20:39:23 +0800 Subject: [PATCH] Rework the modal close + focus logic - 'Esc' a modal will focus on "behind" nested modal - All modals will have 'esc' --- src/components/account-sheet.jsx | 2 -- src/components/modal.jsx | 25 ++++++++++++++++++++-- src/components/modals.jsx | 36 +++++++++++--------------------- src/utils/focus-deck.jsx | 11 ++++++++++ 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/components/account-sheet.jsx b/src/components/account-sheet.jsx index cf7ccc30..37d4a903 100644 --- a/src/components/account-sheet.jsx +++ b/src/components/account-sheet.jsx @@ -11,8 +11,6 @@ function AccountSheet({ account, instance: propInstance, onClose }) { const { masto, instance, authenticated } = api({ instance: propInstance }); const isString = typeof account === 'string'; - const escRef = useHotkeys('esc', onClose, [onClose]); - useEffect(() => { if (!isString) { states.accounts[`${account.id}@${instance}`] = account; diff --git a/src/components/modal.jsx b/src/components/modal.jsx index a70aad57..34b0a58b 100644 --- a/src/components/modal.jsx +++ b/src/components/modal.jsx @@ -2,10 +2,11 @@ import './modal.css'; import { createPortal } from 'preact/compat'; import { useEffect, useRef } from 'preact/hooks'; +import { useHotkeys } from 'react-hotkeys-hook'; const $modalContainer = document.getElementById('modal-container'); -function Modal({ children, onClick, class: className }) { +function Modal({ children, onClose, onClick, class: className }) { if (!children) return null; const modalRef = useRef(); @@ -19,8 +20,28 @@ function Modal({ children, onClick, class: className }) { return () => clearTimeout(timer); }, []); + const escRef = useHotkeys('esc', onClose, [onClose], { + enabled: !!onClose, + }); + const Modal = ( -
+
{ + modalRef.current = node; + escRef.current = node?.querySelector?.('[tabindex="-1"]') || node; + }} + className={className} + onClick={(e) => { + onClick?.(e); + if (e.target === e.currentTarget) { + onClose?.(e); + } + }} + tabIndex="-1" + onFocus={(e) => { + modalRef.current?.querySelector?.('[tabindex="-1"]')?.focus?.(); + }} + > {children}
); diff --git a/src/components/modals.jsx b/src/components/modals.jsx index aa5420b7..47c5871c 100644 --- a/src/components/modals.jsx +++ b/src/components/modals.jsx @@ -76,10 +76,8 @@ export default function Modals() { )} {!!snapStates.showSettings && ( { - if (e.target === e.currentTarget) { - states.showSettings = false; - } + onClose={() => { + states.showSettings = false; }} > { - if (e.target === e.currentTarget) { - states.showAccounts = false; - } + onClose={() => { + states.showAccounts = false; }} > { - if (e.target === e.currentTarget) { - states.showAccount = false; - } + onClose={() => { + states.showAccount = false; }} > { - if (e.target === e.currentTarget) { - states.showDrafts = false; - } + onClose={() => { + states.showDrafts = false; }} > (states.showDrafts = false)} /> @@ -161,10 +153,8 @@ export default function Modals() { {!!snapStates.showShortcutsSettings && ( { - if (e.target === e.currentTarget) { - states.showShortcutsSettings = false; - } + onClose={() => { + states.showShortcutsSettings = false; }} > { - if (e.target === e.currentTarget) { - states.showGenericAccounts = false; - } + onClose={() => { + states.showGenericAccounts = false; }} > { // Focus first column // columns.querySelector('.deck-container')?.focus?.(); } else { + const modals = document.querySelectorAll('#modal-container > *'); + if (modals?.length) { + // Focus last modal + const modal = modals[modals.length - 1]; // last one + const modalFocusElement = + modal.querySelector('[tabindex="-1"]') || modal; + if (modalFocusElement) { + modalFocusElement.focus(); + return; + } + } const backDrop = document.querySelector('.deck-backdrop'); if (backDrop) return; // Focus last deck