mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-11-05 12:42:48 -05:00
On iPad and tablets, the main navigation menu is rendered as a responsive Bootstrap offcanvas drawer (`#navbarSupportedContent.offcanvas-lg`). When users tapped 'Sign in' in the drawer, the auth modal opened while the offcanvas remained open in the DOM, allowing Bootstrap's active FocusTrap on the offcanvas to intercept focusin events on document and steal focus away from the modal inputs, preventing clicking or typing on iPadOS. Update `closeOpenOffcanvases` in `Modal.jsx` and `RecordDialog.jsx` to match all open offcanvases including responsive ones (`.offcanvas.show, [class*="offcanvas-"].show`) and closest offcanvas containers, invoke it when auth dialog triggers are clicked in `AuthDialog.jsx`, restore focus correctly when the modal closes, and add accompanying tests. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
48 lines
2.0 KiB
React
48 lines
2.0 KiB
React
import React, {useEffect, useRef, useState} from 'react';
|
|
|
|
import BlueskyModal from './BlueskyModal';
|
|
import {closeOpenOffcanvases} from './Modal';
|
|
import SignInModal from './SignInModal';
|
|
import SignUpModal from './SignUpModal';
|
|
|
|
// The sign in, sign up and Bluesky dialogs for every server-rendered link to them.
|
|
// Nothing of its own is drawn: it listens for clicks on any
|
|
// [data-auth-dialog="sign_in"], "sign_up" or "bluesky" link (the menu,
|
|
// the home page's buttons) and opens that dialog over the page. The layout
|
|
// mounts it once, for visitors who aren't signed in.
|
|
//
|
|
// The link's own href is the page for the same thing, so it stays the
|
|
// fallback without JavaScript.
|
|
export default function AuthDialog({urls, recaptcha_site_key: recaptchaSiteKey, site_name: siteName}) {
|
|
const [opened, setOpened] = useState(null); // 'sign_in', 'sign_up', 'bluesky' or null
|
|
const open = useRef(null);
|
|
open.current = setOpened;
|
|
|
|
useEffect(() => {
|
|
function onClick(event) {
|
|
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey) return;
|
|
const trigger = event.target.closest('[data-auth-dialog]');
|
|
if (!trigger) return;
|
|
event.preventDefault();
|
|
closeOpenOffcanvases(trigger);
|
|
open.current(trigger.dataset.authDialog);
|
|
}
|
|
|
|
document.addEventListener('click', onClick);
|
|
return () => document.removeEventListener('click', onClick);
|
|
}, []);
|
|
|
|
const onBluesky = urls.bluesky ? () => setOpened('bluesky') : undefined;
|
|
|
|
if (opened === 'sign_in') {
|
|
return <SignInModal urls={urls} onClose={() => setOpened(null)} onSwitch={() => setOpened('sign_up')} onBluesky={onBluesky} />;
|
|
}
|
|
if (opened === 'sign_up') {
|
|
return <SignUpModal urls={urls} recaptchaSiteKey={recaptchaSiteKey} siteName={siteName} onClose={() => setOpened(null)} onSwitch={() => setOpened('sign_in')} onBluesky={onBluesky} />;
|
|
}
|
|
if (opened === 'bluesky') {
|
|
return <BlueskyModal urls={urls} onClose={() => setOpened(null)} />;
|
|
}
|
|
return null;
|
|
}
|