Refactor #5678 - TreeTable CSS and responsive structure improvements

pull/5701/head
tugcekucukoglu 2024-05-03 09:20:10 +03:00
parent 2c65e1b3fd
commit 5cb78b9b3c
7 changed files with 93 additions and 67 deletions

View File

@ -210,12 +210,6 @@ const TreeTableProps = [
default: 'false',
description: 'When specified, enables horizontal and/or vertical scrolling.'
},
{
name: 'scrollDirection',
type: 'string',
default: 'vertical',
description: 'Orientation of the scrolling, options are "vertical", "horizontal" and "both".'
},
{
name: 'scrollHeight',
type: 'string',

View File

@ -150,22 +150,26 @@ export default {
type: Boolean,
default: false
},
scrollDirection: {
type: String,
default: 'vertical'
},
scrollHeight: {
type: String,
default: null
},
responsiveLayout: {
type: String,
default: null
default: 'scroll'
},
size: {
type: String,
default: null
},
tableStyle: {
type: null,
default: null
},
tableClass: {
type: [String, Object],
default: null
},
tableProps: {
type: Object,
default: null

View File

@ -137,7 +137,6 @@ export default {
selected: this.$parent.selected,
frozen: this.columnProp('frozen'),
scrollable: this.$parentInstance.scrollable,
scrollDirection: this.$parentInstance.scrollDirection,
showGridlines: this.$parentInstance.showGridlines,
size: this.$parentInstance?.size
}

View File

@ -99,7 +99,6 @@ export default {
frozen: this.$parentInstance.scrollable && this.columnProp('frozen'),
resizable: this.resizableColumns,
scrollable: this.$parentInstance.scrollable,
scrollDirection: this.$parentInstance.scrollDirection,
showGridlines: this.$parentInstance.showGridlines,
size: this.$parentInstance?.size
}

View File

@ -586,20 +586,18 @@ export interface TreeTableProps {
* Height of the scroll viewport in fixed pixels or the 'flex' keyword for a dynamic size.
*/
scrollHeight?: HintedString<'flex'> | undefined;
/**
* Orientation of the scrolling.
* @defaultValue vertical
*/
scrollDirection?: 'vertical' | 'horizontal' | 'both' | undefined;
/**
* Defines the responsive mode, currently only option is scroll.
* @defaultValue stack
*/
responsiveLayout?: 'stack' | 'scroll' | undefined;
/**
* Defines the size of the table.
*/
size?: 'small' | 'large' | undefined;
/**
* Inline style of the table element.
*/
tableStyle?: string | object | undefined;
/**
* Style class of the table element.
*/
tableClass?: string | object | undefined;
/**
* Props to pass to the table element.
*/

View File

@ -1,5 +1,5 @@
<template>
<div :class="cx('root')" data-scrollselectors=".p-treetable-scrollable-body" role="table" v-bind="ptmi('root')">
<div :class="cx('root')" data-scrollselectors=".p-treetable-scrollable-body" v-bind="ptmi('root')">
<slot></slot>
<div v-if="loading && loadingMode === 'mask'" :class="cx('loading')" v-bind="ptm('loading')">
<div :class="cx('mask')" v-bind="ptm('mask')">
@ -51,9 +51,9 @@
<slot name="paginatorrowsperpagedropdownicon" :class="slotProps.class"></slot>
</template>
</TTPaginator>
<div :class="cx('tableContainer')" :style="{ maxHeight: scrollHeight }" v-bind="ptm('tableContainer')">
<table ref="table" role="table" v-bind="{ ...tableProps, ...ptm('table') }">
<thead :class="cx('thead')" role="rowgroup" v-bind="ptm('thead')">
<div :class="cx('tableContainer')" :style="[sx('tableContainer'), { maxHeight: scrollHeight }]" v-bind="ptm('tableContainer')">
<table ref="table" role="table" :class="[cx('table'), tableClass]" :style="tableStyle" v-bind="{ ...tableProps, ...ptm('table') }">
<thead :class="cx('thead')" :style="sx('thead')" role="rowgroup" v-bind="ptm('thead')">
<tr role="row" v-bind="ptm('headerRow')">
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<TTHeaderCell
@ -111,7 +111,7 @@
</td>
</tr>
</tbody>
<tfoot v-if="hasFooter" :class="cx('tfoot')" role="rowgroup" v-bind="ptm('tfoot')">
<tfoot v-if="hasFooter" :class="cx('tfoot')" :style="sx('tfoot')" role="rowgroup" v-bind="ptm('tfoot')">
<tr role="row" v-bind="ptm('footerRow')">
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<TTFooterCell v-if="!columnProp(col, 'hidden')" :column="col" :index="i" :unstyled="unstyled" :pt="pt"></TTFooterCell>
@ -171,7 +171,7 @@
import { FilterService } from 'primevue/api';
import SpinnerIcon from 'primevue/icons/spinner';
import Paginator from 'primevue/paginator';
import { DomHandler, HelperSet, ObjectUtils } from 'primevue/utils';
import { DomHandler, HelperSet, ObjectUtils, UniqueComponentId } from 'primevue/utils';
import BaseTreeTable from './BaseTreeTable.vue';
import FooterCell from './FooterCell.vue';
import HeaderCell from './HeaderCell.vue';
@ -240,16 +240,10 @@ export default {
}
},
mounted() {
if (this.scrollable && this.scrollDirection !== 'vertical') {
this.updateScrollWidth();
}
},
updated() {
if (this.scrollable && this.scrollDirection !== 'vertical') {
this.updateScrollWidth();
}
this.$el.setAttribute(this.attributeSelector, '');
},
beforeUnmount() {
this.destroyStyleElement();
this.d_columns.clear();
},
methods: {
@ -649,21 +643,18 @@ export default {
let nextColumnWidth = nextColumn.offsetWidth - delta;
if (newColumnWidth > 15 && nextColumnWidth > 15) {
if (!this.scrollable) {
this.resizeColumnElement.style.width = newColumnWidth + 'px';
if (nextColumn) {
nextColumn.style.width = nextColumnWidth + 'px';
}
} else {
this.resizeTableCells(newColumnWidth, nextColumnWidth);
}
}
} else if (this.columnResizeMode === 'expand') {
this.$refs.table.style.width = this.$refs.table.offsetWidth + delta + 'px';
const tableWidth = this.$refs.table.offsetWidth + delta + 'px';
if (!this.scrollable) this.resizeColumnElement.style.width = newColumnWidth + 'px';
else this.resizeTableCells(newColumnWidth);
const updateTableWidth = (el) => {
!!el && (el.style.width = el.style.minWidth = tableWidth);
};
// Reasoning: resize table cells before updating the table width so that it can use existing computed cell widths and adjust only the one column.
this.resizeTableCells(newColumnWidth);
updateTableWidth(this.$refs.table);
}
this.$emit('column-resize-end', {
@ -681,23 +672,31 @@ export default {
},
resizeTableCells(newColumnWidth, nextColumnWidth) {
let colIndex = DomHandler.index(this.resizeColumnElement);
let children = this.$refs.table.children;
let widths = [];
let headers = DomHandler.find(this.$refs.table, 'thead[data-pc-section="thead"] > tr > th');
for (let child of children) {
for (let row of child.children) {
let resizeCell = row.children[colIndex];
headers.forEach((header) => widths.push(DomHandler.getOuterWidth(header)));
resizeCell.style.flex = '0 0 ' + newColumnWidth + 'px';
this.destroyStyleElement();
this.createStyleElement();
if (this.columnResizeMode === 'fit') {
let nextCell = resizeCell.nextElementSibling;
let innerHTML = '';
let selector = `[data-pc-name="treetable"][${this.attributeSelector}] > [data-pc-section="tablecontainer"] > table[data-pc-section="table"]`;
if (nextCell) {
nextCell.style.flex = '0 0 ' + nextColumnWidth + 'px';
}
}
}
widths.forEach((width, index) => {
let colWidth = index === colIndex ? newColumnWidth : nextColumnWidth && index === colIndex + 1 ? nextColumnWidth : width;
let style = `width: ${colWidth}px !important; max-width: ${colWidth}px !important`;
innerHTML += `
${selector} > thead[data-pc-section="thead"] > tr > th:nth-child(${index + 1}),
${selector} > tbody[data-pc-section="tbody"] > tr > td:nth-child(${index + 1}),
${selector} > tfoot[data-pc-section="tfoot"] > tr > td:nth-child(${index + 1}) {
${style}
}
`;
});
this.styleElement.innerHTML = innerHTML;
},
bindColumnResizeEvents() {
if (!this.documentColumnResizeListener) {
@ -750,12 +749,21 @@ export default {
hasGlobalFilter() {
return this.filters && Object.prototype.hasOwnProperty.call(this.filters, 'global');
},
updateScrollWidth() {
this.$refs.table.style.width = this.$refs.table.scrollWidth + 'px';
},
getItemLabel(node) {
return node.data.name;
},
createStyleElement() {
this.styleElement = document.createElement('style');
this.styleElement.type = 'text/css';
DomHandler.setAttribute(this.styleElement, 'nonce', this.$primevue?.config?.csp?.nonce);
document.head.appendChild(this.styleElement);
},
destroyStyleElement() {
if (this.styleElement) {
document.head.removeChild(this.styleElement);
this.styleElement = null;
}
},
setTabindex(node, index) {
if (this.isNodeSelected(node)) {
this.hasASelectedNode = true;
@ -852,6 +860,9 @@ export default {
return data ? data.length : 0;
}
},
attributeSelector() {
return UniqueComponentId();
}
},
components: {

View File

@ -427,6 +427,7 @@ p-treetable-gridlines .p-treetable-tbody > tr:last-child>td {
transition: background-color ${dt('transition.duration')}, color ${dt('transition.duration')}, border-color ${dt('transition.duration')}, box-shadow ${dt('transition.duration')}, outline-color ${dt('transition.duration')};
outline-color: transparent;
user-select: none;
margin-right: 0.5rem;
}
.p-treetable-row-toggle-button:enabled:hover {
@ -443,6 +444,11 @@ p-treetable-gridlines .p-treetable-tbody > tr:last-child>td {
background: ${dt('treetable.row.action.highlight.hover.background')};
color: inherit;
}
.p-treetable .p-treetable-row-checkbox {
vertical-align: middle;
margin-right: 0.5rem;
}
`;
const classes = {
@ -465,12 +471,20 @@ const classes = {
header: 'p-treetable-header',
paginator: ({ position }) => 'p-treetable-paginator-' + position,
tableContainer: 'p-treetable-table-container',
table: ({ props }) => [
'p-treetable-table',
{
'p-treetable-scrollable-table': props.scrollable,
'p-treetable-resizable-table': props.resizableColumns,
'p-treetable-resizable-table-fit': props.resizableColumns && props.columnResizeMode === 'fit'
}
],
thead: 'p-treetable-thead',
headerCell: ({ instance, props }) => [
headerCell: ({ instance, props, context }) => [
{
'p-treetable-sortable-column': instance.columnProp('sortable'),
'p-treetable-resizable-column': props.resizableColumns,
'p-treetable-column-sorted': instance.isColumnSorted(),
'p-treetable-column-sorted': context?.sorted,
'p-treetable-frozen-column': instance.columnProp('frozen')
}
],
@ -503,8 +517,15 @@ const classes = {
columnResizeHelper: 'p-treetable-column-resize-indicator'
};
const inlineStyles = {
tableContainer: { overflow: 'auto' },
thead: { position: 'sticky' },
tfoot: { position: 'sticky' }
};
export default BaseStyle.extend({
name: 'treetable',
theme,
classes
classes,
inlineStyles
});