Fixed #30 - Resizable Columns for DataTable

pull/41/head
cagataycivici 2019-08-07 16:50:07 +03:00
parent 8392145dee
commit 6049081220
7 changed files with 258 additions and 4 deletions

View File

@ -32,11 +32,14 @@ export declare class DataTable extends Vue {
csvSeparator?: string; csvSeparator?: string;
exportFilename?: string; exportFilename?: string;
autoLayout?: boolean; autoLayout?: boolean;
resizableColumns?: boolean;
columnResizeMode?: string;
$emit(eventName: 'page', event: Event): this; $emit(eventName: 'page', event: Event): this;
$emit(eventName: 'sort', event: Event): this; $emit(eventName: 'sort', event: Event): this;
$emit(eventName: 'filter', event: Event): this; $emit(eventName: 'filter', event: Event): this;
$emit(eventName: 'row-select', event: Event): this; $emit(eventName: 'row-select', event: Event): this;
$emit(eventName: 'row-unselect', event: Event): this; $emit(eventName: 'row-unselect', event: Event): this;
$emit(eventName: 'column-resize-end', event: Event): this;
$slots: { $slots: {
header: VNode[]; header: VNode[];
paginatorLeft: VNode[]; paginatorLeft: VNode[];

View File

@ -20,10 +20,11 @@
<slot name="paginatorRight"></slot> <slot name="paginatorRight"></slot>
</template> </template>
</DTPaginator> </DTPaginator>
<table> <table ref="table">
<thead class="p-datatable-thead"> <thead class="p-datatable-thead">
<tr> <tr>
<th v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.headerStyle" :class="getColumnHeaderClass(col)" @click="onColumnHeaderClick($event, col)"> <th v-for="(col,i) of columns" :key="col.columnKey||col.field||i" :style="col.headerStyle" :class="getColumnHeaderClass(col)" @click="onColumnHeaderClick($event, col)">
<span class="p-column-resizer p-clickable" @mousedown="onColumnResizeStart" v-if="resizableColumns"></span>
<ColumnSlot :column="col" type="header" v-if="col.$scopedSlots.header" /> <ColumnSlot :column="col" type="header" v-if="col.$scopedSlots.header" />
<span class="p-column-title" v-if="col.header">{{col.header}}</span> <span class="p-column-title" v-if="col.header">{{col.header}}</span>
<span v-if="col.sortable" :class="getSortableColumnIcon(col)"></span> <span v-if="col.sortable" :class="getSortableColumnIcon(col)"></span>
@ -74,6 +75,7 @@
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>
</div> </div>
<div ref="resizeHelper" class="p-column-resizer-helper p-highlight" style="display: none"></div>
</div> </div>
</template> </template>
@ -228,6 +230,14 @@ export default {
autoLayout: { autoLayout: {
type: Boolean, type: Boolean,
default: false default: false
},
resizableColumns: {
type: Boolean,
default: false
},
columnResizeMode: {
type: String,
default: 'fit'
} }
}, },
data() { data() {
@ -244,6 +254,10 @@ export default {
rowTouched: false, rowTouched: false,
anchorRowIndex: null, anchorRowIndex: null,
rangeRowIndex: null, rangeRowIndex: null,
documentColumnResizeListener: null,
documentColumnResizeEndListener: null,
lastResizeHelperX: null,
resizeColumnElement: null,
watch: { watch: {
first(newValue) { first(newValue) {
this.d_first = newValue; this.d_first = newValue;
@ -269,6 +283,9 @@ export default {
mounted() { mounted() {
this.allChildren = this.$children; this.allChildren = this.$children;
}, },
beforeDestroy() {
this.unbindColumnResizeEvents();
},
methods: { methods: {
resolveFieldData(rowData, field) { resolveFieldData(rowData, field) {
return ObjectUtils.resolveFieldData(rowData, field); return ObjectUtils.resolveFieldData(rowData, field);
@ -388,6 +405,7 @@ export default {
return [column.headerClass, return [column.headerClass,
{'p-sortable-column': column.sortable}, {'p-sortable-column': column.sortable},
{'p-resizable-column': this.resizableColumns},
{'p-highlight': sorted} {'p-highlight': sorted}
]; ];
}, },
@ -834,6 +852,88 @@ export default {
resetPage() { resetPage() {
this.d_first = 0; this.d_first = 0;
this.$emit('update:first', this.d_first); this.$emit('update:first', this.d_first);
},
onColumnResizeStart(event) {
let containerLeft = DomHandler.getOffset(this.$el).left;
this.resizeColumnElement = event.target.parentElement;
this.columnResizing = true;
this.lastResizeHelperX = (event.pageX - containerLeft + this.$el.scrollLeft);
this.bindColumnResizeEvents();
},
onColumnResize(event) {
let containerLeft = DomHandler.getOffset(this.$el).left;
DomHandler.addClass(this.$el, 'p-unselectable-text');
this.$refs.resizeHelper.style.height = this.$el.offsetHeight + 'px';
this.$refs.resizeHelper.style.top = 0 + 'px';
this.$refs.resizeHelper.style.left = (event.pageX - containerLeft + this.$el.scrollLeft) + 'px';
this.$refs.resizeHelper.style.display = 'block';
},
onColumnResizeEnd(event) {
let delta = this.$refs.resizeHelper.offsetLeft - this.lastResizeHelperX;
let columnWidth = this.resizeColumnElement.offsetWidth;
let newColumnWidth = columnWidth + delta;
let minWidth = this.resizeColumnElement.style.minWidth||15;
if(columnWidth + delta > parseInt(minWidth, 10)) {
if(this.columnResizeMode === 'fit') {
let nextColumn = this.resizeColumnElement.nextElementSibling;
let nextColumnWidth = nextColumn.offsetWidth - delta;
if(newColumnWidth > 15 && nextColumnWidth > 15) {
this.resizeColumnElement.style.width = newColumnWidth + 'px';
if(nextColumn) {
nextColumn.style.width = nextColumnWidth + 'px';
}
}
}
else if(this.columnResizeMode === 'expand') {
this.$refs.table.style.width = this.$refs.table.offsetWidth + delta + 'px';
this.resizeColumnElement.style.width = newColumnWidth + 'px';
}
this.$emit('column-resize-end', {
element: this.resizeColumnElement,
delta: delta
});
}
this.$refs.resizeHelper.style.display = 'none';
this.resizeColumn = null;
DomHandler.removeClass(this.$el, 'p-unselectable-text');
this.unbindColumnResizeEvents();
},
bindColumnResizeEvents() {
if (!this.documentColumnResizeListener) {
this.documentColumnResizeListener = document.addEventListener('mousemove', (event) => {
if(this.columnResizing) {
this.onColumnResize(event);
}
});
}
if (!this.documentColumnResizeEndListener) {
this.documentColumnResizeEndListener = document.addEventListener('mouseup', (event) => {
if(this.columnResizing) {
this.columnResizing = false;
this.onColumnResizeEnd(event);
}
});
}
},
unbindColumnResizeEvents() {
if (this.documentColumnResizeListener) {
document.removeEventListener('document', this.documentColumnResizeListener);
this.documentColumnResizeListener = null;
}
if (this.documentColumnResizeEndListener) {
document.removeEventListener('document', this.documentColumnResizeEndListener);
this.documentColumnResizeEndListener = null;
}
} }
}, },
computed: { computed: {
@ -841,7 +941,9 @@ export default {
return [ return [
'p-datatable p-component', { 'p-datatable p-component', {
'p-datatable-hoverable-rows': (this.rowHover || this.selectionMode), 'p-datatable-hoverable-rows': (this.rowHover || this.selectionMode),
'p-datatable-auto-layout': this.autoLayout 'p-datatable-auto-layout': this.autoLayout,
'p-datatable-resizable': this.resizableColumns,
'p-datatable-resizable-fit': this.resizableColumns && this.columnResizeMode === 'fit',
} }
]; ];
}, },

View File

@ -156,6 +156,11 @@ export default new Router({
name: 'datatableexport', name: 'datatableexport',
component: () => import('./views/datatable/DataTableExportDemo.vue') component: () => import('./views/datatable/DataTableExportDemo.vue')
}, },
{
path: '/datatable/colresize',
name: 'datatablecolresize',
component: () => import('./views/datatable/DataTableColResizeDemo.vue')
},
{ {
path: '/datatable/crud', path: '/datatable/crud',
name: 'datatablecrud', name: 'datatablecrud',

View File

@ -0,0 +1,99 @@
<template>
<div>
<DataTableSubMenu />
<div class="content-section introduction">
<div class="feature-intro">
<h1>DataTable - Column Resize</h1>
<p>Columns can be resized using drag drop by setting the resizableColumns to true. There are two resize modes; "fit" and "expand". Fit is the default one and the overall table width does not change when a column is resized.
In "expand" mode, table width also changes along with the column width. onColumnResize is a callback that passes the resized column header as a parameter.</p>
</div>
</div>
<div class="content-section implementation">
<h3>Fit Mode</h3>
<DataTable :value="cars" :resizableColumns="true" columnResizeMode="fit">
<Column field="vin" header="Vin" :resizable="true"></Column>
<Column field="year" header="Year" :resizable="true"></Column>
<Column field="brand" header="Brand" :resizable="true"></Column>
<Column field="color" header="Color" :resizable="true"></Column>
</DataTable>
<h3>Expand Mode</h3>
<DataTable :value="cars" :resizableColumns="true" columnResizeMode="expand">
<Column field="vin" header="Vin" :resizable="true"></Column>
<Column field="year" header="Year" :resizable="true"></Column>
<Column field="brand" header="Brand" :resizable="true"></Column>
<Column field="color" header="Color" :resizable="true"></Column>
</DataTable>
</div>
<div class="content-section documentation">
<TabView>
<TabPanel header="Source">
<CodeHighlight>
<template v-pre>
&lt;h3&gt;Fit Mode&lt;/h3&gt;
&lt;DataTable :value="cars" :resizableColumns="true" columnResizeMode="fit"&gt;
&lt;Column field="vin" header="Vin" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="year" header="Year" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="brand" header="Brand" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="color" header="Color" :resizable="true"&gt;&lt;/Column&gt;
&lt;/DataTable&gt;
&lt;h3&gt;Expand Mode&lt;/h3&gt;
&lt;DataTable :value="cars" :resizableColumns="true" columnResizeMode="expand"&gt;
&lt;Column field="vin" header="Vin" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="year" header="Year" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="brand" header="Brand" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="color" header="Color" :resizable="true"&gt;&lt;/Column&gt;
&lt;/DataTable&gt;
</template>
</CodeHighlight>
<CodeHighlight lang="javascript">
import CarService from '../../service/CarService';
export default {
data() {
return {
cars: null
}
},
carService: null,
created() {
this.carService = new CarService();
},
mounted() {
this.carService.getCarsSmall().then(data => this.cars = data);
}
}
</CodeHighlight>
</TabPanel>
</TabView>
</div>
</div>
</template>
<script>
import CarService from '../../service/CarService';
import DataTableSubMenu from './DataTableSubMenu';
export default {
data() {
return {
cars: null
}
},
carService: null,
created() {
this.carService = new CarService();
},
mounted() {
this.carService.getCarsSmall().then(data => this.cars = data);
},
components: {
'DataTableSubMenu': DataTableSubMenu
}
}
</script>

View File

@ -532,6 +532,34 @@ data() {
&lt;Column field="color" header="Color"&gt;&lt;/Column&gt; &lt;Column field="color" header="Color"&gt;&lt;/Column&gt;
&lt;/DataTable&gt; &lt;/DataTable&gt;
</template> </template>
</CodeHighlight>
<h3>Column Resize</h3>
<p>Columns can be resized using drag drop by setting the <i>resizableColumns</i> to true. There are two resize modes; "fit" and "expand". Fit is the default one and the overall table width does not change when a column is resized.
In "expand" mode, table width also changes along with the column width. <i>column-resize-end</i> is a callback that passes the resized column header and delta change as a parameter.</p>
<CodeHighlight>
<template v-pre>
&lt;DataTable :value="cars" :resizableColumns="true" columnResizeMode="fit | expand"&gt;
&lt;Column field="vin" header="Vin" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="year" header="Year" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="brand" header="Brand" :resizable="true"&gt;&lt;/Column&gt;
&lt;Column field="color" header="Color" :resizable="true"&gt;&lt;/Column&gt;
&lt;/DataTable&gt;
</template>
</CodeHighlight>
</CodeHighlight>
<p>It is important to note that when you need to change column widths, since table width is 100%, giving fixed pixel widths does not work well as browsers scale them, instead give percentage widths.</p>
<CodeHighlight>
<template v-pre>
&lt;DataTable :value="cars" :resizableColumns="true" columnResizeMode="fit | expand"&gt;
&lt;Column field="vin" header="Vin" :resizable="true" headerStyle="width: 20%"&gt;&lt;/Column&gt;
&lt;Column field="year" header="Year" :resizable="true" headerStyle="width: 40%"&gt;&lt;/Column&gt;
&lt;Column field="brand" header="Brand" :resizable="true" headerStyle="width: 20%"&gt;&lt;/Column&gt;
&lt;Column field="color" header="Color" :resizable="true" headerStyle="width: 20%"&gt;&lt;/Column&gt;
&lt;/DataTable&gt;
</template>
</CodeHighlight>
</CodeHighlight> </CodeHighlight>
<h3>Data Export</h3> <h3>Data Export</h3>
@ -918,6 +946,18 @@ export default {
<td>false</td> <td>false</td>
<td>Whether the cell widths scale according to their content or not.</td> <td>Whether the cell widths scale according to their content or not.</td>
</tr> </tr>
<tr>
<td>resizableColumns</td>
<td>boolean</td>
<td>false</td>
<td>When enabled, columns can be resized using drag and drop.</td>
</tr>
<tr>
<td>columnResizeMode</td>
<td>string</td>
<td>fit</td>
<td>Defines whether the overall table width should change on column resize, <br/> valid values are "fit" and "expand".</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -970,6 +1010,12 @@ export default {
event.type: Type of the selection, valid values are "row", "radio" or "checkbox".</td> event.type: Type of the selection, valid values are "row", "radio" or "checkbox".</td>
<td>Callback to invoke when a row is unselected.</td> <td>Callback to invoke when a row is unselected.</td>
</tr> </tr>
<tr>
<td>column-resize-end</td>
<td>event.element: DOM element of the resized column.<br />
event.delta: Change in column width</td>
<td>Callback to invoke when a column is resized.</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -11,6 +11,7 @@
<li><router-link to="/datatable/coltoggle">&#9679; ColToggle</router-link></li> <li><router-link to="/datatable/coltoggle">&#9679; ColToggle</router-link></li>
<li><router-link to="/datatable/responsive">&#9679; Responsive</router-link></li> <li><router-link to="/datatable/responsive">&#9679; Responsive</router-link></li>
<li><router-link to="/datatable/export">&#9679; Export</router-link></li> <li><router-link to="/datatable/export">&#9679; Export</router-link></li>
<li><router-link to="/datatable/colresize">&#9679; ColResize</router-link></li>
<li><router-link to="/datatable/crud">&#9679; Crud</router-link></li> <li><router-link to="/datatable/crud">&#9679; Crud</router-link></li>
</ul> </ul>
</div> </div>

View File

@ -30,7 +30,6 @@
<script> <script>
import NodeService from '../../service/NodeService'; import NodeService from '../../service/NodeService';
import TreeTableDoc from './TreeTableDoc';
import TreeTableSubMenu from './TreeTableSubMenu'; import TreeTableSubMenu from './TreeTableSubMenu';
export default { export default {
@ -47,7 +46,6 @@ export default {
this.nodeService.getTreeTableNodes().then(data => this.nodes = data); this.nodeService.getTreeTableNodes().then(data => this.nodes = data);
}, },
components: { components: {
'TreeTableDoc': TreeTableDoc,
'TreeTableSubMenu': TreeTableSubMenu 'TreeTableSubMenu': TreeTableSubMenu
} }
} }