1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React, { useEffect } from 'react';
20 } from '@material-ui/core';
21 import classnames from 'classnames';
22 import { DefaultTransformOrigin } from 'components/popover/helpers';
23 import { createTree } from 'models/tree';
24 import { DataTableFilters, DataTableFiltersTree } from './data-table-multiselect-tree';
25 import { getNodeDescendants } from 'models/tree';
26 import debounce from 'lodash/debounce';
27 import { green, grey } from '@material-ui/core/colors';
29 export type CssRules = 'root' | 'icon' | 'iconButton' | 'active' | 'checkbox';
31 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
33 // border: '1px dashed green',
37 backgroundColor: grey[200],
40 color: theme.palette.text.primary,
44 color: theme.palette.text.primary,
50 // border: '1px solid red',
55 color: theme.palette.text.primary,
59 color: theme.palette.text.primary,
75 export interface DataTableFilterProps {
77 filters: DataTableFilters;
78 onChange?: (filters: DataTableFilters) => void;
81 * When set to true, only one filter can be selected at a time.
83 mutuallyExclusive?: boolean;
86 * By default `all` filters selection means that label should be grayed out.
87 * Use `none` when label is supposed to be grayed out when no filter is selected.
89 defaultSelection?: SelectionMode;
92 interface DataTableFilterState {
93 anchorEl?: HTMLElement;
94 filters: DataTableFilters;
95 prevFilters: DataTableFilters;
98 export const DataTableMultiselectPopover = withStyles(styles)(
99 class extends React.Component<DataTableFilterProps & WithStyles<CssRules>, DataTableFilterState> {
100 state: DataTableFilterState = {
102 filters: createTree(),
103 prevFilters: createTree(),
105 icon = React.createRef<HTMLElement>();
108 const { name, classes, defaultSelection = SelectionMode.ALL, children } = this.props;
109 const isActive = getNodeDescendants('')(this.state.filters).some((f) => (defaultSelection === SelectionMode.ALL ? !f.selected : f.selected));
112 <Tooltip disableFocusListener title='Multiselect Actions'>
113 <ButtonBase className={classnames([classes.root, { [classes.active]: isActive }])} component='span' onClick={this.open} disableRipple>
115 <IconButton component='span' classes={{ root: classes.iconButton }} tabIndex={-1}>
116 <i className={classnames(['fas fa-sort-down', classes.icon])} data-fa-transform='shrink-3' ref={this.icon} />
121 anchorEl={this.state.anchorEl}
122 open={!!this.state.anchorEl}
123 anchorOrigin={DefaultTransformOrigin}
124 transformOrigin={DefaultTransformOrigin}
129 <Typography variant='caption'>{'foo'}</Typography>
131 <DataTableFiltersTree filters={this.state.filters} mutuallyExclusive={this.props.mutuallyExclusive} onChange={this.onChange} />
132 {this.props.mutuallyExclusive || (
134 <Button color='primary' variant='outlined' size='small' onClick={this.close}>
141 <this.MountHandler />
146 static getDerivedStateFromProps(props: DataTableFilterProps, state: DataTableFilterState): DataTableFilterState {
147 return props.filters !== state.prevFilters ? { ...state, filters: props.filters, prevFilters: props.filters } : state;
151 this.setState({ anchorEl: this.icon.current || undefined });
154 onChange = (filters) => {
155 this.setState({ filters });
156 if (this.props.mutuallyExclusive) {
157 // Mutually exclusive filters apply immediately
158 const { onChange } = this.props;
164 // Non-mutually exclusive filters are debounced
169 submit = debounce(() => {
170 const { onChange } = this.props;
172 onChange(this.state.filters);
176 MountHandler = () => {
179 this.submit.cancel();
186 this.setState((prev) => ({