Skip to content

Commit

Permalink
Fixed #380 - Filtering for TreeTable
Browse files Browse the repository at this point in the history
  • Loading branch information
mertsincan committed Feb 12, 2019
1 parent 375a545 commit 39aa154
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 3 deletions.
155 changes: 154 additions & 1 deletion src/components/treetable/TreeTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export class TreeTable extends Component {
resizableColumns: false,
columnResizeMode: 'fit',
emptyMessage: "No records found",
filters: null,
filterMode: 'lenient',
onFilter: null,
onExpand: null,
onCollapse: null,
onToggle: null,
Expand Down Expand Up @@ -126,6 +129,9 @@ export class TreeTable extends Component {
resizableColumns: PropTypes.bool,
columnResizeMode: PropTypes.string,
emptyMessage: PropTypes.string,
filters: PropTypes.object,
filterMode: PropTypes.string,
onFilter: PropTypes.func,
onExpand: PropTypes.func,
onCollapse: PropTypes.func,
onToggle: PropTypes.func,
Expand Down Expand Up @@ -162,13 +168,18 @@ export class TreeTable extends Component {
state.multiSortMeta = props.multiSortMeta;
}

if (!this.props.onFilter) {
state.filters = props.filters;
}

if (Object.keys(state).length) {
this.state = state;
}

this.onToggle = this.onToggle.bind(this);
this.onPageChange = this.onPageChange.bind(this);
this.onSort = this.onSort.bind(this);
this.onFilter = this.onFilter.bind(this);
this.onColumnResizeStart = this.onColumnResizeStart.bind(this);
this.onColumnDragStart = this.onColumnDragStart.bind(this);
this.onColumnDragOver = this.onColumnDragOver.bind(this);
Expand Down Expand Up @@ -341,6 +352,58 @@ export class TreeTable extends Component {
return (multiSortMeta[index].order * result);
}

filter(value, field, mode) {
this.onFilter({
value: value,
field: field,
matchMode: mode
});
}

onFilter(event) {
let currentFilters = this.getFilters();
let newFilters = currentFilters ? {...currentFilters} : {};

if(!this.isFilterBlank(event.value))
newFilters[event.field] = {value: event.value, matchMode: event.matchMode};
else if(newFilters[event.field])
delete newFilters[event.field];

if (this.props.onFilter) {
this.props.onFilter({
filters: newFilters
});
}
else {
this.setState({
first: 0,
filters: newFilters
});
}

if (this.props.onValueChange) {
this.props.onValueChange(this.processData({
filters: newFilters
}));
}
}

hasFilter() {
let filters = this.getFilters();

return filters && Object.keys(filters).length > 0;
}

isFilterBlank(filter) {
if(filter !== null && filter !== undefined) {
if((typeof filter === 'string' && filter.trim().length === 0) || (filter instanceof Array && filter.length === 0))
return true;
else
return false;
}
return true;
}

onColumnResizeStart(event) {
let containerLeft = DomHandler.getOffset(this.container).left;
this.resizeColumn = event.columnEl;
Expand Down Expand Up @@ -615,6 +678,10 @@ export class TreeTable extends Component {
return this.props.onSort ? this.props.multiSortMeta : this.state.multiSortMeta;
}

getFilters() {
return this.props.onFilter ? this.props.filters : this.state.filters;
}

findColumnByKey(columns, key) {
if(columns && columns.length) {
for(let i = 0; i < columns.length; i++) {
Expand Down Expand Up @@ -686,6 +753,86 @@ export class TreeTable extends Component {
return scrollableColumns;
}

filterLocal(value) {
let filteredNodes = [];
let filters = this.getFilters();
let columns = React.Children.toArray(this.props.children);
const isStrictMode = this.props.filterMode === 'strict';

for(let node of value) {
let copyNode = {...node};
let localMatch = true;

for(let j = 0; j < columns.length; j++) {
let col = columns[j];
let filterMeta = filters ? filters[col.props.field] : null;
let filterField = col.props.field;
let filterValue, filterConstraint, paramsWithoutNode;

//local
if(filterMeta) {
let filterMatchMode = filterMeta.matchMode||col.props.filterMatchMode;
filterValue = filterMeta.value;
filterConstraint = filterMatchMode === 'custom' ? col.props.filterFunction : ObjectUtils.filterConstraints[filterMatchMode];
paramsWithoutNode = {filterField, filterValue, filterConstraint, isStrictMode};
if ((isStrictMode && !(this.findFilteredNodes(copyNode, paramsWithoutNode) || this.isFilterMatched(copyNode, paramsWithoutNode))) ||
(!isStrictMode && !(this.isFilterMatched(copyNode, paramsWithoutNode) || this.findFilteredNodes(copyNode, paramsWithoutNode)))) {
localMatch = false;
}

if(!localMatch) {
break;
}
}
}

if(localMatch) {
filteredNodes.push(copyNode);
}
}

return filteredNodes.length ? filteredNodes : value;
}

findFilteredNodes(node, paramsWithoutNode) {
if (node) {
let matched = false;
if (node.children) {
let childNodes = [...node.children];
node.children = [];
for (let childNode of childNodes) {
let copyChildNode = {...childNode};
if (this.isFilterMatched(copyChildNode, paramsWithoutNode)) {
matched = true;
node.children.push(copyChildNode);
}
}
}

if (matched) {
return true;
}
}
}

isFilterMatched(node, {filterField, filterValue, filterConstraint, isStrictMode}) {
let matched = false;
let dataFieldValue = ObjectUtils.resolveFieldData(node.data, filterField);
if(filterConstraint(dataFieldValue, filterValue)) {
matched = true;
}

if (!matched || (isStrictMode && !this.isNodeLeaf(node))) {
matched = this.findFilteredNodes(node, {filterField, filterValue, filterConstraint, isStrictMode}) || matched;
}

return matched;
}

isNodeLeaf(node) {
return node.leaf === false ? false : !(node.children && node.children.length);
}

processValue() {
let data = this.props.value;

Expand All @@ -697,6 +844,11 @@ export class TreeTable extends Component {
else if(this.props.sortMode === 'multiple')
data = this.sortMultiple(data);
}

let localFilters = this.getFilters();
if (localFilters) {
data = this.filterLocal(data, localFilters);
}
}
}

Expand All @@ -709,7 +861,8 @@ export class TreeTable extends Component {
onSort={this.onSort} sortField={this.getSortField()} sortOrder={this.getSortOrder()} multiSortMeta={this.getMultiSortMeta()}
resizableColumns={this.props.resizableColumns} onResizeStart={this.onColumnResizeStart}
reorderableColumns={this.props.reorderableColumns} onDragStart={this.onColumnDragStart}
onDragOver={this.onColumnDragOver} onDragLeave={this.onColumnDragLeave} onDrop={this.onColumnDrop} />
onDragOver={this.onColumnDragOver} onDragLeave={this.onColumnDragLeave} onDrop={this.onColumnDrop}
onFilter={this.onFilter} filters={this.getFilters()}/>
);
}

Expand Down
33 changes: 31 additions & 2 deletions src/components/treetable/TreeTableHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import DomHandler from '../utils/DomHandler';
import {InputText} from '../inputtext/InputText';

export class TreeTableHeader extends Component {

Expand All @@ -18,7 +19,8 @@ export class TreeTableHeader extends Component {
onDragStart: null,
onDragOver: null,
onDragLeave: null,
onDrop: null
onDrop: null,
onFilter: null
}

static propsTypes = {
Expand All @@ -34,13 +36,15 @@ export class TreeTableHeader extends Component {
onDragStart: PropTypes.func,
onDragOver: PropTypes.func,
onDragLeave: PropTypes.func,
onDrop: PropTypes.func
onDrop: PropTypes.func,
onFilter: PropTypes.func
}

constructor(props) {
super(props);

this.onHeaderMouseDown = this.onHeaderMouseDown.bind(this);
this.onFilterInput = this.onFilterInput.bind(this);
}

onHeaderClick(event, column) {
Expand Down Expand Up @@ -99,6 +103,24 @@ export class TreeTableHeader extends Component {
}
}

onFilterInput(e, column) {
if(column.props.filter && this.props.onFilter) {
if(this.filterTimeout) {
clearTimeout(this.filterTimeout);
}

let filterValue = e.target.value;
this.filterTimeout = setTimeout(() => {
this.props.onFilter({
value: filterValue,
field: column.props.field,
matchMode: column.props.filterMatchMode
});
this.filterTimeout = null;
}, this.filterDelay);
}
}

renderSortIcon(column, sorted, sortOrder) {
if (column.props.sortable) {
const sortIcon = sorted ? sortOrder < 0 ? 'pi-sort-down' : 'pi-sort-up': 'pi-sort';
Expand Down Expand Up @@ -130,6 +152,7 @@ export class TreeTableHeader extends Component {
const multipleSorted = multiSortMetaData !== null;
const sorted = column.props.sortable && (singleSorted || multipleSorted);
let sortOrder = 0;
let filterElement;

if(singleSorted)
sortOrder = this.props.sortOrder;
Expand All @@ -144,6 +167,11 @@ export class TreeTableHeader extends Component {
'p-resizable-column': this.props.resizableColumns
});

if(column.props.filter) {
filterElement = column.props.filterElement||<InputText onInput={(e) => this.onFilterInput(e, column)} type={this.props.filterType} defaultValue={this.props.filters && this.props.filters[this.props.field] ? this.props.filters[this.props.field].value : null}
className="p-column-filter" placeholder={column.props.filterPlaceholder} maxLength={column.props.filterMaxLength}/>;
}

const resizer = this.renderResizer(column);

return (
Expand All @@ -154,6 +182,7 @@ export class TreeTableHeader extends Component {
{resizer}
<span className="p-column-title">{column.props.header}</span>
{sortIconElement}
{filterElement}
</th>
);
}
Expand Down

0 comments on commit 39aa154

Please sign in to comment.