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 PropTypes from "prop-types";
import { ReactSVG } from "react-svg"; 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 { 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 //TODO: Add arrow type
const MenuItem = (props) => { 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"); //console.log("MenuItem render");
const { const {
isHeader, isHeader,
isSeparator, isSeparator,
label, label,
icon, icon,
options,
children, children,
onClick, onClick,
className, className,
} = props; } = 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) => { const onClickAction = (e) => {
onClick && onClick(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}> <StyledMenuItem {...props} className={className} onClick={onClickAction}>
{icon && ( {icon && (
<IconWrapper isHeader={isHeader}> <IconWrapper isHeader={isHeader}>
@ -31,9 +112,11 @@ const MenuItem = (props) => {
{isSeparator ? ( {isSeparator ? (
<></> <></>
) : label ? ( ) : label ? (
<>
<StyledText isHeader={isHeader} truncate={true}> <StyledText isHeader={isHeader} truncate={true}>
{label} {label}
</StyledText> </StyledText>
</>
) : ( ) : (
children && children children && children
)} )}
@ -48,10 +131,12 @@ MenuItem.propTypes = {
isHeader: PropTypes.bool, isHeader: PropTypes.bool,
/** Accepts tab-index */ /** Accepts tab-index */
tabIndex: PropTypes.number, tabIndex: PropTypes.number,
/** menu item text */ /** Menu item text */
label: PropTypes.string, label: PropTypes.string,
/** menu item icon */ /** Menu item icon */
icon: PropTypes.string, icon: PropTypes.string,
/** Tells when the menu item should display like arrow and open context menu */
options: PropTypes.array,
/** Disable default style hover effect */ /** Disable default style hover effect */
noHover: PropTypes.bool, noHover: PropTypes.bool,
/** What the menu item will trigger when clicked */ /** What the menu item will trigger when clicked */

View File

@ -45,6 +45,25 @@ const Template = () => {
<MenuItem onClick={() => console.log("Button 3 clicked")}> <MenuItem onClick={() => console.log("Button 3 clicked")}>
<div>some child without styles</div> <div>some child without styles</div>
</MenuItem> </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> </div>
); );
}; };

View File

@ -17,6 +17,7 @@ const styledMobileText = css`
`; `;
const StyledText = styled(Text)` const StyledText = styled(Text)`
width: 100%;
font-weight: ${(props) => props.theme.menuItem.text.fontWeight}; font-weight: ${(props) => props.theme.menuItem.text.fontWeight};
font-size: ${(props) => props.theme.menuItem.text.fontSize}; font-size: ${(props) => props.theme.menuItem.text.fontSize};
line-height: ${(props) => props.theme.menuItem.text.lineHeight}; line-height: ${(props) => props.theme.menuItem.text.lineHeight};
@ -125,6 +126,10 @@ const StyledMenuItem = styled.div`
cursor: default !important; cursor: default !important;
} }
`} `}
.arrow-icon {
margin: 0 0 0 8px;
}
`; `;
StyledMenuItem.defaultProps = { theme: Base }; 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 | | `id` | `string` | - | - | `contextMenu` | Accepts id |
| `model` | `array` | - | - | `[]` | Items collection | | `model` | `array` | - | - | `[]` | Items collection |
| `header` | `object` | - | - | `{}` | ContextMenu header | | `header` | `object` | - | - | `{}` | ContextMenu header |
| `position` | `object` | - | - | `{}` | ContextMenu position |
| `style` | `obj`, `array` | - | - | - | Accepts css style | | `style` | `obj`, `array` | - | - | - | Accepts css style |
| `targetAreaId` | `string` | - | - | - | Id of container apply to | | `targetAreaId` | `string` | - | - | - | Id of container apply to |
| `withBackdrop` | `bool` | - | - | `true` | Used to display backdrop | | `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 // eslint-disable-next-line react/prop-types
isSeparator={data[index].isSeparator} isSeparator={data[index].isSeparator}
// eslint-disable-next-line react/prop-types // eslint-disable-next-line react/prop-types
options={data[index].options}
// eslint-disable-next-line react/prop-types
onClick={data[index].onClick} onClick={data[index].onClick}
style={style} style={style}
/> />
@ -146,6 +148,25 @@ class NewContextMenu extends React.Component {
} }
position(event) { 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) { if (event) {
let left = event.pageX + 1; let left = event.pageX + 1;
let top = event.pageY + 1; let top = event.pageY + 1;
@ -343,6 +364,7 @@ class NewContextMenu extends React.Component {
icon={item.icon} icon={item.icon}
label={item.label} label={item.label}
isSeparator={item.isSeparator} isSeparator={item.isSeparator}
options={item.options}
onClick={item.onClick} onClick={item.onClick}
/> />
); );
@ -356,10 +378,11 @@ class NewContextMenu extends React.Component {
const className = classNames("p-contextmenu", this.props.className); const className = classNames("p-contextmenu", this.props.className);
const items = this.renderContextMenuItems(); const items = this.renderContextMenuItems();
return ( return (
<> <>
{this.props.withBackdrop && (
<Backdrop visible={this.state.visible} withBackground={true} /> <Backdrop visible={this.state.visible} withBackground={true} />
)}
<StyledContextMenu changeView={this.state.changeView}> <StyledContextMenu changeView={this.state.changeView}>
<CSSTransition <CSSTransition
nodeRef={this.menuRef} nodeRef={this.menuRef}
@ -406,16 +429,20 @@ class NewContextMenu extends React.Component {
NewContextMenu.propTypes = { NewContextMenu.propTypes = {
/** Unique identifier of the element */ /** Unique identifier of the element */
id: PropTypes.string, id: PropTypes.string,
/** An array of menuitems */ /** An array of objects */
model: PropTypes.array, model: PropTypes.array,
/** An object of header */ /** An object of header with icon and label */
header: PropTypes.object, header: PropTypes.object,
/** Position of context menu */
position: PropTypes.object,
/** Inline style of the component */ /** Inline style of the component */
style: PropTypes.object, style: PropTypes.object,
/** Style class of the component */ /** Style class of the component */
className: PropTypes.string, className: PropTypes.string,
/** Attaches the menu to document instead of a particular item */ /** Attaches the menu to document instead of a particular item */
global: PropTypes.bool, global: PropTypes.bool,
/** Tell when context menu was render with backdrop */
withBackdrop: PropTypes.bool,
/** Base zIndex value to use in layering */ /** Base zIndex value to use in layering */
autoZIndex: PropTypes.bool, autoZIndex: PropTypes.bool,
/** Whether to automatically manage layering */ /** Whether to automatically manage layering */
@ -431,6 +458,8 @@ NewContextMenu.propTypes = {
NewContextMenu.defaultProps = { NewContextMenu.defaultProps = {
id: null, id: null,
model: null, model: null,
position: null,
header: null,
style: null, style: null,
className: null, className: null,
global: false, global: false,
@ -439,6 +468,7 @@ NewContextMenu.defaultProps = {
appendTo: null, appendTo: null,
onShow: null, onShow: null,
onHide: null, onHide: null,
withBackdrop: true,
}; };
export default NewContextMenu; export default NewContextMenu;

View File

@ -110,6 +110,20 @@ const Template = (args) => {
icon: "/static/images/catalog.shared.react.svg", icon: "/static/images/catalog.shared.react.svg",
key: "sharing-settings10", key: "sharing-settings10",
label: "Sharing settings", 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"), onClick: () => console.log("item 3 clicked"),
}, },
{ {