mirror of https://github.com/Fabio286/antares.git
Finally a virtual scroll table
This commit is contained in:
parent
1c3323b537
commit
29812f7ee3
|
@ -12884,6 +12884,11 @@
|
|||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
|
||||
"dev": true
|
||||
},
|
||||
"vue-virtual-scroll-list": {
|
||||
"version": "2.2.9",
|
||||
"resolved": "https://registry.npmjs.org/vue-virtual-scroll-list/-/vue-virtual-scroll-list-2.2.9.tgz",
|
||||
"integrity": "sha512-dPlvzIUUtxkaVBVea2/73sWsiTrsIpjWXd+7FWJPUEL+ME1i6LuwWNiMMqu2WVad82ONWeoXSiM5NMSDpMxYGA=="
|
||||
},
|
||||
"vue-virtual-scroller": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-1.0.10.tgz",
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"spectre.css": "^0.5.8",
|
||||
"vue-click-outside": "^1.1.0",
|
||||
"vue-i18n": "^8.18.2",
|
||||
"vue-virtual-scroll-list": "^2.2.9",
|
||||
"vue-virtual-scroller": "^1.0.10",
|
||||
"vuedraggable": "^2.23.2",
|
||||
"vuex": "^3.4.0",
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div class="vscroll-holder">
|
||||
<div
|
||||
class="vscroll-spacer"
|
||||
:style="{
|
||||
opacity: 0,
|
||||
clear: 'both',
|
||||
height: topHeight + 'px'
|
||||
}"
|
||||
/>
|
||||
<slot :items="visibleItems" />
|
||||
<div
|
||||
class="vscroll-spacer"
|
||||
:style="{
|
||||
opacity: 0,
|
||||
clear: 'both',
|
||||
height: bottomHeight + 'px'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// credits: https://github.com/xrado 👼
|
||||
export default {
|
||||
name: 'BaseVirtualScroll',
|
||||
props: {
|
||||
items: Array,
|
||||
itemHeight: Number
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
topHeight: 0,
|
||||
bottomHeight: 0,
|
||||
visibleItems: []
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this._checkScrollPosition = this.checkScrollPosition.bind(this);
|
||||
this.checkScrollPosition();
|
||||
this.$el.addEventListener('scroll', this._checkScrollPosition);
|
||||
this.$el.addEventListener('wheel', this._checkScrollPosition);
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$el.removeEventListener('scroll', this._checkScrollPosition);
|
||||
this.$el.removeEventListener('wheel', this._checkScrollPosition);
|
||||
},
|
||||
methods: {
|
||||
checkScrollPosition (e = {}) {
|
||||
var el = this.$el;
|
||||
|
||||
// prevent parent scroll
|
||||
if ((el.scrollTop === 0 && e.deltaY < 0) || (Math.abs(el.scrollTop - (el.scrollHeight - el.clientHeight)) <= 1 && e.deltaY > 0))
|
||||
e.preventDefault();
|
||||
|
||||
this.updateWindow(e);
|
||||
},
|
||||
|
||||
updateWindow (e) {
|
||||
const visibleItemsCount = Math.ceil(this.$el.clientHeight / this.itemHeight);
|
||||
const totalScrollHeight = this.items.length * this.itemHeight;
|
||||
|
||||
const scrollTop = this.$el.scrollTop;
|
||||
const offset = 5;
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-for="(col, cKey) in source"
|
||||
:key="cKey"
|
||||
class="td"
|
||||
:class="fieldType(col)"
|
||||
>
|
||||
{{ col }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'WorkspaceQueryRow',
|
||||
props: {
|
||||
index: { // index of current item
|
||||
type: Number
|
||||
},
|
||||
source: { // here is: {uid: 'unique_1', text: 'abc'}
|
||||
type: Object,
|
||||
default () {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fieldType (col) {
|
||||
let type = typeof col;
|
||||
if (type === 'object')
|
||||
if (col instanceof Date) type = 'date';
|
||||
if (col instanceof Uint8Array) type = 'blob';
|
||||
if (col === null) type = 'null';
|
||||
|
||||
return `type-${type}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -22,7 +22,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="resultTable" class="workspace-query-results column col-12">
|
||||
<div class="workspace-query-results column col-12">
|
||||
<WorkspaceQueryTable v-if="results" :results="results" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -58,12 +58,6 @@ export default {
|
|||
return this.getWorkspace(this.connection.uid);
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('resize', this.resizeResults);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeResults);
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
|
@ -72,7 +66,6 @@ export default {
|
|||
if (!this.query) return;
|
||||
this.isQuering = true;
|
||||
this.results = {};
|
||||
this.resizeResults();
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
|
@ -92,24 +85,6 @@ export default {
|
|||
}
|
||||
|
||||
this.isQuering = false;
|
||||
},
|
||||
resizeResults (e) {
|
||||
const el = this.$refs.resultTable;
|
||||
const footer = document.getElementById('footer');
|
||||
|
||||
if (el) {
|
||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
||||
el.style.height = size + 'px';
|
||||
}
|
||||
},
|
||||
fieldType (col) {
|
||||
let type = typeof col;
|
||||
if (type === 'object')
|
||||
if (col instanceof Date) type = 'date';
|
||||
if (col instanceof Uint8Array) type = 'blob';
|
||||
if (col === null) type = 'null';
|
||||
|
||||
return `type-${type}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,42 +1,80 @@
|
|||
<template>
|
||||
<div v-if="results" class="table table-hover">
|
||||
<div class="thead">
|
||||
<div class="tr">
|
||||
<div
|
||||
v-for="field in results.fields"
|
||||
:key="field.name"
|
||||
class="th"
|
||||
>
|
||||
{{ field.name }}
|
||||
<BaseVirtualScroll
|
||||
v-if="results.rows"
|
||||
ref="resultTable"
|
||||
:items="results.rows"
|
||||
:item-height="25"
|
||||
class="vscroll"
|
||||
:style="{'height': resultsSize+'px'}"
|
||||
>
|
||||
<template slot-scope="{ items }">
|
||||
<div class="table table-hover">
|
||||
<div class="thead">
|
||||
<div class="tr">
|
||||
<div
|
||||
v-for="field in results.fields"
|
||||
:key="field.name"
|
||||
class="th"
|
||||
>
|
||||
{{ field.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tbody">
|
||||
<div
|
||||
v-for="(row, rKey) in items"
|
||||
:key="rKey"
|
||||
class="tr"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
v-for="(col, cKey) in row"
|
||||
:key="cKey"
|
||||
class="td"
|
||||
:class="fieldType(col)"
|
||||
>
|
||||
{{ col }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tbody">
|
||||
<div
|
||||
v-for="(row, rKey) in results.rows"
|
||||
:key="rKey"
|
||||
class="tr"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
v-for="(col, cKey) in row"
|
||||
:key="cKey"
|
||||
class="td"
|
||||
:class="fieldType(col)"
|
||||
>
|
||||
{{ col }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseVirtualScroll>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||
|
||||
export default {
|
||||
name: 'WorkspaceQueryTable',
|
||||
components: {
|
||||
BaseVirtualScroll
|
||||
},
|
||||
props: {
|
||||
results: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
resultsSize: 1000
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rows () {
|
||||
return this.results.rows ? this.results.rows.map(item => Object.assign({}, item)) : [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
results: function () {
|
||||
if (this.$refs.resultTable)
|
||||
this.resizeResults();
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('resize', this.resizeResults);
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('resize', this.resizeResults);
|
||||
},
|
||||
methods: {
|
||||
fieldType (col) {
|
||||
let type = typeof col;
|
||||
|
@ -46,11 +84,24 @@ export default {
|
|||
if (col === null) type = 'null';
|
||||
|
||||
return `type-${type}`;
|
||||
},
|
||||
resizeResults (e) {
|
||||
const el = this.$refs.resultTable.$el;
|
||||
const footer = document.getElementById('footer');
|
||||
|
||||
if (el) {
|
||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
||||
this.resultsSize = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
.vscroll {
|
||||
height: 1000px;
|
||||
overflow: auto;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
|
||||
import 'material-design-icons/iconfont/material-icons.css';
|
||||
import '@/scss/main.scss';
|
||||
|
||||
|
|
Loading…
Reference in New Issue