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>
53 lines
2.0 KiB
React
53 lines
2.0 KiB
React
import React, {useEffect, useRef, useState} from 'react';
|
|
|
|
import {getJson} from '../api';
|
|
import {closeOpenOffcanvases} from './Modal';
|
|
import PlantingFlow from './PlantingFlow';
|
|
|
|
// The record harvest and save seeds dialogs for every server-rendered link to
|
|
// the full form. Nothing of its own is drawn: it listens for clicks on any
|
|
// [data-record-dialog="harvest"] or [data-record-dialog="seeds"] link on the
|
|
// page and opens the same dialog the home page does. A link for a particular
|
|
// crop says which in data-crop-id and data-crop-name. The layout mounts it once.
|
|
//
|
|
// The member's plantings come from plantingsUrl when first needed. The link's
|
|
// own href stays the fallback without JavaScript, and when the plantings cannot
|
|
// be loaded.
|
|
export default function RecordDialog({plantings_url: plantingsUrl}) {
|
|
const [opened, setOpened] = useState(null); // {action, crop}, or null when closed
|
|
const [plantings, setPlantings] = useState(null);
|
|
const open = useRef(null);
|
|
open.current = async (trigger) => {
|
|
const action = trigger.dataset.recordDialog;
|
|
const crop = trigger.dataset.cropId ? {id: Number(trigger.dataset.cropId), name: trigger.dataset.cropName} : null;
|
|
if (!plantings) {
|
|
try {
|
|
const {ok, data} = await getJson(plantingsUrl);
|
|
if (!ok) throw new Error('load failed');
|
|
setPlantings(data);
|
|
} catch (error) {
|
|
window.location.href = trigger.getAttribute('href');
|
|
return;
|
|
}
|
|
}
|
|
setOpened({action, crop});
|
|
};
|
|
|
|
useEffect(() => {
|
|
function onClick(event) {
|
|
const trigger = event.target.closest('[data-record-dialog]');
|
|
if (!trigger) return;
|
|
event.preventDefault();
|
|
closeOpenOffcanvases(trigger);
|
|
open.current(trigger);
|
|
}
|
|
|
|
document.addEventListener('click', onClick);
|
|
return () => document.removeEventListener('click', onClick);
|
|
}, []);
|
|
|
|
if (!opened || !plantings) return null;
|
|
|
|
return <PlantingFlow action={opened.action} crop={opened.crop} plantings={plantings} onClose={() => setOpened(null)} />;
|
|
}
|