2020-10-16 13:16:01 +00:00
|
|
|
import React from "react";
|
2021-02-05 10:51:26 +00:00
|
|
|
import throttle from "lodash/throttle";
|
2020-10-16 13:16:01 +00:00
|
|
|
import PropTypes from "prop-types";
|
2021-02-05 10:51:26 +00:00
|
|
|
|
2020-10-16 13:16:01 +00:00
|
|
|
import DropDownItem from "../drop-down-item";
|
|
|
|
import DropDown from "../drop-down";
|
|
|
|
import IconButton from "../icon-button";
|
|
|
|
import Backdrop from "../backdrop";
|
|
|
|
import Aside from "../aside";
|
|
|
|
import Heading from "../heading";
|
|
|
|
import Link from "../link";
|
2021-02-24 17:11:23 +00:00
|
|
|
import { desktop } from "../utils/device";
|
2021-03-19 13:56:46 +00:00
|
|
|
import { isMobile } from "react-device-detect";
|
2021-02-05 10:51:26 +00:00
|
|
|
import {
|
|
|
|
StyledBodyContent,
|
|
|
|
StyledHeaderContent,
|
|
|
|
StyledContent,
|
|
|
|
StyledOuter,
|
|
|
|
} from "./styled-context-menu-button";
|
2020-08-19 07:36:27 +00:00
|
|
|
|
2019-09-13 12:35:37 +00:00
|
|
|
class ContextMenuButton extends React.Component {
|
2019-07-14 18:23:55 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2019-06-24 13:50:54 +00:00
|
|
|
|
2019-07-14 18:23:55 +00:00
|
|
|
this.ref = React.createRef();
|
2020-10-16 13:16:01 +00:00
|
|
|
const displayType =
|
|
|
|
props.displayType === "auto" ? this.getTypeByWidth() : props.displayType;
|
2019-06-24 13:50:54 +00:00
|
|
|
|
2019-07-14 18:23:55 +00:00
|
|
|
this.state = {
|
|
|
|
isOpen: props.opened,
|
2020-08-19 07:36:27 +00:00
|
|
|
data: props.data,
|
2020-10-16 13:16:01 +00:00
|
|
|
displayType,
|
2019-07-14 18:23:55 +00:00
|
|
|
};
|
2020-08-19 07:36:27 +00:00
|
|
|
this.throttledResize = throttle(this.resize, 300);
|
|
|
|
}
|
|
|
|
|
|
|
|
getTypeByWidth = () => {
|
|
|
|
if (this.props.displayType !== "auto") return this.props.displayType;
|
|
|
|
return window.innerWidth < desktop.match(/\d+/)[0] ? "aside" : "dropdown";
|
|
|
|
};
|
|
|
|
|
|
|
|
resize = () => {
|
|
|
|
if (this.props.displayType !== "auto") return;
|
|
|
|
const type = this.getTypeByWidth();
|
|
|
|
if (type === this.state.displayType) return;
|
|
|
|
this.setState({ displayType: type });
|
|
|
|
};
|
|
|
|
|
|
|
|
popstate = () => {
|
|
|
|
window.removeEventListener("popstate", this.popstate, false);
|
|
|
|
this.onClose();
|
|
|
|
window.history.go(1);
|
|
|
|
};
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
window.addEventListener("resize", this.throttledResize);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
window.removeEventListener("resize", this.throttledResize);
|
2020-11-17 08:32:04 +00:00
|
|
|
window.removeEventListener("popstate", this.popstate, false);
|
|
|
|
this.throttledResize.cancel();
|
2019-07-14 18:23:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
stopAction = (e) => e.preventDefault();
|
|
|
|
toggle = (isOpen) => this.setState({ isOpen: isOpen });
|
2020-08-19 07:36:27 +00:00
|
|
|
onClose = () => this.setState({ isOpen: !this.state.isOpen });
|
2019-06-24 13:50:54 +00:00
|
|
|
|
2020-01-20 13:25:05 +00:00
|
|
|
componentDidUpdate(prevProps) {
|
2019-07-14 18:23:55 +00:00
|
|
|
if (this.props.opened !== prevProps.opened) {
|
|
|
|
this.toggle(this.props.opened);
|
|
|
|
}
|
2020-08-19 07:36:27 +00:00
|
|
|
|
|
|
|
if (this.props.opened && this.state.displayType === "aside") {
|
|
|
|
window.addEventListener("popstate", this.popstate, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.props.displayType !== prevProps.displayType) {
|
2020-10-16 13:16:01 +00:00
|
|
|
this.setState({ displayType: this.getTypeByWidth() });
|
2020-08-19 07:36:27 +00:00
|
|
|
}
|
2019-07-14 18:23:55 +00:00
|
|
|
}
|
2019-06-24 13:50:54 +00:00
|
|
|
|
2021-04-26 06:45:57 +00:00
|
|
|
onIconButtonClick = (e) => {
|
|
|
|
if (this.props.isDisabled || this.props.isNew) {
|
2020-01-09 13:27:49 +00:00
|
|
|
this.stopAction;
|
|
|
|
return;
|
2019-07-31 18:57:16 +00:00
|
|
|
}
|
2020-01-09 13:27:49 +00:00
|
|
|
|
2020-10-16 13:16:01 +00:00
|
|
|
this.setState(
|
|
|
|
{
|
|
|
|
data: this.props.getData(),
|
|
|
|
isOpen: !this.state.isOpen,
|
|
|
|
},
|
|
|
|
() =>
|
|
|
|
!this.props.isDisabled &&
|
|
|
|
this.state.isOpen &&
|
|
|
|
this.props.onClick &&
|
2021-04-26 06:45:57 +00:00
|
|
|
this.props.onClick(e)
|
2020-10-16 13:16:01 +00:00
|
|
|
); // eslint-disable-line react/prop-types
|
|
|
|
};
|
2020-01-09 13:27:49 +00:00
|
|
|
|
2020-01-20 13:25:05 +00:00
|
|
|
clickOutsideAction = (e) => {
|
2021-03-12 10:42:16 +00:00
|
|
|
const path = e.path || (e.composedPath && e.composedPath());
|
2021-01-27 16:28:34 +00:00
|
|
|
const dropDownItem = path ? path.find((x) => x === this.ref.current) : null;
|
2021-03-12 10:42:16 +00:00
|
|
|
if (dropDownItem) return;
|
2020-10-16 13:16:01 +00:00
|
|
|
|
2021-03-18 15:54:33 +00:00
|
|
|
this.onClose();
|
2020-10-16 13:16:01 +00:00
|
|
|
};
|
2019-07-31 18:57:16 +00:00
|
|
|
|
2020-07-10 11:39:49 +00:00
|
|
|
onDropDownItemClick = (item, e) => {
|
2020-08-21 13:19:09 +00:00
|
|
|
const open = this.state.displayType === "dropdown";
|
|
|
|
item.onClick && item.onClick(e, open);
|
2019-07-31 18:57:16 +00:00
|
|
|
this.toggle(!this.state.isOpen);
|
2020-10-16 13:16:01 +00:00
|
|
|
};
|
2019-07-31 18:57:16 +00:00
|
|
|
|
2019-09-13 12:35:37 +00:00
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
2020-10-16 13:16:01 +00:00
|
|
|
if (
|
|
|
|
this.props.opened === nextProps.opened &&
|
2020-11-18 14:34:59 +00:00
|
|
|
this.state.isOpen === nextState.isOpen &&
|
|
|
|
this.props.displayType === nextProps.displayType
|
2020-10-16 13:16:01 +00:00
|
|
|
) {
|
2019-09-13 12:35:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-04-23 14:58:06 +00:00
|
|
|
callNewMenu = (e) => {
|
2021-04-26 06:45:57 +00:00
|
|
|
if (this.props.isDisabled || !this.props.isNew) {
|
2021-04-23 14:58:06 +00:00
|
|
|
this.stopAction;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-26 06:45:57 +00:00
|
|
|
this.setState(
|
|
|
|
{
|
|
|
|
data: this.props.getData(),
|
|
|
|
},
|
|
|
|
() => this.props.onClick(e)
|
|
|
|
);
|
2021-04-23 14:58:06 +00:00
|
|
|
};
|
|
|
|
|
2019-07-14 18:23:55 +00:00
|
|
|
render() {
|
2021-03-12 08:35:22 +00:00
|
|
|
//console.log("ContextMenuButton render", this.props);
|
2019-10-31 11:42:30 +00:00
|
|
|
const {
|
2020-03-26 14:10:14 +00:00
|
|
|
className,
|
|
|
|
clickColor,
|
2019-10-31 11:42:30 +00:00
|
|
|
color,
|
2020-03-26 14:10:14 +00:00
|
|
|
columnCount,
|
|
|
|
directionX,
|
|
|
|
directionY,
|
2019-10-31 11:42:30 +00:00
|
|
|
hoverColor,
|
2020-03-26 14:10:14 +00:00
|
|
|
iconClickName,
|
|
|
|
iconHoverName,
|
2019-10-31 11:42:30 +00:00
|
|
|
iconName,
|
2020-01-09 07:28:45 +00:00
|
|
|
iconOpenName,
|
2020-03-26 14:10:14 +00:00
|
|
|
id,
|
2019-10-31 11:42:30 +00:00
|
|
|
isDisabled,
|
|
|
|
onMouseEnter,
|
|
|
|
onMouseLeave,
|
|
|
|
onMouseOut,
|
2020-03-26 14:10:14 +00:00
|
|
|
onMouseOver,
|
|
|
|
size,
|
2020-08-14 14:05:47 +00:00
|
|
|
style,
|
2020-09-01 13:55:28 +00:00
|
|
|
isFill, // eslint-disable-line react/prop-types
|
2020-10-16 13:16:01 +00:00
|
|
|
asideHeader, // eslint-disable-line react/prop-types
|
2021-04-23 14:58:06 +00:00
|
|
|
isNew,
|
2021-12-01 12:17:12 +00:00
|
|
|
title,
|
2022-05-04 11:42:27 +00:00
|
|
|
zIndex,
|
2022-06-20 20:01:13 +00:00
|
|
|
usePortal,
|
2019-10-31 11:42:30 +00:00
|
|
|
} = this.props;
|
|
|
|
|
2021-03-18 15:54:33 +00:00
|
|
|
const { isOpen, displayType, offsetX, offsetY } = this.state;
|
2020-01-09 07:28:45 +00:00
|
|
|
const iconButtonName = isOpen && iconOpenName ? iconOpenName : iconName;
|
2019-07-14 18:23:55 +00:00
|
|
|
return (
|
2021-04-23 14:58:06 +00:00
|
|
|
<StyledOuter
|
|
|
|
ref={this.ref}
|
|
|
|
className={className}
|
|
|
|
id={id}
|
|
|
|
style={style}
|
|
|
|
onClick={this.callNewMenu}
|
|
|
|
>
|
2019-07-14 18:23:55 +00:00
|
|
|
<IconButton
|
2019-10-31 11:42:30 +00:00
|
|
|
color={color}
|
|
|
|
hoverColor={hoverColor}
|
|
|
|
clickColor={clickColor}
|
|
|
|
size={size}
|
2020-01-09 07:28:45 +00:00
|
|
|
iconName={iconButtonName}
|
2019-10-31 11:42:30 +00:00
|
|
|
iconHoverName={iconHoverName}
|
|
|
|
iconClickName={iconClickName}
|
2020-08-14 14:05:47 +00:00
|
|
|
isFill={isFill}
|
2019-10-31 11:42:30 +00:00
|
|
|
isDisabled={isDisabled}
|
2019-07-31 18:57:16 +00:00
|
|
|
onClick={this.onIconButtonClick}
|
2019-10-31 11:42:30 +00:00
|
|
|
onMouseEnter={onMouseEnter}
|
|
|
|
onMouseLeave={onMouseLeave}
|
|
|
|
onMouseOver={onMouseOver}
|
|
|
|
onMouseOut={onMouseOut}
|
2021-12-01 12:17:12 +00:00
|
|
|
title={title}
|
2019-07-14 18:23:55 +00:00
|
|
|
/>
|
2020-10-16 13:16:01 +00:00
|
|
|
{displayType === "dropdown" ? (
|
|
|
|
<DropDown
|
2020-08-19 07:36:27 +00:00
|
|
|
directionX={directionX}
|
|
|
|
directionY={directionY}
|
|
|
|
open={isOpen}
|
2021-09-13 09:56:02 +00:00
|
|
|
forwardedRef={this.ref}
|
2020-08-19 07:36:27 +00:00
|
|
|
clickOutsideAction={this.clickOutsideAction}
|
|
|
|
columnCount={columnCount}
|
2021-04-02 16:33:17 +00:00
|
|
|
withBackdrop={!!isMobile}
|
2022-05-04 11:42:27 +00:00
|
|
|
zIndex={zIndex}
|
2022-06-20 20:01:13 +00:00
|
|
|
isDefaultMode={usePortal}
|
2020-08-19 07:36:27 +00:00
|
|
|
>
|
2020-10-16 13:16:01 +00:00
|
|
|
{this.state.data.map(
|
|
|
|
(item, index) =>
|
|
|
|
item &&
|
|
|
|
(item.label || item.icon || item.key) && (
|
|
|
|
<DropDownItem
|
|
|
|
{...item}
|
|
|
|
key={item.key || index}
|
|
|
|
onClick={this.onDropDownItemClick.bind(this, item)}
|
|
|
|
/>
|
2020-08-19 07:36:27 +00:00
|
|
|
)
|
2020-10-16 13:16:01 +00:00
|
|
|
)}
|
2020-08-19 07:36:27 +00:00
|
|
|
</DropDown>
|
2020-10-16 13:16:01 +00:00
|
|
|
) : (
|
|
|
|
<>
|
2020-12-10 17:06:04 +00:00
|
|
|
<Backdrop
|
|
|
|
onClick={this.onClose}
|
|
|
|
visible={isOpen}
|
|
|
|
zIndex={310}
|
|
|
|
isAside={true}
|
|
|
|
/>
|
2022-05-05 08:58:05 +00:00
|
|
|
<Aside
|
|
|
|
visible={isOpen}
|
|
|
|
scale={false}
|
|
|
|
zIndex={310}
|
|
|
|
onClose={this.onClose}
|
|
|
|
>
|
2021-02-05 10:51:26 +00:00
|
|
|
<StyledContent>
|
|
|
|
<StyledHeaderContent>
|
2020-10-16 13:16:01 +00:00
|
|
|
<Heading className="header" size="medium" truncate={true}>
|
|
|
|
{asideHeader}
|
|
|
|
</Heading>
|
2021-02-05 10:51:26 +00:00
|
|
|
</StyledHeaderContent>
|
|
|
|
<StyledBodyContent>
|
2020-10-16 13:16:01 +00:00
|
|
|
{this.state.data.map(
|
|
|
|
(item, index) =>
|
|
|
|
item &&
|
|
|
|
(item.label || item.icon || item.key) && (
|
|
|
|
<Link
|
|
|
|
className={`context-menu-button_link${
|
|
|
|
item.isHeader ? "-header" : ""
|
|
|
|
}`}
|
|
|
|
key={item.key || index}
|
|
|
|
fontSize={item.isHeader ? "15px" : "13px"}
|
|
|
|
noHover={item.isHeader}
|
|
|
|
fontWeight={600}
|
|
|
|
onClick={this.onDropDownItemClick.bind(this, item)}
|
|
|
|
>
|
|
|
|
{item.label}
|
|
|
|
</Link>
|
|
|
|
)
|
|
|
|
)}
|
2021-02-05 10:51:26 +00:00
|
|
|
</StyledBodyContent>
|
|
|
|
</StyledContent>
|
2020-10-16 13:16:01 +00:00
|
|
|
</Aside>
|
2020-08-19 07:36:27 +00:00
|
|
|
</>
|
2020-10-16 13:16:01 +00:00
|
|
|
)}
|
2019-10-31 11:42:30 +00:00
|
|
|
</StyledOuter>
|
2019-07-14 18:23:55 +00:00
|
|
|
);
|
2019-09-10 07:03:36 +00:00
|
|
|
}
|
2019-06-24 13:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ContextMenuButton.propTypes = {
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Tells when the button should present a opened state */
|
2019-06-24 13:50:54 +00:00
|
|
|
opened: PropTypes.bool,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Array of options for display */
|
2019-06-24 13:50:54 +00:00
|
|
|
data: PropTypes.array,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Function for converting to inner data */
|
2019-06-24 13:50:54 +00:00
|
|
|
getData: PropTypes.func.isRequired,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon title */
|
2019-06-25 09:02:44 +00:00
|
|
|
title: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon name */
|
2019-06-26 14:29:18 +00:00
|
|
|
iconName: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon size */
|
2019-07-15 09:44:55 +00:00
|
|
|
size: PropTypes.number,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon color */
|
2019-07-14 18:23:55 +00:00
|
|
|
color: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Tells when the button should present a disabled state */
|
2019-10-31 11:42:30 +00:00
|
|
|
isDisabled: PropTypes.bool,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon hover color */
|
2019-10-31 11:42:30 +00:00
|
|
|
hoverColor: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon click color */
|
2019-10-31 11:42:30 +00:00
|
|
|
clickColor: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon hover name */
|
2019-10-31 11:42:30 +00:00
|
|
|
iconHoverName: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon click name */
|
2019-10-31 11:42:30 +00:00
|
|
|
iconClickName: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Specifies the icon open name */
|
2020-01-09 07:28:45 +00:00
|
|
|
iconOpenName: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** What the button will trigger when mouse hovered */
|
2019-10-31 11:42:30 +00:00
|
|
|
onMouseEnter: PropTypes.func,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** What the button will trigger when mouse leave */
|
2019-10-31 11:42:30 +00:00
|
|
|
onMouseLeave: PropTypes.func,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** What the button will trigger when mouse over button */
|
2019-10-31 11:42:30 +00:00
|
|
|
onMouseOver: PropTypes.func,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** What the button will trigger when mouse out of button */
|
2019-10-31 11:42:30 +00:00
|
|
|
onMouseOut: PropTypes.func,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Direction X */
|
2019-11-27 13:33:53 +00:00
|
|
|
directionX: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Direction Y */
|
2020-02-17 12:30:15 +00:00
|
|
|
directionY: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Accepts class */
|
2019-11-27 13:33:53 +00:00
|
|
|
className: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Accepts id */
|
2019-11-27 13:33:53 +00:00
|
|
|
id: PropTypes.string,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Accepts css style */
|
2020-03-26 14:10:14 +00:00
|
|
|
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Set the number of columns */
|
2020-08-19 07:36:27 +00:00
|
|
|
columnCount: PropTypes.number,
|
2021-03-07 11:57:26 +00:00
|
|
|
/** Set the display type */
|
2020-10-16 13:16:01 +00:00
|
|
|
displayType: PropTypes.string,
|
2021-04-23 14:58:06 +00:00
|
|
|
isNew: PropTypes.bool,
|
2022-06-20 20:01:13 +00:00
|
|
|
usePortal: PropTypes.bool,
|
2019-06-24 13:50:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
ContextMenuButton.defaultProps = {
|
|
|
|
opened: false,
|
|
|
|
data: [],
|
2020-10-16 13:16:01 +00:00
|
|
|
title: "",
|
2021-03-01 08:59:45 +00:00
|
|
|
iconName: "/static/images/vertical-dots.react.svg",
|
2019-07-15 09:44:55 +00:00
|
|
|
size: 16,
|
2019-10-31 11:42:30 +00:00
|
|
|
isDisabled: false,
|
2020-10-16 13:16:01 +00:00
|
|
|
directionX: "left",
|
2020-08-19 07:36:27 +00:00
|
|
|
isFill: false,
|
2020-10-16 13:16:01 +00:00
|
|
|
displayType: "dropdown",
|
2021-04-23 14:58:06 +00:00
|
|
|
isNew: false,
|
2022-06-20 20:01:13 +00:00
|
|
|
usePortal: true,
|
2019-06-24 13:50:54 +00:00
|
|
|
};
|
|
|
|
|
2020-10-16 13:16:01 +00:00
|
|
|
export default ContextMenuButton;
|