Web.Components: LinkWithDropdown: refactoring

This commit is contained in:
Daniil Senkiv 2019-10-17 17:52:16 +03:00
parent 1d85a6ce94
commit ecb74a09bb
2 changed files with 194 additions and 190 deletions

View File

@ -1,229 +1,234 @@
import React from 'react'; import React from "react";
import styled, { css } from 'styled-components'; import styled, { css } from "styled-components";
import PropTypes from 'prop-types'; import PropTypes from "prop-types";
import { Icons } from '../icons'; import { Icons } from "../icons";
import DropDown from '../drop-down'; import DropDown from "../drop-down";
import DropDownItem from '../drop-down-item'; import DropDownItem from "../drop-down-item";
import { Text } from '../text'; import { Text } from "../text";
import { handleAnyClick } from '../../utils/event'; import { handleAnyClick } from "../../utils/event";
const SimpleLinkWithDropdown = ({ isBold, fontSize, isTextOverflow, // eslint-disable-next-line no-unused-vars
isHovered, isSemitransparent, color, title, const SimpleLinkWithDropdown = ({ isBold, fontSize, isTextOverflow, isHovered, isSemitransparent, color, title, dropdownType, data,
dropdownType, data, ...props }) => <a {...props}></a>; ...props
}) => <a {...props}></a>;
const getColor = color => { SimpleLinkWithDropdown.propTypes = {
switch (color) { isBold: PropTypes.bool,
case 'gray': fontSize: PropTypes.number,
return '#A3A9AE'; isTextOverflow: PropTypes.bool,
case 'blue': isHovered: PropTypes.bool,
return '#316DAA'; isSemitransparent: PropTypes.bool,
default: color: PropTypes.string,
return '#333333'; title: PropTypes.string,
} dropdownType: PropTypes.oneOf(["alwaysDashed", "appearDashedAfterHover"]).isRequired,
} data: PropTypes.array
};
const opacityCss = css` const color = props => props.color;
${props => (props.isSemitransparent && `opacity: 0.5`)};
`;
const colorCss = css` // eslint-disable-next-line react/prop-types
color: ${props => getColor(props.color)}; const ExpanderDownIcon = ({ isSemitransparent, dropdownType, ...props }) => (<Icons.ExpanderDownIcon {...props} />);
`;
const hoveredCss = css`
${colorCss};
text-decoration: none;
border-bottom: 1px dotted;
`;
const visitedCss = css`
${colorCss};
`;
const dottedCss = css`
border-bottom: 1px dotted;
`;
const ExpanderDownIcon = ({ isSemitransparent, dropdownType, ...props }) => <Icons.ExpanderDownIcon {...props} />;
const Caret = styled(ExpanderDownIcon)` const Caret = styled(ExpanderDownIcon)`
width: 10px; width: 10px;
min-width: 10px; min-width: 10px;
height: 10px; height: 10px;
min-height: 10px; min-height: 10px;
margin-left: 5px;
margin-top: -4px;
margin-left: 5px; position: absolute;
margin-top: -4px; right: 6px;
${opacityCss}; top: 0;
${props => (props.dropdownType === 'appearDottedAfterHover') && `opacity: 0`}; bottom: 0;
${props => (props.dropdownType === 'appearDottedAfterHover') && `position: absolute`}; margin: auto;
path { path {
fill: ${props => getColor(props.color)}; fill: ${color};
} }
${props => props.dropdownType === "appearDashedAfterHover" && `opacity: 0`};
`; `;
const StyledLinkWithDropdown = styled(SimpleLinkWithDropdown)` const StyledLinkWithDropdown = styled(SimpleLinkWithDropdown)`
${opacityCss};
text-decoration: none;
user-select: none;
&:hover {
${hoveredCss};
}
&:visited {
${visitedCss};
}
&:not([href]):not([tabindex]) {
${colorCss};
text-decoration: none;
&:hover {
${hoveredCss};
}
}
${props => (props.dropdownType === 'alwaysDotted' && dottedCss)}; cursor: pointer;
text-decoration: none;
user-select: none;
padding-right: 20px;
position: relative;
display: inline-grid;
`; color: ${color};
const StyledSpan = styled.span` ${props => props.isSemitransparent && `opacity: 0.5`};
cursor: pointer; ${props => props.dropdownType === "alwaysDashed" && `text-decoration: underline dashed`};
:hover {
svg { &:not([href]):not([tabindex]) {
${props => (props.dropdownType === 'appearDottedAfterHover' && `position: static`)}; ${props => props.dropdownType === "alwaysDashed" && `text-decoration: underline dashed`};
${props => (props.dropdownType === 'appearDottedAfterHover' && `opacity: 1`)}; color: ${color};
${props => (props.isSemitransparent && `opacity: 0.5`)};
} &:hover {
text-decoration: underline dashed;
color: ${color};
} }
}
:hover {
color: ${color};
svg {
${props => props.dropdownType === "appearDashedAfterHover" && `position: absolute; opacity: 1`};
${props => props.isSemitransparent && `opacity: 0.5`};
}
}
`; `;
const SimpleText = ({ color, fontSize, isTextOverflow, ...props }) => <Text.Body as="span" fontSize={fontSize} color={getColor(color)} {...props} /> const SimpleText = ({ isTextOverflow, fontSize, color, ...props }) => (<Text.Body as="span" {...props} />);
const StyledText = styled(SimpleText)` const StyledText = styled(SimpleText)`
${props => (props.isTextOverflow && css` color: ${color};
white-space: nowrap;
overflow: hidden; ${props => props.isTextOverflow && css`
text-overflow: ellipsis; display: inline-block;
`)} max-width: 100%;
`}
`; `;
const DataDropDown = ({ data, color, fontSize, ...props }) => <DropDown {...props}></DropDown>; const DataDropDown = ({ data, color, fontSize, title, ...props }) => (
<DropDown {...props}></DropDown>
);
class LinkWithDropdown extends React.PureComponent { class LinkWithDropdown extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.ref = React.createRef(); this.state = {
isOpen: false
};
this.state = { this.ref = React.createRef();
isOpen: false,
data: props.data
};
this.handleClick = this.handleClick.bind(this); this.handleClick = this.handleClick.bind(this);
this.stopAction = this.stopAction.bind(this); this.toggleDropdown = this.toggleDropdown.bind(this);
this.toggleDropdown = this.toggleDropdown.bind(this); this.onDropDownItemClick = this.onDropDownItemClick.bind(this);
this.onDropDownItemClick = this.onDropDownItemClick.bind(this);
if (props.isOpen) if (props.isOpen) handleAnyClick(true, this.handleClick);
handleAnyClick(true, this.handleClick); }
handleClick = e =>
this.state.isOpen &&
!this.ref.current.contains(e.target) &&
this.toggleDropdown(false);
toggleDropdown = isOpen => this.setState({ isOpen });
clickToDropdown = () => this.setState({ isOpen: !this.state.isOpen });
componentWillUnmount() {
handleAnyClick(false, this.handleClick);
}
componentDidUpdate(prevProps, prevState) {
if (this.props.dropdownType !== prevProps.dropdownType) {
if (this.props.isOpen !== prevProps.isOpen) {
this.toggleDropdown(this.props.isOpen);
}
} else if (this.props.isOpen !== prevProps.isOpen) {
this.toggleDropdown(this.props.isOpen);
} }
handleClick = (e) => this.state.isOpen && !this.ref.current.contains(e.target) && this.toggleDropdown(false); if (this.state.isOpen !== prevState.isOpen) {
stopAction = (e) => !this.props.href && e.preventDefault(); handleAnyClick(this.state.isOpen, this.handleClick);
toggleDropdown = (isOpen) => this.setState({ isOpen: isOpen });
clickToDropdown = () => {
this.setState({
data: this.props.data,
isOpen: !this.state.isOpen
});
} }
}
componentWillUnmount() { onDropDownItemClick = item => {
handleAnyClick(false, this.handleClick); item.onClick && item.onClick();
} this.toggleDropdown(!this.state.isOpen);
};
componentDidUpdate(prevProps, prevState) { render() {
if (this.props.dropdownType !== prevProps.dropdownType) { console.log("LinkWithDropdown render");
if (this.props.isOpen !== prevProps.isOpen) {
this.toggleDropdown(this.props.isOpen);
}
}
else if (this.props.isOpen !== prevProps.isOpen) {
this.toggleDropdown(this.props.isOpen);
}
if (this.state.isOpen !== prevState.isOpen) { const {
handleAnyClick(this.state.isOpen, this.handleClick); isSemitransparent,
} dropdownType,
} isTextOverflow,
fontSize,
color,
isBold,
title
} = this.props;
return (
<>
<StyledLinkWithDropdown
ref={this.ref}
onClick={this.clickToDropdown}
isSemitransparent={isSemitransparent}
dropdownType={dropdownType}
color={color}
>
<StyledText
isTextOverflow={isTextOverflow}
truncate={isTextOverflow}
fontSize={fontSize}
color={color}
isBold={isBold}
title={title}
dropdownType={dropdownType}
>
{this.props.children}
</StyledText>
onDropDownItemClick = (item) => { <Caret
item.onClick && item.onClick(); color={color}
this.toggleDropdown(!this.state.isOpen); dropdownType={dropdownType}
} />
</StyledLinkWithDropdown>
render() { <DataDropDown
// console.log("LinkWithDropdown render"); isOpen={this.state.isOpen}
return ( withArrow={false}
<> {...this.props}
<StyledSpan >
ref={this.ref} {this.props.data.map(item => (
isSemitransparent={this.props.isSemitransparent} <DropDownItem
onClick={this.clickToDropdown} key={item.key}
dropdownType={this.props.dropdownType} onClick={this.onDropDownItemClick.bind(this.props, item)}
> {...item}
<StyledLinkWithDropdown {...this.props}> />
<StyledText ))}
isTextOverflow={this.props.isTextOverflow} </DataDropDown>
fontSize={this.props.fontSize} </>
color={this.props.color} );
isBold={this.props.isBold} }
title={this.props.title}
>
{this.props.children}
</StyledText>
</StyledLinkWithDropdown>
<Caret
isSemitransparent={this.props.isSemitransparent}
color={this.props.color}
dropdownType={this.props.dropdownType}
/>
</StyledSpan>
<DataDropDown isOpen={this.state.isOpen} {...this.props}>
{
this.state.data.map(item =>
<DropDownItem
{...item}
onClick={this.onDropDownItemClick.bind(this.props, item)}
/>
)
}
</DataDropDown>
</>
);
}
} }
LinkWithDropdown.propTypes = { LinkWithDropdown.propTypes = {
color: PropTypes.oneOf(['gray', 'black', 'blue']), color: PropTypes.string,
data: PropTypes.array, data: PropTypes.array,
dropdownType: PropTypes.oneOf(['alwaysDotted', 'appearDottedAfterHover']).isRequired, dropdownType: PropTypes.oneOf(["alwaysDashed", "appearDashedAfterHover"]).isRequired,
fontSize: PropTypes.number, fontSize: PropTypes.number,
isBold: PropTypes.bool, isBold: PropTypes.bool,
isSemitransparent: PropTypes.bool, isSemitransparent: PropTypes.bool,
isTextOverflow: PropTypes.bool, isTextOverflow: PropTypes.bool,
title: PropTypes.string, title: PropTypes.string,
isOpen: PropTypes.bool,
children: PropTypes.any
}; };
LinkWithDropdown.defaultProps = { LinkWithDropdown.defaultProps = {
color: 'black', color: "#333333",
data: [], data: [],
dropdownType: 'alwaysDotted', dropdownType: "alwaysDashed",
fontSize: 13, fontSize: 13,
isBold: false, isBold: false,
isSemitransparent: false, isSemitransparent: false,
isTextOverflow: true, isTextOverflow: true
} };
export default LinkWithDropdown; export default LinkWithDropdown;

