|
|
|
@ -5,6 +5,7 @@ import {
|
|
|
|
useCallback,
|
|
|
|
useCallback,
|
|
|
|
cloneElement,
|
|
|
|
cloneElement,
|
|
|
|
Children,
|
|
|
|
Children,
|
|
|
|
|
|
|
|
useId,
|
|
|
|
} from 'react';
|
|
|
|
} from 'react';
|
|
|
|
|
|
|
|
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
@ -16,6 +17,7 @@ import Overlay from 'react-overlays/Overlay';
|
|
|
|
import type {
|
|
|
|
import type {
|
|
|
|
OffsetValue,
|
|
|
|
OffsetValue,
|
|
|
|
UsePopperOptions,
|
|
|
|
UsePopperOptions,
|
|
|
|
|
|
|
|
Placement,
|
|
|
|
} from 'react-overlays/esm/usePopper';
|
|
|
|
} from 'react-overlays/esm/usePopper';
|
|
|
|
|
|
|
|
|
|
|
|
import { fetchRelationships } from 'mastodon/actions/accounts';
|
|
|
|
import { fetchRelationships } from 'mastodon/actions/accounts';
|
|
|
|
@ -295,6 +297,11 @@ interface DropdownProps<Item = MenuItem> {
|
|
|
|
title?: string;
|
|
|
|
title?: string;
|
|
|
|
disabled?: boolean;
|
|
|
|
disabled?: boolean;
|
|
|
|
scrollable?: boolean;
|
|
|
|
scrollable?: boolean;
|
|
|
|
|
|
|
|
placement?: Placement;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Prevent the `ScrollableList` with this scrollKey
|
|
|
|
|
|
|
|
* from being scrolled while the dropdown is open
|
|
|
|
|
|
|
|
*/
|
|
|
|
scrollKey?: string;
|
|
|
|
scrollKey?: string;
|
|
|
|
status?: ImmutableMap<string, unknown>;
|
|
|
|
status?: ImmutableMap<string, unknown>;
|
|
|
|
forceDropdown?: boolean;
|
|
|
|
forceDropdown?: boolean;
|
|
|
|
@ -316,6 +323,7 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
title = 'Menu',
|
|
|
|
title = 'Menu',
|
|
|
|
disabled,
|
|
|
|
disabled,
|
|
|
|
scrollable,
|
|
|
|
scrollable,
|
|
|
|
|
|
|
|
placement = 'bottom',
|
|
|
|
status,
|
|
|
|
status,
|
|
|
|
forceDropdown = false,
|
|
|
|
forceDropdown = false,
|
|
|
|
renderItem,
|
|
|
|
renderItem,
|
|
|
|
@ -331,16 +339,15 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
);
|
|
|
|
);
|
|
|
|
const [currentId] = useState(id++);
|
|
|
|
const [currentId] = useState(id++);
|
|
|
|
const open = currentId === openDropdownId;
|
|
|
|
const open = currentId === openDropdownId;
|
|
|
|
const activeElement = useRef<HTMLElement | null>(null);
|
|
|
|
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
const targetRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
const menuId = useId();
|
|
|
|
const prefetchAccountId = status
|
|
|
|
const prefetchAccountId = status
|
|
|
|
? status.getIn(['account', 'id'])
|
|
|
|
? status.getIn(['account', 'id'])
|
|
|
|
: undefined;
|
|
|
|
: undefined;
|
|
|
|
|
|
|
|
|
|
|
|
const handleClose = useCallback(() => {
|
|
|
|
const handleClose = useCallback(() => {
|
|
|
|
if (activeElement.current) {
|
|
|
|
if (buttonRef.current) {
|
|
|
|
activeElement.current.focus({ preventScroll: true });
|
|
|
|
buttonRef.current.focus({ preventScroll: true });
|
|
|
|
activeElement.current = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dispatch(
|
|
|
|
dispatch(
|
|
|
|
@ -375,7 +382,7 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
[handleClose, onItemClick, items],
|
|
|
|
[handleClose, onItemClick, items],
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handleClick = useCallback(
|
|
|
|
const toggleDropdown = useCallback(
|
|
|
|
(e: React.MouseEvent | React.KeyboardEvent) => {
|
|
|
|
(e: React.MouseEvent | React.KeyboardEvent) => {
|
|
|
|
const { type } = e;
|
|
|
|
const { type } = e;
|
|
|
|
|
|
|
|
|
|
|
|
@ -423,38 +430,6 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
],
|
|
|
|
],
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handleMouseDown = useCallback(() => {
|
|
|
|
|
|
|
|
if (!open && document.activeElement instanceof HTMLElement) {
|
|
|
|
|
|
|
|
activeElement.current = document.activeElement;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, [open]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleButtonKeyDown = useCallback(
|
|
|
|
|
|
|
|
(e: React.KeyboardEvent) => {
|
|
|
|
|
|
|
|
switch (e.key) {
|
|
|
|
|
|
|
|
case ' ':
|
|
|
|
|
|
|
|
case 'Enter':
|
|
|
|
|
|
|
|
handleMouseDown();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
[handleMouseDown],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleKeyPress = useCallback(
|
|
|
|
|
|
|
|
(e: React.KeyboardEvent) => {
|
|
|
|
|
|
|
|
switch (e.key) {
|
|
|
|
|
|
|
|
case ' ':
|
|
|
|
|
|
|
|
case 'Enter':
|
|
|
|
|
|
|
|
handleClick(e);
|
|
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
[handleClick],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
if (currentId === openDropdownId) {
|
|
|
|
if (currentId === openDropdownId) {
|
|
|
|
@ -465,14 +440,16 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
|
|
|
|
|
|
|
|
let button: React.ReactElement;
|
|
|
|
let button: React.ReactElement;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const buttonProps = {
|
|
|
|
|
|
|
|
disabled,
|
|
|
|
|
|
|
|
onClick: toggleDropdown,
|
|
|
|
|
|
|
|
'aria-expanded': open,
|
|
|
|
|
|
|
|
'aria-controls': menuId,
|
|
|
|
|
|
|
|
ref: buttonRef,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (children) {
|
|
|
|
if (children) {
|
|
|
|
button = cloneElement(Children.only(children), {
|
|
|
|
button = cloneElement(Children.only(children), buttonProps);
|
|
|
|
onClick: handleClick,
|
|
|
|
|
|
|
|
onMouseDown: handleMouseDown,
|
|
|
|
|
|
|
|
onKeyDown: handleButtonKeyDown,
|
|
|
|
|
|
|
|
onKeyPress: handleKeyPress,
|
|
|
|
|
|
|
|
ref: targetRef,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} else if (icon && iconComponent) {
|
|
|
|
} else if (icon && iconComponent) {
|
|
|
|
button = (
|
|
|
|
button = (
|
|
|
|
<IconButton
|
|
|
|
<IconButton
|
|
|
|
@ -480,12 +457,7 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
iconComponent={iconComponent}
|
|
|
|
iconComponent={iconComponent}
|
|
|
|
title={title}
|
|
|
|
title={title}
|
|
|
|
active={open}
|
|
|
|
active={open}
|
|
|
|
disabled={disabled}
|
|
|
|
{...buttonProps}
|
|
|
|
onClick={handleClick}
|
|
|
|
|
|
|
|
onMouseDown={handleMouseDown}
|
|
|
|
|
|
|
|
onKeyDown={handleButtonKeyDown}
|
|
|
|
|
|
|
|
onKeyPress={handleKeyPress}
|
|
|
|
|
|
|
|
ref={targetRef}
|
|
|
|
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@ -499,13 +471,13 @@ export const Dropdown = <Item = MenuItem,>({
|
|
|
|
<Overlay
|
|
|
|
<Overlay
|
|
|
|
show={open}
|
|
|
|
show={open}
|
|
|
|
offset={offset}
|
|
|
|
offset={offset}
|
|
|
|
placement='bottom'
|
|
|
|
placement={placement}
|
|
|
|
flip
|
|
|
|
flip
|
|
|
|
target={targetRef}
|
|
|
|
target={buttonRef}
|
|
|
|
popperConfig={popperConfig}
|
|
|
|
popperConfig={popperConfig}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
{({ props, arrowProps, placement }) => (
|
|
|
|
{({ props, arrowProps, placement }) => (
|
|
|
|
<div {...props}>
|
|
|
|
<div {...props} id={menuId}>
|
|
|
|
<div className={`dropdown-animation dropdown-menu ${placement}`}>
|
|
|
|
<div className={`dropdown-animation dropdown-menu ${placement}`}>
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
className={`dropdown-menu__arrow ${placement}`}
|
|
|
|
className={`dropdown-menu__arrow ${placement}`}
|
|
|
|
|