web: components: Styling AdvancedSelector for People groups selector

This commit is contained in:
Alexey Safronov 2019-09-11 18:00:10 +03:00
parent aade91af35
commit 30027112f9
2 changed files with 175 additions and 27 deletions

View File

@ -12,6 +12,8 @@ import { isArrayEqual } from "../../utils/array";
import findIndex from "lodash/findIndex"; import findIndex from "lodash/findIndex";
import filter from "lodash/filter"; import filter from "lodash/filter";
import DropDown from "../drop-down"; import DropDown from "../drop-down";
import { handleAnyClick } from "../../utils/event";
import isEmpty from 'lodash/isEmpty';
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
@ -32,16 +34,27 @@ const Container = ({
groups, groups,
selectedGroups, selectedGroups,
onChangeGroup, onChangeGroup,
isOpen,
isDropDown,
containerWidth,
containerHeight,
...props ...props
}) => <div {...props} />; }) => <div {...props} />;
/* eslint-enable react/prop-types */ /* eslint-enable react/prop-types */
/* eslint-enable no-unused-vars */ /* eslint-enable no-unused-vars */
const StyledContainer = styled(Container)` const StyledContainer = styled(Container)`
${props => (props.width ? `width: ${props.width}px;` : "")} display: flex;
flex-direction: column;
${props => (props.containerWidth ? `width: ${props.containerWidth}px;` : "")}
${props =>
props.containerHeight
? `height: ${props.containerHeight}px;`
: ""}
.data_container { .data_container {
margin: 16px; margin: 16px 16px 0 16px;
.options_searcher { .options_searcher {
margin-bottom: 12px; margin-bottom: 12px;
@ -53,7 +66,7 @@ const StyledContainer = styled(Container)`
.option_select_all_checkbox { .option_select_all_checkbox {
margin-bottom: 12px; margin-bottom: 12px;
margin-left: 8px; /*margin-left: 8px;*/
} }
.options_list { .options_list {
@ -62,25 +75,26 @@ const StyledContainer = styled(Container)`
cursor: pointer; cursor: pointer;
.option_checkbox { .option_checkbox {
margin-left: 8px; /*margin-left: 8px;*/
} }
.option_link { .option_link {
padding-left: 8px; padding-left: 8px;
} }
&:hover { /*&:hover {
background-color: #eceef1; background-color: #eceef1;
} }*/
} }
} }
} }
.button_container { .button_container {
border-top: 1px solid #eceef1; border-top: 1px solid #eceef1;
display: flex;
.add_members_btn { .add_members_btn {
margin: 16px; margin: 16px;
width: 293px;
} }
} }
`; `;
@ -89,6 +103,8 @@ class AdvancedSelector extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.ref = React.createRef();
const groups = this.convertGroups(this.props.groups); const groups = this.convertGroups(this.props.groups);
const currentGroup = this.getCurrentGroup(groups); const currentGroup = this.getCurrentGroup(groups);
@ -98,25 +114,53 @@ class AdvancedSelector extends React.Component {
groups: groups, groups: groups,
currentGroup: currentGroup currentGroup: currentGroup
}; };
if (props.isOpen) handleAnyClick(true, this.handleClick);
}
handleClick = e => {
if (this.props.isOpen && !this.ref.current.contains(e.target)) {
this.props.onSelect && this.props.onSelect(this.state.selectedOptions);
}
};
componentWillUnmount() {
handleAnyClick(false, this.handleClick);
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
let newState = {};
if (!isArrayEqual(this.props.selectedOptions, prevProps.selectedOptions)) { if (!isArrayEqual(this.props.selectedOptions, prevProps.selectedOptions)) {
this.setState({ selectedOptions: this.props.selectedOptions }); newState = { selectedOptions: this.props.selectedOptions };
} }
if (this.props.isMultiSelect !== prevProps.isMultiSelect) { if (this.props.isMultiSelect !== prevProps.isMultiSelect) {
this.setState({ selectedOptions: [] }); newState = Object.assign({}, newState, {
selectedOptions: []
});
} }
if (this.props.selectedAll !== prevProps.selectedAll) { if (this.props.selectedAll !== prevProps.selectedAll) {
this.setState({ selectedAll: this.props.selectedAll }); newState = Object.assign({}, newState, {
selectedAll: this.props.selectedAll
});
} }
if (!isArrayEqual(this.props.groups, prevProps.groups)) { if (!isArrayEqual(this.props.groups, prevProps.groups)) {
const groups = this.convertGroups(this.props.groups); const groups = this.convertGroups(this.props.groups);
const currentGroup = this.getCurrentGroup(groups); const currentGroup = this.getCurrentGroup(groups);
this.setState({ groups, currentGroup }); newState = Object.assign({}, newState, {
groups, currentGroup
});
}
if(!isEmpty(newState)) {
this.setState({ ...this.state, ...newState });
}
if (this.props.isOpen !== prevProps.isOpen) {
handleAnyClick(this.props.isOpen, this.handleClick);
} }
} }
@ -215,7 +259,6 @@ class AdvancedSelector extends React.Component {
const { const {
value, value,
placeholder, placeholder,
maxHeight,
isDisabled, isDisabled,
onSearchChanged, onSearchChanged,
options, options,
@ -225,9 +268,19 @@ class AdvancedSelector extends React.Component {
} = this.props; } = this.props;
const { selectedOptions, selectedAll, currentGroup, groups } = this.state; const { selectedOptions, selectedAll, currentGroup, groups } = this.state;
const containerHeight = !groups || !groups.length ? 336 : 545;
const containerWidth = !groups || !groups.length ? 325 : 690;
const listHeight = 176;
const itemHeight = 32;
return ( return (
<StyledContainer {...this.props}> <StyledContainer
<div className="data_container"> containerHeight={containerHeight}
containerWidth={containerWidth}
{...this.props}
>
<div className="data_container" ref={this.ref}>
<SearchInput <SearchInput
className="options_searcher" className="options_searcher"
isDisabled={isDisabled} isDisabled={isDisabled}
@ -264,10 +317,10 @@ class AdvancedSelector extends React.Component {
)} )}
<FixedSizeList <FixedSizeList
className="options_list" className="options_list"
height={maxHeight} height={listHeight}
itemSize={32} itemSize={itemHeight}
itemCount={options.length} itemCount={this.props.options.length}
itemData={options} itemData={this.props.options}
outerElementType={CustomScrollbarsVirtualList} outerElementType={CustomScrollbarsVirtualList}
> >
{this.renderRow.bind(this)} {this.renderRow.bind(this)}
@ -310,8 +363,7 @@ AdvancedSelector.propTypes = {
value: PropTypes.string, value: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
isMultiSelect: PropTypes.bool, isMultiSelect: PropTypes.bool,
mode: PropTypes.oneOf(["base", "compact"]), mode: PropTypes.oneOf(["compact", "full"]),
width: PropTypes.number,
maxHeight: PropTypes.number, maxHeight: PropTypes.number,
isDisabled: PropTypes.bool, isDisabled: PropTypes.bool,
onSearchChanged: PropTypes.func, onSearchChanged: PropTypes.func,
@ -330,9 +382,7 @@ AdvancedSelector.propTypes = {
AdvancedSelector.defaultProps = { AdvancedSelector.defaultProps = {
isMultiSelect: false, isMultiSelect: false,
width: 325, mode: "compact",
maxHeight: 545,
mode: "base",
buttonLabel: "Add members", buttonLabel: "Add members",
selectAllLabel: "Select all" selectAllLabel: "Select all"
}; };

View File

@ -0,0 +1,98 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { withKnobs, text } from "@storybook/addon-knobs/react";
import AdvancedSelector from "../advanced-selector";
import Section from "../../../.storybook/decorators/section";
import { boolean } from "@storybook/addon-knobs/dist/deprecated";
import { ArrayValue, BooleanValue } from "react-values";
import Button from "../button";
storiesOf("EXAMPLES|AdvancedSelector", module)
.addDecorator(withKnobs)
// To set a default viewport for all the stories for this component
.addParameters({ viewport: { defaultViewport: "responsive" } })
.add("people group selector", () => {
const options = [
{
key: "group-all",
label: "All groups",
total: 0
},
{
key: "group-dev",
label: "Development",
total: 0
},
{
key: "group-management",
label: "Management",
total: 0
},
{
key: "group-marketing",
label: "Marketing",
total: 0
},
{
key: "group-mobile",
label: "Mobile",
total: 0
},
{
key: "group-support",
label: "Support",
total: 0
},
{
key: "group-web",
label: "Web",
total: 0
}
];
return (
<Section>
<BooleanValue
defaultValue={true}
onChange={() => action("isOpen changed")}
>
{({ value: isOpen, toggle }) => (
<div style={{ position: "relative" }}>
<Button label="Toggle dropdown" onClick={toggle} />
<ArrayValue
defaultValue={options}
onChange={() => action("options onChange")}
>
{({ value, set }) => (
<AdvancedSelector
isDropDown={true}
isOpen={isOpen}
maxHeight={336}
width={379}
placeholder={text("placeholder", "Search")}
onSearchChanged={value => {
action("onSearchChanged")(value);
set(
options.filter(option => {
return option.label.indexOf(value) > -1;
})
);
}}
options={value}
isMultiSelect={boolean("isMultiSelect", true)}
buttonLabel={text("buttonLabel", "Add departments")}
selectAllLabel={text("selectAllLabel", "Select all")}
onSelect={selectedOptions => {
action("onSelect")(selectedOptions);
toggle();
}}
/>
)}
</ArrayValue>
</div>
)}
</BooleanValue>
</Section>
);
});