frontend-web/src/components/PhotoItem.vue
2022-06-22 11:58:10 +02:00

298 lines
6.1 KiB
Vue

<template>
<n-spin :show="loading">
<div
class="card"
:style="card_styles"
@mouseover="setHoverIfLoaded"
@mouseleave="hover=false"
>
<div
class="cover"
:style="cover_styles"
ref="cover"
@click="toggleSelection"
>
<n-skeleton v-if="!isImgLoaded" :height="height" :width="width" />
<div v-if="isImgLoaded" class="selection" :style="selection_styles">
<n-icon size="4em" color="rgba(255,255,255,0.75)"><Check /></n-icon>
</div>
</div>
<div
class="options"
:style="options_styles"
>
<div class="content">
<div class="options-item" v-if="can_change_group">
<n-button text @click="$emit('update:group')">
<template #icon><n-icon><SortFilled /></n-icon></template>
Move Group
</n-button>
</div>
<div class="options-item" v-if="can_crop">
<n-button text @click="$emit('update:crop')">
<template #icon><n-icon><CropRotateFilled /></n-icon></template>
Crop Photo
</n-button>
</div>
<div class="options-item" v-if="can_change_ocr">
<n-button text @click="$emit('update:ocr')">
<template #icon><n-icon><TextAnnotationToggle /></n-icon></template>
Change OCR
</n-button>
</div>
<div class="options-item" v-if="can_change_tag">
<n-button text @click="$emit('update:tag')">
<template #icon><n-icon><MdPricetag /></n-icon></template>
Change Tag
</n-button>
</div>
<div class="options-item" v-if="can_delete">
<n-button text type="error" @click="$emit('update:delete')">
<template #icon><n-icon><TrashBinSharp /></n-icon></template>
Delete Photo
</n-button>
</div>
</div>
</div>
</div>
</n-spin>
</template>
<script>
import TextAnnotationToggle from '@vicons/carbon/TextAnnotationToggle'
import TrashBinSharp from '@vicons/ionicons5/TrashBinSharp'
import SortFilled from '@vicons/material/SortFilled'
import CropRotateFilled from '@vicons/material/CropRotateFilled'
import Check from '@vicons/fa/Check'
import MdPricetag from '@vicons/ionicons4/MdPricetag'
export default {
components: {
TextAnnotationToggle,
TrashBinSharp,
SortFilled,
CropRotateFilled,
Check,
MdPricetag,
},
props: {
src: {
type: String,
required: false,
default: '',
},
can_change_group: {
type: Boolean,
required: false,
default: false,
},
can_select: {
type: Boolean,
required: false,
default: false,
},
can_change_ocr: {
type: Boolean,
required: false,
default: false,
},
can_change_tag: {
type: Boolean,
required: false,
default: false,
},
can_crop: {
type: Boolean,
required: false,
default: false,
},
can_delete: {
type: Boolean,
required: false,
default: false,
},
width: {
type: Number,
required: false,
default: 150,
},
init_selection: {
type: Boolean,
required: false,
default: false,
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
emits: [
'update:select',
'update:delete',
'update:crop',
'update:ocr',
'update:tag',
'update:group',
],
data() {
return {
hover: false,
selected: false,
isImgLoaded: true,
imgSrcObj: null,
}
},
beforeMount() {
this.imgSrcObj = new Image()
this.imgSrcObj.src = this.src
if (this.can_select && this.init_selection) {
this.selected = true
}
},
watch: {
imgSrcObj: {
handler: function () {
if (this.imgSrcObj.complete) {
URL.revokeObjectURL(this.imgSrcObj.src)
this.isImgLoaded = true
}
},
deep: true,
}
},
computed: {
height() { return this.width * 1.41421356 },
options_height() {
return [
this.can_change_group,
this.can_crop,
this.can_change_ocr,
this.can_change_tag,
this.can_delete
].filter(Boolean).length * 35
},
card_styles() {
if (this.hover) {
return `
z-index: 90;
box-shadow: 0px 0px 13px 5px rgba(0,0,0,0.05);
${/*transform: translateY(-10px);*/{}}
`
}
return ''
},
cover_styles() {
let styles = `
background-image: url(${this.src});
width: ${this.width}px;`
if (this.hover) {
styles += `height: ${this.height - this.options_height + 5}px;`
} else {
styles += `height: ${this.height}px;`
}
return styles
},
selection_styles() {
if (this.selected) {
return 'opacity: 100%;'
}
return 'opacity: 0;'
},
options_styles() {
const base_height = 5
const expand_height = this.options_height
if (!this.hover) {
return `
height: ${base_height}px;
`
} else {
return `
height: ${expand_height}px;
`
}
}
},
methods: {
setHoverIfLoaded() {
if (this.isImgLoaded) {
this.hover = true
}
},
toggleSelection() {
if (this.can_select) {
this.selected = !this.selected
this.$emit('update:select', this.selected)
}
}
},
}
</script>
<style scoped>
.card{
transition: all 0.25s;
border-radius: 5px;
}
.cover {
background-repeat: no-repeat;
background-position: center;
background-size: cover;
cursor: pointer;
transition: all 0.25s;
padding: 0;
}
.options {
transition: all 0.25s;
border: 1px solid rgb(238, 238, 238);
border-top: 0;
overflow: hidden;
}
.options .content {
padding: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.options .options-item {
vertical-align: middle;
cursor: pointer;
width: 100%;
padding: 0.125em 1em 0.125em 1em;
margin: 0;
}
.options .options-item:hover {
background-color: rgb(245, 245, 245);
}
.selection {
width: 100%;
height: 100%;
background-color: rgba(0, 162, 255, 0.432);
text-align: center;
vertical-align: middle;
transition: all 0.15s;
}
.selection .n-icon {
transform: translateY(50%);
}
</style>