fix: table header not fixed on top when fast scrolling

This commit is contained in:
Fabio 2020-08-10 16:06:11 +02:00
parent a15e6249e1
commit 13b0816837
4 changed files with 105 additions and 79 deletions

View File

@ -1,5 +1,5 @@
<template>
<div :style="{'height': visibleHeight+'px'}" class="vscroll-holder">
<div class="vscroll-holder">
<div
class="vscroll-spacer"
:style="{
@ -26,48 +26,54 @@ export default {
props: {
items: Array,
itemHeight: Number,
visibleHeight: Number
visibleHeight: Number,
scrollElement: {
type: HTMLDivElement,
default: null
}
},
data () {
return {
topHeight: 0,
bottomHeight: 0,
visibleItems: [],
renderTimeout: null
renderTimeout: null,
localScrollElement: null
};
},
mounted () {
this._checkScrollPosition = this.updateWindow.bind(this);
this._checkScrollPosition = this.checkScrollPosition.bind(this);
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
this.updateWindow();
this.$el.addEventListener('scroll', this._checkScrollPosition);
this.localScrollElement.addEventListener('scroll', this._checkScrollPosition);
},
beforeDestroy () {
this.$el.removeEventListener('scroll', this._checkScrollPosition);
this.localScrollElement.removeEventListener('scroll', this._checkScrollPosition);
},
methods: {
checkScrollPosition () {
},
updateWindow (e) { // TODO: no timeout on first render
const visibleItemsCount = Math.ceil(this.$el.clientHeight / this.itemHeight);
const totalScrollHeight = this.items.length * this.itemHeight;
const offset = 50;
const scrollTop = this.$el.scrollTop;
checkScrollPosition (e) {
clearTimeout(this.renderTimeout);
this.renderTimeout = setTimeout(() => {
const firstVisibleIndex = Math.floor(scrollTop / this.itemHeight);
const lastVisibleIndex = firstVisibleIndex + visibleItemsCount;
const firstCutIndex = Math.max(firstVisibleIndex - offset, 0);
const lastCutIndex = lastVisibleIndex + offset;
this.visibleItems = this.items.slice(firstCutIndex, lastCutIndex);
this.topHeight = firstCutIndex * this.itemHeight;
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
this.updateWindow(e);
}, 200);
},
updateWindow (e) {
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
const totalScrollHeight = this.items.length * this.itemHeight;
const offset = 50;
const scrollTop = this.localScrollElement.scrollTop;
const firstVisibleIndex = Math.floor(scrollTop / this.itemHeight);
const lastVisibleIndex = firstVisibleIndex + visibleItemsCount;
const firstCutIndex = Math.max(firstVisibleIndex - offset, 0);
const lastCutIndex = lastVisibleIndex + offset;
this.visibleItems = this.items.slice(firstCutIndex, lastCutIndex);
this.topHeight = firstCutIndex * this.itemHeight;
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
}
}
};

View File

@ -3,6 +3,15 @@
<WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
<div v-if="workspace.connected" class="workspace-tabs column columns col-gapless">
<ul class="tab tab-block column col-12">
<li
v-if="workspace.breadcrumbs.table"
class="tab-item"
>
<a class="tab-link">
<i class="material-icons md-18 mr-1">tune</i>
<span :title="workspace.breadcrumbs.table">{{ $t('word.properties').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span>
</a>
</li>
<li
v-if="workspace.breadcrumbs.table"
class="tab-item"
@ -11,7 +20,7 @@
>
<a class="tab-link">
<i class="material-icons md-18 mr-1">grid_on</i>
<span :title="workspace.breadcrumbs.table">{{ workspace.breadcrumbs.table }}</span>
<span :title="workspace.breadcrumbs.table">{{ $t('word.data').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span>
</a>
</li>
<li
@ -109,10 +118,6 @@ export default {
width: fit-content;
flex: initial;
&.active a {
opacity: 1;
}
> a {
padding: 0.2rem 0.8rem;
color: $body-font-color;
@ -132,6 +137,10 @@ export default {
text-overflow: ellipsis;
}
}
&.active a {
opacity: 1;
}
}
}
}
@ -155,6 +164,7 @@ export default {
padding: 0;
font-weight: 700;
font-size: 0.7rem;
z-index: 1;
> div {
padding: 0.1rem 0.4rem;
@ -172,6 +182,7 @@ export default {
white-space: nowrap;
overflow: hidden;
font-size: 0.7rem;
position: relative;
&:focus {
box-shadow: inset 0 0 0 1px $body-font-color;

View File

@ -1,5 +1,9 @@
<template>
<div>
<div
ref="tableWrapper"
class="vscroll"
:style="{'height': resultsSize+'px'}"
>
<TableContext
v-if="isContext"
:context-event="contextEvent"
@ -7,55 +11,53 @@
@deleteSelected="deleteSelected"
@closeContext="isContext = false"
/>
<BaseVirtualScroll
v-if="results.rows"
ref="resultTable"
:items="sortedResults"
:item-height="22"
class="vscroll"
:style="{'height': resultsSize+'px'}"
:visible-height="resultsSize"
>
<template slot-scope="{ items }">
<div class="table table-hover">
<div class="thead">
<div class="tr">
<div
v-for="field in fields"
:key="field.name"
class="th c-hand"
>
<div ref="columnResize" class="column-resizable">
<div class="table-column-title" @click="sort(field.name)">
<i
v-if="field.key"
class="material-icons column-key c-help"
:class="`key-${field.key}`"
:title="keyName(field.key)"
>vpn_key</i>
<span>{{ field.name }}</span>
<i v-if="currentSort === field.name" class="material-icons sort-icon">{{ currentSortDir === 'asc' ? 'arrow_upward':'arrow_downward' }}</i>
</div>
</div>
<div ref="table" class="table table-hover">
<div class="thead">
<div class="tr">
<div
v-for="field in fields"
:key="field.name"
class="th c-hand"
>
<div ref="columnResize" class="column-resizable">
<div class="table-column-title" @click="sort(field.name)">
<i
v-if="field.key"
class="material-icons column-key c-help"
:class="`key-${field.key}`"
:title="keyName(field.key)"
>vpn_key</i>
<span>{{ field.name }}</span>
<i v-if="currentSort === field.name" class="material-icons sort-icon">{{ currentSortDir === 'asc' ? 'arrow_upward':'arrow_downward' }}</i>
</div>
</div>
</div>
<div class="tbody">
<WorkspaceQueryTableRow
v-for="row in items"
:key="row._id"
:row="row"
:fields="fields"
class="tr"
:class="{'selected': selectedRows.includes(row._id)}"
@selectRow="selectRow($event, row._id)"
@updateField="updateField($event, row[primaryField.name])"
@contextmenu="contextMenu"
/>
</div>
</div>
</template>
</BaseVirtualScroll>
</div>
<BaseVirtualScroll
v-if="results.rows"
ref="resultTable"
:items="sortedResults"
:item-height="22"
class="tbody"
:visible-height="resultsSize"
:scroll-element="scrollElement"
>
<template slot-scope="{ items }">
<WorkspaceQueryTableRow
v-for="row in items"
:key="row._id"
:row="row"
:fields="fields"
class="tr"
:class="{'selected': selectedRows.includes(row._id)}"
@selectRow="selectRow($event, row._id)"
@updateField="updateField($event, row[primaryField.name])"
@contextmenu="contextMenu"
/>
</template>
</basevirtualscroll>
</div>
</div>
</template>
@ -107,6 +109,9 @@ export default {
}
else
return this.localResults;
},
scrollElement () {
return this.$refs.tableWrapper;
}
},
watch: {
@ -118,7 +123,7 @@ export default {
}
},
updated () {
if (this.$refs.resultTable)
if (this.$refs.table)
this.refreshScroller();
},
mounted () {
@ -161,10 +166,10 @@ export default {
},
resizeResults () {
if (this.$refs.resultTable) {
const el = this.$refs.resultTable.$el;
const footer = document.getElementById('footer');
const el = this.$refs.table;
if (el) {
const footer = document.getElementById('footer');
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
this.resultsSize = size;
}

View File

@ -2,6 +2,7 @@
<div class="tr" @click="selectRow($event, row._id)">
<div
v-for="(col, cKey) in row"
v-show="cKey !== '_id'"
:key="cKey"
class="td p-0"
tabindex="0"
@ -376,6 +377,9 @@ export default {
border: none;
line-height: 1;
width: 100%;
position: absolute;
left: 0;
right: 0;
}
.cell-content {