Initiated scrollable treetable

pull/1196/head^2
Cagatay Civici 2021-05-15 11:25:42 +03:00
parent 0a52948b4c
commit 8329ed5423
6 changed files with 379 additions and 23 deletions

View File

@ -469,6 +469,7 @@
{
"name": "TreeTable",
"meta": ["treetable"],
"badge": "New",
"children": [
{
"name": "Documentation",
@ -510,6 +511,11 @@
"name": "Resize",
"to": "/treetable/colresize"
},
{
"name": "Scroll",
"to": "/treetable/scroll",
"badge": "New"
},
{
"name": "Responsive",
"to": "/treetable/responsive"

View File

@ -153,7 +153,7 @@ export default {
let right = 0;
let next = this.$el.nextElementSibling;
if (next) {
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.left);
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right);
}
this.styleObject.right = right + 'px';
}

View File

@ -17,12 +17,12 @@
<slot name="paginatorRight"></slot>
</template>
</TTPaginator>
<div class="p-treetable-wrapper">
<div class="p-treetable-wrapper" :style="{maxHeight: scrollHeight}">
<table ref="table">
<thead class="p-treetable-thead">
<tr>
<template v-for="(col,i) of columns" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i">
<th v-if="!columnProp(col, 'hidden')" :style="columnProp(col, 'headerStyle')" :class="getColumnHeaderClass(col)" @click="onColumnHeaderClick($event, col)"
<th v-if="!columnProp(col, 'hidden')" :style="[columnProp(col, 'style'),columnProp(col, 'headerStyle')]" :class="getColumnHeaderClass(col)" @click="onColumnHeaderClick($event, col)"
:tabindex="columnProp(col, 'sortable') ? '0' : null" :aria-sort="getAriaSort(col)" @keydown="onColumnKeyDown($event, col)">
<span class="p-column-resizer" @mousedown="onColumnResizeStart" v-if="resizableColumns"></span>
<component :is="col.children.header" :column="col" v-if="col.children && col.children.header" />
@ -34,22 +34,12 @@
</tr>
<tr v-if="hasColumnFilter()">
<template v-for="(col,i) of columns" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i">
<th v-if="!columnProp(col, 'hidden')" :class="getFilterColumnHeaderClass(col)" :style="columnProp(col, 'filterHeaderStyle')">
<th v-if="!columnProp(col, 'hidden')" :class="getFilterColumnHeaderClass(col)" :style="[columnProp(col, 'style'),columnProp(col, 'filterHeaderStyle')]">
<component :is="col.children.filter" :column="col" v-if="col.children && col.children.filter"/>
</th>
</template>
</tr>
</thead>
<tfoot class="p-treetable-tfoot" v-if="hasFooter">
<tr>
<template v-for="(col,i) of columns" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i">
<td v-if="!columnProp(col, 'hidden')" :style="columnProp(col, 'footerStyle')" :class="columnProp(col, 'footerClass')">
<component :is="col.children.footer" :column="col" v-if="col.children && col.children.footer" />
{{columnProp(col, 'footer')}}
</td>
</template>
</tr>
</tfoot>
<tbody class="p-treetable-tbody">
<template v-if="!empty">
<TTRow v-for="node of dataToRender" :key="node.key" :columns="columns" :node="node" :level="0"
@ -62,6 +52,16 @@
</td>
</tr>
</tbody>
<tfoot class="p-treetable-tfoot" v-if="hasFooter">
<tr>
<template v-for="(col,i) of columns" :key="columnProp(col, 'columnKey')||columnProp(col, 'field')||i">
<td v-if="!columnProp(col, 'hidden')" :style="[columnProp(col, 'style'),columnProp(col, 'footerStyle')]" :class="getColumnFooterClass(col)">
<component :is="col.children.footer" :column="col" v-if="col.children && col.children.footer" />
{{columnProp(col, 'footer')}}
</td>
</template>
</tr>
</tfoot>
</table>
</div>
<TTPaginator v-if="paginatorBottom" :rows="d_rows" :first="d_first" :totalRecords="totalRecordsLength" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions"
@ -222,6 +222,18 @@ export default {
showGridlines: {
type: Boolean,
default: false
},
scrollable: {
type: Boolean,
default: false
},
scrollDirection: {
type: String,
default: "vertical"
},
scrollHeight: {
type: String,
default: null
}
},
documentColumnResizeListener: null,
@ -391,14 +403,22 @@ export default {
return false;
},
getColumnHeaderClass(column) {
return [this.columnProp(column, 'headerClass'),
{'p-sortable-column': this.columnProp(column, 'sortable')},
{'p-resizable-column': this.resizableColumns},
{'p-highlight': this.isColumnSorted(column)}
];
return [this.columnProp(column, 'headerClass'), {
'p-sortable-column': this.columnProp(column, 'sortable'),
'p-resizable-column': this.resizableColumns,
'p-highlight': this.isColumnSorted(column),
'p-frozen-column': this.columnProp(column, 'frozen')
}];
},
getFilterColumnHeaderClass(column) {
return ['p-filter-column', this.columnProp(column, 'filterHeaderClass')];
return ['p-filter-column', this.columnProp(column, 'filterHeaderClass'), {
'p-frozen-column': this.columnProp(column, 'frozen')
}];
},
getColumnFooterClass(column) {
return [this.columnProp(column, 'footerClass'), {
'p-frozen-column': this.columnProp(column, 'frozen')
}];
},
getSortableColumnIcon(column) {
let sorted = false;
@ -805,7 +825,12 @@ export default {
'p-treetable-auto-layout': this.autoLayout,
'p-treetable-resizable': this.resizableColumns,
'p-treetable-resizable-fit': this.resizableColumns && this.columnResizeMode === 'fit',
'p-treetable-gridlines': this.showGridlines
'p-treetable-gridlines': this.showGridlines,
'p-treetable-scrollable': this.scrollable,
'p-treetable-scrollable-vertical': this.scrollable && this.scrollDirection === 'vertical',
'p-treetable-scrollable-horizontal': this.scrollable && this.scrollDirection === 'horizontal',
'p-treetable-scrollable-both': this.scrollable && this.scrollDirection === 'both',
'p-treetable-flex-scrollable': (this.scrollable && this.scrollHeight === 'flex')
}];
},
columns() {
@ -1006,4 +1031,75 @@ export default {
justify-content: center;
z-index: 2;
}
/* Scrollable */
.p-treetable-scrollable .p-treetable-wrapper {
position: relative;
overflow: auto;
}
.p-treetable-scrollable .p-treetable-table {
display: block;
}
.p-treetable-scrollable .p-treetable-thead,
.p-treetable-scrollable .p-treetable-tbody,
.p-treetable-scrollable .p-treetable-tfoot {
display: block;
}
.p-treetable-scrollable .p-treetable-thead > tr,
.p-treetable-scrollable .p-treetable-tbody > tr,
.p-treetable-scrollable .p-treetable-tfoot > tr {
display: flex;
flex-wrap: nowrap;
width: 100%;
}
.p-treetable-scrollable .p-treetable-thead > tr > th,
.p-treetable-scrollable .p-treetable-tbody > tr > td,
.p-treetable-scrollable .p-treetable-tfoot > tr > td {
display: flex;
flex: 1 1 0;
align-items: center;
}
.p-treetable-scrollable .p-treetable-thead {
position: sticky;
top: 0;
z-index: 1;
}
.p-treetable-scrollable .p-treetable-tfoot {
position: sticky;
bottom: 0;
z-index: 1;
}
.p-treetable-scrollable .p-frozen-column {
position: sticky;
background: inherit;
}
.p-treetable-scrollable-both .p-treetable-thead > tr > th,
.p-treetable-scrollable-both .p-treetable-tbody > tr > td,
.p-treetable-scrollable-both .p-treetable-tfoot > tr > td,
.p-treetable-scrollable-horizontal .p-treetable-thead > tr > th
.p-treetable-scrollable-horizontal .p-treetable-tbody > tr > td,
.p-treetable-scrollable-horizontal .p-treetable-tfoot > tr > td {
flex: 0 0 auto;
}
.p-treetable-flex-scrollable {
display: flex;
flex-direction: column;
height: 100%;
}
.p-treetable-flex-scrollable .p-treetable-wrapper {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
}
</style>

View File

@ -1,7 +1,7 @@
<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">
<td v-if="!columnProp(col, 'hidden')" :style="columnProp(col, 'bodyStyle')" :class="columnProp(col, 'bodyClass')">
<td v-if="!columnProp(col, 'hidden')" :style="[columnProp(col, 'style'),columnProp(col, 'bodyStyle')]" :class="getColumnBodyClass(col)">
<button type="button" class="p-treetable-toggler p-link" @click="toggle" v-if="columnProp(col, 'expander')" :style="togglerStyle" tabindex="-1" v-ripple>
<i :class="togglerIcon"></i>
</button>
@ -245,7 +245,12 @@ export default {
check: event.check,
selectionKeys: _selectionKeys
});
}
},
getColumnBodyClass(column) {
return [this.columnProp(column, 'bodyClass'), {
'p-frozen-column': this.columnProp(column, 'frozen')
}];
},
},
computed: {
containerClass() {

View File

@ -771,6 +771,11 @@ const routes = [
name: 'treetablecolresize',
component: () => import('../views/treetable/TreeTableColResizeDemo.vue')
},
{
path: '/treetable/scroll',
name: 'treetablescroll',
component: () => import('../views/treetable/TreeTableScrollDemo.vue')
},
{
path: '/tristatecheckbox',
name: 'tristatecheckbox',

View File

@ -0,0 +1,244 @@
<template>
<div>
<div class="content-section introduction">
<div class="feature-intro">
<h1>TreeTable <span>Scroll</span></h1>
<p>Data scrolling is available horizontally, vertically or both with support for frozen columns.</p>
</div>
<AppDemoActions />
</div>
<div class="content-section implementation">
<div class="card">
<h5>Vertical</h5>
<TreeTable :value="nodes" style="margin-bottom: 2rem" :scrollable="true" scrollHeight="400px">
<Column field="name" header="Name" :expander="true" style="min-width:200px"></Column>
<Column field="size" header="Size" style="min-width:200px"></Column>
<Column field="type" header="Type" style="min-width:200px"></Column>
</TreeTable>
</div>
<div class="card">
<h5>Flexible Scroll</h5>
<p>Flex scroll feature makes the scrollable viewport section dynamic insteaf of a fixed value so that it can grow or shrink relative to the parent size of the table.
Click the button below to display a maximizable Dialog where data viewport adjusts itself according to the size changes.</p>
<Button label="Show" icon="pi pi-external-link" @click="openDialog" />
</div>
<Dialog header="Flex Scroll" v-model:visible="dialogVisible" :style="{width: '75vw'}" :maximizable="true" :modal="true" :contentStyle="{height: '300px'}">
<TreeTable :value="nodes" :scrollable="true" scrollHeight="flex">
<Column field="name" header="Name" :expander="true" style="min-width:200px"></Column>
<Column field="size" header="Size" style="min-width:200px"></Column>
<Column field="type" header="Type" style="min-width:200px"></Column>
</TreeTable>
<template #footer>
<Button label="Ok" icon="pi pi-check" @click="closeDialog" />
</template>
</Dialog>
<div class="card">
<h5>Horizontal and Vertical with Footer</h5>
<TreeTable :value="nodes" :scrollable="true" scrollHeight="400px" scrollDirection="both">
<Column field="name" header="Name" footer="Name" :expander="true" style="width:300px"></Column>
<Column header="Key" footer="Key" style="width:300px">
<template #body="{node}">
{{node.key}}
</template>
</Column>
<Column field="size" header="Size" footer="Size" style="width:300px"></Column>
<Column field="type" header="Type" footer="Type" style="width:300px"></Column>
<Column header="Children" footer="Children" style="width:300px">
<template #body="{node}">
{{node.children ? node.children.length : 0}}
</template>
</Column>
<Column header="Options" footer="Options" style="width:300px">
<template #body>
<Button type="Button" icon="pi pi-check" label="Edit" class="p-mr-2"></Button>
<Button type="Button" icon="pi pi-check" label="Delete" class="p-button-warning"></Button>
</template>
</Column>
</TreeTable>
</div>
<div class="card">
<h5>Frozen Columns</h5>
<ToggleButton v-model="optionsFrozen" onIcon="pi pi-lock" offIcon="pi pi-lock-open" onLabel="Unfreeze Options" offLabel="Freeze Options" style="width: 12rem" />
<TreeTable :value="nodes" :scrollable="true" scrollHeight="400px" scrollDirection="both" class="p-mt-3">
<Column field="name" header="Name" :expander="true" style="width:300px" frozen></Column>
<Column header="Key" style="width:300px">
<template #body="{node}">
{{node.key}}
</template>
</Column>
<Column field="size" header="Size" style="width:300px"></Column>
<Column field="type" header="Type" style="width:300px"></Column>
<Column header="Children" style="width:300px">
<template #body="{node}">
{{node.children ? node.children.length : 0}}
</template>
</Column>
<Column header="Options" style="width:200px" alignFrozen="right" :frozen="balanceFrozen">
<template #body>
<Button type="Button" icon="pi pi-check" label="Edit Item" class="p-mr-2" style="width:100%"></Button>
</template>
</Column>
</TreeTable>
</div>
</div>
<AppDoc name="TreeTableScrollDemo" :sources="sources" :service="['NodeService']" :data="['treetablenodes']" github="treetable/TreeTableSizeDemo.vue" />
</div>
</template>
<script>
import NodeService from '../../service/NodeService';
export default {
data() {
return {
nodes: null,
dialogVisible: false,
optionsFrozen: false,
sources: {
'options-api': {
tabName: 'Options API Source',
content: `
<template>
<div>
<div class="card">
<TreeTable :value="nodes" class="p-treetable-sm" style="margin-bottom: 2rem">
<template #header>
Small Table
</template>
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
</TreeTable>
</div>
<div class="card">
<TreeTable :value="nodes" style="margin-bottom: 2rem">
<template #header>
Normal Table
</template>
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
</TreeTable>
</div>
<div class="card">
<TreeTable :value="nodes" class="p-treetable-lg" >
<template #header>
Large Table
</template>
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
</TreeTable>
</div>
</div>
</template>
<script>
import NodeService from './service/NodeService';
export default {
data() {
return {
nodes: null
}
},
nodeService: null,
created() {
this.nodeService = new NodeService();
},
mounted() {
this.nodeService.getTreeTableNodes().then(data => this.nodes = data);
}
}
<\\/script>
`
},
'composition-api': {
tabName: 'Composition API Source',
content: `
<template>
<div>
<div class="card">
<TreeTable :value="nodes" class="p-treetable-sm" style="margin-bottom: 2rem">
<template #header>
Small Table
</template>
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
</TreeTable>
</div>
<div class="card">
<TreeTable :value="nodes" style="margin-bottom: 2rem">
<template #header>
Normal Table
</template>
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
</TreeTable>
</div>
<div class="card">
<TreeTable :value="nodes" class="p-treetable-lg" >
<template #header>
Large Table
</template>
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
</TreeTable>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import NodeService from './service/NodeService';
export default {
setup() {
onMounted(() => {
nodeService.value.getTreeTableNodes().then(data => nodes.value = data);
})
const nodes = ref();
const nodeService = ref(new NodeService());
return { nodes, nodeService }
}
}
<\\/script>
`
}
}
}
},
nodeService: null,
created() {
this.nodeService = new NodeService();
},
mounted() {
this.nodeService.getTreeTableNodes().then(data => this.nodes = data);
},
methods: {
openDialog() {
this.dialogVisible = true;
},
closeDialog() {
this.dialogVisible = false;
}
}
}
</script>