Skip to content

Commit

Permalink
Added virtual scroll
Browse files Browse the repository at this point in the history
Removed virtual scroll package, implement custom virtual scroll

Added offset prop

Fix issue with scroll not being stopped and stop rerendering of already seen items

Refresh items after they have been changed
  • Loading branch information
Lukas Hirt authored and LukasHirt committed Oct 21, 2019
1 parent 7727a73 commit bdb7f32
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 82 deletions.
182 changes: 100 additions & 82 deletions apps/files/src/components/FileList.vue
Original file line number Diff line number Diff line change
@@ -1,89 +1,99 @@
<template>
<div class="uk-height-1-1">
<div class="uk-flex uk-flex-column uk-height-1-1">
<div id="files-list-container" class="uk-overflow-auto uk-flex-auto">
<oc-table middle divider class="oc-filelist uk-margin-remove-bottom" id="files-list" v-show="!loadingFolder">
<thead>
<oc-table-row>
<oc-table-cell shrink type="head">
<oc-checkbox class="uk-margin-small-left" id="filelist-check-all" @click.native="toggleAll" :value="selectedAll" />
</oc-table-cell>
<oc-table-cell shrink type="head" v-if="!publicPage()" />
<oc-table-cell type="head" class="uk-text-truncate" v-translate>Name</oc-table-cell>
<oc-table-cell shrink type="head" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }"><translate>Size</translate></oc-table-cell>
<oc-table-cell shrink type="head" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }" class="uk-text-nowrap" v-translate>Modification Time</oc-table-cell>
<oc-table-cell shrink type="head" :class="{ 'uk-visible@s' : _sidebarOpen }" v-translate>Actions</oc-table-cell>
</oc-table-row>
</thead>
<oc-table-group>
<oc-table-row v-for="(item, index) in fileData" :key="index" :class="_rowClasses(item)" @click="selectRow(item, $event)" :id="'file-row-' + item.id">
<oc-table-cell>
<oc-checkbox class="uk-margin-small-left" @click.stop @change.native="$emit('toggle', item)" :value="selectedFiles.indexOf(item) >= 0" />
</oc-table-cell>
<oc-table-cell class="uk-padding-remove" v-if="!publicPage()">
<oc-star class="uk-display-block" @click.native.stop="toggleFileFavorite(item)" :shining="item.starred" />
</oc-table-cell>
<oc-table-cell class="uk-text-truncate">
<oc-file @click.native.stop="item.type === 'folder' ? navigateTo(item.path.substr(1)) : openFileActionBar(item)"
:name="$_ocFileName(item)" :extension="item.extension" class="file-row-name" :icon="fileTypeIcon(item)"
:filename="item.name" :key="item.id"/>
<oc-spinner
v-if="$_actionInProgress(item)"
size="small"
:uk-tooltip="$_disabledActionTooltip(item)"
class="uk-margin-small-left"
/>
</oc-table-cell>
<oc-table-cell class="uk-text-meta uk-text-nowrap" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }">
{{ item.size | fileSize }}
</oc-table-cell>
<oc-table-cell class="uk-text-meta uk-text-nowrap" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }">
{{ formDateFromNow(item.mdate) }}
</oc-table-cell>
<oc-table-cell :class="{ 'uk-visible@s' : _sidebarOpen }" class="uk-position-relative">
<div class="uk-button-group uk-margin-small-right" :class="{ 'uk-visible@m' : !_sidebarOpen, 'uk-visible@xl' : _sidebarOpen }">
<virtual-scroll
v-if="activeFiles.length"
id="files-list-container"
:items="activeFiles"
:item-height="70"
:offset="10"
class="uk-height-1-1 uk-overflow-auto oc-virtual-scroll"
>
<template slot-scope="{ items }">
<oc-table middle divider class="oc-filelist uk-margin-remove-bottom" id="files-list" v-show="!loadingFolder">
<thead>
<oc-table-row>
<oc-table-cell shrink type="head">
<oc-checkbox class="uk-margin-small-left" id="filelist-check-all" @click.native="toggleAll" :value="selectedAll" />
</oc-table-cell>
<oc-table-cell shrink type="head" v-if="!publicPage()" />
<oc-table-cell type="head" class="uk-text-truncate" v-translate>Name</oc-table-cell>
<oc-table-cell shrink type="head" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }"><translate>Size</translate></oc-table-cell>
<oc-table-cell shrink type="head" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }" class="uk-text-nowrap" v-translate>Modification Time</oc-table-cell>
<oc-table-cell shrink type="head" :class="{ 'uk-visible@s' : _sidebarOpen }" v-translate>Actions</oc-table-cell>
</oc-table-row>
</thead>
<oc-table-group>
<oc-table-row v-for="(item, index) in items" :key="item.id" :class="_rowClasses(item)" @click="selectRow(item, $event)" :id="'file-row-' + item.id">
<oc-table-cell>
<oc-checkbox class="uk-margin-small-left" @click.stop @change.native="$emit('toggle', item)" :value="selectedFiles.indexOf(item) >= 0" />
</oc-table-cell>
<oc-table-cell class="uk-padding-remove" v-if="!publicPage()">
<oc-star class="uk-display-block" @click.native.stop="toggleFileFavorite(item)" :shining="item.starred" />
</oc-table-cell>
<oc-table-cell class="uk-text-truncate">
<oc-file @click.native.stop="item.type === 'folder' ? navigateTo(item.path.substr(1)) : openFileActionBar(item)"
:name="$_ocFileName(item)" :extension="item.extension" class="file-row-name" :icon="fileTypeIcon(item)"
:filename="item.name" :key="item.id"
/>
<oc-spinner
v-if="$_actionInProgress(item)"
size="small"
:uk-tooltip="$_disabledActionTooltip(item)"
class="uk-margin-small-left"
/>
</oc-table-cell>
<oc-table-cell class="uk-text-meta uk-text-nowrap" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }">
{{ item.size | fileSize }}
</oc-table-cell>
<oc-table-cell class="uk-text-meta uk-text-nowrap" :class="{ 'uk-visible@s' : !_sidebarOpen, 'uk-visible@m' : _sidebarOpen }">
{{ formDateFromNow(item.mdate) }}
</oc-table-cell>
<oc-table-cell :class="{ 'uk-visible@s' : _sidebarOpen }" class="uk-position-relative">
<div class="uk-button-group uk-margin-small-right" :class="{ 'uk-visible@m' : !_sidebarOpen, 'uk-visible@xl' : _sidebarOpen }">
<oc-button
v-for="action in actions"
:key="action.ariaLabel"
@click.stop="action.handler(item, action.handlerData)"
:disabled="!action.isEnabled(item, parentFolder) || $_actionInProgress(item)"
:icon="action.icon"
:ariaLabel="action.ariaLabel"
:uk-tooltip="$_disabledActionTooltip(item)"
/>
</div>
<oc-button
v-for="action in actions"
:key="action.ariaLabel"
@click.stop="action.handler(item, action.handlerData)"
:disabled="!action.isEnabled(item, parentFolder) || $_actionInProgress(item)"
:icon="action.icon"
:ariaLabel="action.ariaLabel"
:uk-tooltip="$_disabledActionTooltip(item)"
:id="'files-file-list-action-button-small-resolution-' + index"
icon="more_vert"
:class="{ 'uk-hidden@m' : !_sidebarOpen, 'uk-visible@s uk-hidden@xl' : _sidebarOpen }"
:disabled="$_actionInProgress(item)"
:aria-label="'show-file-actions'"
@click.stop
/>
</div>
<oc-button
:id="'files-file-list-action-button-small-resolution-' + index"
icon="more_vert"
:class="{ 'uk-hidden@m' : !_sidebarOpen, 'uk-visible@s uk-hidden@xl' : _sidebarOpen }"
:disabled="$_actionInProgress(item)"
:aria-label="'show-file-actions'"
@click.stop
/>
<oc-drop
v-if="!$_ocDialog_isOpen"
:toggle="'#files-file-list-action-button-small-resolution-' + index"
:options="{ offset: 0 }"
position="bottom-right"
>
<ul class="uk-list">
<li v-for="action in enabledActions(item)" :key="action.ariaLabel">
<oc-button
class="uk-width-1-1"
@click.native.stop="action.handler(item, action.handlerData)"
:icon="action.icon"
:ariaLabel="action.ariaLabel"
>
{{ action.ariaLabel }}
</oc-button>
</li>
</ul>
</oc-drop>
</oc-table-cell>
</oc-table-row>
</oc-table-group>
</oc-table>
</div>
<oc-drop
v-if="!$_ocDialog_isOpen"
:toggle="'#files-file-list-action-button-small-resolution-' + index"
:options="{ offset: 0 }"
position="bottom-right"
>
<ul class="uk-list">
<li v-for="action in enabledActions(item)" :key="action.ariaLabel">
<oc-button
class="uk-width-1-1"
@click.native.stop="action.handler(item, action.handlerData)"
:icon="action.icon"
:ariaLabel="action.ariaLabel"
>
{{ action.ariaLabel }}
</oc-button>
</li>
</ul>
</oc-drop>
</oc-table-cell>
</oc-table-row>
</oc-table-group>
</oc-table>
</template>
</virtual-scroll>
<oc-grid gutter="large" class="uk-width-1-1 uk-padding-small" v-if="!loadingFolder">
<div v-if="activeFilesCount.folders > 0 || activeFilesCount.files > 0" class="uk-text-nowrap uk-text-meta">
<template v-if="activeFilesCount.folders > 0">
Expand Down Expand Up @@ -122,14 +132,16 @@
</div>
</template>
<script>
import VirtualScroll from '../utils/VirtualScroll.vue'
import OcDialogPrompt from './ocDialogPrompt.vue'
import { mapGetters, mapActions, mapState } from 'vuex'
import Mixins from '../mixins'
export default {
components: {
OcDialogPrompt
OcDialogPrompt,
VirtualScroll
},
mixins: [
Mixins
Expand Down Expand Up @@ -238,3 +250,9 @@ export default {
}
}
</script>

<style scoped>
.oc-virtaul-scroll {
overflow-anchor: none;
}
</style>
112 changes: 112 additions & 0 deletions apps/files/src/utils/VirtualScroll.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<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>
export default {
name: 'VirtualScroll',
props: {
items: {
type: Array,
required: true
},
itemHeight: {
type: Number,
required: true
},
offset: {
type: Number,
default: 5
}
},
data () {
return {
topHeight: 0,
bottomHeight: 0,
visibleItems: [],
lastRenderedItemIndex: 0
}
},
watch: {
items: function () {
this.refreshItems()
}
},
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 = {}) {
const 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 () {
const visibleItemsCount = Math.ceil(
this.$el.clientHeight / this.itemHeight
)
const totalScrollHeight = this.items.length * this.itemHeight
const scrollTop = this.$el.scrollTop
const firstVisibleIndex = Math.floor(scrollTop / this.itemHeight)
const lastVisibleIndex = firstVisibleIndex + visibleItemsCount
const lastCutIndex = lastVisibleIndex + this.offset
if (lastCutIndex > this.lastRenderedItemIndex) {
this.visibleItems = this.items.slice(0, lastCutIndex)
this.lastRenderedItemIndex = lastCutIndex
}
this.bottomHeight =
totalScrollHeight -
this.visibleItems.length * this.itemHeight -
this.topHeight
},
refreshItems () {
this.visibleItems = this.items.slice(0, this.lastRenderedItemIndex)
}
}
}
</script>
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6120,7 +6120,11 @@ node-pre-gyp@^0.12.0:
semver "^5.3.0"
tar "^4"

<<<<<<< HEAD
node-releases@^1.1.36:
=======
node-releases@^1.1.29:
>>>>>>> cab3761... Added virtual scroll
version "1.1.36"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.36.tgz#44b7cb8254138e87bdbfa47761d0f825e20900b4"
integrity sha512-ggXhX6QGyJSjj3r+6ml2LqqC28XOWmKtpb+a15/Zpr9V3yoNazxJNlcQDS9bYaid5FReEWHEgToH1mwoUceWwg==
Expand Down

0 comments on commit bdb7f32

Please sign in to comment.