web: components: Re-written GroupButton component to class style (issue: useState, useEffect break React functional components rules of usage)
This commit is contained in:
parent
f610230b2e
commit
a84312376b
@ -1,146 +1,187 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Icons } from '../icons'
|
||||
import DropDown from '../drop-down'
|
||||
import Checkbox from '../checkbox'
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { Icons } from "../icons";
|
||||
import DropDown from "../drop-down";
|
||||
import DropDownItem from "../drop-down-item";
|
||||
|
||||
const textColor = '#333333',
|
||||
disabledTextColor = '#A3A9AE';
|
||||
const textColor = "#333333",
|
||||
disabledTextColor = "#A3A9AE";
|
||||
|
||||
const activatedCss = css`
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const hoveredCss = css`
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledGroupButton = styled.div`
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
`;
|
||||
|
||||
const StyledDropdownToggle = styled.div`
|
||||
font-family: Open Sans;
|
||||
font-style: normal;
|
||||
font-weight: ${props => props.fontWeight};
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
font-family: Open Sans;
|
||||
font-style: normal;
|
||||
font-weight: ${props => props.fontWeight};
|
||||
font-size: 14px;
|
||||
line-height: 19px;
|
||||
|
||||
cursor: default;
|
||||
cursor: default;
|
||||
|
||||
color: ${props => (props.disabled ? disabledTextColor : textColor)};
|
||||
|
||||
float: left;
|
||||
height: 19px;
|
||||
margin: 18px 12px 19px 12px;
|
||||
overflow: hidden;
|
||||
padding: 0px;
|
||||
color: ${props => (props.disabled ? disabledTextColor : textColor)};
|
||||
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
float: left;
|
||||
height: 19px;
|
||||
margin: 18px 12px 19px 12px;
|
||||
overflow: hidden;
|
||||
padding: 0px;
|
||||
|
||||
user-select: none;
|
||||
-o-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
|
||||
${props => !props.disabled && (props.activated ? `${activatedCss}` : css`
|
||||
&:active {
|
||||
user-select: none;
|
||||
-o-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
||||
${props =>
|
||||
!props.disabled &&
|
||||
(props.activated
|
||||
? `${activatedCss}`
|
||||
: css`
|
||||
&:active {
|
||||
${activatedCss}
|
||||
}`)
|
||||
}
|
||||
}
|
||||
`)}
|
||||
|
||||
${props => !props.disabled && (props.hovered ? `${hoveredCss}` : css`
|
||||
&:hover {
|
||||
${props =>
|
||||
!props.disabled &&
|
||||
(props.hovered
|
||||
? `${hoveredCss}`
|
||||
: css`
|
||||
&:hover {
|
||||
${hoveredCss}
|
||||
}`)
|
||||
}
|
||||
}
|
||||
`)}
|
||||
`;
|
||||
|
||||
const Caret = styled(Icons.ExpanderDownIcon)`
|
||||
width: 10px;
|
||||
margin-left: 4px;
|
||||
width: 10px;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
|
||||
const Separator = styled.div`
|
||||
vertical-align: middle;
|
||||
border: 0.5px solid #ECEEF1;
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
margin-top: 16px;
|
||||
vertical-align: middle;
|
||||
border: 0.5px solid #eceef1;
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
const useOuterClickNotifier = (onOuterClick, ref) => {
|
||||
useEffect(() => {
|
||||
const handleClick = (e) => !ref.current.contains(e.target) && onOuterClick(e);
|
||||
class GroupButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (ref.current) {
|
||||
document.addEventListener("click", handleClick);
|
||||
}
|
||||
this.ref = React.createRef();
|
||||
|
||||
return () => document.removeEventListener("click", handleClick);
|
||||
},
|
||||
[onOuterClick, ref]
|
||||
);
|
||||
}
|
||||
this.state = {
|
||||
isOpen: props.opened
|
||||
};
|
||||
}
|
||||
|
||||
const GroupButton = (props) => {
|
||||
const { label, isDropdown, opened, disabled, isSeparator} = props;
|
||||
const [isOpen, toggle] = useState(opened);
|
||||
onOuterClick = e => this.toggle(false);
|
||||
handleClick = e =>
|
||||
!this.ref.current.contains(e.target) && this.onOuterClick(e);
|
||||
stopAction = e => e.preventDefault();
|
||||
toggle = isOpen => this.setState({ isOpen: isOpen });
|
||||
|
||||
let ref = useRef(null);
|
||||
componentDidMount() {
|
||||
if (this.ref.current) {
|
||||
document.addEventListener("click", this.handleClick);
|
||||
}
|
||||
}
|
||||
|
||||
useOuterClickNotifier(() => toggle(false), ref);
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("click", this.handleClick);
|
||||
}
|
||||
|
||||
const dropMenu = <DropDown isOpen={isOpen} {...props}/>;
|
||||
componentDidUpdate(prevProps) {
|
||||
// Store prevId in state so we can compare when props change.
|
||||
// Clear out previously-loaded data (so we don't render stale stuff).
|
||||
if (this.props.opened !== prevProps.opened) {
|
||||
this.toggle(this.props.opened);
|
||||
}
|
||||
}
|
||||
|
||||
const dropDownButton =
|
||||
<StyledDropdownToggle {...props} onClick={() => { disabled ? false : toggle(!isOpen) }}>
|
||||
{label}
|
||||
<Caret size='small' color={disabled ? disabledTextColor : textColor} />
|
||||
</StyledDropdownToggle>;
|
||||
render() {
|
||||
const { label, isDropdown, disabled, isSeparator } = this.props;
|
||||
|
||||
return (
|
||||
<StyledGroupButton ref={ref}>
|
||||
{isDropdown
|
||||
? {...dropDownButton}
|
||||
: <StyledDropdownToggle {...props} >
|
||||
{label}
|
||||
</StyledDropdownToggle>
|
||||
<StyledGroupButton ref={this.ref}>
|
||||
{isDropdown ? (
|
||||
<>
|
||||
<StyledDropdownToggle
|
||||
{...this.props}
|
||||
onClick={() => {
|
||||
disabled ? false : this.toggle(!this.state.isOpen);
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
<Caret
|
||||
size="small"
|
||||
color={disabled ? disabledTextColor : textColor}
|
||||
/>
|
||||
</StyledDropdownToggle>
|
||||
<DropDown isOpen={this.state.isOpen}>
|
||||
{
|
||||
React.Children.map(this.props.children, (child) =>
|
||||
<DropDownItem
|
||||
{...child.props}
|
||||
onClick={() => {
|
||||
child.props.onClick && child.props.onClick();
|
||||
this.toggle(!this.state.isOpen);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{isSeparator && <Separator/>}
|
||||
{isDropdown && {...dropMenu}}
|
||||
</StyledGroupButton>
|
||||
</DropDown>
|
||||
</>
|
||||
) : (
|
||||
<StyledDropdownToggle {...this.props}>{label}</StyledDropdownToggle>
|
||||
)}
|
||||
{isSeparator && <Separator />}
|
||||
</StyledGroupButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GroupButton.propTypes = {
|
||||
label: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
activated: PropTypes.bool,
|
||||
opened: PropTypes.bool,
|
||||
hovered: PropTypes.bool,
|
||||
isDropdown: PropTypes.bool,
|
||||
isSeparator: PropTypes.bool,
|
||||
tabIndex: PropTypes.number,
|
||||
onClick: PropTypes.func,
|
||||
fontWeight: PropTypes.string
|
||||
label: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
activated: PropTypes.bool,
|
||||
opened: PropTypes.bool,
|
||||
hovered: PropTypes.bool,
|
||||
isDropdown: PropTypes.bool,
|
||||
isSeparator: PropTypes.bool,
|
||||
tabIndex: PropTypes.number,
|
||||
onClick: PropTypes.func,
|
||||
fontWeight: PropTypes.string
|
||||
};
|
||||
|
||||
GroupButton.defaultProps = {
|
||||
label: 'Group button',
|
||||
disabled: false,
|
||||
activated: false,
|
||||
opened: false,
|
||||
hovered: false,
|
||||
isDropdown: false,
|
||||
isSeparator: false,
|
||||
tabIndex: -1,
|
||||
fontWeight: '600'
|
||||
label: "Group button",
|
||||
disabled: false,
|
||||
activated: false,
|
||||
opened: false,
|
||||
hovered: false,
|
||||
isDropdown: false,
|
||||
isSeparator: false,
|
||||
tabIndex: -1,
|
||||
fontWeight: "600"
|
||||
};
|
||||
|
||||
export default GroupButton
|
||||
export default GroupButton;
|
||||
|
@ -25,10 +25,10 @@ storiesOf('Components|GroupButton', module)
|
||||
<Col><GroupButton disabled /></Col>
|
||||
</Row>
|
||||
<Row style={rowStyle}>
|
||||
<Col><GroupButton isDropdown ><DropDownItem /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown hovered ><DropDownItem /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown activated ><DropDownItem /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown disabled ><DropDownItem /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown ><DropDownItem label="Action 1" /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown hovered ><DropDownItem label="Action 2" /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown activated ><DropDownItem label="Action 3" /></GroupButton></Col>
|
||||
<Col><GroupButton isDropdown disabled ><DropDownItem label="Action 4" /></GroupButton></Col>
|
||||
</Row>
|
||||
</Container>
|
||||
));
|
@ -15,6 +15,8 @@ storiesOf('Components|GroupButton', module)
|
||||
isDropdown = {boolean('isDropdown', false)}
|
||||
opened = {boolean('opened', false)}
|
||||
>
|
||||
<DropDownItem/>
|
||||
<DropDownItem label="Action 1" />
|
||||
<DropDownItem label="Action 2" />
|
||||
<DropDownItem label="Action 3" />
|
||||
</GroupButton>
|
||||
));
|
||||
|
Loading…
Reference in New Issue
Block a user