Convert DropdownMenu to typescript
This commit is contained in:
parent
a080ed8647
commit
96ccc66641
5 changed files with 175 additions and 127 deletions
|
@ -1,36 +1,51 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import { spring } from 'react-motion';
|
||||||
import spring from 'react-motion/lib/spring';
|
// @ts-ignore: TODO: upgrade react-overlays. v3.1 and above have TS definitions
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
import Overlay from 'react-overlays/lib/Overlay';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
|
||||||
import Icon from 'soapbox/components/icon';
|
import Icon from 'soapbox/components/icon';
|
||||||
|
import { IconButton } from 'soapbox/components/ui';
|
||||||
|
import Motion from 'soapbox/features/ui/util/optional_motion';
|
||||||
|
|
||||||
import Motion from '../features/ui/util/optional_motion';
|
import type { Status } from 'soapbox/types/entities';
|
||||||
|
|
||||||
import { IconButton } from './ui';
|
|
||||||
|
|
||||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
|
||||||
@withRouter
|
export interface MenuItem {
|
||||||
class DropdownMenu extends React.PureComponent {
|
action: React.EventHandler<React.KeyboardEvent | React.MouseEvent>,
|
||||||
|
middleClick?: React.EventHandler<React.MouseEvent>,
|
||||||
|
text: string,
|
||||||
|
href?: string,
|
||||||
|
to?: string,
|
||||||
|
newTab?: boolean,
|
||||||
|
isLogout?: boolean,
|
||||||
|
icon: string,
|
||||||
|
destructive?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
static propTypes = {
|
export type Menu = Array<MenuItem | null>;
|
||||||
items: PropTypes.array.isRequired,
|
|
||||||
onClose: PropTypes.func.isRequired,
|
|
||||||
style: PropTypes.object,
|
|
||||||
placement: PropTypes.string,
|
|
||||||
arrowOffsetLeft: PropTypes.string,
|
|
||||||
arrowOffsetTop: PropTypes.string,
|
|
||||||
openedViaKeyboard: PropTypes.bool,
|
|
||||||
history: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
interface IDropdownMenu extends RouteComponentProps {
|
||||||
|
items: Menu,
|
||||||
|
onClose: () => void,
|
||||||
|
style?: React.CSSProperties,
|
||||||
|
placement?: DropdownPlacement,
|
||||||
|
arrowOffsetLeft?: string,
|
||||||
|
arrowOffsetTop?: string,
|
||||||
|
openedViaKeyboard: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDropdownMenuState {
|
||||||
|
mounted: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
class DropdownMenu extends React.PureComponent<IDropdownMenu, IDropdownMenuState> {
|
||||||
|
|
||||||
|
static defaultProps: Partial<IDropdownMenu> = {
|
||||||
style: {},
|
style: {},
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
};
|
};
|
||||||
|
@ -39,8 +54,11 @@ class DropdownMenu extends React.PureComponent {
|
||||||
mounted: false,
|
mounted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDocumentClick = e => {
|
node: HTMLDivElement | null = null;
|
||||||
if (this.node && !this.node.contains(e.target)) {
|
focusedItem: HTMLAnchorElement | null = null;
|
||||||
|
|
||||||
|
handleDocumentClick = (e: Event) => {
|
||||||
|
if (this.node && !this.node.contains(e.target as Node)) {
|
||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,22 +74,24 @@ class DropdownMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
document.removeEventListener('click', this.handleDocumentClick);
|
||||||
document.removeEventListener('keydown', this.handleKeyDown, false);
|
document.removeEventListener('keydown', this.handleKeyDown);
|
||||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
document.removeEventListener('touchend', this.handleDocumentClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRef = c => {
|
setRef: React.RefCallback<HTMLDivElement> = c => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusRef = c => {
|
setFocusRef: React.RefCallback<HTMLAnchorElement> = c => {
|
||||||
this.focusedItem = c;
|
this.focusedItem = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown = e => {
|
handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (!this.node) return;
|
||||||
|
|
||||||
const items = Array.from(this.node.getElementsByTagName('a'));
|
const items = Array.from(this.node.getElementsByTagName('a'));
|
||||||
const index = items.indexOf(document.activeElement);
|
const index = items.indexOf(document.activeElement as any);
|
||||||
let element = null;
|
let element = null;
|
||||||
|
|
||||||
switch(e.key) {
|
switch(e.key) {
|
||||||
|
@ -106,15 +126,17 @@ class DropdownMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleItemKeyPress = e => {
|
handleItemKeyPress: React.EventHandler<React.KeyboardEvent> = e => {
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
this.handleClick(e);
|
this.handleClick(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick = e => {
|
handleClick: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = e => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
const { action, to } = this.props.items[i];
|
const item = this.props.items[i];
|
||||||
|
if (!item) return;
|
||||||
|
const { action, to } = item;
|
||||||
|
|
||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
|
|
||||||
|
@ -127,9 +149,11 @@ class DropdownMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMiddleClick = e => {
|
handleMiddleClick: React.EventHandler<React.MouseEvent> = e => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
const { middleClick } = this.props.items[i];
|
const item = this.props.items[i];
|
||||||
|
if (!item) return;
|
||||||
|
const { middleClick } = item;
|
||||||
|
|
||||||
this.props.onClose();
|
this.props.onClose();
|
||||||
|
|
||||||
|
@ -139,13 +163,13 @@ class DropdownMenu extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAuxClick = e => {
|
handleAuxClick: React.EventHandler<React.MouseEvent> = e => {
|
||||||
if (e.button === 1) {
|
if (e.button === 1) {
|
||||||
this.handleMiddleClick(e);
|
this.handleMiddleClick(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem(option, i) {
|
renderItem(option: MenuItem | null, i: number): JSX.Element {
|
||||||
if (option === null) {
|
if (option === null) {
|
||||||
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
||||||
}
|
}
|
||||||
|
@ -157,14 +181,14 @@ class DropdownMenu extends React.PureComponent {
|
||||||
<a
|
<a
|
||||||
href={href || to || '#'}
|
href={href || to || '#'}
|
||||||
role='button'
|
role='button'
|
||||||
tabIndex='0'
|
tabIndex={0}
|
||||||
ref={i === 0 ? this.setFocusRef : null}
|
ref={i === 0 ? this.setFocusRef : null}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
onAuxClick={this.handleAuxClick}
|
onAuxClick={this.handleAuxClick}
|
||||||
onKeyPress={this.handleItemKeyPress}
|
onKeyPress={this.handleItemKeyPress}
|
||||||
data-index={i}
|
data-index={i}
|
||||||
target={newTab ? '_blank' : null}
|
target={newTab ? '_blank' : undefined}
|
||||||
data-method={isLogout ? 'delete' : null}
|
data-method={isLogout ? 'delete' : undefined}
|
||||||
>
|
>
|
||||||
{icon && <Icon src={icon} />}
|
{icon && <Icon src={icon} />}
|
||||||
{text}
|
{text}
|
||||||
|
@ -182,7 +206,7 @@ class DropdownMenu extends React.PureComponent {
|
||||||
// It should not be transformed when mounting because the resulting
|
// It should not be transformed when mounting because the resulting
|
||||||
// size will be used to determine the coordinate of the menu by
|
// size will be used to determine the coordinate of the menu by
|
||||||
// react-overlays
|
// react-overlays
|
||||||
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : undefined }} ref={this.setRef}>
|
||||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
||||||
<ul>
|
<ul>
|
||||||
{items.map((option, i) => this.renderItem(option, i))}
|
{items.map((option, i) => this.renderItem(option, i))}
|
||||||
|
@ -195,40 +219,56 @@ class DropdownMenu extends React.PureComponent {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default @withRouter
|
const RouterDropdownMenu = withRouter(DropdownMenu);
|
||||||
class Dropdown extends React.PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
export interface IDropdown extends RouteComponentProps {
|
||||||
icon: PropTypes.string,
|
icon?: string,
|
||||||
src: PropTypes.string,
|
src: string,
|
||||||
items: PropTypes.array.isRequired,
|
items: Menu,
|
||||||
size: PropTypes.number,
|
size?: number,
|
||||||
active: PropTypes.bool,
|
active?: boolean,
|
||||||
pressed: PropTypes.bool,
|
pressed?: boolean,
|
||||||
title: PropTypes.string,
|
title: string,
|
||||||
disabled: PropTypes.bool,
|
disabled?: boolean,
|
||||||
status: ImmutablePropTypes.record,
|
status?: Status,
|
||||||
isUserTouching: PropTypes.func,
|
isUserTouching?: () => boolean,
|
||||||
isModalOpen: PropTypes.bool.isRequired,
|
isModalOpen?: boolean,
|
||||||
onOpen: PropTypes.func.isRequired,
|
onOpen?: (
|
||||||
onClose: PropTypes.func.isRequired,
|
id: number,
|
||||||
dropdownPlacement: PropTypes.string,
|
onItemClick: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,
|
||||||
openDropdownId: PropTypes.number,
|
dropdownPlacement: DropdownPlacement,
|
||||||
openedViaKeyboard: PropTypes.bool,
|
keyboard: boolean,
|
||||||
text: PropTypes.string,
|
) => void,
|
||||||
onShiftClick: PropTypes.func,
|
onClose?: (id: number) => void,
|
||||||
history: PropTypes.object,
|
dropdownPlacement?: string,
|
||||||
};
|
openDropdownId?: number,
|
||||||
|
openedViaKeyboard?: boolean,
|
||||||
|
text?: string,
|
||||||
|
onShiftClick?: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,
|
||||||
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
interface IDropdownState {
|
||||||
|
id: number,
|
||||||
|
open: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DropdownPlacement = 'top' | 'bottom';
|
||||||
|
|
||||||
|
class Dropdown extends React.PureComponent<IDropdown, IDropdownState> {
|
||||||
|
|
||||||
|
static defaultProps: Partial<IDropdown> = {
|
||||||
title: 'Menu',
|
title: 'Menu',
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
id: id++,
|
id: id++,
|
||||||
|
open: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClick = e => {
|
target: HTMLButtonElement | null = null;
|
||||||
|
activeElement: Element | null = null;
|
||||||
|
|
||||||
|
handleClick: React.EventHandler<React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>> = e => {
|
||||||
const { onOpen, onShiftClick, openDropdownId } = this.props;
|
const { onOpen, onShiftClick, openDropdownId } = this.props;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -237,38 +277,41 @@ class Dropdown extends React.PureComponent {
|
||||||
onShiftClick(e);
|
onShiftClick(e);
|
||||||
} else if (this.state.id === openDropdownId) {
|
} else if (this.state.id === openDropdownId) {
|
||||||
this.handleClose();
|
this.handleClose();
|
||||||
} else {
|
} else if(onOpen) {
|
||||||
const { top } = e.target.getBoundingClientRect();
|
const { top } = e.currentTarget.getBoundingClientRect();
|
||||||
const placement = top * 2 < innerHeight ? 'bottom' : 'top';
|
const placement: DropdownPlacement = top * 2 < innerHeight ? 'bottom' : 'top';
|
||||||
|
|
||||||
onOpen(this.state.id, this.handleItemClick, placement, e.type !== 'click');
|
onOpen(this.state.id, this.handleItemClick, placement, e.type !== 'click');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClose = () => {
|
handleClose = () => {
|
||||||
if (this.activeElement) {
|
if (this.activeElement && this.activeElement === this.target) {
|
||||||
this.activeElement.focus();
|
(this.activeElement as HTMLButtonElement).focus();
|
||||||
this.activeElement = null;
|
this.activeElement = null;
|
||||||
}
|
}
|
||||||
this.props.onClose(this.state.id);
|
|
||||||
|
if (this.props.onClose) {
|
||||||
|
this.props.onClose(this.state.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseDown = () => {
|
handleMouseDown: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = () => {
|
||||||
if (!this.state.open) {
|
if (!this.state.open) {
|
||||||
this.activeElement = document.activeElement;
|
this.activeElement = document.activeElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleButtonKeyDown = (e) => {
|
handleButtonKeyDown: React.EventHandler<React.KeyboardEvent> = (e) => {
|
||||||
switch(e.key) {
|
switch(e.key) {
|
||||||
case ' ':
|
case ' ':
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
this.handleMouseDown();
|
this.handleMouseDown(e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress = (e) => {
|
handleKeyPress: React.EventHandler<React.KeyboardEvent<HTMLButtonElement>> = (e) => {
|
||||||
switch(e.key) {
|
switch(e.key) {
|
||||||
case ' ':
|
case ' ':
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
|
@ -279,9 +322,12 @@ class Dropdown extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleItemClick = e => {
|
handleItemClick: React.EventHandler<React.MouseEvent> = e => {
|
||||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||||
const { action, to } = this.props.items[i];
|
const item = this.props.items[i];
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
const { action, to } = item;
|
||||||
|
|
||||||
this.handleClose();
|
this.handleClose();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -290,11 +336,11 @@ class Dropdown extends React.PureComponent {
|
||||||
if (typeof action === 'function') {
|
if (typeof action === 'function') {
|
||||||
action(e);
|
action(e);
|
||||||
} else if (to) {
|
} else if (to) {
|
||||||
this.props.history.push(to);
|
this.props.history?.push(to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTargetRef = c => {
|
setTargetRef: React.RefCallback<HTMLButtonElement> = c => {
|
||||||
this.target = c;
|
this.target = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +355,7 @@ class Dropdown extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { src, items, size, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard, pressed, text } = this.props;
|
const { src, items, title, disabled, dropdownPlacement, openDropdownId, openedViaKeyboard = false, pressed, text } = this.props;
|
||||||
const open = this.state.id === openDropdownId;
|
const open = this.state.id === openDropdownId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -322,8 +368,7 @@ class Dropdown extends React.PureComponent {
|
||||||
})}
|
})}
|
||||||
title={title}
|
title={title}
|
||||||
src={src}
|
src={src}
|
||||||
pressed={pressed}
|
aria-pressed={pressed}
|
||||||
size={size}
|
|
||||||
text={text}
|
text={text}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
|
@ -333,10 +378,12 @@ class Dropdown extends React.PureComponent {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
||||||
<DropdownMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} />
|
<RouterDropdownMenu items={items} onClose={this.handleClose} openedViaKeyboard={openedViaKeyboard} />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default withRouter(Dropdown);
|
|
@ -24,6 +24,7 @@ import { IconButton, Hoverable } from './ui';
|
||||||
|
|
||||||
import type { History } from 'history';
|
import type { History } from 'history';
|
||||||
import type { AnyAction, Dispatch } from 'redux';
|
import type { AnyAction, Dispatch } from 'redux';
|
||||||
|
import type { Menu } from 'soapbox/components/dropdown_menu';
|
||||||
import type { RootState } from 'soapbox/store';
|
import type { RootState } from 'soapbox/store';
|
||||||
import type { Status } from 'soapbox/types/entities';
|
import type { Status } from 'soapbox/types/entities';
|
||||||
import type { Features } from 'soapbox/utils/features';
|
import type { Features } from 'soapbox/utils/features';
|
||||||
|
@ -367,7 +368,7 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
const ownAccount = status.getIn(['account', 'id']) === me;
|
const ownAccount = status.getIn(['account', 'id']) === me;
|
||||||
const username = String(status.getIn(['account', 'username']));
|
const username = String(status.getIn(['account', 'username']));
|
||||||
|
|
||||||
const menu = [];
|
const menu: Menu = [];
|
||||||
|
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.open),
|
text: intl.formatMessage(messages.open),
|
||||||
|
@ -487,13 +488,13 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
text: intl.formatMessage(messages.admin_account, { name: username }),
|
text: intl.formatMessage(messages.admin_account, { name: username }),
|
||||||
href: `/pleroma/admin/#/users/${status.getIn(['account', 'id'])}/`,
|
href: `/pleroma/admin/#/users/${status.getIn(['account', 'id'])}/`,
|
||||||
icon: require('@tabler/icons/icons/gavel.svg'),
|
icon: require('@tabler/icons/icons/gavel.svg'),
|
||||||
action: (event: Event) => event.stopPropagation(),
|
action: (event) => event.stopPropagation(),
|
||||||
});
|
});
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.admin_status),
|
text: intl.formatMessage(messages.admin_status),
|
||||||
href: `/pleroma/admin/#/statuses/${status.get('id')}/`,
|
href: `/pleroma/admin/#/statuses/${status.get('id')}/`,
|
||||||
icon: require('@tabler/icons/icons/pencil.svg'),
|
icon: require('@tabler/icons/icons/pencil.svg'),
|
||||||
action: (event: Event) => event.stopPropagation(),
|
action: (event) => event.stopPropagation(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,13 +608,11 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
reblogButton = (
|
reblogButton = (
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
items={reblogMenu}
|
items={reblogMenu}
|
||||||
// @ts-ignore
|
|
||||||
disabled={!publicStatus}
|
disabled={!publicStatus}
|
||||||
active={status.get('reblogged')}
|
active={status.reblogged}
|
||||||
pressed={status.get('reblogged')}
|
pressed={status.reblogged}
|
||||||
title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)}
|
title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)}
|
||||||
src={reblogIcon}
|
src={reblogIcon}
|
||||||
direction='right'
|
|
||||||
onShiftClick={this.handleReblogClick}
|
onShiftClick={this.handleReblogClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -714,11 +713,9 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
||||||
<StatusAction>
|
<StatusAction>
|
||||||
<DropdownMenuContainer
|
<DropdownMenuContainer
|
||||||
items={menu}
|
items={menu}
|
||||||
// @ts-ignore
|
|
||||||
title={intl.formatMessage(messages.more)}
|
title={intl.formatMessage(messages.more)}
|
||||||
status={status}
|
status={status}
|
||||||
src={require('@tabler/icons/icons/dots.svg')}
|
src={require('@tabler/icons/icons/dots.svg')}
|
||||||
direction='right'
|
|
||||||
/>
|
/>
|
||||||
</StatusAction>
|
</StatusAction>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,15 +4,10 @@ import InlineSVG from 'react-inlinesvg';
|
||||||
|
|
||||||
import Text from '../text/text';
|
import Text from '../text/text';
|
||||||
|
|
||||||
interface IIconButton {
|
interface IIconButton extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
alt?: string,
|
|
||||||
className?: string,
|
|
||||||
iconClassName?: string,
|
iconClassName?: string,
|
||||||
disabled?: boolean,
|
|
||||||
src: string,
|
src: string,
|
||||||
onClick?: React.EventHandler<React.MouseEvent>,
|
|
||||||
text?: string,
|
text?: string,
|
||||||
title?: string,
|
|
||||||
transparent?: boolean
|
transparent?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { openDropdownMenu, closeDropdownMenu } from '../actions/dropdown_menu';
|
|
||||||
import { openModal, closeModal } from '../actions/modals';
|
|
||||||
import DropdownMenu from '../components/dropdown_menu';
|
|
||||||
import { isUserTouching } from '../is_mobile';
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
isModalOpen: Boolean(state.get('modals').size && state.get('modals').last().modalType === 'ACTIONS'),
|
|
||||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
|
||||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
|
||||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { status, items }) => ({
|
|
||||||
onOpen(id, onItemClick, dropdownPlacement, keyboard) {
|
|
||||||
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
|
||||||
status,
|
|
||||||
actions: items,
|
|
||||||
onClick: onItemClick,
|
|
||||||
}) : openDropdownMenu(id, dropdownPlacement, keyboard));
|
|
||||||
},
|
|
||||||
onClose(id) {
|
|
||||||
dispatch(closeModal('ACTIONS'));
|
|
||||||
dispatch(closeDropdownMenu(id));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DropdownMenu);
|
|
38
app/soapbox/containers/dropdown_menu_container.ts
Normal file
38
app/soapbox/containers/dropdown_menu_container.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { openDropdownMenu, closeDropdownMenu } from '../actions/dropdown_menu';
|
||||||
|
import { openModal, closeModal } from '../actions/modals';
|
||||||
|
import DropdownMenu from '../components/dropdown_menu';
|
||||||
|
import { isUserTouching } from '../is_mobile';
|
||||||
|
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
import type { DropdownPlacement, IDropdown } from 'soapbox/components/dropdown_menu';
|
||||||
|
import type { RootState } from 'soapbox/store';
|
||||||
|
|
||||||
|
const mapStateToProps = (state: RootState) => ({
|
||||||
|
isModalOpen: Boolean(state.modals.size && state.modals.last().modalType === 'ACTIONS'),
|
||||||
|
dropdownPlacement: state.dropdown_menu.get('placement'),
|
||||||
|
openDropdownId: state.dropdown_menu.get('openId'),
|
||||||
|
openedViaKeyboard: state.dropdown_menu.get('keyboard'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch, { status, items }: Partial<IDropdown>) => ({
|
||||||
|
onOpen(
|
||||||
|
id: number,
|
||||||
|
onItemClick: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,
|
||||||
|
dropdownPlacement: DropdownPlacement,
|
||||||
|
keyboard: boolean,
|
||||||
|
) {
|
||||||
|
dispatch(isUserTouching() ? openModal('ACTIONS', {
|
||||||
|
status,
|
||||||
|
actions: items,
|
||||||
|
onClick: onItemClick,
|
||||||
|
}) : openDropdownMenu(id, dropdownPlacement, keyboard));
|
||||||
|
},
|
||||||
|
onClose(id: number) {
|
||||||
|
dispatch(closeModal('ACTIONS'));
|
||||||
|
dispatch(closeDropdownMenu(id));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(DropdownMenu);
|
Loading…
Reference in a new issue