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> <template>
<div :style="{'height': visibleHeight+'px'}" class="vscroll-holder"> <div class="vscroll-holder">
<div <div
class="vscroll-spacer" class="vscroll-spacer"
:style="{ :style="{
@ -26,48 +26,54 @@ export default {
props: { props: {
items: Array, items: Array,
itemHeight: Number, itemHeight: Number,
visibleHeight: Number visibleHeight: Number,
scrollElement: {
type: HTMLDivElement,
default: null
}
}, },
data () { data () {
return { return {
topHeight: 0, topHeight: 0,
bottomHeight: 0, bottomHeight: 0,
visibleItems: [], visibleItems: [],
renderTimeout: null renderTimeout: null,
localScrollElement: null
}; };
}, },
mounted () { mounted () {
this._checkScrollPosition = this.updateWindow.bind(this); this._checkScrollPosition = this.checkScrollPosition.bind(this);
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
this.updateWindow(); this.updateWindow();
this.$el.addEventListener('scroll', this._checkScrollPosition); this.localScrollElement.addEventListener('scroll', this._checkScrollPosition);
}, },
beforeDestroy () { beforeDestroy () {
this.$el.removeEventListener('scroll', this._checkScrollPosition); this.localScrollElement.removeEventListener('scroll', this._checkScrollPosition);
}, },
methods: { methods: {
checkScrollPosition () { checkScrollPosition (e) {
},
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;
clearTimeout(this.renderTimeout); clearTimeout(this.renderTimeout);
this.renderTimeout = setTimeout(() => { this.renderTimeout = setTimeout(() => {
const firstVisibleIndex = Math.floor(scrollTop / this.itemHeight); this.updateWindow(e);
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;
}, 200); }, 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" /> <WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
<div v-if="workspace.connected" class="workspace-tabs column columns col-gapless"> <div v-if="workspace.connected" class="workspace-tabs column columns col-gapless">
<ul class="tab tab-block column col-12"> <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 <li
v-if="workspace.breadcrumbs.table" v-if="workspace.breadcrumbs.table"
class="tab-item" class="tab-item"
@ -11,7 +20,7 @@
> >
<a class="tab-link"> <a class="tab-link">
<i class="material-icons md-18 mr-1">grid_on</i> <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> </a>
</li> </li>
<li <li
@ -109,10 +118,6 @@ export default {
width: fit-content; width: fit-content;
flex: initial; flex: initial;
&.active a {
opacity: 1;
}
> a { > a {
padding: 0.2rem 0.8rem; padding: 0.2rem 0.8rem;
color: $body-font-color; color: $body-font-color;
@ -132,6 +137,10 @@ export default {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
} }
&.active a {
opacity: 1;
}
} }
} }
} }
@ -155,6 +164,7 @@ export default {
padding: 0; padding: 0;
font-weight: 700; font-weight: 700;
font-size: 0.7rem; font-size: 0.7rem;
z-index: 1;
> div { > div {
padding: 0.1rem 0.4rem; padding: 0.1rem 0.4rem;
@ -172,6 +182,7 @@ export default {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
font-size: 0.7rem; font-size: 0.7rem;
position: relative;
&:focus { &:focus {
box-shadow: inset 0 0 0 1px $body-font-color; box-shadow: inset 0 0 0 1px $body-font-color;

View File

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

View File

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