View File

@ -2,13 +2,12 @@ import React from 'react';
import { storiesOf } from '@storybook/react'; import { storiesOf } from '@storybook/react';
import LinkWithDropdown from '.'; import LinkWithDropdown from '.';
import Readme from './README.md'; import Readme from './README.md';
import { text, boolean, withKnobs, select, number } from '@storybook/addon-knobs/react'; import { text, boolean, withKnobs, select, number, color } from '@storybook/addon-knobs/react';
import withReadme from 'storybook-readme/with-readme'; import withReadme from 'storybook-readme/with-readme';
import Section from '../../../.storybook/decorators/section'; import Section from '../../../.storybook/decorators/section';
import { Col } from 'reactstrap'; import { Col } from 'reactstrap';
const colors = ['black', 'gray', 'blue']; const dropdownType = ["alwaysDashed", "appearDashedAfterHover"];
const dropdownType = ['alwaysDotted', 'appearDottedAfterHover'];
const dropdownItems = [ const dropdownItems = [
{ {
@ -39,8 +38,8 @@ storiesOf('Components|LinkWithDropdown', module)
<Section> <Section>
<Col> <Col>
<LinkWithDropdown <LinkWithDropdown
dropdownType={select('dropdownType', dropdownType, 'alwaysDotted')} dropdownType={select('dropdownType', dropdownType, 'alwaysDashed')}
color={select('color', colors, 'black')} color={color('color', '#333333')}
fontSize={number('fontSize', 13)} fontSize={number('fontSize', 13)}
isBold={boolean('isBold', false)} isBold={boolean('isBold', false)}
title={text('title', undefined)} title={text('title', undefined)}