Merge branch 'dropdown-menu-improvements' into 'develop'
Dropdown menu improvements and fixes See merge request soapbox-pub/soapbox!2541
This commit is contained in:
commit
ea298472a8
2 changed files with 27 additions and 44 deletions
|
@ -87,7 +87,7 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
|
||||||
title={item.text}
|
title={item.text}
|
||||||
className={
|
className={
|
||||||
clsx({
|
clsx({
|
||||||
'flex px-4 py-2.5 text-sm text-gray-700 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none cursor-pointer': true,
|
'flex px-4 py-2.5 text-sm text-gray-700 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gray-100 dark:focus:bg-gray-800 focus:outline-none cursor-pointer': true,
|
||||||
'text-danger-600 dark:text-danger-400': item.destructive,
|
'text-danger-600 dark:text-danger-400': item.destructive,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -106,4 +106,4 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DropdownMenuItem;
|
export default DropdownMenuItem;
|
||||||
|
|
|
@ -4,12 +4,9 @@ import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
import { closeDropdownMenu as closeDropdownMenuRedux, openDropdownMenu } from 'soapbox/actions/dropdown-menu';
|
||||||
closeDropdownMenu as closeDropdownMenuRedux,
|
|
||||||
openDropdownMenu,
|
|
||||||
} from 'soapbox/actions/dropdown-menu';
|
|
||||||
import { closeModal, openModal } from 'soapbox/actions/modals';
|
import { closeModal, openModal } from 'soapbox/actions/modals';
|
||||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
import { isUserTouching } from 'soapbox/is-mobile';
|
import { isUserTouching } from 'soapbox/is-mobile';
|
||||||
|
|
||||||
import { IconButton, Portal } from '../ui';
|
import { IconButton, Portal } from '../ui';
|
||||||
|
@ -53,10 +50,8 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
const isOpenRedux = useAppSelector(state => state.dropdown_menu.isOpen);
|
|
||||||
|
|
||||||
const arrowRef = useRef<HTMLDivElement>(null);
|
const arrowRef = useRef<HTMLDivElement>(null);
|
||||||
const activeElement = useRef<Element | null>(null);
|
|
||||||
|
|
||||||
const isOnMobile = isUserTouching();
|
const isOnMobile = isUserTouching();
|
||||||
|
|
||||||
|
@ -116,10 +111,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
if (activeElement.current && activeElement.current === refs.reference.current) {
|
(refs.reference.current as HTMLButtonElement)?.focus();
|
||||||
(activeElement.current as any).focus();
|
|
||||||
activeElement.current = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOnMobile) {
|
if (isOnMobile) {
|
||||||
dispatch(closeModal('ACTIONS'));
|
dispatch(closeModal('ACTIONS'));
|
||||||
|
@ -134,24 +126,13 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeDropdownMenu = () => {
|
const closeDropdownMenu = () => {
|
||||||
if (isOpenRedux) {
|
dispatch((dispatch, getState) => {
|
||||||
dispatch(closeDropdownMenuRedux());
|
const isOpenRedux = getState().dropdown_menu.isOpen;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseDown: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = () => {
|
if (isOpenRedux) {
|
||||||
if (!isOpen) {
|
dispatch(closeDropdownMenuRedux());
|
||||||
activeElement.current = document.activeElement;
|
}
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const handleButtonKeyDown: React.EventHandler<React.KeyboardEvent> = (event) => {
|
|
||||||
switch (event.key) {
|
|
||||||
case ' ':
|
|
||||||
case 'Enter':
|
|
||||||
handleMouseDown(event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyPress: React.EventHandler<React.KeyboardEvent<HTMLButtonElement>> = (event) => {
|
const handleKeyPress: React.EventHandler<React.KeyboardEvent<HTMLButtonElement>> = (event) => {
|
||||||
|
@ -263,16 +244,22 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('click', handleDocumentClick, false);
|
if (isOpen) {
|
||||||
document.addEventListener('keydown', handleKeyDown, false);
|
if (refs.floating.current) {
|
||||||
document.addEventListener('touchend', handleDocumentClick, listenerOptions);
|
(refs.floating.current?.querySelector('li a[role=\'button\']') as HTMLAnchorElement)?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
document.addEventListener('click', handleDocumentClick, false);
|
||||||
document.removeEventListener('click', handleDocumentClick);
|
document.addEventListener('keydown', handleKeyDown, false);
|
||||||
document.removeEventListener('keydown', handleKeyDown);
|
document.addEventListener('touchend', handleDocumentClick, listenerOptions);
|
||||||
document.removeEventListener('touchend', handleDocumentClick);
|
|
||||||
};
|
return () => {
|
||||||
}, [refs.floating.current]);
|
document.removeEventListener('click', handleDocumentClick);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
document.removeEventListener('touchend', handleDocumentClick);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [isOpen, refs.floating.current]);
|
||||||
|
|
||||||
if (items.length === 0) {
|
if (items.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -284,8 +271,6 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
React.cloneElement(children, {
|
React.cloneElement(children, {
|
||||||
disabled,
|
disabled,
|
||||||
onClick: handleClick,
|
onClick: handleClick,
|
||||||
onMouseDown: handleMouseDown,
|
|
||||||
onKeyDown: handleButtonKeyDown,
|
|
||||||
onKeyPress: handleKeyPress,
|
onKeyPress: handleKeyPress,
|
||||||
ref: refs.setReference,
|
ref: refs.setReference,
|
||||||
})
|
})
|
||||||
|
@ -299,8 +284,6 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
title={title}
|
title={title}
|
||||||
src={src}
|
src={src}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
onKeyDown={handleButtonKeyDown}
|
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
ref={refs.setReference}
|
ref={refs.setReference}
|
||||||
/>
|
/>
|
||||||
|
@ -346,4 +329,4 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DropdownMenu;
|
export default DropdownMenu;
|
||||||
|
|
Loading…
Reference in a new issue