From f48dc1f0b874758ea5740868fb167461fa4e630f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 17 Aug 2024 00:26:29 +0200 Subject: [PATCH] Fix navigation in modal + dropdown combination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- src/actions/modals.ts | 5 ++++ .../dropdown-menu/dropdown-menu.tsx | 4 +++- src/components/modal-root.tsx | 18 +++++++++++---- .../compose/components/privacy-dropdown.tsx | 23 +------------------ 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/actions/modals.ts b/src/actions/modals.ts index ef3f55a2c..82f220ada 100644 --- a/src/actions/modals.ts +++ b/src/actions/modals.ts @@ -24,9 +24,14 @@ const closeModal = (type?: ModalType) => ({ modalType: type, }); +type ModalsAction = + ReturnType + | ReturnType; + export { MODAL_OPEN, MODAL_CLOSE, openModal, closeModal, + type ModalsAction, }; diff --git a/src/components/dropdown-menu/dropdown-menu.tsx b/src/components/dropdown-menu/dropdown-menu.tsx index cc28011fe..6cee89a01 100644 --- a/src/components/dropdown-menu/dropdown-menu.tsx +++ b/src/components/dropdown-menu/dropdown-menu.tsx @@ -111,6 +111,7 @@ const DropdownMenu = (props: IDropdownMenu) => { const { state } = history.location; if (goBack && state && (state as any).soapboxDropdownKey === dropdownHistoryKey.current) { history.goBack(); + (history.location.state as any).soapboxDropdownKey = true; } closeDropdownMenu(); @@ -145,6 +146,7 @@ const DropdownMenu = (props: IDropdownMenu) => { const handleDocumentClick = (event: Event) => { if (refs.floating.current && !refs.floating.current.contains(event.target as Node)) { handleClose(); + event.stopPropagation(); } }; @@ -244,7 +246,7 @@ const DropdownMenu = (props: IDropdownMenu) => { unlistenHistory.current = history.listen(({ state }, action) => { if (!(state as any)?.soapboxDropdownKey) { - handleClose(); + handleClose(false); } else if (action === 'POP') { handleClose(false); } diff --git a/src/components/modal-root.tsx b/src/components/modal-root.tsx index 34b2699d4..70a9ccf30 100644 --- a/src/components/modal-root.tsx +++ b/src/components/modal-root.tsx @@ -8,7 +8,7 @@ import { cancelReplyCompose } from 'soapbox/actions/compose'; import { saveDraftStatus } from 'soapbox/actions/draft-statuses'; import { cancelEventCompose } from 'soapbox/actions/events'; import { openModal, closeModal } from 'soapbox/actions/modals'; -import { useAppDispatch, usePrevious } from 'soapbox/hooks'; +import { useAppDispatch, useAppSelector, usePrevious } from 'soapbox/hooks'; import type { ModalType } from 'soapbox/features/ui/components/modal-root'; import type { ReducerCompose } from 'soapbox/reducers/compose'; @@ -49,6 +49,8 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) const dispatch = useAppDispatch(); const [revealed, setRevealed] = useState(!!children); + const isDropdownOpen = useAppSelector(state => state.dropdown_menu.isOpen); + const wasDropdownOpen = usePrevious(isDropdownOpen); const ref = useRef(null); const activeElement = useRef(revealed ? document.activeElement as HTMLDivElement | null : null); @@ -56,7 +58,6 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) const unlistenHistory = useRef>(); const prevChildren = usePrevious(children); - const prevType = usePrevious(type); const visible = !!children; @@ -158,7 +159,7 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) }); }; - const handleModalClose = (type: string) => { + const handleModalClose = () => { if (unlistenHistory.current) { unlistenHistory.current(); } @@ -206,7 +207,7 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) activeElement.current = null; getSiblings().forEach(sibling => (sibling as HTMLDivElement).removeAttribute('inert')); - handleModalClose(prevType!); + handleModalClose(); } if (children) { @@ -218,6 +219,15 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) } }, [children]); + useEffect(() => { + if (isDropdownOpen && unlistenHistory.current) { + unlistenHistory.current(); + } else if (!isDropdownOpen && wasDropdownOpen) { + // TODO find a better solution + setTimeout(() => handleModalOpen(), 100); + } + }, [isDropdownOpen]); + if (!visible) { return (
diff --git a/src/features/compose/components/privacy-dropdown.tsx b/src/features/compose/components/privacy-dropdown.tsx index 6f98f8f56..8bab2beac 100644 --- a/src/features/compose/components/privacy-dropdown.tsx +++ b/src/features/compose/components/privacy-dropdown.tsx @@ -1,6 +1,5 @@ import clsx from 'clsx'; -import { supportsPassiveEvents } from 'detect-passive-events'; -import React, { useRef, useEffect } from 'react'; +import React, { useRef } from 'react'; import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import { changeComposeFederated, changeComposeVisibility } from 'soapbox/actions/compose'; @@ -28,8 +27,6 @@ const messages = defineMessages({ local: { id: 'privacy.local', defaultMessage: '{privacy} (local-only)' }, }); -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; - interface Option { icon: string; value: string; @@ -54,12 +51,6 @@ const PrivacyDropdownMenu: React.FC = ({ const node = useRef(null); const focusedItem = useRef(null); - const handleDocumentClick = (e: MouseEvent | TouchEvent) => { - if (node.current && !node.current.contains(e.target as HTMLElement)) { - onClose(); - } - }; - const handleKeyDown: React.KeyboardEventHandler = e => { const index = [...e.currentTarget.parentElement!.children].indexOf(e.currentTarget); let element: ChildNode | null | undefined = null; @@ -113,18 +104,6 @@ const PrivacyDropdownMenu: React.FC = ({ } }; - useEffect(() => { - document.addEventListener('click', handleDocumentClick, false); - document.addEventListener('touchend', handleDocumentClick, listenerOptions); - - focusedItem.current?.focus({ preventScroll: true }); - - return () => { - document.removeEventListener('click', handleDocumentClick, false); - document.removeEventListener('touchend', handleDocumentClick); - }; - }, []); - return (
    {items.map(item => {