aboutsummaryrefslogtreecommitdiff
path: root/src/site/components/grid
diff options
context:
space:
mode:
authorPitu <[email protected]>2020-05-10 21:22:48 +0900
committerPitu <[email protected]>2020-05-10 21:22:48 +0900
commit124ff68f06bcfee7494e4b8117d87dd206c6ecbe (patch)
tree979db4ae46f9b85ebe6712ffe450f76e871aa054 /src/site/components/grid
parentfeat: Proper deleting of albums (diff)
downloadhost.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.vue441
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>