mirror of https://github.com/Fabio286/antares.git
fix: table header not fixed on top when fast scrolling
This commit is contained in:
parent
a15e6249e1
commit
13b0816837
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue