Created
August 14, 2019 10:42
-
-
Save johnspackman/42e7d5e746ad69ad346c7f1c22c1c913 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Header cell widget for filtering | |
* | |
* @childControl label {qx.ui.basic.Label} label of the header cell | |
* @childControl icon {qx.ui.basic.Image} icon of the header cell | |
* | |
* @asset(grasshopper/decoration/table/16/*) | |
* @asset(grasshopper/icon/16/tick.png) | |
* @asset(grasshopper/icon/16/cross.png) | |
*/ | |
qx.Class.define("grasshopper.af.table.filter.FilterHeaderCell", { | |
extend: qx.ui.container.Composite, | |
construct: function() { | |
this.base(arguments); | |
var layout = new qx.ui.layout.Canvas(); | |
this.setLayout(layout); | |
this._add(this.getChildControl("label"), { top: 0, left: 0, right: 0 }); | |
this._add(this.getChildControl("filter-icon"), { bottom: 0, right: 0 }); | |
}, | |
properties: { | |
appearance: { | |
refine: true, | |
init: "table-filter-header-cell" | |
}, | |
/** header cell label */ | |
label: { | |
check: "String", | |
init: null, | |
nullable: true, | |
apply: "_applyLabel" | |
}, | |
cellInfo: { | |
nullable: false, | |
apply: "_applyCellInfo" | |
}, | |
filtered: { | |
check: "Boolean", | |
init: false, | |
nullable: false, | |
apply: "_applyFiltered" | |
}, | |
sorted: { | |
check: [ "asc", "desc" ], | |
init: null, | |
nullable: true, | |
apply: "_applySorted" | |
}, | |
dataType: { | |
check: [ "string", "number", "date" ], | |
init: null, | |
nullable: true | |
}, | |
caseInsensitive: { | |
check: "Boolean", | |
init: true, | |
nullable: false | |
}, | |
searchable: { | |
check: "Boolean", | |
init: true, | |
nullable: false | |
}, | |
dateFormat: { | |
check: "qx.util.format.DateFormat", | |
init: null, | |
nullable: true | |
} | |
}, | |
members: { | |
_applyCellInfo: function(cellInfo) { | |
var label = (cellInfo.name && cellInfo.name.translate ? cellInfo.name.translate() : cellInfo.name)||""; | |
this.setLabel(label.replace(/\n/g, "<br>")); | |
this.setSorted(cellInfo.sorted ? (cellInfo.sortedAscending ? "asc" : "desc") : null); | |
}, | |
// property apply | |
_applyLabel: function(value, old) { | |
if (value) { | |
this._showChildControl("label").setValue(value); | |
} else { | |
this._excludeChildControl("label"); | |
} | |
}, | |
_applyFiltered: function(value, oldValue) { | |
if (value) | |
this.addState("filtered"); | |
else | |
this.removeState("filtered"); | |
this.__updateIcon(); | |
}, | |
_applySorted: function(value, oldValue) { | |
if (!value) { | |
this.removeState("sorted"); | |
this.removeState("sortedAsc"); | |
this.removeState("sortedDesc"); | |
} else if (value == "asc") { | |
this.addState("sorted"); | |
this.removeState("sortedDesc"); | |
this.addState("sortedAsc"); | |
} else { | |
this.addState("sorted"); | |
this.removeState("sortedAsc"); | |
this.addState("sortedDesc"); | |
} | |
this.__updateIcon(); | |
}, | |
__updateIcon: function() { | |
var filtered = this.getFiltered(); | |
var sorted = this.getSorted(); | |
var str = filtered ? "filtered" : "unfiltered"; | |
str += "-"; | |
str += !sorted ? "unsorted" : sorted; | |
this.getChildControl("filter-icon").setIcon("grasshopper/decoration/table/16/" + str + ".png"); | |
}, | |
open: function() { | |
this.getChildControl("search").setValue(""); | |
var popup = this.getChildControl("popup"); | |
popup.placeToWidget(this, true); | |
popup.show(); | |
this.addState("open"); | |
}, | |
close: function() { | |
var popup = this.getChildControl("popup"); | |
popup.hide(); | |
}, | |
toggle: function() { | |
if (this.getChildControl("popup").isVisible()) | |
this.close(); | |
else | |
this.open(); | |
}, | |
_onPopupHide: function() { | |
this.removeState("open"); | |
}, | |
_onFilterClick: function() { | |
if (this.getChildControl("popup").isVisible()) { | |
this.close(); | |
return; | |
} | |
var lst = this.getChildControl("list"); | |
var cellInfo = this.getCellInfo(); | |
var tm = cellInfo.table.getTableModel(); | |
function createLookup(rows) { | |
var lookup = {}; | |
rows.forEach(row => { | |
var cell = row[cellInfo.col]; | |
var value = cell.filter(); | |
if (lookup[value] === undefined) | |
lookup[value] = cell.text(); | |
}); | |
return lookup; | |
} | |
var lookup = createLookup(tm.getUnfiltered()); | |
var lookupArray = Object.keys(lookup); | |
if (lookupArray.length > 2000) | |
lookupArray.splice(2000, lookupArray.length - 2000); | |
lookupArray.sort((l, r) => { | |
l = l.toLowerCase(); | |
r = r.toLowerCase(); | |
return l < r ? -1 : l > r ? 1 : 0; | |
}); | |
var filteredLookup = createLookup(tm.getData()); | |
var pool = tm.getCheckboxPool(); | |
var children = lst.getChildren(); | |
lookupArray.forEach((key, index) => { | |
var cbx = null; | |
if (index < children.length) | |
cbx = children[index]; | |
else { | |
cbx = pool.getObject(qx.ui.form.CheckBox); | |
if (cbx == null) | |
cbx = new qx.ui.form.CheckBox(key); | |
lst.add(cbx); | |
} | |
cbx.set({ | |
label: key, | |
value: !!filteredLookup[key] | |
}); | |
}); | |
var len = lookupArray.length; | |
while (children.length > len) { | |
var item = children[children.length - 1]; | |
lst.remove(item); | |
pool.poolObject(item); | |
} | |
this.open(); | |
}, | |
_onSearch: function(str) { | |
str = (str||"").toLowerCase().trim(); | |
var lst = this.getChildControl("list"); | |
qx.lang.Array.clone(lst.getChildren()).forEach(item => { | |
if (item.getLabel().toLowerCase().indexOf(str) > -1) | |
item.setVisibility("visible"); | |
else | |
item.setVisibility("excluded"); | |
}); | |
}, | |
onFilter: function(row) { | |
if (this.__lookupFilter === null) | |
return true; | |
var cellInfo = this.getCellInfo(); | |
var cell = row[cellInfo.col]; | |
var value = cell.filter(); | |
return !!this.__lookupFilter[value]; | |
}, | |
__lookupFilter: null, | |
_onApplyFilter: function() { | |
this.getChildControl("popup").hide(); | |
var lst = this.getChildControl("list"); | |
var cellInfo = this.getCellInfo(); | |
var lookup = this.__lookupFilter = {}; | |
var hasUnchecked = false; | |
lst.getChildren().forEach(item => { | |
if (item.getValue()) | |
lookup[item.getLabel()] = true; | |
else | |
hasUnchecked = true; | |
}); | |
if (!hasUnchecked) | |
this.__lookupFilter = null; | |
this.setFiltered(hasUnchecked); | |
cellInfo.table.getTableModel().updateFilters(); | |
}, | |
_onCancelFilter: function() { | |
this.getChildControl("popup").hide(); | |
}, | |
_onSelectAll: function() { | |
var lst = this.getChildControl("list"); | |
lst.getChildren().forEach(item => { | |
item.setValue(true); | |
}); | |
}, | |
_onSelectNone: function() { | |
var lst = this.getChildControl("list"); | |
lst.getChildren().forEach(item => { | |
item.setValue(false); | |
}); | |
}, | |
comparatorAscending: function(l, r, lrow, rrow, i) { | |
function get(cell) { | |
var value = cell.sortValue !== undefined ? cell.sortValue : cell.value; | |
if (typeof value == "function") | |
value = value.call(cell); | |
return value; | |
} | |
l = get(l); | |
r = get(r); | |
if (this.getDataType() === "string" && this.isCaseInsensitive()) { | |
l = l.toLowerCase(); | |
r = r.toLowerCase(); | |
} | |
return l < r ? -1 : l > r ? 1 : 0; | |
}, | |
comparatorDescending: function(l, r, lrow, rrow, i) { | |
return this.comparatorAscending(l, r, i) * -1; | |
}, | |
// overridden | |
_createChildControlImpl: function(id, hash) { | |
switch (id) { | |
case "label": | |
var control = new qx.ui.basic.Label(this.getLabel()).set({ | |
anonymous: true, | |
allowShrinkX: true, | |
rich: true | |
}); | |
return control; | |
case "popup": | |
var pop = new qx.ui.popup.Popup(new qx.ui.layout.VBox()).set({ | |
autoHide: false, | |
keepActive: true | |
}); | |
pop.add(this.getChildControl("toolbar")); | |
pop.add(this.getChildControl("search")); | |
pop.add(this.getChildControl("listScroll"), { flex: 1 }); | |
pop.addListener("close", this._onPopupHide, this); | |
return pop; | |
case "toolbar": | |
var tb = new qx.ui.toolbar.ToolBar(); | |
tb.add(this.getChildControl("btnApply")); | |
tb.add(this.getChildControl("btnCancel")); | |
tb.add(this.getChildControl("btnSelectAll")); | |
tb.add(this.getChildControl("btnSelectNone")); | |
return tb; | |
case "btnApply": | |
var btn = new qx.ui.toolbar.Button("Apply", "grasshopper/icon/16/tick.png").set({ appearance: "toolbar-slim-button" }); | |
btn.addListener("execute", () => this._onApplyFilter()); | |
return btn; | |
case "btnCancel": | |
var btn = new qx.ui.toolbar.Button("Cancel", "grasshopper/icon/16/cross.png").set({ appearance: "toolbar-slim-button" }); | |
btn.addListener("execute", () => this._onCancelFilter()); | |
return btn; | |
case "btnSelectAll": | |
var btn = new qx.ui.toolbar.Button("All", "grasshopper/icon/16/select-all.png").set({ appearance: "toolbar-slim-button" }); | |
btn.addListener("execute", () => this._onSelectAll()); | |
return btn; | |
case "btnSelectNone": | |
var btn = new qx.ui.toolbar.Button("None", "grasshopper/icon/16/select-none.png").set({ appearance: "toolbar-slim-button" }); | |
btn.addListener("execute", () => this._onSelectNone()); | |
return btn; | |
case "search": | |
var edt = new qx.ui.form.TextField().set({ placeholder: "Search", liveUpdate: true }); | |
edt.addListener("changeValue", evt => this._onSearch(evt.getData())); | |
return edt; | |
case "listScroll": | |
return new qx.ui.container.Scroll(this.getChildControl("list")).set({ maxHeight: 300 }); | |
case "list": | |
var comp = new qx.ui.container.Composite(new qx.ui.layout.VBox()); | |
return comp; | |
case "filter-icon": | |
var control = new qx.ui.form.Button("Filter", "grasshopper/icon/16/table-column-unfiltered.png").set({ | |
show: "icon" | |
}); | |
control.addListener("execute", () => this._onFilterClick()); | |
return control; | |
} | |
return this.base(arguments, id); | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
qx.Class.define("grasshopper.af.table.filter.FilterHeaderRenderer", { | |
extend: qx.core.Object, | |
implement: qx.ui.table.IHeaderRenderer, | |
/* | |
* **************************************************************************** | |
* MEMBERS | |
* **************************************************************************** | |
*/ | |
properties: { | |
dataType: { | |
check: [ "string", "number", "date" ], | |
init: null, | |
nullable: true | |
}, | |
caseInsensitive: { | |
check: "Boolean", | |
init: true, | |
nullable: false | |
}, | |
searchable: { | |
check: "Boolean", | |
init: true, | |
nullable: false | |
}, | |
dateFormat: { | |
check: "qx.util.format.DateFormat", | |
init: null, | |
nullable: true | |
} | |
}, | |
members: { | |
// overridden | |
createHeaderCell: function(cellInfo) { | |
var widget = new grasshopper.af.table.filter.FilterHeaderCell(); | |
this.updateHeaderCell(cellInfo, widget); | |
return widget; | |
}, | |
// overridden | |
updateHeaderCell: function(cellInfo, cellWidget) { | |
cellWidget.setCellInfo(qx.lang.Object.clone(cellInfo)); | |
cellWidget.set({ | |
dataType: this.getDataType(), | |
caseInsensitive: this.getCaseInsensitive(), | |
searchable: this.getSearchable(), | |
dateFormat: this.getDateFormat() | |
}); | |
}, | |
getCellContentHtml: function(cellInfo) { | |
var cell = cellInfo.value; | |
if (!cell) | |
return ""; | |
if (this.getDataType() == "date") { | |
var dt = cell.value(); | |
var DF = this.getDateFormat() || qx.util.format.DateFormat.getDateInstance(); | |
return dt ? DF.format(dt) : ""; | |
} | |
return cell.text(cellInfo); | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment