ag-grid
Version:
Advanced Javascript Datagrid. Supports raw Javascript, AngularJS 1.x, AngularJS 2.0 and Web Components
414 lines (357 loc) • 16.2 kB
text/typescript
/// <reference path="../utils.ts" />
/// <reference path="setFilterModel.ts" />
/// <reference path="setFilterParameters.ts" />
module ag.grid {
var _ = Utils;
var template =
'<div>'+
'<div class="ag-filter-header-container">'+
'<input class="ag-filter-filter" type="text" placeholder="[SEARCH...]"/>'+
'</div>'+
'<div class="ag-filter-header-container">'+
'<label>'+
'<input id="selectAll" type="checkbox" class="ag-filter-checkbox"/>'+
'([SELECT ALL])'+
'</label>'+
'</div>'+
'<div class="ag-filter-list-viewport">'+
'<div class="ag-filter-list-container">'+
'<div id="itemForRepeat" class="ag-filter-item">'+
'<label>'+
'<input type="checkbox" class="ag-filter-checkbox" filter-checkbox="true"/>'+
'<span class="ag-filter-value"></span>'+
'</label>'+
'</div>'+
'</div>'+
'</div>'+
'<div class="ag-filter-apply-panel" id="applyPanel">'+
'<button type="button" id="applyButton">[APPLY FILTER]</button>' +
'</div>'+
'</div>';
var DEFAULT_ROW_HEIGHT = 20;
export class SetFilter implements Filter {
private eGui: any;
private filterParams: any;
private rowHeight: any;
private model: any;
private filterChangedCallback: any;
private filterModifiedCallback: any;
private valueGetter: any;
private rowsInBodyContainer: any;
private colDef: any;
private localeTextFunc: any;
private cellRenderer: any;
private eListContainer: any;
private eFilterValueTemplate: any;
private eSelectAll: any;
private eListViewport: any;
private eMiniFilter: any;
private api: any;
private applyActive: any;
private eApplyButton: any;
public init(params: any): void {
this.filterParams = params.filterParams;
this.rowHeight = (this.filterParams && this.filterParams.cellHeight) ? this.filterParams.cellHeight : DEFAULT_ROW_HEIGHT;
this.applyActive = this.filterParams && this.filterParams.apply == true;
this.model = new SetFilterModel(params.colDef, params.rowModel, params.valueGetter, params.doesRowPassOtherFilter);
this.filterChangedCallback = params.filterChangedCallback;
this.filterModifiedCallback = params.filterModifiedCallback;
this.valueGetter = params.valueGetter;
this.rowsInBodyContainer = {};
this.colDef = params.colDef;
this.localeTextFunc = params.localeTextFunc;
if (this.filterParams) {
this.cellRenderer = this.filterParams.cellRenderer;
}
this.createGui();
this.addScrollListener();
this.createApi();
}
// we need to have the gui attached before we can draw the virtual rows, as the
// virtual row logic needs info about the gui state
public afterGuiAttached(): void {
this.drawVirtualRows();
}
public isFilterActive(): boolean {
return this.model.isFilterActive();
}
public doesFilterPass(node: any): boolean {
// if no filter, always pass
if (this.model.isEverythingSelected()) {
return true;
}
// if nothing selected in filter, always fail
if (this.model.isNothingSelected()) {
return false;
}
var value = this.valueGetter(node);
value = _.makeNull(value);
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) {
if (this.model.isValueSelected(value[i])) {
return true
}
}
return false
} else {
return this.model.isValueSelected(value);
}
}
public getGui(): any {
return this.eGui;
}
public onNewRowsLoaded(): void {
var keepSelection = this.filterParams && this.filterParams.newRowsAction === 'keep';
var isSelectAll = this.eSelectAll && this.eSelectAll.checked && !this.eSelectAll.indeterminate;
// default is reset
this.model.refreshAfterNewRowsLoaded(keepSelection, isSelectAll);
this.setContainerHeight();
this.refreshVirtualRows();
}
public onAnyFilterChanged(): void {
this.model.refreshAfterAnyFilterChanged();
this.setContainerHeight();
this.refreshVirtualRows();
}
private createTemplate() {
return template
.replace('[SELECT ALL]', this.localeTextFunc('selectAll', 'Select All'))
.replace('[SEARCH...]', this.localeTextFunc('searchOoo', 'Search...'))
.replace('[APPLY FILTER]', this.localeTextFunc('applyFilter', 'Apply Filter'));
}
private createGui() {
var _this = this;
this.eGui = _.loadTemplate(this.createTemplate());
this.eListContainer = this.eGui.querySelector(".ag-filter-list-container");
this.eFilterValueTemplate = this.eGui.querySelector("#itemForRepeat");
this.eSelectAll = this.eGui.querySelector("#selectAll");
this.eListViewport = this.eGui.querySelector(".ag-filter-list-viewport");
this.eMiniFilter = this.eGui.querySelector(".ag-filter-filter");
this.eListContainer.style.height = (this.model.getUniqueValueCount() * this.rowHeight) + "px";
this.setContainerHeight();
this.eMiniFilter.value = this.model.getMiniFilter();
_.addChangeListener(this.eMiniFilter, function () {
_this.onMiniFilterChanged();
});
_.removeAllChildren(this.eListContainer);
this.eSelectAll.onclick = this.onSelectAll.bind(this);
if (this.model.isEverythingSelected()) {
this.eSelectAll.indeterminate = false;
this.eSelectAll.checked = true;
} else if (this.model.isNothingSelected()) {
this.eSelectAll.indeterminate = false;
this.eSelectAll.checked = false;
} else {
this.eSelectAll.indeterminate = true;
}
this.setupApply();
}
private setupApply() {
if (this.applyActive) {
this.eApplyButton = this.eGui.querySelector('#applyButton');
this.eApplyButton.addEventListener('click', () => {
this.filterChangedCallback();
});
} else {
_.removeElement(this.eGui, '#applyPanel');
}
}
private setContainerHeight() {
this.eListContainer.style.height = (this.model.getDisplayedValueCount() * this.rowHeight) + "px";
}
private drawVirtualRows() {
var topPixel = this.eListViewport.scrollTop;
var bottomPixel = topPixel + this.eListViewport.offsetHeight;
var firstRow = Math.floor(topPixel / this.rowHeight);
var lastRow = Math.floor(bottomPixel / this.rowHeight);
this.ensureRowsRendered(firstRow, lastRow);
}
private ensureRowsRendered(start: any, finish: any) {
var _this = this;
//at the end, this array will contain the items we need to remove
var rowsToRemove = Object.keys(this.rowsInBodyContainer);
//add in new rows
for (var rowIndex = start; rowIndex <= finish; rowIndex++) {
//see if item already there, and if yes, take it out of the 'to remove' array
if (rowsToRemove.indexOf(rowIndex.toString()) >= 0) {
rowsToRemove.splice(rowsToRemove.indexOf(rowIndex.toString()), 1);
continue;
}
//check this row actually exists (in case overflow buffer window exceeds real data)
if (this.model.getDisplayedValueCount() > rowIndex) {
var value = this.model.getDisplayedValue(rowIndex);
_this.insertRow(value, rowIndex);
}
}
//at this point, everything in our 'rowsToRemove' . . .
this.removeVirtualRows(rowsToRemove);
}
//takes array of row id's
private removeVirtualRows(rowsToRemove: any) {
var _this = this;
rowsToRemove.forEach(function (indexToRemove: any) {
var eRowToRemove = _this.rowsInBodyContainer[indexToRemove];
_this.eListContainer.removeChild(eRowToRemove);
delete _this.rowsInBodyContainer[indexToRemove];
});
}
private insertRow(value: any, rowIndex: any) {
var _this = this;
var eFilterValue = this.eFilterValueTemplate.cloneNode(true);
var valueElement = eFilterValue.querySelector(".ag-filter-value");
if (this.cellRenderer) {
//renderer provided, so use it
var resultFromRenderer = this.cellRenderer({
value: value
});
if (_.isNode(resultFromRenderer)) {
//a dom node or element was returned, so add child
valueElement.appendChild(resultFromRenderer);
} else {
//otherwise assume it was html, so just insert
valueElement.innerHTML = resultFromRenderer;
}
} else {
//otherwise display as a string
var blanksText = '(' + this.localeTextFunc('blanks', 'Blanks') + ')';
var displayNameOfValue = value === null ? blanksText : value;
valueElement.innerHTML = displayNameOfValue;
}
var eCheckbox = eFilterValue.querySelector("input");
eCheckbox.checked = this.model.isValueSelected(value);
eCheckbox.onclick = function () {
_this.onCheckboxClicked(eCheckbox, value);
};
eFilterValue.style.top = (this.rowHeight * rowIndex) + "px";
this.eListContainer.appendChild(eFilterValue);
this.rowsInBodyContainer[rowIndex] = eFilterValue;
}
private onCheckboxClicked(eCheckbox: any, value: any) {
var checked = eCheckbox.checked;
if (checked) {
this.model.selectValue(value);
if (this.model.isEverythingSelected()) {
this.eSelectAll.indeterminate = false;
this.eSelectAll.checked = true;
} else {
this.eSelectAll.indeterminate = true;
}
} else {
this.model.unselectValue(value);
//if set is empty, nothing is selected
if (this.model.isNothingSelected()) {
this.eSelectAll.indeterminate = false;
this.eSelectAll.checked = false;
} else {
this.eSelectAll.indeterminate = true;
}
}
this.filterChanged();
}
private filterChanged() {
this.filterModifiedCallback();
if (!this.applyActive) {
this.filterChangedCallback();
}
}
private onMiniFilterChanged() {
var miniFilterChanged = this.model.setMiniFilter(this.eMiniFilter.value);
if (miniFilterChanged) {
this.setContainerHeight();
this.refreshVirtualRows();
}
}
private refreshVirtualRows() {
this.clearVirtualRows();
this.drawVirtualRows();
}
private clearVirtualRows() {
var rowsToRemove = Object.keys(this.rowsInBodyContainer);
this.removeVirtualRows(rowsToRemove);
}
private onSelectAll() {
var checked = this.eSelectAll.checked;
if (checked) {
this.model.selectEverything();
} else {
this.model.selectNothing();
}
this.updateAllCheckboxes(checked);
this.filterChanged();
}
private updateAllCheckboxes(checked: any) {
var currentlyDisplayedCheckboxes: any = this.eListContainer.querySelectorAll("[filter-checkbox=true]");
for (var i = 0, l = currentlyDisplayedCheckboxes.length; i < l; i++) {
currentlyDisplayedCheckboxes[i].checked = checked;
}
}
private addScrollListener() {
var _this = this;
this.eListViewport.addEventListener("scroll", function () {
_this.drawVirtualRows();
});
}
public getApi() {
return this.api;
}
private createApi() {
var model = this.model;
var that = this;
this.api = {
setMiniFilter: function (newMiniFilter: any) {
model.setMiniFilter(newMiniFilter);
},
getMiniFilter: function () {
return model.getMiniFilter();
},
selectEverything: function () {
that.eSelectAll.indeterminate = false;
that.eSelectAll.checked = true;
// not sure if we need to call this, as checking the checkout above might
// fire events.
model.selectEverything();
},
isFilterActive: function () {
return model.isFilterActive();
},
selectNothing: function () {
that.eSelectAll.indeterminate = false;
that.eSelectAll.checked = false;
// not sure if we need to call this, as checking the checkout above might
// fire events.
model.selectNothing();
},
unselectValue: function (value: any) {
model.unselectValue(value);
that.refreshVirtualRows();
},
selectValue: function (value: any) {
model.selectValue(value);
that.refreshVirtualRows();
},
isValueSelected: function (value: any) {
return model.isValueSelected(value);
},
isEverythingSelected: function () {
return model.isEverythingSelected();
},
isNothingSelected: function () {
return model.isNothingSelected();
},
getUniqueValueCount: function () {
return model.getUniqueValueCount();
},
getUniqueValue: function (index: any) {
return model.getUniqueValue(index);
},
getModel: function () {
return model.getModel();
},
setModel: function (dataModel: any) {
model.setModel(dataModel);
that.refreshVirtualRows();
}
};
}
}
}