Web:Components:MenuItem: add arrow type

This commit is contained in:
Timofey Boyko 2021-11-02 20:39:45 +08:00
parent 853037c00d
commit cad4948cab
6 changed files with 164 additions and 10 deletions

View File

@ -2,26 +2,107 @@ import React from "react";
import PropTypes from "prop-types";
import { ReactSVG } from "react-svg";
import DomHelpers from "../utils/domHelpers";
import { isMobile } from "react-device-detect";
import {
isTablet as isTabletUtils,
isMobile as isMobileUtils,
} from "../utils/device";
import { StyledMenuItem, StyledText, IconWrapper } from "./styled-menu-item";
import ArrowIcon from "./svg/folder-arrow.react.svg";
import ArrowMobileIcon from "./svg/folder-arrow.mobile.react.svg";
import NewContextMenu from "../new-context-menu";
//TODO: Add arrow type
const MenuItem = (props) => {
const [hover, setHover] = React.useState(false);
const [positionContextMenu, setPositionContextMenu] = React.useState(null);
const itemRef = React.useRef(null);
const cmRef = React.useRef(null);
//console.log("MenuItem render");
const {
isHeader,
isSeparator,
label,
icon,
options,
children,
onClick,
className,
} = props;
const onHover = () => {
if (!cmRef.current) return;
if (hover) {
getPosition();
cmRef.current.show(new Event("click"));
} else {
cmRef.current.hide(new Event("click"));
}
};
const getPosition = () => {
if (!cmRef.current) return;
if (!itemRef.current) return;
const outerWidth = DomHelpers.getOuterWidth(itemRef.current);
const offset = DomHelpers.getOffset(itemRef.current);
setPositionContextMenu({
top: offset.top,
left: offset.left + outerWidth + 10,
width: outerWidth,
});
};
React.useEffect(() => {
onHover();
}, [hover]);
const onClickAction = (e) => {
onClick && onClick(e);
};
return (
return options ? (
<StyledMenuItem
{...props}
className={className}
onClick={onClickAction}
ref={itemRef}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
{icon && (
<IconWrapper isHeader={isHeader}>
<ReactSVG src={icon} className="drop-down-item_icon" />
</IconWrapper>
)}
{isSeparator ? (
<></>
) : label ? (
<>
<StyledText isHeader={isHeader} truncate={true}>
{label}
</StyledText>
{isMobile || isTabletUtils() || isMobileUtils() ? (
<ArrowMobileIcon className="arrow-icon" />
) : (
<ArrowIcon className="arrow-icon" />
)}
<NewContextMenu
ref={cmRef}
model={options}
withBackdrop={false}
position={positionContextMenu}
/>
</>
) : (
children && children
)}
</StyledMenuItem>
) : (
<StyledMenuItem {...props} className={className} onClick={onClickAction}>
{icon && (
<IconWrapper isHeader={isHeader}>
@ -31,9 +112,11 @@ const MenuItem = (props) => {
{isSeparator ? (
<></>
) : label ? (
<>
<StyledText isHeader={isHeader} truncate={true}>
{label}
</StyledText>
</>
) : (
children && children
)}
@ -48,10 +131,12 @@ MenuItem.propTypes = {
isHeader: PropTypes.bool,
/** Accepts tab-index */
tabIndex: PropTypes.number,
/** menu item text */
/** Menu item text */
label: PropTypes.string,
/** menu item icon */
/** Menu item icon */
icon: PropTypes.string,
/** Tells when the menu item should display like arrow and open context menu */
options: PropTypes.array,
/** Disable default style hover effect */
noHover: PropTypes.bool,
/** What the menu item will trigger when clicked */

View File

@ -45,6 +45,25 @@ const Template = () => {
<MenuItem onClick={() => console.log("Button 3 clicked")}>
<div>some child without styles</div>
</MenuItem>
<MenuItem
icon={"static/images/nav.logo.react.svg"}
label="Item after separator"
options={[
{
key: "key1",
icon: "static/images/nav.logo.react.svg",
label: "Item after separator",
onClick: () => console.log("Button 1 clicked"),
},
{
key: "key2",
icon: "static/images/nav.logo.react.svg",
label: "Item after separator",
onClick: () => console.log("Button 2 clicked"),
},
]}
onClick={() => console.log("Button 2 clicked")}
/>
</div>
);
};

View File

@ -17,6 +17,7 @@ const styledMobileText = css`
`;
const StyledText = styled(Text)`
width: 100%;
font-weight: ${(props) => props.theme.menuItem.text.fontWeight};
font-size: ${(props) => props.theme.menuItem.text.fontSize};
line-height: ${(props) => props.theme.menuItem.text.lineHeight};
@ -125,6 +126,10 @@ const StyledMenuItem = styled.div`
cursor: default !important;
}
`}
.arrow-icon {
margin: 0 0 0 8px;
}
`;
StyledMenuItem.defaultProps = { theme: Base };

View File

@ -29,6 +29,7 @@ and header(show only tablet or mobile, when view changed).
| `id` | `string` | - | - | `contextMenu` | Accepts id |
| `model` | `array` | - | - | `[]` | Items collection |
| `header` | `object` | - | - | `{}` | ContextMenu header |
| `position` | `object` | - | - | `{}` | ContextMenu position |
| `style` | `obj`, `array` | - | - | - | Accepts css style |
| `targetAreaId` | `string` | - | - | - | Id of container apply to |
| `withBackdrop` | `bool` | - | - | `true` | Used to display backdrop |

View File

@ -33,6 +33,8 @@ const Row = React.memo(({ data, index, style }) => {
// eslint-disable-next-line react/prop-types
isSeparator={data[index].isSeparator}
// eslint-disable-next-line react/prop-types
options={data[index].options}
// eslint-disable-next-line react/prop-types
onClick={data[index].onClick}
style={style}
/>
@ -146,6 +148,25 @@ class NewContextMenu extends React.Component {
}
position(event) {
if (this.props.position) {
let width = this.menuRef.current.offsetParent
? this.menuRef.current.offsetWidth
: DomHelpers.getHiddenElementOuterWidth(this.menuRef.current);
let viewport = DomHelpers.getViewport();
if (
this.props.position.left + width >
viewport.width - DomHelpers.calculateScrollbarWidth()
) {
this.menuRef.current.style.right =
// -1 * this.props.position.width + width + "px";
0 + "px";
} else {
this.menuRef.current.style.left = this.props.position.left + "px";
}
this.menuRef.current.style.top = this.props.position.top + "px";
return;
}
if (event) {
let left = event.pageX + 1;
let top = event.pageY + 1;
@ -343,6 +364,7 @@ class NewContextMenu extends React.Component {
icon={item.icon}
label={item.label}
isSeparator={item.isSeparator}
options={item.options}
onClick={item.onClick}
/>
);
@ -356,10 +378,11 @@ class NewContextMenu extends React.Component {
const className = classNames("p-contextmenu", this.props.className);
const items = this.renderContextMenuItems();
return (
<>
{this.props.withBackdrop && (
<Backdrop visible={this.state.visible} withBackground={true} />
)}
<StyledContextMenu changeView={this.state.changeView}>
<CSSTransition
nodeRef={this.menuRef}
@ -406,16 +429,20 @@ class NewContextMenu extends React.Component {
NewContextMenu.propTypes = {
/** Unique identifier of the element */
id: PropTypes.string,
/** An array of menuitems */
/** An array of objects */
model: PropTypes.array,
/** An object of header */
/** An object of header with icon and label */
header: PropTypes.object,
/** Position of context menu */
position: PropTypes.object,
/** Inline style of the component */
style: PropTypes.object,
/** Style class of the component */
className: PropTypes.string,
/** Attaches the menu to document instead of a particular item */
global: PropTypes.bool,
/** Tell when context menu was render with backdrop */
withBackdrop: PropTypes.bool,
/** Base zIndex value to use in layering */
autoZIndex: PropTypes.bool,
/** Whether to automatically manage layering */
@ -431,6 +458,8 @@ NewContextMenu.propTypes = {
NewContextMenu.defaultProps = {
id: null,
model: null,
position: null,
header: null,
style: null,
className: null,
global: false,
@ -439,6 +468,7 @@ NewContextMenu.defaultProps = {
appendTo: null,
onShow: null,
onHide: null,
withBackdrop: true,
};
export default NewContextMenu;

View File

@ -110,6 +110,20 @@ const Template = (args) => {
icon: "/static/images/catalog.shared.react.svg",
key: "sharing-settings10",
label: "Sharing settings",
options: [
{
key: "key1",
icon: "static/images/nav.logo.react.svg",
label: "Item after separator",
onClick: () => console.log("Button 1 clicked"),
},
{
key: "key2",
icon: "static/images/nav.logo.react.svg",
label: "Item after separator",
onClick: () => console.log("Button 2 clicked"),
},
],
onClick: () => console.log("item 3 clicked"),
},
{