-
-
Notifications
You must be signed in to change notification settings - Fork 309
/
PhotoThumb.vue
125 lines (115 loc) · 4.44 KB
/
PhotoThumb.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<template>
<router-link
:to="{ name: 'photo', params: { albumid: props.album?.id ?? 'search', photoid: props.photo.id } }"
:class="cssClass"
:data-width="props.photo.size_variants.original?.width"
:data-height="props.photo.size_variants.original?.height"
:data-id="props.photo.id"
:data-album-id="props.album?.id"
>
<span
class="thumbimg absolute w-full h-full border-none overflow-hidden"
:class="(props.photo.precomputed.is_video ? 'video' : '') + ' ' + (props.photo.precomputed.is_livephoto ? 'livephoto' : '')"
>
<img
v-show="props.photo.size_variants.placeholder?.url"
:alt="$t('lychee.PHOTO_PLACEHOLDER')"
class="absolute w-full h-full top-0 left-0 blur-md"
:class="{ 'animate-fadeout animate-fill-forwards': isImageLoaded }"
:src="props.photo.size_variants.placeholder?.url ?? ''"
data-overlay="false"
draggable="false"
/>
<img
alt="Photo thumbnail"
class="h-full w-full border-none object-cover object-center"
:src="props.photo.size_variants.small?.url ?? props.photo.size_variants.thumb?.url ?? srcNoImage"
:srcset="
props.photo.size_variants.small2x ? props.photo.size_variants.small + ' 1x, ' + props.photo.size_variants.small2x + ' 2x' : ''
"
data-overlay="false"
draggable="false"
:loading="props.isLazy ? 'lazy' : 'eager'"
@load="onImageLoad"
/>
</span>
<div class="overlay w-full absolute bottom-0 m-0 bg-gradient-to-t from-[#00000066] text-shadow-sm" :class="cssOverlay">
<h1 class="min-h-[19px] mt-3 mb-1 ml-3 text-surface-0 text-base font-bold overflow-hidden whitespace-nowrap text-ellipsis">
{{ props.photo.title }}
</h1>
<span v-if="props.photo.preformatted.taken_at" class="block mt-0 mr-0 mb-2 ml-3 text-2xs text-surface-300">
<span title="Camera Date"><MiniIcon icon="camera-slr" class="w-2 h-2 m-0 mr-1 fill-neutral-300" /></span
>{{ props.photo.preformatted.taken_at }}
</span>
<span v-else class="block mt-0 mr-0 mb-2 ml-3 text-2xs text-surface-300">{{ props.photo.preformatted.created_at }}</span>
</div>
<div
v-if="props.photo.precomputed.is_video"
class="w-full top-0 h-full absolute hover:opacity-70 transition-opacity duration-300 flex justify-center items-center"
>
<img class="absolute aspect-square w-fit h-fit" alt="play" :src="srcPlay" />
</div>
<!-- TODO: make me an option. -->
<div v-if="user?.id" class="badges absolute mt-[-1px] ml-1 top-0 left-0">
<ThumbBadge v-if="props.photo.is_starred" class="bg-yellow-500" icon="star" />
<ThumbBadge v-if="is_cover_id" class="bg-yellow-500" icon="folder-cover" />
<ThumbBadge v-if="is_header_id" class="bg-slate-400" pi="image" />
</div>
</router-link>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { useAuthStore } from "@/stores/Auth";
import MiniIcon from "@/components/icons/MiniIcon.vue";
import ThumbBadge from "@/components/gallery/thumbs/ThumbBadge.vue";
import { useLycheeStateStore } from "@/stores/LycheeState";
import { storeToRefs } from "pinia";
import { useImageHelpers } from "@/utils/Helpers";
const { getNoImageIcon, getPlayIcon } = useImageHelpers();
const props = defineProps<{
isSelected: boolean;
isLazy: boolean;
album:
| App.Http.Resources.Models.AlbumResource
| App.Http.Resources.Models.TagAlbumResource
| App.Http.Resources.Models.SmartAlbumResource
| undefined;
photo: App.Http.Resources.Models.PhotoResource;
}>();
const auth = useAuthStore();
const lycheeStore = useLycheeStateStore();
const srcPlay = ref(getPlayIcon());
const srcNoImage = ref(getNoImageIcon());
const isImageLoaded = ref(false);
function onImageLoad() {
isImageLoaded.value = true;
}
// @ts-expect-error
const is_cover_id = computed(() => props.album?.cover_id === props.photo.id);
// @ts-expect-error
const is_header_id = computed(() => props.album?.header_id === props.photo.id);
const { user } = storeToRefs(auth);
auth.getUser();
const cssClass = computed(() => {
let css = "photo group shadow-md shadow-black/25 animate-zoomIn transition-all ease-in duration-200 block absolute";
if (props.isSelected) {
css += " outline outline-1.5 outline-primary-500";
}
return css;
});
const cssOverlay = computed(() => {
if (lycheeStore.display_thumb_photo_overlay === "never") {
return "hidden";
}
if (lycheeStore.display_thumb_photo_overlay === "hover") {
return "opacity-0 group-hover:opacity-100 transition-all ease-out";
}
return "";
});
watch(
() => props.photo,
() => {
isImageLoaded.value = false;
},
);
</script>