diff options
Diffstat (limited to 'src/site/components/image-modal')
| -rw-r--r-- | src/site/components/image-modal/AlbumInfo.vue | 73 | ||||
| -rw-r--r-- | src/site/components/image-modal/ImageInfo.vue | 61 | ||||
| -rw-r--r-- | src/site/components/image-modal/TagInfo.vue | 103 |
3 files changed, 219 insertions, 18 deletions
diff --git a/src/site/components/image-modal/AlbumInfo.vue b/src/site/components/image-modal/AlbumInfo.vue new file mode 100644 index 0000000..9d57e55 --- /dev/null +++ b/src/site/components/image-modal/AlbumInfo.vue @@ -0,0 +1,73 @@ +<template> + <b-dropdown + v-model="selectedOptions" + multiple + expanded + scrollable + inline + aria-role="list" + max-height="500px"> + <button slot="trigger" class="button is-primary" type="button"> + <span>Albums ({{ selectedOptions.length }})</span> + <b-icon icon="menu-down" /> + </button> + + <b-dropdown-item + v-for="album in albums" + :key="album.id" + :value="album.id" + aria-role="listitem" + @click="handleClick(album.id)"> + <span>{{ album. name }}</span> + </b-dropdown-item> + </b-dropdown> +</template> + +<script> +import { mapState } from 'vuex'; + +export default { + name: 'Albuminfo', + props: { + imageId: { + type: Number, + default: 0, + }, + imageAlbums: { + type: Array, + default: () => [], + }, + }, + data() { + return { + selectedOptions: this.imageAlbums.map((e) => e.id), + }; + }, + computed: { + ...mapState({ + albums: (state) => state.albums.tinyDetails, + }), + }, + methods: { + isAlbumSelected(id) { + if (!this.showingModalForFile) return false; + const found = this.showingModalForFile.albums.find((el) => el.id === id); + return !!(found && found.id); + }, + async handleClick(id) { + // here the album should be already removed from the selected list + if (this.selectedOptions.indexOf(id) > -1) { + this.$handler.executeAction('images/addToAlbum', { + albumId: id, + fileId: this.imageId, + }); + } else { + this.$handler.executeAction('images/removeFromAlbum', { + albumId: id, + fileId: this.imageId, + }); + } + }, + }, +}; +</script> diff --git a/src/site/components/image-modal/ImageInfo.vue b/src/site/components/image-modal/ImageInfo.vue index c9dba1a..c3f0041 100644 --- a/src/site/components/image-modal/ImageInfo.vue +++ b/src/site/components/image-modal/ImageInfo.vue @@ -1,10 +1,13 @@ <template> <div class="container has-background-lolisafe"> <div class="columns is-marginless"> - <div class="column fucking-opl-shut-up"> - <img src="https://placehold.it/1024x10024"> + <div class="column image-col has-centered-items"> + <img v-if="!isVideo(file.type)" class="col-img" :src="file.url"> + <video v-else class="col-vid" controls> + <source :src="file.url" :type="file.type"> + </video> </div> - <div class="column is-one-third"> + <div class="column data-col is-one-third"> <div class="sticky"> <div class="divider is-lolisafe has-text-light"> File information @@ -90,21 +93,16 @@ <span class="fake-input"><timeago :since="file.createdAt" /></span> </div> </b-field> + <div class="divider is-lolisafe has-text-light"> - Albums + Tags </div> + <Taginfo :imageId="file.id" :imageTags="tags" /> <div class="divider is-lolisafe has-text-light"> - Tags + Albums </div> - <b-field label="Add some tags"> - <b-taginput - v-model="tags" - class="lolisafe" - ellipsis - icon="label" - placeholder="Add a tag" /> - </b-field> + <Albuminfo :imageId="file.id" :imageAlbums="albums" /> </div> </div> </div> @@ -114,17 +112,27 @@ <script> import { mapState } from 'vuex'; +import Albuminfo from './AlbumInfo.vue'; +import Taginfo from './TagInfo.vue'; + export default { + components: { + Taginfo, + Albuminfo, + }, props: { file: { type: Object, default: () => ({}), }, - }, - data() { - return { - tags: [], - }; + albums: { + type: Array, + default: () => ([]), + }, + tags: { + type: Array, + default: () => ([]), + }, }, computed: mapState(['images']), methods: { @@ -139,6 +147,9 @@ export default { return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; }, + isVideo(type) { + return type.startsWith('video'); + }, }, }; </script> @@ -176,4 +187,18 @@ export default { .divider:first-child { margin: 10px 0 25px; } + +.col-vid { + width: 100%; +} + +.image-col { + align-items: start; +} + +.data-col { + @media screen and (min-width: 769px) { + padding-right: 1.5rem; + } +} </style> diff --git a/src/site/components/image-modal/TagInfo.vue b/src/site/components/image-modal/TagInfo.vue new file mode 100644 index 0000000..2054e6a --- /dev/null +++ b/src/site/components/image-modal/TagInfo.vue @@ -0,0 +1,103 @@ +<template> + <b-field label="Add some tags"> + <b-taginput + :value="selectedTags" + :data="filteredTags" + class="lolisafe taginp" + ellipsis + icon="label" + placeholder="Add a tag" + autocomplete + allow-new + @typing="getFilteredTags" + @add="tagAdded" + @remove="tagRemoved" /> + </b-field> +</template> + +<script> +import { mapState } from 'vuex'; + +export default { + name: 'Taginfo', + props: { + imageId: { + type: Number, + default: 0, + }, + imageTags: { + type: Array, + default: () => [], + }, + }, + data() { + return { + filteredTags: [], + }; + }, + computed: { + ...mapState({ + tags: (state) => state.tags.tagsList, + }), + selectedTags() { return this.imageTags.map((e) => e.name); }, + lowercaseTags() { return this.imageTags.map((e) => e.name.toLowerCase()); }, + }, + methods: { + getFilteredTags(str) { + this.filteredTags = this.tags.map((e) => e.name).filter((e) => { + // check if the search string matches any of the tags + const sanitezedTag = e.toString().toLowerCase(); + const matches = sanitezedTag.indexOf(str.toLowerCase()) >= 0; + + // check if this tag is already added to our image, to avoid duplicates + if (matches) { + const foundIndex = this.lowercaseTags.indexOf(sanitezedTag); + if (foundIndex === -1) { + return true; + } + } + + return false; + }); + }, + async tagAdded(tag) { + if (!tag) return; + + // normalize into NFC form (diactirics and moonrunes) + // replace all whitespace with _ + // replace multiple __ with a single one + tag = tag.normalize('NFC').replace(/\s/g, '_').replace(/_+/g, '_'); + + const foundIndex = this.tags.findIndex(({ name }) => name === tag); + + if (foundIndex === -1) { + await this.$handler.executeAction('tags/createTag', tag); + } + + await this.$handler.executeAction('images/addTag', { fileId: this.imageId, tagName: tag }); + }, + tagRemoved(tag) { + this.$handler.executeAction('images/removeTag', { fileId: this.imageId, tagName: tag }); + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import '~/assets/styles/_colors.scss'; + +.taginp { + ::v-deep .dropdown-content { + background-color: hsl(0, 0%, 100%); + + .dropdown-item { + color: hsl(0, 0%, 29%); + + &:hover { + color: hsl(0, 0%, 4%); + background-color: hsl(0, 0%, 90%); + } + } + } +} +</style> |