273 lines
9.1 KiB
Vue
Executable File
273 lines
9.1 KiB
Vue
Executable File
<template>
|
|
<tr :class="containerClass" @click="onClick" @keydown="onKeyDown" @touchend="onTouchEnd" :style="node.style" tabindex="0">
|
|
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
|
|
<TTBodyCell
|
|
v-if="!columnProp(col, 'hidden')"
|
|
:column="col"
|
|
:node="node"
|
|
:level="level"
|
|
:leaf="leaf"
|
|
:indentation="indentation"
|
|
:expanded="expanded"
|
|
:selectionMode="selectionMode"
|
|
:checked="checked"
|
|
:partialChecked="partialChecked"
|
|
@node-toggle="$emit('node-toggle', $event)"
|
|
@checkbox-toggle="toggleCheckbox"
|
|
></TTBodyCell>
|
|
</template>
|
|
</tr>
|
|
<template v-if="expanded && node.children && node.children.length">
|
|
<TreeTableRow
|
|
v-for="childNode of node.children"
|
|
:key="childNode.key"
|
|
:columns="columns"
|
|
:node="childNode"
|
|
:parentNode="node"
|
|
:level="level + 1"
|
|
:expandedKeys="expandedKeys"
|
|
:selectionMode="selectionMode"
|
|
:selectionKeys="selectionKeys"
|
|
:indentation="indentation"
|
|
@node-toggle="$emit('node-toggle', $event)"
|
|
@node-click="$emit('node-click', $event)"
|
|
@checkbox-change="onCheckboxChange"
|
|
/>
|
|
</template>
|
|
</template>
|
|
|
|
<script>
|
|
import { DomHandler, ObjectUtils } from 'primevue/utils';
|
|
import BodyCell from './BodyCell.vue';
|
|
|
|
export default {
|
|
name: 'TreeTableRow',
|
|
emits: ['node-click', 'node-toggle', 'checkbox-change', 'nodeClick', 'nodeToggle', 'checkboxChange'],
|
|
props: {
|
|
node: {
|
|
type: null,
|
|
default: null
|
|
},
|
|
parentNode: {
|
|
type: null,
|
|
default: null
|
|
},
|
|
columns: {
|
|
type: null,
|
|
default: null
|
|
},
|
|
expandedKeys: {
|
|
type: null,
|
|
default: null
|
|
},
|
|
selectionKeys: {
|
|
type: null,
|
|
default: null
|
|
},
|
|
selectionMode: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
level: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
indentation: {
|
|
type: Number,
|
|
default: 1
|
|
}
|
|
},
|
|
nodeTouched: false,
|
|
methods: {
|
|
columnProp(col, prop) {
|
|
return ObjectUtils.getVNodeProp(col, prop);
|
|
},
|
|
toggle() {
|
|
this.$emit('node-toggle', this.node);
|
|
},
|
|
onClick(event) {
|
|
if (DomHandler.isClickable(event.target) || DomHandler.hasClass(event.target, 'p-treetable-toggler') || DomHandler.hasClass(event.target.parentElement, 'p-treetable-toggler')) {
|
|
return;
|
|
}
|
|
|
|
this.$emit('node-click', {
|
|
originalEvent: event,
|
|
nodeTouched: this.nodeTouched,
|
|
node: this.node
|
|
});
|
|
|
|
this.nodeTouched = false;
|
|
},
|
|
onTouchEnd() {
|
|
this.nodeTouched = true;
|
|
},
|
|
onKeyDown(event) {
|
|
if (event.target === this.$el) {
|
|
const rowElement = this.$el;
|
|
|
|
switch (event.which) {
|
|
//down arrow
|
|
case 40: {
|
|
const nextRow = rowElement.nextElementSibling;
|
|
|
|
if (nextRow) {
|
|
nextRow.focus();
|
|
}
|
|
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
|
|
//up arrow
|
|
case 38: {
|
|
const previousRow = rowElement.previousElementSibling;
|
|
|
|
if (previousRow) {
|
|
previousRow.focus();
|
|
}
|
|
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
|
|
//right-left arrows
|
|
case 37:
|
|
|
|
case 39: {
|
|
if (!this.leaf) {
|
|
this.$emit('node-toggle', this.node);
|
|
event.preventDefault();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//enter
|
|
case 13: {
|
|
this.onClick(event);
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
//no op
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
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
|
|
});
|
|
},
|
|
onCheckboxChange(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: {
|
|
containerClass() {
|
|
return [
|
|
this.node.styleClass,
|
|
{
|
|
'p-highlight': this.selected
|
|
}
|
|
];
|
|
},
|
|
hasChildren() {
|
|
return this.node.children && this.node.children.length > 0;
|
|
},
|
|
expanded() {
|
|
return this.expandedKeys && this.expandedKeys[this.node.key] === true;
|
|
},
|
|
leaf() {
|
|
return this.node.leaf === false ? false : !(this.node.children && this.node.children.length);
|
|
},
|
|
selected() {
|
|
return this.selectionMode && this.selectionKeys ? this.selectionKeys[this.node.key] === true : false;
|
|
},
|
|
childLevel() {
|
|
return this.level + 1;
|
|
},
|
|
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: {
|
|
TTBodyCell: BodyCell
|
|
}
|
|
};
|
|
</script>
|