<template> <div class="movie-tile" :class="{ unstashed: !favorited && pageStash && user && pageStash.user.id === user?.id, }" > <div class="cover-container" :title="movie.title" > <a :href="`/movie/${movie.id}/${movie.slug}`" class="cover-link" > <img v-if="movie.covers[0]" :src="getPath(movie.covers[0], 'thumbnail')" :style="{ 'background-image': `url(${getPath(movie.covers[0], 'lazy')})` }" class="thumbnail" loading="lazy" > <img v-else src="/public/img/icons/movie.svg" class="nocover" > </a> <Heart v-if="details" domain="movies" :item="movie" :show-secondary="false" class="light tiled" @stashed="(stash) => { favorited = stash.id === currentStash.id ? true : favorited; }" @unstashed="(stash) => { favorited = stash.id === currentStash.id ? false : favorited; }" /> </div> <div v-if="details" class="tile-info" > <div class="tile-meta"> <div class="channel"> <Link :href="movie.channel.isIndependent || !movie.network ? `/${movie.channel.type}/${movie.channel.slug}` : `/${movie.network.type}/${movie.network.slug}`" class="favicon-link" > <img :src="movie.channel.isIndependent || !movie.network ? `/logos/${movie.channel.slug}/favicon.png` : `/logos/${movie.network.slug}/favicon.png`" class="favicon" > </Link> <Link :href="`/${movie.channel.type}/${movie.channel.slug}`" class="nolink channel-link" >{{ movie.channel.name }}</Link> </div> <time :datetime="movie.effectiveDate.toISOString()" class="date" :class="{ nodate: !movie.date }" >{{ format(movie.effectiveDate, movie.effectiveDate.getFullYear() === currentYear ? 'MMM d' : 'MMM d, y') }}</time> </div> <a :href="`/movie/${movie.id}/${movie.slug}`" :title="movie.title" class="title nolink" >{{ movie.title }}</a> <ul :title="movie.actors.map((actor) => actor.name).join(', ')" class="actors nolist" > <li v-for="actor in movie.actors" :key="`actor-${movie.id}-${actor.slug}`" class="actor-item" > <a :href="`/actor/${actor.id}/${actor.slug}`" class="actor nolink" >{{ actor.name }}</a> </li> </ul> <ul :title="movie.tags.map((tag) => tag.name).join(', ')" class="tags nolist" > <li v-for="tag in movie.tags" :key="`tag-${movie.id}-${tag.slug}`" > <a :href="`/tag/${tag.slug}`" class="tag nolink" >{{ tag.name }}</a> </li> </ul> </div> <a v-else :href="`/movie/${movie.id}/${movie.slug}`" :title="movie.title" class="tile-info title nolink" >{{ movie.title }}</a> </div> </template> <script setup> import { ref, inject } from 'vue'; import { format } from 'date-fns'; import getPath from '#/src/get-path.js'; import Heart from '#/components/stashes/heart.vue'; const props = defineProps({ movie: { type: Object, default: null, }, details: { type: Boolean, default: true, }, }); const pageContext = inject('pageContext'); const user = pageContext.user; const pageStash = pageContext.pageProps.stash; const currentStash = pageStash || user?.primaryStash; const currentYear = new Date().getFullYear(); const favorited = ref(props.movie.stashes?.some((movieStash) => movieStash.id === currentStash.id)); </script> <style scoped> .movie-tile { display: flex; flex-direction: column; box-shadow: 0 0 3px var(--shadow-weak-30); border-radius: .25rem; overflow: hidden; background: var(--background-base); &:hover { box-shadow: 0 0 3px var(--shadow-weak-20); :deep(.bookmarks) .icon:not(.favorited):not(:hover) { fill: var(--text-light); } } &.unstashed { opacity: .5; } } .cover-container { display: flex; align-items: center; justify-content: center; flex-grow: 1; position: relative; background: var(--shadow-weak-30); aspect-ratio: 2/3; overflow: hidden; } .cover-link { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; font-size: 0; } .thumbnail { width: 100%; height: 100%; object-fit: cover; background-size: cover; background-position: center; } .nocover { width: 25%; opacity: .1; } .tile-info { flex-shrink: 0; font-size: 0; } .tile-meta { display: flex; justify-content: space-between; align-items: center; padding: .4rem .5rem; border-radius: 0 0 .25rem .25rem; margin-bottom: .5rem; background: var(--shadow-strong-30); color: var(--text-light); font-size: .8rem; } .channel { display: inline-flex; align-items: center; margin-right: .5rem; white-space: nowrap; overflow: hidden; } .channel-link { overflow: hidden; text-overflow: ellipsis; font-weight: bold; } .favicon-link { display: inline-flex; } .favicon { width: 1rem; height: 1rem; margin-right: .5rem; object-fit: contain; } .date { flex-shrink: 0; } .nodate { color: var(--highlight-strong-10); } .title { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: bold; padding: 0 .5rem; margin-bottom: .3rem; font-size: .9rem; &.tile-info { padding: .5rem; margin: 0; } } .actors { height: 2.4rem; display: flex; flex-wrap: wrap; overflow: hidden; font-size: .9rem; padding: 0 .5rem; margin-bottom: .3rem; line-height: 1.35; } .actor-item:not(:last-child):after { content: ',\00a0'; } .actor { &:hover { color: var(--primary); } } .tags { height: 1rem; display: flex; flex-wrap: wrap; gap: .5rem; overflow: hidden; padding: 0 .5rem; margin-bottom: .25rem; color: var(--shadow-strong-10); font-size: .75rem; } .tag { flex-shrink: 0; &:hover { color: var(--primary); } } .icon.heart { width: 2rem; height: 1.5rem; position: absolute; top: 0; right: 0; padding: .5rem .5rem 1rem 1rem; fill: var(--highlight-strong-10); filter: drop-shadow(0 0 3px var(--shadow)); &:hover { cursor: pointer; fill: var(--primary); } &.favorited { fill: var(--primary); } } </style>