Fixed scroll component so it uses slot props instead of the depcrecated .
|  | @ -63,10 +63,7 @@ | |||
| 			:items-per-page="limit" | ||||
| 		/> | ||||
| 
 | ||||
| 		<div | ||||
| 			v-lazy-container="{ selector: '.lazy' }" | ||||
| 			class="tiles" | ||||
| 		> | ||||
| 		<div class="tiles"> | ||||
| 			<Actor | ||||
| 				v-for="actor in actors" | ||||
| 				:key="`actor-${actor.id}`" | ||||
|  |  | |||
|  | @ -16,9 +16,9 @@ | |||
| 		> | ||||
| 			<img | ||||
| 				:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`" | ||||
| 				:data-src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`" | ||||
| 				:data-loading="sfw ? `/img/${actor.avatar.sfw.lazy}` : `/media/${actor.avatar.lazy}`" | ||||
| 				:style="{ 'background-image': sfw ? `/img/${actor.avatar.sfw.lazy}` : `/media/${actor.avatar.lazy}` }" | ||||
| 				:title="actor.avatar.credit && `© ${actor.avatar.credit}`" | ||||
| 				loading="lazy" | ||||
| 				class="avatar photo" | ||||
| 				@load="$parent.$emit('load')" | ||||
| 			> | ||||
|  | @ -34,9 +34,9 @@ | |||
| 		> | ||||
| 			<img | ||||
| 				:src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`" | ||||
| 				:data-src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`" | ||||
| 				:data-loading="sfw ? `/img/${photo.sfw.lazy}` : `/media/${photo.lazy}`" | ||||
| 				:style="{ 'background-image': sfw ? `/img/${photo.sfw.lazy}` : `/media/${photo.lazy}` }" | ||||
| 				:title="`© ${photo.credit || photo.entity.name}`" | ||||
| 				loading="lazy" | ||||
| 				class="photo" | ||||
| 				@load="$parent.$emit('load')" | ||||
| 			> | ||||
|  | @ -76,9 +76,6 @@ export default { | |||
| 	padding: .5rem 1rem; | ||||
| 	margin: 0 1rem 0 0; | ||||
|     font-size: 0; | ||||
| 	overflow-x: scroll; | ||||
| 	scroll-behavior: smooth; | ||||
| 	scrollbar-width: none; | ||||
| 
 | ||||
| 	&.expanded { | ||||
| 		flex-wrap: wrap; | ||||
|  | @ -101,10 +98,6 @@ export default { | |||
|     .avatar-link { | ||||
|         display: none; | ||||
|     } | ||||
| 
 | ||||
| 	&::-webkit-scrollbar { | ||||
| 		display: none; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| .photo-link { | ||||
|  | @ -115,6 +108,7 @@ export default { | |||
|     height: 15rem; | ||||
|     box-shadow: 0 0 3px var(--darken-weak); | ||||
| 	object-fit: cover; | ||||
| 	background-size: cover; | ||||
| } | ||||
| 
 | ||||
| @media(max-width: $breakpoint3) { | ||||
|  |  | |||
|  | @ -45,9 +45,10 @@ | |||
| 			<div class="avatar-container"> | ||||
| 				<img | ||||
| 					v-if="actor.avatar" | ||||
| 					:data-src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`" | ||||
| 					:data-loading="sfw ? `/img/${actor.avatar.sfw.lazy}` : `/media/${actor.avatar.lazy}`" | ||||
| 					class="avatar lazy" | ||||
| 					:src="sfw ? `/img/${actor.avatar.sfw.thumbnail}` : `/media/${actor.avatar.thumbnail}`" | ||||
| 					:style="{ 'background-image': sfw ? `url(/img/${actor.avatar.sfw.lazy})`: `url(/img/${actor.avatar.lazy})` }" | ||||
| 					loading="lazy" | ||||
| 					class="avatar" | ||||
| 				> | ||||
| 
 | ||||
| 				<span | ||||
|  | @ -232,6 +233,7 @@ export default { | |||
|     justify-content: center; | ||||
|     object-fit: cover; | ||||
|     object-position: 50% 0; | ||||
| 	background-size: cover; | ||||
| } | ||||
| 
 | ||||
| .avatar-fallback { | ||||
|  |  | |||
|  | @ -28,8 +28,6 @@ | |||
| <script> | ||||
| import { mapState } from 'vuex'; | ||||
| 
 | ||||
| import EventBus from '../../js/event-bus'; | ||||
| 
 | ||||
| import Warning from './warning.vue'; | ||||
| import Header from '../header/header.vue'; | ||||
| import Sidebar from '../sidebar/sidebar.vue'; | ||||
|  | @ -49,12 +47,6 @@ async function setConsent(consent) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| function mounted() { | ||||
| 	document.addEventListener('click', () => { | ||||
| 		EventBus.$emit('blur'); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| export default { | ||||
| 	components: { | ||||
| 		Header, | ||||
|  | @ -72,7 +64,6 @@ export default { | |||
| 			theme, | ||||
| 		}), | ||||
| 	}, | ||||
| 	mounted, | ||||
| 	methods: { | ||||
| 		toggleSidebar, | ||||
| 		setConsent, | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ | |||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import Vue from 'vue'; | ||||
| import { nextTick } from 'vue'; | ||||
| 
 | ||||
| import FilterBar from '../filters/filter-bar.vue'; | ||||
| import Pagination from '../pagination/pagination.vue'; | ||||
|  | @ -135,7 +135,7 @@ async function fetchEntity() { | |||
| 
 | ||||
| 	this.pageTitle = entity.name; | ||||
| 
 | ||||
| 	Vue.nextTick(() => { | ||||
| 	nextTick(() => { | ||||
| 		this.$refs.content.scrollTop = 0; | ||||
| 	}); | ||||
| } | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
| 			>New</router-link> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<!-- | ||||
| 		<div class="filters"> | ||||
| 			<ActorFilter | ||||
| 				class="filters-filter" | ||||
|  | @ -39,6 +40,7 @@ | |||
| 				:available-tags="availableTags" | ||||
| 			/> | ||||
| 		</div> | ||||
| 		--> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
|  |  | |||
|  | @ -88,64 +88,63 @@ | |||
| 					</div> | ||||
| 				</div> | ||||
| 
 | ||||
| 				<div | ||||
| 					slot="popover" | ||||
| 					class="menu" | ||||
| 				> | ||||
| 					<ul class="menu-items noselect"> | ||||
| 						<li class="menu-item disabled"> | ||||
| 							<Icon icon="enter2" />Sign in | ||||
| 						</li> | ||||
| 				<template v-slot:popover> | ||||
| 					<div class="menu"> | ||||
| 						<ul class="menu-items noselect"> | ||||
| 							<li class="menu-item disabled"> | ||||
| 								<Icon icon="enter2" />Sign in | ||||
| 							</li> | ||||
| 
 | ||||
| 						<li | ||||
| 							v-show="!sfw" | ||||
| 							class="menu-item" | ||||
| 							@click="setSfw(true)" | ||||
| 						> | ||||
| 							<Icon | ||||
| 								icon="flower" | ||||
| 								class="toggle noselect" | ||||
| 							/>Safe mode | ||||
| 						</li> | ||||
| 							<li | ||||
| 								v-show="!sfw" | ||||
| 								class="menu-item" | ||||
| 								@click="setSfw(true)" | ||||
| 							> | ||||
| 								<Icon | ||||
| 									icon="flower" | ||||
| 									class="toggle noselect" | ||||
| 								/>Safe mode | ||||
| 							</li> | ||||
| 
 | ||||
| 						<li | ||||
| 							v-show="sfw" | ||||
| 							class="menu-item" | ||||
| 							@click="setSfw(false)" | ||||
| 						> | ||||
| 							<Icon | ||||
| 								icon="fire" | ||||
| 								class="toggle noselect" | ||||
| 							/>Filth mode | ||||
| 						</li> | ||||
| 							<li | ||||
| 								v-show="sfw" | ||||
| 								class="menu-item" | ||||
| 								@click="setSfw(false)" | ||||
| 							> | ||||
| 								<Icon | ||||
| 									icon="fire" | ||||
| 									class="toggle noselect" | ||||
| 								/>Filth mode | ||||
| 							</li> | ||||
| 
 | ||||
| 						<li | ||||
| 							v-show="theme === 'light'" | ||||
| 							class="menu-item" | ||||
| 							@click="setTheme('dark')" | ||||
| 						> | ||||
| 							<Icon | ||||
| 								icon="moon" | ||||
| 								class="toggle noselect" | ||||
| 							/>Dark theme | ||||
| 						</li> | ||||
| 							<li | ||||
| 								v-show="theme === 'light'" | ||||
| 								class="menu-item" | ||||
| 								@click="setTheme('dark')" | ||||
| 							> | ||||
| 								<Icon | ||||
| 									icon="moon" | ||||
| 									class="toggle noselect" | ||||
| 								/>Dark theme | ||||
| 							</li> | ||||
| 
 | ||||
| 						<li | ||||
| 							v-show="theme === 'dark'" | ||||
| 							class="menu-item" | ||||
| 							@click="setTheme('light')" | ||||
| 						> | ||||
| 							<Icon | ||||
| 								icon="sun" | ||||
| 								class="toggle noselect" | ||||
| 							/>Light theme | ||||
| 						</li> | ||||
| 							<li | ||||
| 								v-show="theme === 'dark'" | ||||
| 								class="menu-item" | ||||
| 								@click="setTheme('light')" | ||||
| 							> | ||||
| 								<Icon | ||||
| 									icon="sun" | ||||
| 									class="toggle noselect" | ||||
| 								/>Light theme | ||||
| 							</li> | ||||
| 
 | ||||
| 						<li class="menu-item"> | ||||
| 							<Icon icon="filter" />Filters | ||||
| 						</li> | ||||
| 					</ul> | ||||
| 				</div> | ||||
| 							<li class="menu-item"> | ||||
| 								<Icon icon="filter" />Filters | ||||
| 							</li> | ||||
| 						</ul> | ||||
| 					</div> | ||||
| 				</template> | ||||
| 			</v-popover> | ||||
| 
 | ||||
| 			<Search class="search-full" /> | ||||
|  |  | |||
|  | @ -1,8 +1,5 @@ | |||
| <template> | ||||
| 	<div | ||||
| 		v-lazy-container | ||||
| 		class="media" | ||||
| 	> | ||||
| 	<div class="media"> | ||||
| 		<div | ||||
| 			v-if="release.trailer || release.teaser" | ||||
| 			class="trailer-container" | ||||
|  | @ -33,8 +30,9 @@ | |||
| 
 | ||||
| 			<img | ||||
| 				v-else-if="release.teaser && /^image\//.test(release.teaser.mime)" | ||||
| 				:data-src="sfw ? `/img/${release.teaser.sfw.thumbnail}` : `/media/${release.teaser.path}`" | ||||
| 				:src="sfw ? `/img/${release.teaser.sfw.thumbnail}` : `/media/${release.teaser.path}`" | ||||
| 				:alt="release.title" | ||||
| 				loading="lazy" | ||||
| 				class="item trailer" | ||||
| 			> | ||||
| 
 | ||||
|  | @ -65,10 +63,11 @@ | |||
| 				rel="noopener noreferrer" | ||||
| 			> | ||||
| 				<img | ||||
| 					:data-src="`/media/${cover.thumbnail}`" | ||||
| 					:data-loading="`/media/${cover.lazy}`" | ||||
| 					:src="`/media/${cover.thumbnail}`" | ||||
| 					:style="{ background: sfw ? `/media/${cover.sfw.lazy}` : `/media/${cover.lazy}` }" | ||||
| 					class="item cover" | ||||
| 					@load="$parent.$emit('load')" | ||||
| 					loading="lazy" | ||||
| 					@load="$emit('load', $event)" | ||||
| 				> | ||||
| 			</a> | ||||
| 		</template> | ||||
|  | @ -86,11 +85,12 @@ | |||
| 				rel="noopener noreferrer" | ||||
| 			> | ||||
| 				<img | ||||
| 					:data-src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`" | ||||
| 					:data-loading="sfw ? `/img/${photo.sfw.lazy}` : `/media/${photo.lazy}`" | ||||
| 					:src="sfw ? `/img/${photo.sfw.thumbnail}` : `/media/${photo.thumbnail}`" | ||||
| 					:style="{ background: sfw ? `/img/${photo.sfw.lazy}` : `/media/${photo.lazy}` }" | ||||
| 					:alt="`Photo ${photo.index + 1}`" | ||||
| 					loading="lazy" | ||||
| 					class="item" | ||||
| 					@load="$parent.$emit('load')" | ||||
| 					@load="$emit('load', $event)" | ||||
| 				> | ||||
| 
 | ||||
| 				<span | ||||
|  | @ -139,6 +139,10 @@ export default { | |||
| 			type: Boolean, | ||||
| 			default: false, | ||||
| 		}, | ||||
| 		test: { | ||||
| 			type: String, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
|  | @ -159,15 +163,8 @@ export default { | |||
| .media { | ||||
|     flex-shrink: 0; | ||||
|     white-space: nowrap; | ||||
|     overflow-x: auto; | ||||
|     scrollbar-width: none; | ||||
| 	scroll-behavior: smooth; | ||||
|     font-size: 0; | ||||
| 
 | ||||
|     &::-webkit-scrollbar { | ||||
|         display: none; | ||||
|     } | ||||
| 
 | ||||
| 	&.expanded { | ||||
| 		display: grid; | ||||
| 		grid-template-columns: repeat(auto-fill, minmax(32rem, 1fr)); | ||||
|  | @ -249,6 +246,7 @@ export default { | |||
| 	height: 18rem; | ||||
| 	max-width: 100%; | ||||
| 	box-shadow: 0 0 3px var(--shadow-weak); | ||||
| 	background-size: cover; | ||||
| } | ||||
| 
 | ||||
| .trailer-container { | ||||
|  |  | |||
|  | @ -11,12 +11,14 @@ | |||
| 		/> | ||||
| 
 | ||||
| 		<Scroll | ||||
| 			v-slot="slotProps" | ||||
| 			class="scroll-light" | ||||
| 			:expandable="false" | ||||
| 		> | ||||
| 			<Media | ||||
| 				:release="release" | ||||
| 				:class="{ expanded }" | ||||
| 				@load="slotProps.loaded" | ||||
| 			/> | ||||
| 		</Scroll> | ||||
| 
 | ||||
|  | @ -56,10 +58,7 @@ | |||
| 			</div> | ||||
| 
 | ||||
| 			<div class="row associations"> | ||||
| 				<ul | ||||
| 					v-lazy-container="{ selector: '.lazy' }" | ||||
| 					class="actors nolist" | ||||
| 				> | ||||
| 				<ul class="actors nolist"> | ||||
| 					<li | ||||
| 						v-for="actor in release.actors" | ||||
| 						:key="actor.id" | ||||
|  |  | |||
|  | @ -8,7 +8,6 @@ | |||
| 		<ul | ||||
| 			v-if="releases.length > 0" | ||||
| 			:key="sfw" | ||||
| 			v-lazy-container="{ selector: '.thumbnail' }" | ||||
| 			class="nolist tiles" | ||||
| 		> | ||||
| 			<li | ||||
|  |  | |||
|  | @ -13,10 +13,10 @@ | |||
| 			> | ||||
| 				<img | ||||
| 					v-if="release.poster" | ||||
| 					:data-src="sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`" | ||||
| 					:data-loading="sfw ? `/img/${release.poster.sfw.lazy}` : `/media/${release.poster.lazy}`" | ||||
| 					:src="sfw ? `/img/${release.poster.sfw.thumbnail}` : `/media/${release.poster.thumbnail}`" | ||||
| 					:alt="release.title" | ||||
| 					class="thumbnail" | ||||
| 					loading="lazy" | ||||
| 				> | ||||
| 
 | ||||
| 				<span | ||||
|  | @ -26,10 +26,10 @@ | |||
| 					<img | ||||
| 						v-for="cover in release.covers" | ||||
| 						:key="cover.id" | ||||
| 						:data-src="sfw ? `/img/${cover.sfw.thumbnail}` : `/media/${cover.thumbnail}`" | ||||
| 						:data-loading="sfw ? `/img/${cover.sfw.lazy}` : `/media/${cover.lazy}`" | ||||
| 						:src="sfw ? `/img/${cover.sfw.thumbnail}` : `/media/${cover.thumbnail}`" | ||||
| 						:alt="release.title" | ||||
| 						class="thumbnail cover" | ||||
| 						loading="lazy" | ||||
| 					> | ||||
| 				</span> | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,7 +22,12 @@ | |||
| 				@click="scroll('left')" | ||||
| 			><Icon icon="arrow-left3" /></div> | ||||
| 
 | ||||
| 			<slot /> | ||||
| 			<div | ||||
| 				ref="content" | ||||
| 				class="content" | ||||
| 			> | ||||
| 				<slot :loaded="loaded" /> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<div | ||||
| 				v-show="enabled && !expanded" | ||||
|  | @ -52,34 +57,35 @@ | |||
| import Expand from '../expand/expand.vue'; | ||||
| 
 | ||||
| function updateScroll() { | ||||
| 	this.scrollable = this.target.scrollWidth > this.target.clientWidth; | ||||
| 	this.scrollAtStart = this.target.scrollLeft === 0; | ||||
| 	this.scrollAtEnd = this.target.scrollWidth - this.target.clientWidth === this.target.scrollLeft; | ||||
| 	this.scrollable = this.$refs.content.scrollWidth > this.$refs.content.clientWidth; | ||||
| 	this.scrollAtStart = this.$refs.content.scrollLeft === 0; | ||||
| 	this.scrollAtEnd = this.$refs.content.scrollWidth - this.$refs.content.clientWidth === this.$refs.content.scrollLeft; | ||||
| } | ||||
| 
 | ||||
| function scroll(direction) { | ||||
| 	if (direction === 'right') { | ||||
| 		this.target.scrollLeft = this.target.scrollLeft + this.target.clientWidth - 100; | ||||
| 		this.$refs.content.scrollLeft = this.$refs.content.scrollLeft + this.$refs.content.clientWidth - 100; | ||||
| 	} | ||||
| 
 | ||||
| 	if (direction === 'left') { | ||||
| 		this.target.scrollLeft = this.target.scrollLeft - this.target.clientWidth + 100; | ||||
| 		this.$refs.content.scrollLeft = this.$refs.content.scrollLeft - this.$refs.content.clientWidth + 100; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function mounted() { | ||||
| 	this.target = this.$slots.default[0].elm; | ||||
| function loaded(_event) { | ||||
| 	// typically triggered by slotted component when an image loads, affecting scrollWidth | ||||
| 	this.updateScroll(); | ||||
| } | ||||
| 
 | ||||
| 	this.target.addEventListener('scroll', () => this.updateScroll()); | ||||
| function mounted() { | ||||
| 	this.$refs.content.addEventListener('scroll', () => this.updateScroll()); | ||||
| 	window.addEventListener('resize', this.updateScroll); | ||||
| 
 | ||||
| 	// typically triggered by slotted component when an image loads, affecting scrollWidth | ||||
| 	this.$on('load', () => this.updateScroll()); | ||||
| 	this.updateScroll(); | ||||
| } | ||||
| 
 | ||||
| function beforeDestroy() { | ||||
| 	this.target.removeEventListener('scroll', this.updateScroll); | ||||
| 	this.$refs.content.removeEventListener('scroll', this.updateScroll); | ||||
| 
 | ||||
| 	window.removeEventListener('resize', this.updateScroll); | ||||
| } | ||||
|  | @ -108,7 +114,6 @@ export default { | |||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			target: null, | ||||
| 			scrollable: true, | ||||
| 			scrollAtStart: true, | ||||
| 			scrollAtEnd: false, | ||||
|  | @ -119,6 +124,7 @@ export default { | |||
| 	beforeDestroy, | ||||
| 	methods: { | ||||
| 		scroll, | ||||
| 		loaded, | ||||
| 		updateScroll, | ||||
| 	}, | ||||
| }; | ||||
|  | @ -143,6 +149,16 @@ export default { | |||
| 	position: relative; | ||||
| } | ||||
| 
 | ||||
| .content { | ||||
| 	overflow-x: scroll; | ||||
| 	scroll-behavior: smooth; | ||||
| 	scrollbar-width: none; | ||||
| 
 | ||||
|     &::-webkit-scrollbar { | ||||
|         display: none; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .expand-light { | ||||
| 	display: none; | ||||
| } | ||||
|  | @ -233,6 +249,14 @@ export default { | |||
| 		display: none; | ||||
| 	} | ||||
| 
 | ||||
| 	&.scroll-start { | ||||
| 		left: 0; | ||||
| 	} | ||||
| 
 | ||||
| 	&.scroll-end { | ||||
| 		right: 0; | ||||
| 	} | ||||
| 
 | ||||
| 	&:hover { | ||||
| 		display: flex; | ||||
| 		cursor: pointer; | ||||
|  |  | |||
|  | @ -249,7 +249,7 @@ export default { | |||
| 	fill: var(--primary); | ||||
| } | ||||
| 
 | ||||
| ::v-deep .search { | ||||
| :deep(.search) { | ||||
| 	height: 3rem; | ||||
| 	border-bottom: solid 1px var(--shadow-hint); | ||||
| 	padding: 0; | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ | |||
| 
 | ||||
| 			<div | ||||
| 				:key="sfw" | ||||
| 				v-lazy-container | ||||
| 				class="tiles" | ||||
| 			> | ||||
| 				<Tag | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| 			<img | ||||
| 				v-if="!lazy && !sfw" | ||||
| 				:src="`/img/${tag.poster.thumbnail}`" | ||||
| 				:style="{ 'background-image': `url(/img/${tag.poster.lazy})` }" | ||||
| 				:title="tag.poster.comment" | ||||
| 				:alt="tag.name" | ||||
| 				class="poster" | ||||
|  | @ -19,6 +20,7 @@ | |||
| 			<img | ||||
| 				v-if="!lazy && sfw" | ||||
| 				:src="`/img/${tag.poster.sfw.thumbnail}`" | ||||
| 				:style="{ 'background-image': `url(/img/${tag.poster.sfw.lazy})` }" | ||||
| 				:title="tag.poster.sfw.comment" | ||||
| 				:alt="tag.name" | ||||
| 				class="poster" | ||||
|  | @ -26,19 +28,21 @@ | |||
| 
 | ||||
| 			<img | ||||
| 				v-if="lazy && !sfw" | ||||
| 				:data-src="`/img/${tag.poster.thumbnail}`" | ||||
| 				:data-loading="`/img/${tag.poster.lazy}`" | ||||
| 				:src="`/img/${tag.poster.thumbnail}`" | ||||
| 				:style="{ 'background-image': `url(/img/${tag.poster.lazy})` }" | ||||
| 				:title="tag.poster.comment" | ||||
| 				:alt="tag.name" | ||||
| 				loading="lazy" | ||||
| 				class="poster" | ||||
| 			> | ||||
| 
 | ||||
| 			<img | ||||
| 				v-if="lazy && sfw" | ||||
| 				:data-src="`/img/${tag.poster.sfw.thumbnail}`" | ||||
| 				:data-loading="`/img/${tag.poster.sfw.lazy}`" | ||||
| 				:src="`/img/${tag.poster.sfw.thumbnail}`" | ||||
| 				:style="{ 'background-image': `url(/img/${tag.poster.sfw.lazy})` }" | ||||
| 				:title="tag.poster.sfw.comment" | ||||
| 				:alt="tag.name" | ||||
| 				loading="lazy" | ||||
| 				class="poster" | ||||
| 			> | ||||
| 		</router-link> | ||||
|  | @ -99,7 +103,11 @@ export default { | |||
| } | ||||
| 
 | ||||
| .poster { | ||||
| 	display: inline-block; | ||||
|     width: 100%; | ||||
| 	height: 13.5rem; | ||||
| 	object-fit: cover; | ||||
| 	background-size: cover; | ||||
|     box-shadow: 0 0 3px var(--darken-weak); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| <template> | ||||
| 	<div class="tooltip-container"> | ||||
| 		<div class="trigger"> | ||||
| 			<slot /> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<div class="tooltip"> | ||||
| 			<div class="tooltip-wrapper"> | ||||
| 				<slot name="popover" /> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .tooltip-container { | ||||
| 	position: relative; | ||||
| 	font-size: 1rem; | ||||
| } | ||||
| 
 | ||||
| .tooltip { | ||||
| 	position: absolute; | ||||
| 	z-index: 10; | ||||
| 	top: 2rem; | ||||
| 	background: var(--background); | ||||
| } | ||||
| </style> | ||||
|  | @ -13,6 +13,10 @@ body { | |||
|     font-family: Arial, Helvetica, sans-serif; | ||||
| } | ||||
| 
 | ||||
| #container { | ||||
| 	height: 100%; | ||||
| } | ||||
| 
 | ||||
| .nolist { | ||||
|     list-style: none; | ||||
|     padding: 0; | ||||
|  |  | |||
|  | @ -1,5 +0,0 @@ | |||
| import Vue from 'vue'; | ||||
| 
 | ||||
| const EventBus = new Vue(); | ||||
| 
 | ||||
| export default EventBus; | ||||
|  | @ -1,6 +1,4 @@ | |||
| import Vue from 'vue'; | ||||
| import VTooltip from 'v-tooltip'; | ||||
| import VueLazyLoad from 'vue-lazyload'; | ||||
| import { createApp } from 'vue'; | ||||
| import dayjs from 'dayjs'; | ||||
| 
 | ||||
| import router from './router'; | ||||
|  | @ -14,9 +12,11 @@ import '../css/style.scss'; | |||
| import Container from '../components/container/container.vue'; | ||||
| import Icon from '../components/icon/icon.vue'; | ||||
| import Footer from '../components/footer/footer.vue'; | ||||
| import Tooltip from '../components/tooltip/tooltip.vue'; | ||||
| 
 | ||||
| function init() { | ||||
| async function init() { | ||||
| 	const store = initStore(router); | ||||
| 	const app = createApp(Container); | ||||
| 
 | ||||
| 	initUiObservers(store, router); | ||||
| 
 | ||||
|  | @ -24,10 +24,17 @@ function init() { | |||
| 		store.dispatch('setSfw', true); | ||||
| 	} | ||||
| 
 | ||||
| 	Vue.mixin({ | ||||
| 	app.use(store); | ||||
| 	app.use(router); | ||||
| 
 | ||||
| 	await router.isReady(); | ||||
| 
 | ||||
| 	app.mixin({ | ||||
| 		components: { | ||||
| 			Icon, | ||||
| 			Footer, | ||||
| 			Tooltip, | ||||
| 			'v-popover': Tooltip, | ||||
| 		}, | ||||
| 		watch: { | ||||
| 			pageTitle(title) { | ||||
|  | @ -47,23 +54,14 @@ function init() { | |||
| 		}, | ||||
| 	}); | ||||
| 
 | ||||
| 	Vue.use(VTooltip, { | ||||
| 		popover: { | ||||
| 			defaultContainer: '.container', | ||||
| 	app.directive('tooltip', { | ||||
| 		beforeMount(el, binding) { | ||||
| 			// console.log(binding.modifiers);
 | ||||
| 			el.title = binding.value; // eslint-disable-line no-param-reassign
 | ||||
| 		}, | ||||
| 	}); | ||||
| 	Vue.use(VueLazyLoad, { | ||||
| 		throttleWait: 0, | ||||
| 	}); | ||||
| 
 | ||||
| 	new Vue({ // eslint-disable-line no-new
 | ||||
| 		el: '#container', | ||||
| 		store, | ||||
| 		router, | ||||
| 		render(createElement) { | ||||
| 			return createElement(Container); | ||||
| 		}, | ||||
| 	}); | ||||
| 	app.mount('#container'); | ||||
| } | ||||
| 
 | ||||
| init(); | ||||
|  |  | |||
|  | @ -1,14 +1 @@ | |||
| import Vue from 'vue'; | ||||
| 
 | ||||
| function setCache(state, { target, releases }) { | ||||
| 	Vue.set(state.cache, target, releases); | ||||
| } | ||||
| 
 | ||||
| function deleteCache(state, target) { | ||||
| 	Vue.delete(state.cache, target); | ||||
| } | ||||
| 
 | ||||
| export default { | ||||
| 	setCache, | ||||
| 	deleteCache, | ||||
| }; | ||||
| export default {}; | ||||
|  |  | |||
|  | @ -1,5 +1,4 @@ | |||
| import Vue from 'vue'; | ||||
| import VueRouter from 'vue-router'; | ||||
| import { createRouter, createWebHistory } from 'vue-router'; | ||||
| 
 | ||||
| import Home from '../components/home/home.vue'; | ||||
| import Release from '../components/releases/release.vue'; | ||||
|  | @ -14,8 +13,6 @@ import Search from '../components/search/search.vue'; | |||
| import Stats from '../components/stats/stats.vue'; | ||||
| import NotFound from '../components/errors/404.vue'; | ||||
| 
 | ||||
| Vue.use(VueRouter); | ||||
| 
 | ||||
| const routes = [ | ||||
| 	{ | ||||
| 		path: '/', | ||||
|  | @ -184,15 +181,15 @@ const routes = [ | |||
| 		component: NotFound, | ||||
| 	}, | ||||
| 	{ | ||||
| 		path: '*', | ||||
| 		path: '/:catchAll(.*)', | ||||
| 		redirect: { | ||||
| 			name: 'not-found', | ||||
| 		}, | ||||
| 	}, | ||||
| ]; | ||||
| 
 | ||||
| const router = new VueRouter({ | ||||
| 	mode: 'history', | ||||
| const router = createRouter({ | ||||
| 	history: createWebHistory(), | ||||
| 	routes, | ||||
| }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import Vue from 'vue'; | ||||
| import Vuex from 'vuex'; | ||||
| 
 | ||||
| import initUiStore from './ui/ui'; | ||||
|  | @ -9,8 +8,6 @@ import initActorsStore from './actors/actors'; | |||
| import initTagsStore from './tags/tags'; | ||||
| 
 | ||||
| function initStore(router) { | ||||
| 	Vue.use(Vuex); | ||||
| 
 | ||||
| 	const store = new Vuex.Store(); | ||||
| 
 | ||||
| 	store.registerModule('ui', initUiStore(store, router)); | ||||
|  |  | |||
							
								
								
									
										19
									
								
								package.json
								
								
								
								
							
							
						
						|  | @ -43,11 +43,12 @@ | |||
|         "@babel/plugin-proposal-optional-chaining": "^7.8.3", | ||||
|         "@babel/preset-env": "^7.8.4", | ||||
|         "@babel/register": "^7.8.3", | ||||
|         "@vue/compiler-sfc": "^3.0.4", | ||||
|         "autoprefixer": "^9.7.4", | ||||
|         "babel-eslint": "^10.1.0", | ||||
|         "babel-loader": "^8.0.6", | ||||
|         "babel-preset-airbnb": "^3.3.2", | ||||
|         "css-loader": "^2.1.1", | ||||
|         "css-loader": "^5.0.1", | ||||
|         "eslint": "^5.16.0", | ||||
|         "eslint-config-airbnb": "^17.1.1", | ||||
|         "eslint-config-airbnb-base": "^13.2.0", | ||||
|  | @ -57,15 +58,14 @@ | |||
|         "eslint-plugin-react": "^7.18.3", | ||||
|         "eslint-plugin-vue": "^6.2.1", | ||||
|         "eslint-watch": "^4.0.2", | ||||
|         "mini-css-extract-plugin": "^0.7.0", | ||||
|         "mini-css-extract-plugin": "^1.3.3", | ||||
|         "node-sass": "^4.13.1", | ||||
|         "postcss-loader": "^3.0.0", | ||||
|         "raw-loader": "^2.0.0", | ||||
|         "raw-loader": "^4.0.2", | ||||
|         "sass-loader": "^7.3.1", | ||||
|         "style-loader": "^0.23.1", | ||||
|         "vue-loader": "^15.9.0", | ||||
|         "vue-template-compiler": "^2.6.11", | ||||
|         "webpack": "^4.41.6", | ||||
|         "vue-loader": "^16.1.2", | ||||
|         "webpack": "^5.11.0", | ||||
|         "webpack-cli": "^3.3.11" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|  | @ -126,10 +126,9 @@ | |||
|         "tunnel": "0.0.6", | ||||
|         "url-pattern": "^1.0.3", | ||||
|         "v-tooltip": "^2.0.3", | ||||
|         "vue": "^2.6.11", | ||||
|         "vue-lazyload": "^1.3.3", | ||||
|         "vue-router": "^3.1.6", | ||||
|         "vuex": "^3.1.2", | ||||
|         "vue": "^3.0.4", | ||||
|         "vue-router": "^4.0.1", | ||||
|         "vuex": "^4.0.0-rc.2", | ||||
|         "winston": "^3.2.1", | ||||
|         "winston-daily-rotate-file": "^4.4.2", | ||||
|         "yargs": "^13.3.0" | ||||
|  |  | |||
| Before Width: | Height: | Size: 529 KiB After Width: | Height: | Size: 440 KiB | 
| Before Width: | Height: | Size: 440 KiB | 
| After Width: | Height: | Size: 529 KiB | 
| Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.6 KiB | 
| After Width: | Height: | Size: 5.1 KiB | 
| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 22 KiB | 
| After Width: | Height: | Size: 20 KiB | 
|  | @ -1,6 +1,6 @@ | |||
| const webpack = require('webpack'); | ||||
| const path = require('path'); | ||||
| const VueLoaderPlugin = require('vue-loader/lib/plugin'); | ||||
| const { VueLoaderPlugin } = require('vue-loader'); | ||||
| const MiniCssExtractPlugin = require('mini-css-extract-plugin'); | ||||
| const autoprefixer = require('autoprefixer'); | ||||
| 
 | ||||
|  | @ -42,7 +42,13 @@ module.exports = { | |||
| 				test: /\.scss$/, | ||||
| 				use: [ | ||||
| 					MiniCssExtractPlugin.loader, | ||||
| 					'css-loader?sourceMap', | ||||
| 					{ | ||||
| 						loader: 'css-loader', | ||||
| 						options: { | ||||
| 							sourceMap: true, | ||||
| 							url: false, | ||||
| 						}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						loader: 'postcss-loader', | ||||
| 						options: { | ||||
|  |  | |||