最终效果:
代码如下
interface
export interface IFilter {
/** unique id */
id: string;
/** machine serial number */
machineSn: string;
/** if value is true, user select this data */
checked: boolean;
}
component
import * as React from "react";
import {IFilters} from "./IFilters";
import "../../public/style/Filter.scss";
interface IFilterProps {
filters?: IFilters[];
filterUpdate?: (arrays: IFilters[]) => void;
}
interface IFilterState {
checkAll: boolean;
displayDialog: boolean;
listOfFilters: IFilters[];
queryInputValue: string;
metadataList: IFilters[];
}
export class MachineSnFilter extends React.Component {
constructor(props: IFilterProps) {
super(props);
this.state = {
checkAll: false,
displayDialog: false,
listOfFilters: [],
queryInputValue: "",
metadataList: [],
};
this.changeQueryInputValue = this.changeQueryInputValue.bind(this);
this.checkAllFilters = this.checkAllFilters.bind(this);
this.cancelOperation = this.cancelOperation.bind(this);
this.filterOnCheckChange = this.filterOnCheckChange.bind(this);
this.saveFilters = this.saveFilters.bind(this);
this.makePropsDataToState = this.makePropsDataToState.bind(this);
}
public render() {
const dialogClassName = this.state.displayDialog
? "filter-dialog"
: "filter-dialog filter-dialog-hidden";
return (
{/* switch button */}
{this.switchButtonRender()}
{/* dialog */}
{/* check all */}
{this.checkAllCheckboxRender()}
{this.closeIconRender()}
{/* input */}
{this.queryInputBoxRender()}
{/* checkbox */}
{this.machineSnFiltersRender()}
{/* button( save/ cancel ) */}
);
}
public componentDidMount() {
this.makePropsDataToState();
}
public componentDidUpdate(nextProps) {
if (this.props.filters !== nextProps.filters) {
this.makePropsDataToState();
}
}
/** when component will un mount, reset all state */
public componentWillUnmount() {
if (this.props.filters) {
const checkAll = this.isCheckAll(this.props.filters);
this.setState({
checkAll,
displayDialog: false,
listOfFilters: this.props.filters,
queryInputValue: "",
metadataList: this.props.filters,
});
}
}
/** render toggle button */
private switchButtonRender() {
return (
{
this.setState({displayDialog: !this.state.displayDialog});
}}>
打开
{/* arrow */}
);
}
/** render checkbox of 'checkAll' */
private checkAllCheckboxRender() {
return (
<>
{this.state.checkAll
? "uncCheck all"
: "check all"}
>
);
}
/** render close icon , when click this icon, close dialog */
private closeIconRender() {
return (
);
}
/** render query input box */
private queryInputBoxRender() {
return (
);
}
/** render list of machineSn filter items */
private machineSnFiltersRender() {
return this.state.listOfFilters.map((e, index) => (
{e.machineSn}
));
}
/** merge props 'filters' to state */
private makePropsDataToState() {
const newArr: IFilters[] = [];
if (this.props.filters) {
this.props.filters.forEach(item => {
newArr.push(item);
});
}
const checkAll = this.isCheckAll(newArr);
this.setState({listOfFilters: newArr, metadataList: newArr, checkAll});
}
/** user input field changed, change state 'checkAll','listOfFilters' */
private changeQueryInputValue(event) {
const value = event.target.value;
this.setState({queryInputValue: value}, () => {
const {metadataList} = {...this.state};
const newArr = metadataList.filter(item => item.machineSn.includes(value));
const checkAll = this.isCheckAll(newArr);
this.setState({
checkAll,
listOfFilters: newArr,
});
});
}
/** make all machineSn filter items are selected, or make all items are unselected */
private checkAllFilters() {
this.setState({checkAll: !this.state.checkAll}, () => {
const newArr: IFilters[] = [];
this.state.listOfFilters.forEach(item => {
item = {...item, checked: this.state.checkAll ? true : false};
newArr.push(item);
});
const checkAll = this.isCheckAll(newArr);
const newSaveItems = this.changeItemsCheckedProperty(this.state.metadataList, newArr);
this.setState({
checkAll,
listOfFilters: newArr,
metadataList: newSaveItems,
});
});
}
/**
* user select machineSn filters, change state 'listOfFilters' and 'checkAll' 'metadata'
* @param e:`IFilters` filter item selected by the user
*/
private filterOnCheckChange(e) {
const filterArr: IFilters[] = [
{
...e,
checked: !e.checked,
},
];
const newListOfItems = this.changeItemsCheckedProperty(this.state.listOfFilters, filterArr);
const checkAll = this.isCheckAll(newListOfItems);
const newSaveItems = this.changeItemsCheckedProperty(this.state.metadataList, filterArr);
this.setState({
checkAll,
listOfFilters: newListOfItems,
metadataList: newSaveItems,
});
}
/**
* change specified items property of 'checked' in old list, return new list of items
* @param arr: `IFilters[]` old list of items
* @param filterArr: `IFilters[]` specified items
*/
private changeItemsCheckedProperty(arr: IFilters[], filterArr: IFilters[]) {
const newArr = [...arr];
for (let i = 0; i < newArr.length; i++) {
for (let j = 0; j < filterArr.length; j++) {
let item: IFilters = newArr[i];
if (newArr[i].id === filterArr[j].id) {
item = {
...newArr[i],
checked: filterArr[j].checked,
};
// replace selected item
newArr.splice(i, 1, item);
}
}
}
return newArr;
}
/**
* * if value equal true, there are some item are unselected
* * if value equal false, all item are selected
* @param itemList: `IFilters[]` List of data to traverse
*/
private isCheckAll(itemList: IFilters[]) {
const unSelectedItems = itemList.filter(e => e.checked === false);
if (unSelectedItems.length === 0 && itemList.length !== 0) {
return true;
}
return false;
}
/** save machineSn filters selected by the user, update metadata, close dialog */
private saveFilters() {
if (this.props.filterUpdate) {
this.props.filterUpdate(this.state.metadataList);
}
this.setState({
displayDialog: false,
queryInputValue: "",
});
}
/** close dialog of machineSn filters, reset state */
private cancelOperation() {
let checkAll = false;
let listOfFilters: IFilters[] = [];
let metadataList: IFilters[] = [];
if (this.props.filters) {
checkAll = this.isCheckAll(this.props.filters);
listOfFilters = this.props.filters;
metadataList = this.props.filters;
}
this.setState({
checkAll,
displayDialog: false,
listOfFilters,
queryInputValue: "",
metadataList,
});
}
}
scss
%filter-input {
width: 1rem;
height: 1rem;
cursor: pointer;
}
%filter-input-label {
margin-left: 0.6rem;
cursor: pointer;
}
// flex layout
%filter-flex {
display: flex;
align-items: center;
justify-content: space-between;
}
// button color
$filter-color: #dcdcdc;
// border width
$filter-width: 0.1rem;
.filters {
margin-top: 2rem;
position: relative;
label {
margin: 0;
}
// switch button
.filter-word {
cursor: pointer;
width: 10rem;
padding: 0.6rem 1rem;
border: $filter-width solid black;
font-size: 1.125rem;
@extend %filter-flex;
.filter-arrow {
width: 1.125rem;
height: 1.125rem;
}
.filter-arrowUp {
transform: rotate(180deg);
}
}
// dialog
.filter-dialog {
display: block;
background-color: white;
width: 26rem;
height: 25rem;
border: $filter-width solid black;
position: absolute;
z-index: 5;
top: 3.1rem;
left: 0;
text-align: center;
// check all
.filter-checkAll-label {
@extend %filter-flex;
height: 3rem;
padding: 0 1rem;
border-bottom: $filter-width solid $filter-color;
.filter-checkAll-image {
@extend %filter-input;
}
.filter-checkAll-word {
@extend %filter-input-label;
}
// close icon
.filter-closeIcon {
display: inline-block;
width: 2rem;
height: 2rem;
cursor: pointer;
}
}
// input
.filter-queryInput {
line-height: 3rem;
text-align: left;
padding-left: 1rem;
border-bottom: $filter-width solid $filter-color;
.filter-queryInput-input {
height: 2rem;
outline: none;
border: $filter-width solid #d9d9d9;
padding-left: 2rem;
box-sizing: border-box;
border-radius: 0.4rem;
&:hover {
outline: none;
border: $filter-width solid #40a9ff;
}
&:focus {
outline: none;
border: $filter-width solid #40a9ff;
box-shadow: 0 0 0 0.12rem rgba(24, 144, 255, 0.2);
}
}
}
// checkbox
.filter-checkbox-part {
height: 15rem;
overflow-y: auto;
border-bottom: $filter-width solid $filter-color;
.filter-checkbox {
display: flex;
padding: 0.7em 0 0.8rem 1rem;
align-items: center;
&:hover {
background-color: $filter-color;
}
.filter-checkbox-image {
@extend %filter-input;
}
.filter-checkbox-word {
@extend %filter-input-label;
}
}
}
// button(save/cancel)
.filter-button {
margin-top: 1rem;
.filter-button-save,
.filter-button-cancel {
border: 0;
background-color: $filter-color;
margin: 0 1rem;
padding: $filter-width 1rem;
width: 6rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
&:hover {
background-color: #cfcfcf;
}
}
}
}
.filter-dialog-hidden {
display: none;
}
}
svg
arrow.svg
checkIcon.svg
closeIcon.svg
unCheckIcon.svg