diff options
Diffstat (limited to 'src/components/Collection')
-rw-r--r-- | src/components/Collection/CollectionContent.vue | 154 | ||||
-rw-r--r-- | src/components/Collection/CollectionCover.vue | 141 | ||||
-rw-r--r-- | src/components/Collection/CollectionsList.vue | 104 |
3 files changed, 399 insertions, 0 deletions
diff --git a/src/components/Collection/CollectionContent.vue b/src/components/Collection/CollectionContent.vue new file mode 100644 index 00000000..5e483317 --- /dev/null +++ b/src/components/Collection/CollectionContent.vue @@ -0,0 +1,154 @@ +<!-- + - @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me> + - + - @author Louis Chemineau <louis@chmn.me> + - + - @license AGPL-3.0-or-later + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> +<template> + <!-- Errors handlers--> + <NcEmptyContent v-if="collection === undefined && !loading" + class="empty-content-with-illustration" + :title="t('photos', 'This collection does not exist')"> + <FolderMultipleImage /> + </NcEmptyContent> + <NcEmptyContent v-else-if="error" :title="t('photos', 'An error occurred')"> + <AlertCircle slot="icon" /> + </NcEmptyContent> + + <div v-else class="collection"> + <!-- Header --> + <slot class="collection__header" name="header" :selected-file-ids="selectedFileIds" /> + + <!-- No content --> + <slot v-if="collectionFileIds.length === 0 && !loading" name="empty-content" /> + + <!-- Media list --> + <FilesListViewer v-if="collection !== undefined" + :container-element="appContent" + class="collection__media" + :file-ids="collectionFileIds" + :base-height="isMobile ? 120 : 200" + :loading="loading"> + <File slot-scope="{file, visibility}" + :file="files[file.id]" + :allow-selection="true" + :selected="selection[file.id] === true" + :visibility="visibility" + :semaphore="semaphore" + @click="openViewer" + @select-toggled="onFileSelectToggle" /> + </FilesListViewer> + </div> +</template> + +<script> +import { mapGetters } from 'vuex' +import AlertCircle from 'vue-material-design-icons/AlertCircle' +import FolderMultipleImage from 'vue-material-design-icons/FolderMultipleImage' + +import { NcEmptyContent, isMobile } from '@nextcloud/vue' + +import FilesSelectionMixin from '../../mixins/FilesSelectionMixin.js' +import FilesListViewer from '.././FilesListViewer.vue' +import File from '.././File.vue' +import FolderIllustration from '../../assets/Illustrations/folder.svg' +import SemaphoreWithPriority from '../../utils/semaphoreWithPriority.js' + +export default { + name: 'CollectionContent', + + components: { + AlertCircle, + FolderMultipleImage, + NcEmptyContent, + FilesListViewer, + File, + }, + + mixins: [ + FilesSelectionMixin, + isMobile, + ], + + props: { + collection: { + type: Object, + default: () => undefined, + }, + + collectionFileIds: { + type: Array, + required: true, + }, + + loading: { + type: Boolean, + default: false, + }, + + error: { + type: [Error], + default: '', + }, + + semaphore: { + type: SemaphoreWithPriority, + required: true, + }, + }, + + data() { + return { + FolderIllustration, + appContent: document.getElementById('app-content-vue'), + } + }, + + computed: { + ...mapGetters([ + 'files', + ]), + }, + + methods: { + openViewer(fileId) { + const file = this.files[fileId] + OCA.Viewer.open({ + fileInfo: file, + list: this.collectionFileIds.map(fileId => this.files[fileId]).filter(file => !file.sectionHeader), + loadMore: file.loadMore ? async () => await file.loadMore(true) : () => [], + canLoop: file.canLoop, + }) + }, + }, +} +</script> +<style lang="scss" scoped> +.collection { + display: flex; + flex-direction: column; + + &__media { + padding: 0 64px; + + @media only screen and (max-width: 1200px) { + padding: 0 4px; + } + } +} +</style> diff --git a/src/components/Collection/CollectionCover.vue b/src/components/Collection/CollectionCover.vue new file mode 100644 index 00000000..2d35365e --- /dev/null +++ b/src/components/Collection/CollectionCover.vue @@ -0,0 +1,141 @@ +<!-- + - @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me> + - + - @author Louis Chemineau <louis@chmn.me> + - + - @license AGPL-3.0-or-later + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> + +<template> + <li> + <router-link class="collection-cover" :to="link"> + <img v-if="coverUrl !== ''" + class="collection-cover__image" + :src="coverUrl" + :alt="altImg"> + + <div v-else class="collection-cover__image collection-cover__image--placeholder"> + <ImageMultiple :size="128" /> + </div> + <div class="collection-cover__details"> + <div class="collection-cover__details__title"> + <slot name="default" /> + </div> + <div class="collection-cover__details__subtitle"> + <slot name="subtitle" /> + </div> + </div> + </router-link> + </li> +</template> + +<script> + +import { mapGetters } from 'vuex' +// import ShareVariant from 'vue-material-design-icons/ShareVariant' +// import AccountMultiple from 'vue-material-design-icons/AccountMultiple' +import ImageMultiple from 'vue-material-design-icons/ImageMultiple' + +export default { + name: 'CollectionCover', + + components: { + ImageMultiple, + }, + + props: { + coverUrl: { + type: String, + required: true, + }, + altImg: { + type: String, + required: true, + }, + link: { + type: String, + required: true, + }, + }, + + computed: { + ...mapGetters([ + 'files', + 'albums', + ]), + }, +} +</script> + +<style lang="scss" scoped> +.collection-cover { + display: flex; + flex-direction: column; + padding: 16px; + border-radius: var(--border-radius-large); + + &:hover, &:focus { + background: var(--color-background-dark); + } + + &__image { + width: 350px; + height: 350px; + object-fit: none; + border-radius: var(--border-radius-large); + + @media only screen and (max-width: 1200px) { + width: 250px; + height: 250px; + } + + &--placeholder { + background: var(--color-primary-light); + + ::v-deep .material-design-icon { + width: 100%; + height: 100%; + + .material-design-icon__svg { + fill: var(--color-primary); + } + } + } + } + + &__details { + display: flex; + flex-direction: column; + margin-top: 16px; + width: 350px; + + @media only screen and (max-width: 1200px) { + width: 250px; + } + + &__title { + display: flex; + } + + &__subtitle { + display: flex; + color: var(--color-text-lighter); + } + } + +} +</style> diff --git a/src/components/Collection/CollectionsList.vue b/src/components/Collection/CollectionsList.vue new file mode 100644 index 00000000..c9256ac0 --- /dev/null +++ b/src/components/Collection/CollectionsList.vue @@ -0,0 +1,104 @@ +<!-- + - @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me> + - + - @author Louis Chemineau <louis@chmn.me> + - + - @license AGPL-3.0-or-later + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> +<template> + <!-- Errors handlers--> + <NcEmptyContent v-if="error" :title="t('photos', 'An error occurred') "> + <AlertCircle slot="icon" /> + </NcEmptyContent> + + <div v-else class="collections"> + <!-- Collection header --> + <slot name="header" /> + + <!-- No collections --> + <slot v-if="noCollection && !loading" name="empty-collections-list" class="collections__empty" /> + + <!-- List --> + <ul v-else-if="!noCollection" class="collections__list"> + <slot v-for="collection in collections" + :collection="collection" + class="collection" /> + </ul> + </div> +</template> + +<script> +import AlertCircle from 'vue-material-design-icons/AlertCircle' + +import { NcEmptyContent } from '@nextcloud/vue' + +export default { + name: 'CollectionsList', + + components: { + AlertCircle, + NcEmptyContent, + }, + + props: { + collections: { + type: Object, + required: true, + }, + loading: { + type: Boolean, + default: false, + }, + error: { + type: Error, + default: null, + }, + }, + + computed: { + /** + * @return {boolean} Whether the list of collections is empty or not. + */ + noCollection() { + return Object.keys(this.collections).length === 0 + }, + }, +} +</script> +<style lang="scss" scoped> +.collections { + display: flex; + flex-direction: column; + height: 100%; + + &__list { + padding: 32px 48px; + flex-grow: 1; + display: flex; + flex-wrap: wrap; + gap: 16px; + align-items: flex-start; + height: calc(100% - 60px); + overflow-x: scroll; + + @media only screen and (max-width: 1200px) { + padding: 32px 12px; + justify-content: center; + } + } +} +</style> |