Initiated treeTable
parent
5c045d2fc4
commit
fb366ea68a
|
@ -63,6 +63,7 @@
|
||||||
<router-link to="/paginator">● Paginator</router-link>
|
<router-link to="/paginator">● Paginator</router-link>
|
||||||
<router-link to="/picklist">● PickList</router-link>
|
<router-link to="/picklist">● PickList</router-link>
|
||||||
<router-link to="/tree">● Tree</router-link>
|
<router-link to="/tree">● Tree</router-link>
|
||||||
|
<router-link to="/treetable">● TreeTable</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
|
@ -61,6 +61,10 @@ export default {
|
||||||
selectionMode: {
|
selectionMode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null
|
||||||
|
},
|
||||||
|
expander: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
<template>
|
||||||
|
<div :class="containerClass">
|
||||||
|
<slot></slot>
|
||||||
|
<div class="p-treetable-header" v-if="$scopedSlots.header">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="p-treetable-wrapper">
|
||||||
|
<table>
|
||||||
|
<thead class="p-treetable-thead">
|
||||||
|
<tr>
|
||||||
|
<th v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.headerStyle" :class="getColumnHeaderClass(col)" @click="onColumnHeaderClick($event, col)">
|
||||||
|
<TTColumnSlot :column="col" type="header" v-if="col.$scopedSlots.header" />
|
||||||
|
<span class="p-column-title" v-if="col.header">{{col.header}}</span>
|
||||||
|
<span v-if="col.sortable" :class="getSortableColumnIcon(col)"></span>
|
||||||
|
<TTColumnSlot :column="col" type="filter" v-if="col.$scopedSlots.filter" />
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tfoot class="p-treetable-tfoot" v-if="hasFooter">
|
||||||
|
<tr>
|
||||||
|
<td v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.footerStyle" :class="col.footerClass">
|
||||||
|
<TTColumnSlot :column="col" type="footer" v-if="col.$scopedSlots.footer" />
|
||||||
|
{{col.footer}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<tbody class="p-treetable-tbody">
|
||||||
|
<template v-if="!empty">
|
||||||
|
<TTRow v-for="node of value" :key="node.key" :columns="columns" :node="node" :level="0"
|
||||||
|
:expandedKeys="d_expandedKeys" @node-toggle="onNodeToggle"
|
||||||
|
:selectionMode="selectionMode" :selectionKeys="selectionKeys"></TTRow>
|
||||||
|
</template>
|
||||||
|
<tr v-else class="p-treetable-emptymessage">
|
||||||
|
<td :colspan="columns.length">
|
||||||
|
<slot name="empty"></slot>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="p-treetable-footer" v-if="$scopedSlots.header">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TreeTableColumnSlot from './TreeTableColumnSlot';
|
||||||
|
import TreeTableRow from './TreeTableRow';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: null,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
expandedKeys: {
|
||||||
|
type: null,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
selectionKeys: {
|
||||||
|
type: null,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
selectionMode: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
metaKeySelection: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
loadingIcon: {
|
||||||
|
type: String,
|
||||||
|
default: 'pi pi-spinner'
|
||||||
|
},
|
||||||
|
rowHover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
autoLayout: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
sortField: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
sortOrder: {
|
||||||
|
type: Number,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
defaultSortOrder: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
multiSortMeta: {
|
||||||
|
type: Array,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
sortMode: {
|
||||||
|
type: String,
|
||||||
|
default: 'single'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
allChildren: null,
|
||||||
|
d_expandedKeys: this.expandedKeys || {},
|
||||||
|
d_first: this.first,
|
||||||
|
d_rows: this.rows,
|
||||||
|
d_sortField: this.sortField,
|
||||||
|
d_sortOrder: this.sortOrder,
|
||||||
|
d_multiSortMeta: this.multiSortMeta ? [...this.multiSortMeta] : [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
expandedKeys(newValue) {
|
||||||
|
this.d_expandedKeys = newValue;
|
||||||
|
},
|
||||||
|
first(newValue) {
|
||||||
|
this.d_first = newValue;
|
||||||
|
},
|
||||||
|
rows(newValue) {
|
||||||
|
this.d_rows = newValue;
|
||||||
|
},
|
||||||
|
sortField(newValue) {
|
||||||
|
this.d_sortField = newValue;
|
||||||
|
},
|
||||||
|
sortOrder(newValue) {
|
||||||
|
this.d_sortOrder = newValue;
|
||||||
|
},
|
||||||
|
multiSortMeta(newValue) {
|
||||||
|
this.d_multiSortMeta = newValue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.allChildren = this.$children;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNodeToggle(node) {
|
||||||
|
const key = node.key;
|
||||||
|
|
||||||
|
if (this.d_expandedKeys[key]) {
|
||||||
|
delete this.d_expandedKeys[key];
|
||||||
|
this.$emit('node-collapse', node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.d_expandedKeys[key] = true;
|
||||||
|
this.$emit('node-expand', node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.d_expandedKeys = {...this.d_expandedKeys};
|
||||||
|
this.$emit('update:expandedKeys', this.d_expandedKeys);
|
||||||
|
},
|
||||||
|
getColumnHeaderClass(column) {
|
||||||
|
const sorted = this.sortMode === 'single' ? (this.d_sortField === (column.field || column.sortField)) : this.getMultiSortMetaIndex(column) > -1;
|
||||||
|
|
||||||
|
return [column.headerClass,
|
||||||
|
{'p-sortable-column': column.sortable},
|
||||||
|
{'p-highlight': sorted}
|
||||||
|
];
|
||||||
|
},
|
||||||
|
getSortableColumnIcon(column) {
|
||||||
|
let sorted = false;
|
||||||
|
let sortOrder = null;
|
||||||
|
|
||||||
|
if (this.sortMode === 'single') {
|
||||||
|
sorted = this.d_sortField === (column.field || column.sortField);
|
||||||
|
sortOrder = sorted ? this.d_sortOrder: 0;
|
||||||
|
}
|
||||||
|
else if (this.sortMode === 'multiple') {
|
||||||
|
let metaIndex = this.getMultiSortMetaIndex(column);
|
||||||
|
if (metaIndex > -1) {
|
||||||
|
sorted = true;
|
||||||
|
sortOrder = this.d_multiSortMeta[metaIndex].order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'p-sortable-column-icon pi pi-fw',
|
||||||
|
{'pi-sort': !sorted},
|
||||||
|
{'pi-sort-up': sorted && sortOrder > 0},
|
||||||
|
{'pi-sort-down': sorted && sortOrder < 0},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
getMultiSortMetaIndex(column) {
|
||||||
|
let index = -1;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.d_multiSortMeta.length; i++) {
|
||||||
|
let meta = this.d_multiSortMeta[i];
|
||||||
|
if (meta.field === (column.field || column.sortField)) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
},
|
||||||
|
onColumnHeaderClick() {
|
||||||
|
|
||||||
|
},
|
||||||
|
isNodeSelected() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
containerClass() {
|
||||||
|
return ['p-treetable p-component', {
|
||||||
|
'p-treetable-hoverable-rows': (this.rowHover || this.selectionMode),
|
||||||
|
'p-treetable-auto-layout': this.autoLayout
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
columns() {
|
||||||
|
if (this.allChildren) {
|
||||||
|
return this.allChildren.filter(child => child.$options._propKeys.indexOf('columnKey') !== -1);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
processedData() {
|
||||||
|
return this.value;
|
||||||
|
},
|
||||||
|
empty() {
|
||||||
|
const data = this.processedData;
|
||||||
|
return (!data || data.length === 0);
|
||||||
|
},
|
||||||
|
hasFooter() {
|
||||||
|
let hasFooter = false;
|
||||||
|
|
||||||
|
for (let col of this.columns) {
|
||||||
|
if (col.footer || col.$scopedSlots.footer) {
|
||||||
|
hasFooter = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasFooter;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'TTColumnSlot': TreeTableColumnSlot,
|
||||||
|
'TTRow': TreeTableRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.p-treetable {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-toggler {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-treetable-thead > tr > th,
|
||||||
|
.p-treetable .p-treetable-tbody > tr > td,
|
||||||
|
.p-treetable .p-treetable-tfoot > tr > td {
|
||||||
|
padding: .25em .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-treetable-thead > tr > th .p-column-title {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-sortable-column {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-sortable-column-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-auto-layout > .p-treetable-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-auto-layout > .p-treetable-wrapper > table {
|
||||||
|
table-layout: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
.p-treetable-header,
|
||||||
|
.p-treetable-footer {
|
||||||
|
padding: .25em .5em;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-header {
|
||||||
|
border-bottom: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-footer {
|
||||||
|
border-top: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paginator */
|
||||||
|
.p-treetable .p-paginator-top {
|
||||||
|
border-bottom: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-paginator-bottom {
|
||||||
|
border-top: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollable */
|
||||||
|
.p-treetable-scrollable-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.p-treetable-scrollable-header,
|
||||||
|
.p-treetable-scrollable-footer {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-scrollable-body {
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-scrollable-body > table > .p-treetable-tbody > tr:first-child > td {
|
||||||
|
border-top: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-virtual-table {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Frozen Columns */
|
||||||
|
.p-treetable-frozen-view .p-treetable-scrollable-body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-frozen-view > .p-treetable-scrollable-body > table > .p-treetable-tbody > tr > td:last-child {
|
||||||
|
border-right: 0 none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-unfrozen-view {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter */
|
||||||
|
.p-column-filter {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resizable */
|
||||||
|
.p-treetable-resizable > .p-treetable-tablewrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-resizable .p-treetable-thead > tr > th,
|
||||||
|
.p-treetable-resizable .p-treetable-tfoot > tr > td,
|
||||||
|
.p-treetable-resizable .p-treetable-tbody > tr > td {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-resizable .p-resizable-column {
|
||||||
|
background-clip: padding-box;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-resizable-fit .p-resizable-column:last-child .p-column-resizer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-column-resizer {
|
||||||
|
display: block;
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: .5em;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0px;
|
||||||
|
cursor:col-resize;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-column-resizer-helper {
|
||||||
|
width: 1px;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selection */
|
||||||
|
.p-treetable .p-treetable-checkbox {
|
||||||
|
margin: 0 .5em 0 .25em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Edit */
|
||||||
|
.p-treetable .p-treetable-tbody > tr > td.p-cell-editing .p-component {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reorder */
|
||||||
|
.p-treetable-reorder-indicator-up,
|
||||||
|
.p-treetable-reorder-indicator-down {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
.p-treetable-responsive .p-treetable-tbody > tr > td .p-column-title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 40em) {
|
||||||
|
.p-treetable-responsive .p-treetable-thead > tr > th,
|
||||||
|
.p-treetable-responsive .p-treetable-tfoot > tr > td {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-responsive .p-treetable-tbody > tr > td {
|
||||||
|
text-align: left;
|
||||||
|
display: block;
|
||||||
|
border: 0 none;
|
||||||
|
width: 100% !important;
|
||||||
|
float: left;
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-responsive .p-treetable-tbody > tr > td .p-column-title {
|
||||||
|
padding: .4em;
|
||||||
|
min-width: 30%;
|
||||||
|
display: inline-block;
|
||||||
|
margin: -.4em 1em -.4em -.4em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loader */
|
||||||
|
.p-treetable-loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=10)";
|
||||||
|
opacity: 0.1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable-loading-content {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
margin-top: -1em;
|
||||||
|
margin-left: -1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-treetable .p-treetable-loading-icon {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
functional: true,
|
||||||
|
props: {
|
||||||
|
column: {
|
||||||
|
type: null,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: null,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render(createElement, context) {
|
||||||
|
const content = context.props.column.$scopedSlots[context.props.type]({
|
||||||
|
'data': context.props.data,
|
||||||
|
'column': context.props.column
|
||||||
|
});
|
||||||
|
return [content];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,96 @@
|
||||||
|
<template>
|
||||||
|
<tr :class="containerClass">
|
||||||
|
<td v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.bodyStyle" :class="col.bodyClass">
|
||||||
|
<span class="p-treetable-toggler p-unselectable-text" @click="toggle" v-if="col.expander" :style="togglerStyle">
|
||||||
|
<i :class="togglerIcon"></i>
|
||||||
|
</span>
|
||||||
|
<TTColumnSlot :data="node" :column="col" type="body" v-if="col.$scopedSlots.body" />
|
||||||
|
<template v-else>{{resolveFieldData(node.data, col.field)}}</template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ObjectUtils from '../utils/ObjectUtils';
|
||||||
|
import TreeTableColumnSlot from './TreeTableColumnSlot';
|
||||||
|
import { Fragment } from 'vue-fragment';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'sub-ttnode',
|
||||||
|
props: {
|
||||||
|
node: {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
resolveFieldData(rowData, field) {
|
||||||
|
return ObjectUtils.resolveFieldData(rowData, field);
|
||||||
|
},
|
||||||
|
toggle() {
|
||||||
|
this.$emit('node-toggle', this.node);
|
||||||
|
},
|
||||||
|
onChildNodeToggle(node) {
|
||||||
|
this.$emit('node-toggle', node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
togglerIcon() {
|
||||||
|
return ['p-treetable-toggler-icon pi pi-fw', {'pi-chevron-right': !this.expanded, 'pi-chevron-down': this.expanded}];
|
||||||
|
},
|
||||||
|
togglerStyle() {
|
||||||
|
return {
|
||||||
|
marginLeft: this.level * 16 + 'px',
|
||||||
|
visibility: this.leaf ? 'hidden' : 'visible'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
childLevel() {
|
||||||
|
return this.level + 1;
|
||||||
|
},
|
||||||
|
childrenToRender() {
|
||||||
|
return this.expanded ? this.node.children: null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'TTColumnSlot': TreeTableColumnSlot,
|
||||||
|
'TTFragment': Fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -48,6 +48,7 @@ import TabView from './components/tabview/TabView';
|
||||||
import TabPanel from './components/tabpanel/TabPanel';
|
import TabPanel from './components/tabpanel/TabPanel';
|
||||||
import Textarea from './components/textarea/Textarea';
|
import Textarea from './components/textarea/Textarea';
|
||||||
import Tree from './components/tree/Tree';
|
import Tree from './components/tree/Tree';
|
||||||
|
import TreeTable from './components/treetable/TreeTable';
|
||||||
import Toast from './components/toast/Toast';
|
import Toast from './components/toast/Toast';
|
||||||
import ToastService from './components/toast/ToastService';
|
import ToastService from './components/toast/ToastService';
|
||||||
import Toolbar from './components/toolbar/Toolbar';
|
import Toolbar from './components/toolbar/Toolbar';
|
||||||
|
@ -119,6 +120,7 @@ Vue.component('Toast', Toast);
|
||||||
Vue.component('Toolbar', Toolbar);
|
Vue.component('Toolbar', Toolbar);
|
||||||
Vue.component('ToggleButton', ToggleButton);
|
Vue.component('ToggleButton', ToggleButton);
|
||||||
Vue.component('Tree', Tree);
|
Vue.component('Tree', Tree);
|
||||||
|
Vue.component('TreeTable', TreeTable);
|
||||||
Vue.component('TriStateCheckbox', TriStateCheckbox);
|
Vue.component('TriStateCheckbox', TriStateCheckbox);
|
||||||
Vue.component('ValidationMessage', ValidationMessage);
|
Vue.component('ValidationMessage', ValidationMessage);
|
||||||
|
|
||||||
|
|
|
@ -380,7 +380,12 @@ export default new Router({
|
||||||
path: '/tree/filter',
|
path: '/tree/filter',
|
||||||
name: 'treefilter',
|
name: 'treefilter',
|
||||||
component: () => import('./views/tree/TreeFilterDemo.vue')
|
component: () => import('./views/tree/TreeFilterDemo.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/treetable',
|
||||||
|
name: 'treetable',
|
||||||
|
component: () => import('./views/treetable/TreeTableDemo.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/tristatecheckbox',
|
path: '/tristatecheckbox',
|
||||||
name: 'tristatecheckbox',
|
name: 'tristatecheckbox',
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<TreeTableSubMenu />
|
||||||
|
|
||||||
|
<div class="content-section introduction">
|
||||||
|
<div class="feature-intro">
|
||||||
|
<h1>TreeTable</h1>
|
||||||
|
<p>TreeTable is used to display hierarchical data in tabular format.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-section implementation">
|
||||||
|
<h3>Basic</h3>
|
||||||
|
<TreeTable :value="nodes">
|
||||||
|
<Column field="name" header="Name" :expander="true"></Column>
|
||||||
|
<Column field="size" header="Size"></Column>
|
||||||
|
<Column field="type" header="Type"></Column>
|
||||||
|
</TreeTable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TreeTableDoc />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
import TreeTableDoc from './TreeTableDoc';
|
||||||
|
import TreeTableSubMenu from './TreeTableSubMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: null,
|
||||||
|
expandedKeys: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeTableNodes().then(data => this.nodes = data);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
expandAll() {
|
||||||
|
for (let node of this.nodes) {
|
||||||
|
this.expandNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expandedKeys = {...this.expandedKeys};
|
||||||
|
},
|
||||||
|
collapseAll() {
|
||||||
|
this.expandedKeys = {};
|
||||||
|
},
|
||||||
|
expandNode(node) {
|
||||||
|
this.expandedKeys[node.key] = true;
|
||||||
|
if (node.children && node.children.length) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
this.expandNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
'TreeTableDoc': TreeTableDoc,
|
||||||
|
'TreeTableSubMenu': TreeTableSubMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
button {
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,783 @@
|
||||||
|
<template>
|
||||||
|
<div class="content-section documentation">
|
||||||
|
<TabView>
|
||||||
|
<TabPanel header="Documentation">
|
||||||
|
<h3>Import</h3>
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import Tree from 'primevue/tree';
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Getting Started</h3>
|
||||||
|
<p>Tree component requires an array of TreeNode objects as its <i>value</i>.</p>
|
||||||
|
|
||||||
|
<h3>TreeNode API</h3>
|
||||||
|
<div class="doc-tablewrapper">
|
||||||
|
<table class="doc-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Default</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>key</td>
|
||||||
|
<td>any</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Mandatory unique key of the node.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>label</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Label of the node.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>data</td>
|
||||||
|
<td>any</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Data represented by the node.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>type</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Type of the node to match a template.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>icon</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Icon of the node to display next to content.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>children</td>
|
||||||
|
<td>TreeNode[]</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>An array of treenodes as children.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>style</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Inline style of the node.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>styleClass</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Style class of the node.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>selectable</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Whether the node is selectable when selection mode is enabled.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>leaf</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Specifies if the node has children. Used in lazy loading.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Example below loads the tree nodes from a remote datasource via a service called NodeService.</p>
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<Tree :value="nodes"></Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => this.nodes = data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default class NodeService {
|
||||||
|
|
||||||
|
getTreeNodes() {
|
||||||
|
return axios.get('demo/data/treenodes.json').then(res => res.data.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<p>The json response sample would be as following.</p>
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
{
|
||||||
|
"root": [
|
||||||
|
{
|
||||||
|
"key": "0",
|
||||||
|
"label": "Documents",
|
||||||
|
"data": "Documents Folder",
|
||||||
|
"icon": "pi pi-fw pi-inbox",
|
||||||
|
"children": [{
|
||||||
|
"key": "0-0",
|
||||||
|
"label": "Work",
|
||||||
|
"data": "Work Folder",
|
||||||
|
"icon": "pi pi-fw pi-cog",
|
||||||
|
"children": [{ "key": "0-0-0", "label": "Expenses.doc", "icon": "pi pi-fw pi-file", "data": "Expenses Document" }, { "key": "0-0-1", "label": "Resume.doc", "icon": "pi pi-fw pi-file", "data": "Resume Document" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "0-1",
|
||||||
|
"label": "Home",
|
||||||
|
"data": "Home Folder",
|
||||||
|
"icon": "pi pi-fw pi-home",
|
||||||
|
"children": [{ "key": "0-1-0", "label": "Invoices.txt", "icon": "pi pi-fw pi-file", "data": "Invoices for this month" }]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "1",
|
||||||
|
"label": "Events",
|
||||||
|
"data": "Events Folder",
|
||||||
|
"icon": "pi pi-fw pi-calendar",
|
||||||
|
"children": [
|
||||||
|
{ "key": "1-0", "label": "Meeting", "icon": "pi pi-fw pi-calendar-plus", "data": "Meeting" },
|
||||||
|
{ "key": "1-1", "label": "Product Launch", "icon": "pi pi-fw pi-calendar-plus", "data": "Product Launch" },
|
||||||
|
{ "key": "1-2", "label": "Report Review", "icon": "pi pi-fw pi-calendar-plus", "data": "Report Review" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "2",
|
||||||
|
"label": "Movies",
|
||||||
|
"data": "Movies Folder",
|
||||||
|
"icon": "pi pi-fw pi-star",
|
||||||
|
"children": [{
|
||||||
|
"key": "2-0",
|
||||||
|
"icon": "pi pi-fw pi-star",
|
||||||
|
"label": "Al Pacino",
|
||||||
|
"data": "Pacino Movies",
|
||||||
|
"children": [{ "key": "2-0-0", "label": "Scarface", "icon": "pi pi-fw pi-video", "data": "Scarface Movie" }, { "key": "2-0-1", "label": "Serpico", "icon": "pi pi-fw pi-video", "data": "Serpico Movie" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "2-1",
|
||||||
|
"label": "Robert De Niro",
|
||||||
|
"icon": "pi pi-fw pi-star",
|
||||||
|
"data": "De Niro Movies",
|
||||||
|
"children": [{ "key": "2-1-0", "label": "Goodfellas", "icon": "pi pi-fw pi-video", "data": "Goodfellas Movie" }, { "key": "2-1-1", "label": "Untouchables", "icon": "pi pi-fw pi-video", "data": "Untouchables Movie" }]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Programmatic Control</h3>
|
||||||
|
<p>Tree state can be controlled programmatically with the <i>expandedKeys</i> property that defines the keys
|
||||||
|
that are expanded. This property is a Map instance whose key is the key of a node and value is a boolean. Note that <i>expandedKeys</i> also supports two-way binding with the sync modifier.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Example below expands and collapses all nodes with buttons.</p>
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<div>
|
||||||
|
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
|
||||||
|
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
|
||||||
|
</div>
|
||||||
|
<Tree :value="nodes" :expandedKeys="expandedKeys"></Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: null,
|
||||||
|
expandedKeys: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => this.nodes = data);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
expandAll() {
|
||||||
|
for (let node of this.nodes) {
|
||||||
|
this.expandNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expandedKeys = {...this.expandedKeys};
|
||||||
|
},
|
||||||
|
collapseAll() {
|
||||||
|
this.expandedKeys = {};
|
||||||
|
},
|
||||||
|
expandNode(node) {
|
||||||
|
this.expandedKeys[node.key] = true;
|
||||||
|
if (node.children && node.children.length) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
this.expandNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<p>To display some nodes as expanded by default, simply add their keys to the map.</p>
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: null,
|
||||||
|
expandedKeys: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => {
|
||||||
|
this.nodes = data;
|
||||||
|
this.expandedKeys[this.nodes[0].key] = true;
|
||||||
|
this.expandedKeys[this.nodes[1].key] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Selection</h3>
|
||||||
|
<p>Tree supports <b>single</b>, <b>multiple</b> and <b>checkbox</b> selection modes. Define the <i>selectionKeys</i> with the sync operator and the <i>selectionMode</i> properties to enable the selection.
|
||||||
|
By default in multiple selection mode, metaKey is necessary to add to existing selections however this can be configured with <i>metaKeySelection</i> property. Note that
|
||||||
|
in touch enabled devices, Tree does not require metaKey. In addition selection on a particular node can be disabled if the <i>selectable</i> is false on the node instance.</p>
|
||||||
|
|
||||||
|
<p>Similarly to the <i>expandedKeys</i>, <i>selectionKeys</i> is a Map instance whose key is the key of a node and value is a boolean in "single" and "multiple" cases. On the other hand
|
||||||
|
in "checkbox" mode, instead of a boolean, value should be an object that has "checked" and "partialChecked" properties to represent the checked state of a node.</p>
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<h3>Single Selection</h3>
|
||||||
|
<Tree :value="nodes" selectionMode="single" :selectionKeys.sync="selectedKey1"></Tree>
|
||||||
|
|
||||||
|
<h3>Multiple Selection with MetaKey</h3>
|
||||||
|
<Tree :value="nodes" selectionMode="multiple" :selectionKeys.sync="selectedKeys1"></Tree>
|
||||||
|
|
||||||
|
<h3>Multiple Selection without MetaKey</h3>
|
||||||
|
<Tree :value="nodes" selectionMode="multiple" :selectionKeys.sync="selectedKeys2" :metaKeySelection="false"></Tree>
|
||||||
|
|
||||||
|
<h3>Checkbox Selection</h3>
|
||||||
|
<Tree :value="nodes" selectionMode="checkbox" :selectionKeys.sync="selectedKeys3"></Tree>
|
||||||
|
|
||||||
|
<h3>Events</h3>
|
||||||
|
<Tree :value="nodes" selectionMode="single" :selectionKeys.sync="selectedKey2" :metaKeySelection="false"
|
||||||
|
@node-select="onNodeSelect" @node-unselect="onNodeUnselect"></Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedKey1: null,
|
||||||
|
selectedKey2: null,
|
||||||
|
selectedKeys1: null,
|
||||||
|
selectedKeys2: null,
|
||||||
|
selectedKeys3: null,
|
||||||
|
nodes: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => this.nodes = data);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNodeSelect(node) {
|
||||||
|
this.$toast.add({severity:'success', summary: 'Node Selected', detail: node.label, life: 3000});
|
||||||
|
},
|
||||||
|
onNodeUnselect(node) {
|
||||||
|
this.$toast.add({severity:'success', summary: 'Node Unselected', detail: node.label, life: 3000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<p>To display some nodes as selected by default, simply add their keys to the map.</p>
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedKey1: null,
|
||||||
|
selectedKey2: null,
|
||||||
|
selectedKeys1: null,
|
||||||
|
selectedKeys2: null,
|
||||||
|
selectedKeys3: null,
|
||||||
|
nodes: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => {
|
||||||
|
this.nodes = data;
|
||||||
|
|
||||||
|
//single preselection
|
||||||
|
this.selectedKey1[this.nodes[0].key] = true;
|
||||||
|
|
||||||
|
//multiple preselection
|
||||||
|
this.selectedKeys2[this.nodes[0].key] = true;
|
||||||
|
this.selectedKeys2[this.nodes[1].key] = true;
|
||||||
|
|
||||||
|
//checkbox preselection
|
||||||
|
this.selectedKeys2[this.nodes[1].key] = {checked: true};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Lazy</h3>
|
||||||
|
<p>Lazy Loading is handy to deal with huge datasets. Idea is instead of loading the whole tree, load child nodes on demand
|
||||||
|
using expand expand. The important part is setting <i>leaf</i> to true on a node instance so that even without children,
|
||||||
|
tree would render an expand icon. Example below uses an in memory collection to mimic a lazy loading scenario with timeouts.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<Tree :value="nodes" @node-expand="onNodeExpand" :loading="loading"></Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
nodes: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.nodes = this.initateNodes();
|
||||||
|
this.loading = false;
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onNodeExpand(node) {
|
||||||
|
if (!node.children) {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
let _node = {...node};
|
||||||
|
_node.children = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
_node.children.push({
|
||||||
|
key: node.key + '-' + i,
|
||||||
|
label: 'Lazy ' + node.label + '-' + i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let _nodes = {...this.nodes}
|
||||||
|
_nodes[parseInt(node.key, 10)] = _node;
|
||||||
|
|
||||||
|
this.nodes = _nodes;
|
||||||
|
this.loading = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
initateNodes() {
|
||||||
|
return [{
|
||||||
|
key: '0',
|
||||||
|
label: 'Node 0',
|
||||||
|
leaf: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'Node 1',
|
||||||
|
leaf: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: 'Node 2',
|
||||||
|
leaf: false
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Templating</h3>
|
||||||
|
<p>The <i>type</i> property of a TreeNode is used to map a template to a node to create the node label. If it is undefined and no default template is available,
|
||||||
|
label of the node is used.</p>
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<Tree :value="nodes">
|
||||||
|
<template #default="slotProps">
|
||||||
|
<b>{{slotProps.node.label}}</b>
|
||||||
|
</template>
|
||||||
|
<template #url="slotProps">
|
||||||
|
<a :href="slotProps.node.data">{{slotProps.node.label}}</a>
|
||||||
|
</template>
|
||||||
|
</Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
key: '0',
|
||||||
|
label: 'Introduction',
|
||||||
|
children: [
|
||||||
|
{key: '0-0', label: 'What is Vue.js?', data:'https://vuejs.org/v2/guide/#What-is-Vue-js', type: 'url'},
|
||||||
|
{key: '0-1', label: 'Getting Started', data: 'https://vuejs.org/v2/guide/#Getting-Started', type: 'url'},
|
||||||
|
{key: '0-2', label: 'Declarative Rendering', data:'https://vuejs.org/v2/guide/#Declarative-Rendering', type: 'url'},
|
||||||
|
{key: '0-3', label: 'Conditionals and Loops', data: 'https://vuejs.org/v2/guide/#Conditionals-and-Loops', type: 'url'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'Components In-Depth',
|
||||||
|
children: [
|
||||||
|
{key: '1-0', label: 'Component Registration', data: 'https://vuejs.org/v2/guide/components-registration.html', type: 'url'},
|
||||||
|
{key: '1-1', llabel: 'Props', data: 'https://vuejs.org/v2/guide/components-props.html', type: 'url'},
|
||||||
|
{key: '1-2', llabel: 'Custom Events', data: 'https://vuejs.org/v2/guide/components-custom-events.html', type: 'url'},
|
||||||
|
{key: '1-3', llabel: 'Slots', data: 'https://vuejs.org/v2/guide/components-slots.html', type: 'url'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Filtering</h3>
|
||||||
|
<p>Filtering is enabled by setting the <i>filter</i> property to true, by default label property of a node
|
||||||
|
is used to compare against the value in the text field, in order to customize which field(s) should be used during search, define the <i>filterBy</i> property as a comma separated list.</p>
|
||||||
|
|
||||||
|
<p>In addition <i>filterMode</i> specifies the filtering strategy. In <b>lenient</b> mode when the query matches a node, children of the node are not searched further as all descendants of the node are included. On the other hand,
|
||||||
|
in <b>strict</b> mode when the query matches a node, filtering continues on all descendants.</p>
|
||||||
|
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<h3>Lenient Filter</h3>
|
||||||
|
<Tree :value="nodes" :filter="true" filterMode="lenient"></Tree>
|
||||||
|
|
||||||
|
<h3>Strict Filter</h3>
|
||||||
|
<Tree :value="nodes" :filter="true" filterMode="strict"></Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: null,
|
||||||
|
expandedKeys: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => this.nodes = data);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
expandAll() {
|
||||||
|
for (let node of this.nodes) {
|
||||||
|
this.expandNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expandedKeys = {...this.expandedKeys};
|
||||||
|
},
|
||||||
|
collapseAll() {
|
||||||
|
this.expandedKeys = {};
|
||||||
|
},
|
||||||
|
expandNode(node) {
|
||||||
|
this.expandedKeys[node.key] = true;
|
||||||
|
if (node.children << node.children.length) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
this.expandNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<h3>Properties</h3>
|
||||||
|
<p>Any valid attribute such as name and autofocus are passed to the underlying input element. Following is the additional property to configure the component.</p>
|
||||||
|
<div class="doc-tablewrapper">
|
||||||
|
<table class="doc-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Default</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>value</td>
|
||||||
|
<td>array</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>An array of treenodes.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>expandedKeys</td>
|
||||||
|
<td>array</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>A map of keys to represent the state of the tree expansion state in controlled mode.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>selectionMode</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Defines the selection mode, valid values "single", "multiple", and "checkbox".</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>selectionKeys</td>
|
||||||
|
<td>any</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>A map of keys to control the selection state.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>metaKeySelection</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>true</td>
|
||||||
|
<td>Defines how multiple items can be selected, when true metaKey needs to be pressed to select or unselect an item and when set to false selection of each item
|
||||||
|
can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>loading</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>false</td>
|
||||||
|
<td>Whether to display loading indicator.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>loadingIcon</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>pi pi-spin</td>
|
||||||
|
<td>Icon to display when tree is loading.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>filter</td>
|
||||||
|
<td>boolean</td>
|
||||||
|
<td>false</td>
|
||||||
|
<td>When specified, displays an input field to filter the items.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>filterBy</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>label</td>
|
||||||
|
<td>When filtering is enabled, filterBy decides which field or fields (comma separated) to search against.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>filterMode</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>lenient</td>
|
||||||
|
<td>Mode for filtering valid values are "lenient" and "strict". Default is lenient.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>filterPlaceholder</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>null</td>
|
||||||
|
<td>Placeholder text to show when filter input is empty.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Events</h3>
|
||||||
|
<div class="doc-tablewrapper">
|
||||||
|
<table class="doc-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Parameters</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>node-select</td>
|
||||||
|
<td>node: Node instance</td>
|
||||||
|
<td>Callback to invoke when a node is selected.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>node-unselect</td>
|
||||||
|
<td>node: Node instance</td>
|
||||||
|
<td>Callback to invoke when a node is unselected.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>node-expand</td>
|
||||||
|
<td>node: Node instance</td>
|
||||||
|
<td>Callback to invoke when a node is expanded.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>node-collapse</td>
|
||||||
|
<td>node: Node instance</td>
|
||||||
|
<td>Callback to invoke when a node is collapsed.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Styling</h3>
|
||||||
|
<p>Following is the list of structural style classes, for theming classes visit <router-link to="/theming">theming</router-link> page.</p>
|
||||||
|
<div class="doc-tablewrapper">
|
||||||
|
<table class="doc-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Element</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>p-tree</td>
|
||||||
|
<td>Main container element</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-tree-horizontal</td>
|
||||||
|
<td>Main container element in horizontal mode</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-tree-container</td>
|
||||||
|
<td>Container of nodes</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode</td>
|
||||||
|
<td>A treenode element</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode-content</td>
|
||||||
|
<td>Content of a treenode</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode-toggler</td>
|
||||||
|
<td>Toggle element</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode-toggler-icon</td>
|
||||||
|
<td>Toggle icon</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode-icon</td>
|
||||||
|
<td>Icon of a treenode</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode-label</td>
|
||||||
|
<td>Label of a treenode</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>p-treenode-children</td>
|
||||||
|
<td>Container element for node children</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Dependencies</h3>
|
||||||
|
<p>None.</p>
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
<TabPanel header="Source">
|
||||||
|
<a href="https://github.com/primefaces/primevue/tree/master/src/views/tree" class="btn-viewsource" target="_blank" rel="noopener noreferrer">
|
||||||
|
<span>View on GitHub</span>
|
||||||
|
</a>
|
||||||
|
<CodeHighlight>
|
||||||
|
<template v-pre>
|
||||||
|
<h3>Basic</h3>
|
||||||
|
<Tree :value="nodes"></Tree>
|
||||||
|
|
||||||
|
<h3>Programmatic Control</h3>
|
||||||
|
<div style="margin-bottom: 1em">
|
||||||
|
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
|
||||||
|
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
|
||||||
|
</div>
|
||||||
|
<Tree :value="nodes" :expandedKeys="expandedKeys"></Tree>
|
||||||
|
</template>
|
||||||
|
</CodeHighlight>
|
||||||
|
|
||||||
|
<CodeHighlight lang="javascript">
|
||||||
|
import NodeService from '../../service/NodeService';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
nodes: null,
|
||||||
|
expandedKeys: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeService: null,
|
||||||
|
created() {
|
||||||
|
this.nodeService = new NodeService();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.nodeService.getTreeNodes().then(data => this.nodes = data);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
expandAll() {
|
||||||
|
for (let node of this.nodes) {
|
||||||
|
this.expandNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expandedKeys = {...this.expandedKeys};
|
||||||
|
},
|
||||||
|
collapseAll() {
|
||||||
|
this.expandedKeys = {};
|
||||||
|
},
|
||||||
|
expandNode(node) {
|
||||||
|
this.expandedKeys[node.key] = true;
|
||||||
|
if (node.children && node.children.length) {
|
||||||
|
for (let child of node.children) {
|
||||||
|
this.expandNode(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</CodeHighlight>
|
||||||
|
</TabPanel>
|
||||||
|
</TabView>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div class="content-section content-submenu p-clearfix">
|
||||||
|
<ul>
|
||||||
|
<li><router-link to="/treetable">● Documentation</router-link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
Loading…
Reference in New Issue