From 9656540538bc4e25ecb65a95da0113044e2519bc Mon Sep 17 00:00:00 2001 From: cagataycivici Date: Wed, 7 Aug 2019 14:17:03 +0300 Subject: [PATCH] Checkbox Selection for TreeTable --- src/components/treetable/TreeTable.vue | 25 ++++- src/components/treetable/TreeTableRow.vue | 101 +++++++++++++++--- .../treetable/TreeTableRowLoader.vue | 39 ++++++- 3 files changed, 146 insertions(+), 19 deletions(-) diff --git a/src/components/treetable/TreeTable.vue b/src/components/treetable/TreeTable.vue index f6d2930c8..362cbfad2 100644 --- a/src/components/treetable/TreeTable.vue +++ b/src/components/treetable/TreeTable.vue @@ -36,8 +36,8 @@ @@ -232,7 +232,7 @@ export default { this.$emit('update:expandedKeys', this.d_expandedKeys); }, onNodeClick(event) { - if (this.selectionMode != null && event.node.selectable !== false) { + if (this.rowSelectionMode && event.node.selectable !== false) { const metaSelection = event.nodeTouched ? false : this.metaKeySelection; const _selectionKeys = metaSelection ? this.handleSelectionWithMetaKey(event) : this.handleSelectionWithoutMetaKey(event); @@ -304,6 +304,14 @@ export default { return _selectionKeys; }, + onCheckboxChange(event) { + this.$emit('update:selectionKeys', event.selectionKeys); + + if (event.check) + this.$emit('node-select', event.node); + else + this.$emit('node-unselect', event.node); + }, isSingleSelectionMode() { return this.selectionMode === 'single'; }, @@ -595,7 +603,7 @@ export default { computed: { containerClass() { return ['p-treetable p-component', { - 'p-treetable-hoverable-rows': (this.rowHover || this.selectionMode), + 'p-treetable-hoverable-rows': (this.rowHover || this.rowSelectionMode), 'p-treetable-auto-layout': this.autoLayout }]; }, @@ -673,6 +681,15 @@ export default { paginatorBottom() { return this.paginator && (this.paginatorPosition !== 'top' || this.paginatorPosition === 'both'); }, + singleSelectionMode() { + return this.selectionMode && this.selectionMode === 'single'; + }, + multipleSelectionMode() { + return this.selectionMode && this.selectionMode === 'multiple'; + }, + rowSelectionMode() { + return this.singleSelectionMode || this.multipleSelectionMode; + }, totalRecordsLength() { if (this.lazy) { return this.totalRecords; diff --git a/src/components/treetable/TreeTableRow.vue b/src/components/treetable/TreeTableRow.vue index ca4a6edad..fc786bd82 100644 --- a/src/components/treetable/TreeTableRow.vue +++ b/src/components/treetable/TreeTableRow.vue @@ -4,6 +4,14 @@ +
+
+ +
+
+ +
+
@@ -22,6 +30,10 @@ export default { type: null, default: null }, + parentNode: { + type: null, + default: null + }, columns: { type: null, default: null @@ -52,34 +64,82 @@ export default { this.$emit('node-toggle', this.node); }, onClick(event) { - if (DomHandler.hasClass(event.target, 'p-treetable-toggler') || DomHandler.hasClass(event.target, 'p-treetable-toggler-icon')) { + let targetNode = event.target.nodeName; + if (targetNode === 'INPUT' || targetNode === 'BUTTON' || targetNode === 'A' || DomHandler.hasClass(event.target, 'p-clickable') + || DomHandler.hasClass(event.target, 'p-treetable-toggler') || DomHandler.hasClass(event.target.parentElement, 'p-treetable-toggler')) { return; } - if (this.isCheckboxSelectionMode()) { - this.toggleCheckbox(); - } - else { - this.$emit('node-click', { + this.$emit('node-click', { originalEvent: event, nodeTouched: this.nodeTouched, node: this.node }); - } this.nodeTouched = false; - }, - isCheckboxSelectionMode() { - return this.selectionMode === 'checkbox'; - }, - toggleCheckbox() { - }, onTouchEnd() { this.nodeTouched = true; }, onKeyDown(event) { //todo + }, + toggleCheckbox() { + let _selectionKeys = this.selectionKeys ? {...this.selectionKeys} : {}; + const _check = !this.checked; + + this.propagateDown(this.node, _check, _selectionKeys); + + this.$emit('checkbox-change', { + node: this.node, + check: _check, + selectionKeys: _selectionKeys + }); + }, + propagateDown(node, check, selectionKeys) { + if (check) + selectionKeys[node.key] = {checked: true, partialChecked: false}; + else + delete selectionKeys[node.key]; + + if (node.children && node.children.length) { + for (let child of node.children) { + this.propagateDown(child, check, selectionKeys); + } + } + }, + propagateUp(event) { + let check = event.check; + let _selectionKeys = {...event.selectionKeys}; + let checkedChildCount = 0; + let childPartialSelected = false; + + for(let child of this.node.children) { + if(_selectionKeys[child.key] && _selectionKeys[child.key].checked) + checkedChildCount++; + else if(_selectionKeys[child.key] && _selectionKeys[child.key].partialChecked) + childPartialSelected = true; + } + + if(check && checkedChildCount === this.node.children.length) { + _selectionKeys[this.node.key] = {checked: true, partialChecked: false}; + } + else { + if (!check) { + delete _selectionKeys[this.node.key]; + } + + if(childPartialSelected || (checkedChildCount > 0 && checkedChildCount !== this.node.children.length)) + _selectionKeys[this.node.key] = {checked: false, partialChecked: true}; + else + _selectionKeys[this.node.key] = {checked: false, partialChecked: false}; + } + + this.$emit('checkbox-change', { + node: event.node, + check: event.check, + selectionKeys: _selectionKeys + }); } }, computed: { @@ -111,6 +171,21 @@ export default { }, childLevel() { return this.level + 1; + }, + checkboxSelectionMode() { + return this.selectionMode === 'checkbox'; + }, + checkboxClass() { + return ['p-checkbox-box', {'p-highlight': this.checked}]; + }, + checkboxIcon() { + return ['p-checkbox-icon p-c', {'pi pi-check': this.checked, 'pi pi-minus': this.partialChecked}]; + }, + checked() { + return this.selectionKeys ? this.selectionKeys[this.node.key] && this.selectionKeys[this.node.key].checked: false; + }, + partialChecked() { + return this.selectionKeys ? this.selectionKeys[this.node.key] && this.selectionKeys[this.node.key].partialChecked: false; } }, components: { diff --git a/src/components/treetable/TreeTableRowLoader.vue b/src/components/treetable/TreeTableRowLoader.vue index 4c91c0dca..b009a74a2 100644 --- a/src/components/treetable/TreeTableRowLoader.vue +++ b/src/components/treetable/TreeTableRowLoader.vue @@ -34,7 +34,8 @@ const TreeTableRowLoader = { props: context.props, on: { 'node-toggle': context.listeners['node-toggle'], - 'node-click': context.listeners['node-click'] + 'node-click': context.listeners['node-click'], + 'checkbox-change': context.listeners['checkbox-change'] } }); @@ -46,13 +47,47 @@ const TreeTableRowLoader = { for (let childNode of node.children) { let childNodeProps = {...context.props}; childNodeProps.node = childNode; + childNodeProps.parentNode = node; childNodeProps.level = context.props.level + 1; let childNodeElement = createElement(TreeTableRowLoader, { props: childNodeProps, on: { 'node-toggle': context.listeners['node-toggle'], - 'node-click': context.listeners['node-click'] + 'node-click': context.listeners['node-click'], + 'checkbox-change': (event) => { + let check = event.check; + let _selectionKeys = {...event.selectionKeys}; + let checkedChildCount = 0; + let childPartialSelected = false; + + for(let child of node.children) { + if(_selectionKeys[child.key] && _selectionKeys[child.key].checked) + checkedChildCount++; + else if(_selectionKeys[child.key] && _selectionKeys[child.key].partialChecked) + childPartialSelected = true; + } + + if(check && checkedChildCount === node.children.length) { + _selectionKeys[node.key] = {checked: true, partialChecked: false}; + } + else { + if (!check) { + delete _selectionKeys[node.key]; + } + + if(childPartialSelected || (checkedChildCount > 0 && checkedChildCount !== node.children.length)) + _selectionKeys[node.key] = {checked: false, partialChecked: true}; + else + _selectionKeys[node.key] = {checked: false, partialChecked: false}; + } + + context.listeners['checkbox-change']({ + node: event.node, + check: event.check, + selectionKeys: _selectionKeys + }); + } } });