mirror of
				https://github.com/Fabio286/antares.git
				synced 2025-06-05 21:59:22 +02:00 
			
		
		
		
	Finally a virtual scroll table
This commit is contained in:
		
							
								
								
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -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", | ||||
|   | ||||
							
								
								
									
										77
									
								
								src/renderer/components/BaseVirtualScroll.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/renderer/components/BaseVirtualScroll.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -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> | ||||
							
								
								
									
										43
									
								
								src/renderer/components/WorkspaceQueryRow.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/renderer/components/WorkspaceQueryRow.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -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,5 +1,14 @@ | ||||
| <template> | ||||
|    <div v-if="results" class="table table-hover"> | ||||
|    <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 | ||||
| @@ -13,7 +22,7 @@ | ||||
|             </div> | ||||
|             <div class="tbody"> | ||||
|                <div | ||||
|             v-for="(row, rKey) in results.rows" | ||||
|                   v-for="(row, rKey) in items" | ||||
|                   :key="rKey" | ||||
|                   class="tr" | ||||
|                   tabindex="0" | ||||
| @@ -30,13 +39,42 @@ | ||||
|             </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'; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user