123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- import React from 'react';
- import styled from '@emotion/styled';
- import omit from 'lodash/omit';
- import Link from 'app/components/links/link';
- import space from 'app/styles/space';
- import {callIfFunction} from 'app/utils/callIfFunction';
- import {Theme} from 'app/utils/theme';
- type MenuItemProps = {
- /**
- * Should this item act as a header
- */
- header?: boolean;
- /**
- * Should this item act as a divider
- */
- divider?: boolean;
- /**
- * The title/tooltipe of the item
- */
- title?: string;
- /**
- * Is the item disabled?
- */
- disabled?: boolean;
- /**
- * Triggered when the item is clicked
- */
- onSelect?: (eventKey: any) => void;
- /**
- * Provided to the onSelect callback when this item is selected
- */
- eventKey?: any;
- /**
- * Is the item actively seleted?
- */
- isActive?: boolean;
- /**
- * Enable to provide custom button/contents via children
- */
- noAnchor?: boolean;
- /**
- * A router target destination
- */
- to?: React.ComponentProps<typeof Link>['to'];
- /**
- * A server rendered URL.
- */
- href?: string;
- /**
- * Enable to allow default event on click
- */
- allowDefaultEvent?: boolean;
- className?: string;
- };
- type Props = MenuItemProps & Omit<React.HTMLProps<HTMLLIElement>, keyof MenuItemProps>;
- class MenuItem extends React.Component<Props> {
- handleClick = (e: React.MouseEvent): void => {
- const {onSelect, disabled, eventKey, allowDefaultEvent} = this.props;
- if (disabled) {
- return;
- }
- if (onSelect) {
- if (allowDefaultEvent !== true) {
- e.preventDefault();
- }
- callIfFunction(onSelect, eventKey);
- }
- };
- renderAnchor = (): React.ReactNode => {
- const {to, href, title, disabled, isActive, children} = this.props;
- if (to) {
- return (
- <MenuLink
- to={to}
- title={title}
- onClick={this.handleClick}
- tabIndex={-1}
- isActive={isActive}
- disabled={disabled}
- >
- {children}
- </MenuLink>
- );
- }
- if (href) {
- return (
- <MenuAnchor
- href={href}
- onClick={this.handleClick}
- tabIndex={-1}
- isActive={isActive}
- disabled={disabled}
- >
- {children}
- </MenuAnchor>
- );
- }
- return (
- <MenuTarget
- role="button"
- title={title}
- onClick={this.handleClick}
- tabIndex={-1}
- isActive={isActive}
- disabled={disabled}
- >
- {this.props.children}
- </MenuTarget>
- );
- };
- render() {
- const {
- header,
- divider,
- isActive,
- noAnchor,
- className,
- children,
- ...props
- } = this.props;
- let renderChildren: React.ReactNode | null = null;
- if (noAnchor) {
- renderChildren = children;
- } else if (header) {
- renderChildren = children;
- } else if (!divider) {
- renderChildren = this.renderAnchor();
- }
- return (
- <MenuListItem
- className={className}
- role="presentation"
- isActive={isActive}
- divider={divider}
- noAnchor={noAnchor}
- header={header}
- {...omit(props, ['href', 'title', 'onSelect', 'eventKey', 'to', 'as'])}
- >
- {renderChildren}
- </MenuListItem>
- );
- }
- }
- type MenuListItemProps = {
- header?: boolean;
- noAnchor?: boolean;
- isActive?: boolean;
- disabled?: boolean;
- divider?: boolean;
- } & React.HTMLProps<HTMLLIElement>;
- function getListItemStyles(props: MenuListItemProps & {theme: Theme}) {
- const common = `
- display: block;
- padding: ${space(0.5)} ${space(2)};
- &:focus {
- outline: none;
- }
- `;
- if (props.disabled) {
- return `
- ${common}
- color: ${props.theme.disabled};
- background: transparent;
- cursor: not-allowed;
- `;
- }
- if (props.isActive) {
- return `
- ${common}
- color: ${props.theme.white};
- background: ${props.theme.active};
- &:hover {
- color: ${props.theme.black};
- }
- `;
- }
- return `
- ${common}
- &:hover {
- background: ${props.theme.focus};
- }
- `;
- }
- function getChildStyles(props: MenuListItemProps & {theme: Theme}) {
- if (!props.noAnchor) {
- return '';
- }
- return `
- & a {
- ${getListItemStyles(props)}
- }
- `;
- }
- const MenuAnchor = styled('a', {
- shouldForwardProp: p =>
- typeof p === 'string' && ['isActive', 'disabled'].includes(p) === false,
- })<MenuListItemProps>`
- ${getListItemStyles}
- `;
- const MenuListItem = styled('li')<MenuListItemProps>`
- display: block;
- ${p =>
- p.divider &&
- `
- height: 1px;
- margin: ${space(0.5)} 0;
- overflow: hidden;
- background-color: ${p.theme.innerBorder};
- `}
- ${p =>
- p.header &&
- `
- padding: ${space(0.25)} ${space(1)};
- font-size: ${p.theme.fontSizeSmall};
- line-height: 1.4;
- color: ${p.theme.gray300};
- `}
- ${getChildStyles}
- `;
- const MenuTarget = styled('span')<MenuListItemProps>`
- ${getListItemStyles}
- display: flex;
- `;
- const MenuLink = styled(Link, {
- shouldForwardProp: p =>
- typeof p === 'string' && ['isActive', 'disabled'].includes(p) === false,
- })<MenuListItemProps>`
- ${getListItemStyles}
- `;
- export default MenuItem;
|