2022-08-31 02:35:06 -07:00
|
|
|
import classNames from 'clsx';
|
2022-01-10 14:17:52 -08:00
|
|
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
|
|
|
import React from 'react';
|
2022-04-02 11:03:12 -07:00
|
|
|
import { spring } from 'react-motion';
|
|
|
|
// @ts-ignore: TODO: upgrade react-overlays. v3.1 and above have TS definitions
|
2022-01-10 14:17:52 -08:00
|
|
|
import Overlay from 'react-overlays/lib/Overlay';
|
2022-04-02 11:03:12 -07:00
|
|
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
2022-01-10 14:25:06 -08:00
|
|
|
|
2022-11-25 10:28:43 -08:00
|
|
|
import { Counter, IconButton } from 'soapbox/components/ui';
|
2022-04-24 15:53:03 -07:00
|
|
|
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
2022-11-16 05:32:32 -08:00
|
|
|
import Motion from 'soapbox/features/ui/util/optional-motion';
|
2022-01-10 14:25:06 -08:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
import type { Status } from 'soapbox/types/entities';
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2020-10-15 07:10:45 -07:00
|
|
|
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
2020-03-27 13:59:38 -07:00
|
|
|
let id = 0;
|
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
export interface MenuItem {
|
2022-04-04 12:17:24 -07:00
|
|
|
action?: React.EventHandler<React.KeyboardEvent | React.MouseEvent>,
|
2022-04-02 11:03:12 -07:00
|
|
|
middleClick?: React.EventHandler<React.MouseEvent>,
|
2022-06-30 07:51:36 -07:00
|
|
|
text: string,
|
2022-04-02 11:03:12 -07:00
|
|
|
href?: string,
|
|
|
|
to?: string,
|
|
|
|
newTab?: boolean,
|
|
|
|
isLogout?: boolean,
|
2022-06-09 11:56:14 -07:00
|
|
|
icon?: string,
|
2022-04-28 14:20:21 -07:00
|
|
|
count?: number,
|
2022-04-02 11:03:12 -07:00
|
|
|
destructive?: boolean,
|
2022-06-07 09:25:53 -07:00
|
|
|
meta?: string,
|
|
|
|
active?: boolean,
|
2022-04-02 11:03:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export type Menu = Array<MenuItem | null>;
|
|
|
|
|
|
|
|
interface IDropdownMenu extends RouteComponentProps {
|
|
|
|
items: Menu,
|
|
|
|
onClose: () => void,
|
|
|
|
style?: React.CSSProperties,
|
|
|
|
placement?: DropdownPlacement,
|
|
|
|
arrowOffsetLeft?: string,
|
|
|
|
arrowOffsetTop?: string,
|
|
|
|
openedViaKeyboard: boolean,
|
|
|
|
}
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
interface IDropdownMenuState {
|
|
|
|
mounted: boolean,
|
|
|
|
}
|
|
|
|
|
|
|
|
class DropdownMenu extends React.PureComponent<IDropdownMenu, IDropdownMenuState> {
|
|
|
|
|
|
|
|
static defaultProps: Partial<IDropdownMenu> = {
|
2020-03-27 13:59:38 -07:00
|
|
|
style: {},
|
|
|
|
placement: 'bottom',
|
|
|
|
};
|
|
|
|
|
|
|
|
state = {
|
|
|
|
mounted: false,
|
|
|
|
};
|
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
node: HTMLDivElement | null = null;
|
|
|
|
focusedItem: HTMLAnchorElement | null = null;
|
|
|
|
|
|
|
|
handleDocumentClick = (e: Event) => {
|
|
|
|
if (this.node && !this.node.contains(e.target as Node)) {
|
2020-03-27 13:59:38 -07:00
|
|
|
this.props.onClose();
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2020-04-14 14:47:35 -07:00
|
|
|
componentDidMount() {
|
2020-03-27 13:59:38 -07:00
|
|
|
document.addEventListener('click', this.handleDocumentClick, false);
|
|
|
|
document.addEventListener('keydown', this.handleKeyDown, false);
|
|
|
|
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
2021-08-28 05:52:39 -07:00
|
|
|
if (this.focusedItem && this.props.openedViaKeyboard) {
|
2021-08-28 06:38:51 -07:00
|
|
|
this.focusedItem.focus({ preventScroll: true });
|
2021-08-28 05:52:39 -07:00
|
|
|
}
|
2020-03-27 13:59:38 -07:00
|
|
|
this.setState({ mounted: true });
|
|
|
|
}
|
|
|
|
|
2020-04-14 14:47:35 -07:00
|
|
|
componentWillUnmount() {
|
2022-04-02 11:03:12 -07:00
|
|
|
document.removeEventListener('click', this.handleDocumentClick);
|
|
|
|
document.removeEventListener('keydown', this.handleKeyDown);
|
|
|
|
document.removeEventListener('touchend', this.handleDocumentClick);
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
setRef: React.RefCallback<HTMLDivElement> = c => {
|
2020-03-27 13:59:38 -07:00
|
|
|
this.node = c;
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
setFocusRef: React.RefCallback<HTMLAnchorElement> = c => {
|
2020-03-27 13:59:38 -07:00
|
|
|
this.focusedItem = c;
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleKeyDown = (e: KeyboardEvent) => {
|
|
|
|
if (!this.node) return;
|
|
|
|
|
2020-03-27 13:59:38 -07:00
|
|
|
const items = Array.from(this.node.getElementsByTagName('a'));
|
2022-04-02 11:03:12 -07:00
|
|
|
const index = items.indexOf(document.activeElement as any);
|
2021-08-28 06:37:53 -07:00
|
|
|
let element = null;
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-05-11 10:40:34 -07:00
|
|
|
switch (e.key) {
|
2022-05-11 14:06:35 -07:00
|
|
|
case 'ArrowDown':
|
2022-05-11 10:40:34 -07:00
|
|
|
element = items[index + 1] || items[0];
|
2022-05-11 14:06:35 -07:00
|
|
|
break;
|
|
|
|
case 'ArrowUp':
|
|
|
|
element = items[index - 1] || items[items.length - 1];
|
|
|
|
break;
|
|
|
|
case 'Tab':
|
|
|
|
if (e.shiftKey) {
|
|
|
|
element = items[index - 1] || items[items.length - 1];
|
|
|
|
} else {
|
|
|
|
element = items[index + 1] || items[0];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'Home':
|
|
|
|
element = items[0];
|
|
|
|
break;
|
|
|
|
case 'End':
|
|
|
|
element = items[items.length - 1];
|
|
|
|
break;
|
|
|
|
case 'Escape':
|
|
|
|
this.props.onClose();
|
|
|
|
break;
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
2021-08-28 06:37:53 -07:00
|
|
|
|
|
|
|
if (element) {
|
|
|
|
element.focus();
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleItemKeyPress: React.EventHandler<React.KeyboardEvent> = e => {
|
2021-08-28 05:52:39 -07:00
|
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
2020-03-27 13:59:38 -07:00
|
|
|
this.handleClick(e);
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleClick: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = e => {
|
2020-03-27 13:59:38 -07:00
|
|
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
2022-04-02 11:03:12 -07:00
|
|
|
const item = this.props.items[i];
|
|
|
|
if (!item) return;
|
|
|
|
const { action, to } = item;
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
this.props.onClose();
|
|
|
|
|
2022-11-10 08:01:41 -08:00
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
if (to) {
|
2020-03-27 13:59:38 -07:00
|
|
|
e.preventDefault();
|
2022-03-17 18:17:28 -07:00
|
|
|
this.props.history.push(to);
|
2022-11-10 08:01:41 -08:00
|
|
|
} else if (typeof action === 'function') {
|
|
|
|
e.preventDefault();
|
|
|
|
action(e);
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleMiddleClick: React.EventHandler<React.MouseEvent> = e => {
|
2021-03-29 21:22:54 -07:00
|
|
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
2022-04-02 11:03:12 -07:00
|
|
|
const item = this.props.items[i];
|
|
|
|
if (!item) return;
|
|
|
|
const { middleClick } = item;
|
2021-03-29 21:22:54 -07:00
|
|
|
|
|
|
|
this.props.onClose();
|
|
|
|
|
|
|
|
if (e.button === 1 && typeof middleClick === 'function') {
|
|
|
|
e.preventDefault();
|
|
|
|
middleClick(e);
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2021-03-29 21:22:54 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleAuxClick: React.EventHandler<React.MouseEvent> = e => {
|
2021-03-29 21:22:54 -07:00
|
|
|
if (e.button === 1) {
|
|
|
|
this.handleMiddleClick(e);
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2021-03-29 21:22:54 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
renderItem(option: MenuItem | null, i: number): JSX.Element {
|
2020-03-27 13:59:38 -07:00
|
|
|
if (option === null) {
|
|
|
|
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
|
|
|
}
|
|
|
|
|
2022-04-28 14:20:21 -07:00
|
|
|
const { text, href, to, newTab, isLogout, icon, count, destructive } = option;
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
return (
|
2022-04-24 15:53:03 -07:00
|
|
|
<li className={classNames('dropdown-menu__item truncate', { destructive })} key={`${text}-${i}`}>
|
2020-03-27 13:59:38 -07:00
|
|
|
<a
|
2021-03-29 21:26:50 -07:00
|
|
|
href={href || to || '#'}
|
2020-03-27 13:59:38 -07:00
|
|
|
role='button'
|
2022-04-02 11:03:12 -07:00
|
|
|
tabIndex={0}
|
2020-03-27 13:59:38 -07:00
|
|
|
ref={i === 0 ? this.setFocusRef : null}
|
|
|
|
onClick={this.handleClick}
|
2021-03-29 21:22:54 -07:00
|
|
|
onAuxClick={this.handleAuxClick}
|
2021-08-28 06:05:26 -07:00
|
|
|
onKeyPress={this.handleItemKeyPress}
|
2020-03-27 13:59:38 -07:00
|
|
|
data-index={i}
|
2022-04-02 11:03:12 -07:00
|
|
|
target={newTab ? '_blank' : undefined}
|
|
|
|
data-method={isLogout ? 'delete' : undefined}
|
2022-11-10 08:01:41 -08:00
|
|
|
title={text}
|
2020-04-14 11:44:40 -07:00
|
|
|
>
|
2022-11-25 09:04:11 -08:00
|
|
|
{icon && <SvgIcon src={icon} className='mr-3 rtl:ml-3 rtl:mr-0 h-5 w-5 flex-none' />}
|
2022-04-28 14:20:21 -07:00
|
|
|
|
2022-04-24 15:53:03 -07:00
|
|
|
<span className='truncate'>{text}</span>
|
2022-04-28 14:20:21 -07:00
|
|
|
|
|
|
|
{count ? (
|
2022-04-28 14:29:15 -07:00
|
|
|
<span className='ml-auto h-5 w-5 flex-none'>
|
|
|
|
<Counter count={count} />
|
2022-04-28 14:20:21 -07:00
|
|
|
</span>
|
|
|
|
) : null}
|
2020-03-27 13:59:38 -07:00
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-14 14:47:35 -07:00
|
|
|
render() {
|
2020-03-27 13:59:38 -07:00
|
|
|
const { items, style, placement, arrowOffsetLeft, arrowOffsetTop } = this.props;
|
|
|
|
const { mounted } = this.state;
|
|
|
|
return (
|
|
|
|
<Motion defaultStyle={{ opacity: 0, scaleX: 1, scaleY: 1 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
|
|
|
{({ opacity, scaleX, scaleY }) => (
|
|
|
|
// It should not be transformed when mounting because the resulting
|
|
|
|
// size will be used to determine the coordinate of the menu by
|
|
|
|
// react-overlays
|
2022-09-12 11:42:15 -07:00
|
|
|
<div
|
|
|
|
className={`dropdown-menu ${placement}`}
|
|
|
|
style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : undefined }}
|
|
|
|
ref={this.setRef}
|
|
|
|
data-testid='dropdown-menu'
|
|
|
|
>
|
2020-03-27 13:59:38 -07:00
|
|
|
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
|
|
|
<ul>
|
|
|
|
{items.map((option, i) => this.renderItem(option, i))}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</Motion>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
const RouterDropdownMenu = withRouter(DropdownMenu);
|
|
|
|
|
|
|
|
export interface IDropdown extends RouteComponentProps {
|
|
|
|
icon?: string,
|
2022-04-02 16:43:34 -07:00
|
|
|
src?: string,
|
2022-04-02 11:03:12 -07:00
|
|
|
items: Menu,
|
|
|
|
size?: number,
|
|
|
|
active?: boolean,
|
|
|
|
pressed?: boolean,
|
2022-04-02 16:43:34 -07:00
|
|
|
title?: string,
|
2022-04-02 11:03:12 -07:00
|
|
|
disabled?: boolean,
|
|
|
|
status?: Status,
|
|
|
|
isUserTouching?: () => boolean,
|
|
|
|
isModalOpen?: boolean,
|
|
|
|
onOpen?: (
|
|
|
|
id: number,
|
|
|
|
onItemClick: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,
|
|
|
|
dropdownPlacement: DropdownPlacement,
|
|
|
|
keyboard: boolean,
|
|
|
|
) => void,
|
|
|
|
onClose?: (id: number) => void,
|
|
|
|
dropdownPlacement?: string,
|
2022-06-05 07:17:26 -07:00
|
|
|
openDropdownId?: number | null,
|
2022-04-02 11:03:12 -07:00
|
|
|
openedViaKeyboard?: boolean,
|
|
|
|
text?: string,
|
|
|
|
onShiftClick?: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,
|
2022-04-02 16:43:34 -07:00
|
|
|
children?: JSX.Element,
|
2022-11-24 14:15:30 -08:00
|
|
|
dropdownMenuStyle?: React.CSSProperties,
|
2022-04-02 11:03:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
interface IDropdownState {
|
|
|
|
id: number,
|
|
|
|
open: boolean,
|
|
|
|
}
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
export type DropdownPlacement = 'top' | 'bottom';
|
|
|
|
|
|
|
|
class Dropdown extends React.PureComponent<IDropdown, IDropdownState> {
|
|
|
|
|
|
|
|
static defaultProps: Partial<IDropdown> = {
|
2020-03-27 13:59:38 -07:00
|
|
|
title: 'Menu',
|
|
|
|
};
|
|
|
|
|
|
|
|
state = {
|
|
|
|
id: id++,
|
2022-04-02 11:03:12 -07:00
|
|
|
open: false,
|
2020-03-27 13:59:38 -07:00
|
|
|
};
|
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
target: HTMLButtonElement | null = null;
|
|
|
|
activeElement: Element | null = null;
|
|
|
|
|
|
|
|
handleClick: React.EventHandler<React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>> = e => {
|
2022-01-25 13:59:26 -08:00
|
|
|
const { onOpen, onShiftClick, openDropdownId } = this.props;
|
2022-03-21 11:09:01 -07:00
|
|
|
e.stopPropagation();
|
2022-01-25 13:59:26 -08:00
|
|
|
|
|
|
|
if (onShiftClick && e.shiftKey) {
|
|
|
|
e.preventDefault();
|
|
|
|
onShiftClick(e);
|
|
|
|
} else if (this.state.id === openDropdownId) {
|
2020-03-27 13:59:38 -07:00
|
|
|
this.handleClose();
|
2022-04-11 12:58:48 -07:00
|
|
|
} else if (onOpen) {
|
2022-04-02 11:03:12 -07:00
|
|
|
const { top } = e.currentTarget.getBoundingClientRect();
|
|
|
|
const placement: DropdownPlacement = top * 2 < innerHeight ? 'bottom' : 'top';
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-01-25 13:59:26 -08:00
|
|
|
onOpen(this.state.id, this.handleItemClick, placement, e.type !== 'click');
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
handleClose = () => {
|
2022-04-02 11:03:12 -07:00
|
|
|
if (this.activeElement && this.activeElement === this.target) {
|
|
|
|
(this.activeElement as HTMLButtonElement).focus();
|
2021-08-28 06:05:26 -07:00
|
|
|
this.activeElement = null;
|
|
|
|
}
|
2022-04-02 11:03:12 -07:00
|
|
|
|
|
|
|
if (this.props.onClose) {
|
|
|
|
this.props.onClose(this.state.id);
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleMouseDown: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = () => {
|
2021-08-28 06:05:26 -07:00
|
|
|
if (!this.state.open) {
|
|
|
|
this.activeElement = document.activeElement;
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2021-08-28 06:05:26 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleButtonKeyDown: React.EventHandler<React.KeyboardEvent> = (e) => {
|
2022-05-11 10:40:34 -07:00
|
|
|
switch (e.key) {
|
2022-05-11 14:06:35 -07:00
|
|
|
case ' ':
|
|
|
|
case 'Enter':
|
|
|
|
this.handleMouseDown(e);
|
|
|
|
break;
|
2021-08-28 06:05:26 -07:00
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2021-08-28 06:05:26 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleKeyPress: React.EventHandler<React.KeyboardEvent<HTMLButtonElement>> = (e) => {
|
2022-05-11 10:40:34 -07:00
|
|
|
switch (e.key) {
|
2022-05-11 14:06:35 -07:00
|
|
|
case ' ':
|
|
|
|
case 'Enter':
|
|
|
|
this.handleClick(e);
|
|
|
|
e.stopPropagation();
|
|
|
|
e.preventDefault();
|
|
|
|
break;
|
2021-08-28 06:05:26 -07:00
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2021-08-28 06:05:26 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
handleItemClick: React.EventHandler<React.MouseEvent> = e => {
|
2020-03-27 13:59:38 -07:00
|
|
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
2022-04-02 11:03:12 -07:00
|
|
|
const item = this.props.items[i];
|
|
|
|
if (!item) return;
|
|
|
|
|
|
|
|
const { action, to } = item;
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
this.handleClose();
|
2022-03-21 11:09:01 -07:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
if (typeof action === 'function') {
|
2022-03-21 11:09:01 -07:00
|
|
|
action(e);
|
2020-03-27 13:59:38 -07:00
|
|
|
} else if (to) {
|
2022-04-02 11:03:12 -07:00
|
|
|
this.props.history?.push(to);
|
2020-03-27 13:59:38 -07:00
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2022-04-02 11:03:12 -07:00
|
|
|
setTargetRef: React.RefCallback<HTMLButtonElement> = c => {
|
2020-03-27 13:59:38 -07:00
|
|
|
this.target = c;
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
findTarget = () => {
|
|
|
|
return this.target;
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
componentWillUnmount = () => {
|
|
|
|
if (this.state.id === this.props.openDropdownId) {
|
|
|
|
this.handleClose();
|
|
|
|
}
|
2023-01-05 09:55:08 -08:00
|
|
|
};
|
2020-03-27 13:59:38 -07:00
|
|
|
|
2020-04-14 14:47:35 -07:00
|
|
|
render() {
|
2022-11-24 14:15:30 -08:00
|
|
|
const { src = require('@tabler/icons/dots.svg'), items, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard = false, pressed, text, children, dropdownMenuStyle } = this.props;
|
2020-03-27 13:59:38 -07:00
|
|
|
const open = this.state.id === openDropdownId;
|
|
|
|
|
|
|
|
return (
|
2022-03-21 11:09:01 -07:00
|
|
|
<>
|
2022-04-02 16:43:34 -07:00
|
|
|
{children ? (
|
|
|
|
React.cloneElement(children, {
|
|
|
|
disabled,
|
|
|
|
onClick: this.handleClick,
|
|
|
|
onMouseDown: this.handleMouseDown,
|
|
|
|
onKeyDown: this.handleButtonKeyDown,
|
|
|
|
onKeyPress: this.handleKeyPress,
|
|
|
|
ref: this.setTargetRef,
|
|
|
|
})
|
|
|
|
) : (
|
|
|
|
<IconButton
|
|
|
|
disabled={disabled}
|
|
|
|
className={classNames({
|
2022-07-22 10:30:16 -07:00
|
|
|
'text-gray-600 hover:text-gray-700 dark:hover:text-gray-500': true,
|
|
|
|
'text-gray-700 dark:text-gray-500': open,
|
2022-04-02 16:43:34 -07:00
|
|
|
})}
|
|
|
|
title={title}
|
|
|
|
src={src}
|
|
|
|
aria-pressed={pressed}
|
|
|
|
text={text}
|
|
|
|
onClick={this.handleClick}
|
|
|
|
onMouseDown={this.handleMouseDown}
|
|
|
|
onKeyDown={this.handleButtonKeyDown}
|
|
|
|
onKeyPress={this.handleKeyPress}
|
|
|
|
ref={this.setTargetRef}
|
|
|
|
/>
|
|
|
|
)}
|
2020-03-27 13:59:38 -07:00
|
|
|
|
|
|
|
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
2022-11-24 14:15:30 -08:00
|
|
|
<RouterDropdownMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} style={dropdownMenuStyle} />
|
2020-03-27 13:59:38 -07:00
|
|
|
</Overlay>
|
2022-03-21 11:09:01 -07:00
|
|
|
</>
|
2020-03-27 13:59:38 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-04-02 11:03:12 -07:00
|
|
|
|
|
|
|
export default withRouter(Dropdown);
|