diff options
| author | Pitu <[email protected]> | 2020-05-10 21:22:48 +0900 |
|---|---|---|
| committer | Pitu <[email protected]> | 2020-05-10 21:22:48 +0900 |
| commit | 124ff68f06bcfee7494e4b8117d87dd206c6ecbe (patch) | |
| tree | 979db4ae46f9b85ebe6712ffe450f76e871aa054 /src/site/components/grid | |
| parent | feat: Proper deleting of albums (diff) | |
| download | host.fuwn.me-124ff68f06bcfee7494e4b8117d87dd206c6ecbe.tar.xz host.fuwn.me-124ff68f06bcfee7494e4b8117d87dd206c6ecbe.zip | |
Feat: implement lazy loading for waterfall view
Diffstat (limited to 'src/site/components/grid')
| -rw-r--r-- | src/site/components/grid/Grid.vue | 441 |
1 files changed, 258 insertions, 183 deletions
diff --git a/src/site/components/grid/Grid.vue b/src/site/components/grid/Grid.vue index ca3d0f9..3b65011 100644 --- a/src/site/components/grid/Grid.vue +++ b/src/site/components/grid/Grid.vue @@ -1,110 +1,3 @@ -<style lang="scss" scoped> - @import '~/assets/styles/_colors.scss'; - .item-move { - transition: all .25s cubic-bezier(.55,0,.1,1); - -webkit-transition: all .25s cubic-bezier(.55,0,.1,1); - } - - div.toolbar { - padding: 1rem; - - .block { - text-align: right; - } - } - - span.extension { - position: absolute; - width: 100%; - height: 100%; - z-index: 0; - top: 0; - left: 0; - display: flex; - align-items: center; - justify-content: center; - font-size: 2rem; - pointer-events: none; - opacity: .75; - max-width: 150px; - } - - div.actions { - opacity: 0; - -webkit-transition: opacity 0.1s linear; - -moz-transition: opacity 0.1s linear; - -ms-transition: opacity 0.1s linear; - -o-transition: opacity 0.1s linear; - transition: opacity 0.1s linear; - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: calc(100% - 6px); - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - - span { - padding: 3px; - &.more { - position: absolute; - top: 0; - right: 0; - } - - &:nth-child(1), &:nth-child(2) { - align-items: flex-end; - } - - &:nth-child(1), &:nth-child(3) { - justify-content: flex-end; - } - a { - width: 30px; - height: 30px; - color: white; - justify-content: center; - align-items: center; - display: flex; - &.btn:before { - content: ''; - width: 30px; - height: 30px; - border: 1px solid white; - border-radius: 50%; - position: absolute; - } - } - } - - &.fixed { - position: relative; - opacity: 1; - background: none; - - a { - width: auto; - height: auto; - color: $defaultTextColor; - &:before { - display: none; - } - } - - } - } -</style> - -<style lang="scss"> - .waterfall-item:hover { - div.actions { - opacity: 1 - } - } -</style> - <template> <div> <div v-if="enableToolbar" @@ -123,86 +16,89 @@ </div> </div> - <Waterfall v-if="!showList" - :gutterWidth="10" - :gutterHeight="4"> - <!-- - TODO: Implement search based on originalName, albumName and tags - <input v-if="enableSearch" - v-model="searchTerm" - type="text" - placeholder="Search..." - @input="search()" - @keyup.enter="search()"> - --> + <template v-if="!showList"> + <Waterfall :gutterWidth="10" + :gutterHeight="4"> + <!-- + TODO: Implement search based on originalName, albumName and tags + <input v-if="enableSearch" + v-model="searchTerm" + type="text" + placeholder="Search..." + @input="search()" + @keyup.enter="search()"> + --> - <!-- TODO: Implement pagination --> + <!-- TODO: Implement pagination --> - <WaterfallItem v-for="(item, index) in files" - v-if="showWaterfall" - :key="index" - :width="width" - move-class="item-move"> - <template v-if="isPublic"> - <a :href="`${item.url}`" - target="_blank"> + <WaterfallItem v-for="(item, index) in gridFiles" + v-if="showWaterfall" + :key="index" + :width="width" + move-class="item-move"> + <template v-if="isPublic"> + <a :href="`${item.url}`" + target="_blank"> + <img :src="item.thumb ? item.thumb : blank"> + <span v-if="!item.thumb && item.name" + class="extension">{{ item.name.split('.').pop() }}</span> + </a> + </template> + <template v-else> <img :src="item.thumb ? item.thumb : blank"> <span v-if="!item.thumb && item.name" class="extension">{{ item.name.split('.').pop() }}</span> - </a> - </template> - <template v-else> - <img :src="item.thumb ? item.thumb : blank"> - <span v-if="!item.thumb && item.name" - class="extension">{{ item.name.split('.').pop() }}</span> - <div v-if="!isPublic" - :class="{ fixed }" - class="actions"> - <b-tooltip label="Link" - position="is-top"> - <a :href="`${item.url}`" - target="_blank" - class="btn"> - <i class="icon-web-code" /> - </a> - </b-tooltip> - <b-tooltip label="Albums" - position="is-top"> - <a class="btn" - @click="$parent.openAlbumModal(item)"> - <i class="icon-interface-window" /> - </a> - </b-tooltip> - <!-- - <b-tooltip label="Tags" - position="is-top"> - <a @click="manageTags(item)"> - <i class="icon-ecommerce-tag-c" /> - </a> - </b-tooltip> - --> - <b-tooltip label="Delete" - position="is-top"> - <a class="btn" - @click="deleteFile(item, index)"> - <i class="icon-editorial-trash-a-l" /> - </a> - </b-tooltip> - <b-tooltip v-if="user && user.isAdmin" - label="More info" - position="is-top" - class="more"> - <nuxt-link :to="`/dashboard/admin/file/${item.id}`"> - <i class="icon-interface-more" /> - </nuxt-link> - </b-tooltip> - </div> - </template> - </WaterfallItem> - </Waterfall> + <div v-if="!isPublic" + :class="{ fixed }" + class="actions"> + <b-tooltip label="Link" + position="is-top"> + <a :href="`${item.url}`" + target="_blank" + class="btn"> + <i class="icon-web-code" /> + </a> + </b-tooltip> + <b-tooltip label="Albums" + position="is-top"> + <a class="btn" + @click="openAlbumModal(item)"> + <i class="icon-interface-window" /> + </a> + </b-tooltip> + <!-- + <b-tooltip label="Tags" + position="is-top"> + <a @click="manageTags(item)"> + <i class="icon-ecommerce-tag-c" /> + </a> + </b-tooltip> + --> + <b-tooltip label="Delete" + position="is-top"> + <a class="btn" + @click="deleteFile(item, index)"> + <i class="icon-editorial-trash-a-l" /> + </a> + </b-tooltip> + <b-tooltip v-if="user && user.isAdmin" + label="More info" + position="is-top" + class="more"> + <nuxt-link :to="`/dashboard/admin/file/${item.id}`"> + <i class="icon-interface-more" /> + </nuxt-link> + </b-tooltip> + </div> + </template> + </WaterfallItem> + </Waterfall> + <button class="button is-primary" + @click="loadMoreFilesWaterfall">Load more</button> + </template> <div v-else> <b-table - :data="files || []" + :data="gridFiles || []" :mobile-cards="true"> <template slot-scope="props"> <template v-if="!props.row.hideFromList"> @@ -237,7 +133,7 @@ <b-tooltip label="Albums" position="is-top"> <a class="btn" - @click="$parent.openAlbumModal(props.row)"> + @click="openAlbumModal(props.row)"> <i class="icon-interface-window" /> </a> </b-tooltip> @@ -275,6 +171,28 @@ </template> </b-table> </div> + <b-modal :active.sync="isAlbumsModalActive" + :width="640" + scroll="keep"> + <div class="card albumsModal"> + <div class="card-content"> + <div class="content"> + <h3 class="subtitle">Select the albums this file should be a part of</h3> + <hr> + <div class="albums-container"> + <div v-for="(album, index) in albums" + :key="index" + class="album"> + <div class="field"> + <b-checkbox :value="isAlbumSelected(album.id)" + @input="albumCheckboxClicked($event, album.id)">{{ album.name }}</b-checkbox> + </div> + </div> + </div> + </div> + </div> + </div> + </b-modal> </div> </template> <script> @@ -289,7 +207,7 @@ export default { props: { files: { type: Array, - default: null + default: () => [] }, fixed: { type: Boolean, @@ -316,7 +234,13 @@ export default { return { showWaterfall: true, searchTerm: null, - showList: false + showList: false, + albums: [], + isAlbumsModalActive: false, + showingModalForFile: null, + filesOffsetWaterfall: 0, + filesOffsetEndWaterfall: 50, + filesPerPageWaterfall: 50 }; }, computed: { @@ -325,9 +249,15 @@ export default { }, blank() { return require('@/assets/images/blank.png'); + }, + gridFiles() { + return this.files.slice(this.filesOffsetWaterfall, this.filesOffsetEndWaterfall); } }, methods: { + loadMoreFilesWaterfall() { + this.filesOffsetEndWaterfall = this.filesOffsetEndWaterfall + this.filesPerPageWaterfall; + }, async search() { const data = await this.$search.do(this.searchTerm, [ 'name', @@ -359,7 +289,152 @@ export default { return this.$buefy.toast.open(response.message); } }); + }, + isAlbumSelected(id) { + if (!this.showingModalForFile) return; + const found = this.showingModalForFile.albums.find(el => el.id === id); + return found ? found.id ? true : false : false; + }, + async openAlbumModal(file) { + this.showingModalForFile = file; + this.showingModalForFile.albums = []; + this.isAlbumsModalActive = true; + + const response = await this.$axios.$get(`file/${file.id}/albums`); + this.showingModalForFile.albums = response.albums; + + this.getAlbums(); + }, + async albumCheckboxClicked(value, id) { + const response = await this.$axios.$post(`file/album/${value ? 'add' : 'del'}`, { + albumId: id, + fileId: this.showingModalForFile.id + }); + this.$buefy.toast.open(response.message); + + // Not the prettiest solution to refetch on each click but it'll do for now + this.$parent.getFiles(); + }, + async getAlbums() { + const response = await this.$axios.$get(`albums/dropdown`); + this.albums = response.albums; + this.$forceUpdate(); } } }; </script> +<style lang="scss" scoped> + @import '~/assets/styles/_colors.scss'; + .item-move { + transition: all .25s cubic-bezier(.55,0,.1,1); + } + + div.toolbar { + padding: 1rem; + + .block { + text-align: right; + } + } + + span.extension { + position: absolute; + width: 100%; + height: 100%; + z-index: 0; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + pointer-events: none; + opacity: .75; + max-width: 150px; + } + + div.actions { + opacity: 0; + -webkit-transition: opacity 0.1s linear; + -moz-transition: opacity 0.1s linear; + -ms-transition: opacity 0.1s linear; + -o-transition: opacity 0.1s linear; + transition: opacity 0.1s linear; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: calc(100% - 6px); + background: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + + span { + padding: 3px; + &.more { + position: absolute; + top: 0; + right: 0; + } + + &:nth-child(1), &:nth-child(2) { + align-items: flex-end; + } + + &:nth-child(1), &:nth-child(3) { + justify-content: flex-end; + } + a { + width: 30px; + height: 30px; + color: white; + justify-content: center; + align-items: center; + display: flex; + &.btn:before { + content: ''; + width: 30px; + height: 30px; + border: 1px solid white; + border-radius: 50%; + position: absolute; + } + } + } + + &.fixed { + position: relative; + opacity: 1; + background: none; + + a { + width: auto; + height: auto; + color: $defaultTextColor; + &:before { + display: none; + } + } + + } + } + + .albums-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + .album { + flex-basis: 33%; + text-align: left; + } + } +</style> + +<style lang="scss"> + .waterfall-item:hover { + div.actions { + opacity: 1 + } + } +</style> |