Added title modals

This commit is contained in:
Aleksi Lassila
2023-08-09 15:51:45 +03:00
parent a8d328dec5
commit a52e96e0e1
13 changed files with 163 additions and 33 deletions

View File

@@ -0,0 +1,203 @@
<script lang="ts">
import { TMDB_IMAGES_ORIGINAL, TMDB_POSTER_SMALL } from '$lib/constants';
import classNames from 'classnames';
import { ChevronLeft, DotFilled, ExternalLink } from 'radix-icons-svelte';
import { fade } from 'svelte/transition';
import Carousel from '../Carousel/Carousel.svelte';
import CarouselPlaceholderItems from '../Carousel/CarouselPlaceholderItems.svelte';
import IconButton from '../IconButton.svelte';
export let isModal = false;
export let handleCloseModal: () => void = () => {};
export let tmdbId: number;
export let type: 'movie' | 'series';
export let backdropUriCandidates: string[];
export let posterPath: string;
export let title: string;
export let tagline: string;
export let overview: string;
let topHeight: number;
let bottomHeight: number;
let windowHeight: number;
let imageHeight: number;
$: imageHeight = isModal && topHeight ? topHeight : windowHeight - bottomHeight * 0.3;
function getBackdropUri(uris: string[]) {
return uris[Math.max(2, Math.floor(uris.length / 8))] || uris[uris.length - 1] || '';
}
</script>
<svelte:window bind:outerHeight={windowHeight} />
<div
style={"background-image: url('" +
TMDB_IMAGES_ORIGINAL +
getBackdropUri(backdropUriCandidates) +
"'); height: " +
imageHeight.toFixed() +
'px'}
class={classNames('hidden sm:block inset-x-0 bg-center bg-cover z-[-10]', {
absolute: isModal,
fixed: !isModal
})}
>
<div class="absolute inset-0 bg-darken" />
</div>
<div
style={"background-image: url('" +
TMDB_IMAGES_ORIGINAL +
posterPath +
"'); height: " +
imageHeight.toFixed() +
'px'}
class="sm:hidden fixed inset-x-0 bg-center bg-cover z-[-10]"
>
<div class="absolute inset-0 bg-darken" />
</div>
<div
class={classNames('flex flex-col', {
'h-[85vh] sm:h-screen': !isModal,
'': isModal
})}
transition:fade
>
<div
class={classNames('flex-1 relative flex pt-24 px-4 sm:px-8 pb-6', {
'min-h-[60vh]': isModal
})}
bind:clientHeight={topHeight}
>
{#if isModal}
<a href={`/${type}/${tmdbId}`} class="absolute top-8 right-4 sm:right-8 z-10">
<IconButton>
<ExternalLink size={20} />
</IconButton>
</a>
<div class="sm:hidden absolute top-8 left-4 sm:left-8 z-10">
<button class="flex items-center" on:click={handleCloseModal}>
<ChevronLeft size={20} />
Back
</button>
</div>
{/if}
<div class="absolute inset-0 bg-gradient-to-t from-stone-950 to-30%" />
<div class="z-[1] flex-1 flex justify-end gap-8 items-end max-w-screen-2xl mx-auto">
<div
class="aspect-[2/3] w-52 bg-center bg-cover rounded-md hidden sm:block"
style={"background-image: url('" + TMDB_POSTER_SMALL + posterPath + "')"}
/>
<div class="flex-1 flex gap-4 justify-between flex-col lg:flex-row lg:items-end">
<div>
<div class="text-zinc-300 text-sm uppercase font-semibold flex items-center gap-1">
<p class="flex-shrink-0">
<slot name="title-info-1" />
</p>
<DotFilled />
<p class="flex-shrink-0">
<slot name="title-info-2" />
</p>
<DotFilled />
<p class="flex-shrink-0"><slot name="title-info-3" /></p>
<!-- <DotFilled />
<p class="line-clamp-1">{series?.genres?.map((g) => g.name).join(', ')}</p> -->
</div>
<h1 class="text-4xl sm:text-5xl md:text-6xl font-semibold">{title}</h1>
</div>
<div class="flex-shrink-0">
<slot name="title-right" />
</div>
</div>
</div>
</div>
<div bind:clientHeight={bottomHeight} class="pb-6 bg-stone-950">
<div class="max-w-screen-2xl mx-auto">
<slot name="episodes-carousel" />
</div>
</div>
</div>
<div
class={classNames('flex flex-col gap-6 bg-stone-950 px-2 sm:px-4 lg:px-8 pb-6', {
'2xl:px-0': !isModal
})}
>
<div
class="grid grid-cols-4 sm:grid-cols-6 gap-4 sm:gap-x-8 rounded-xl max-w-screen-2xl 2xl:mx-auto py-4"
>
<slot name="info-description">
<div
class="flex flex-col gap-3 max-w-5xl row-span-3 col-span-4 sm:col-span-6 lg:col-span-3 mb-4 lg:mr-8"
>
<div class="flex gap-4 justify-between">
<h1 class="font-semibold text-xl sm:text-2xl">{tagline}</h1>
<!-- <div class="flex items-center gap-4">
<a
target="_blank"
href={'https://www.themoviedb.org/tv/' + tmdbId}
class="opacity-60 hover:opacity-100"
>
<img src="/tmdb.svg" alt="tmdb" width="25px" />
</a>
{#if $itemStore.item?.sonarrSeries?.titleSlug}
<a
target="_blank"
href={PUBLIC_SONARR_BASE_URL +
'/series/' +
$itemStore.item?.sonarrSeries?.titleSlug}
class="opacity-60 hover:opacity-100"
>
<img src="/sonarr.svg" alt="sonarr" width="15px" />
</a>
{/if}
{#if series?.homepage}
<a
target="_blank"
href={series.homepage}
class="flex gap-1 items-center opacity-60 hover:opacity-100"
>
<Globe size={15} />
</a>
{/if} -->
</div>
<p class="pl-4 border-l-2 text-sm sm:text-base">{overview}</p>
</div>
</slot>
<slot name="info-components" />
<slot name="servarr-components">
<div class="flex gap-4 flex-wrap col-span-4 sm:col-span-6 mt-4">
<div class="placeholder h-10 w-40 rounded-xl" />
<div class="placeholder h-10 w-40 rounded-xl" />
</div>
</slot>
</div>
<div class="max-w-screen-2xl 2xl:mx-auto w-full">
<Carousel gradientFromColor="from-stone-950">
<slot name="cast-crew-carousel-title" slot="title" />
<slot name="cast-crew-carousel">
<CarouselPlaceholderItems />
</slot>
</Carousel>
</div>
<div class="max-w-screen-2xl 2xl:mx-auto w-full">
<Carousel gradientFromColor="from-stone-950">
<slot name="recommendations-carousel-title" slot="title" />
<slot name="recommendations-carousel">
<CarouselPlaceholderItems />
</slot>
</Carousel>
</div>
<div class="max-w-screen-2xl 2xl:mx-auto w-full">
<Carousel gradientFromColor="from-stone-950">
<slot name="similar-carousel-title" slot="title" />
<slot name="similar-carousel">
<CarouselPlaceholderItems />
</slot>
</Carousel>
</div>
</div>

View File

@@ -0,0 +1,44 @@
<script lang="ts">
import { fly } from 'svelte/transition';
import MoviePage from '../../../routes/movie/[id]/MoviePage.svelte';
import SeriesPage from '../../../routes/series/[id]/SeriesPage.svelte';
import { createModalProps } from '../Modal/Modal';
import Modal from '../Modal/Modal.svelte';
import { titlePageModal } from './TitlePageModal';
import { onMount } from 'svelte';
const modalProps = createModalProps(() => {
titlePageModal.close();
});
function handleCloseModal() {
modalProps.close();
}
onMount(() => {
console.log('modal mounted');
return () => modalProps.close();
});
</script>
{#if $titlePageModal.tmdbId}
{@const tmdbId = $titlePageModal.tmdbId}
<Modal {...modalProps}>
<div
class="max-w-screen-2xl overflow-x-hidden overflow-y-scroll h-screen sm:mx-4 lg:mx-12 xl:mx-16 scrollbar-hide"
>
<div
class="relative overflow-hidden"
in:fly|global={{ y: 20, duration: 200, delay: 200 }}
out:fly|global={{ y: 20, duration: 200 }}
>
{#if $titlePageModal.type === 'movie'}
<MoviePage {tmdbId} isModal={true} {handleCloseModal} />
{:else}
<SeriesPage {tmdbId} isModal={true} {handleCloseModal} />
{/if}
</div>
</div>
</Modal>
{/if}

View File

@@ -0,0 +1,18 @@
import { writable } from 'svelte/store';
type Type = 'movie' | 'series' | undefined;
function createTitlePageModalStore() {
const store = writable<{ tmdbId: number | undefined; type: Type }>({
tmdbId: undefined,
type: undefined
});
return {
subscribe: store.subscribe,
set: (tmdbId: number | undefined, type: Type) => store.set({ tmdbId, type }),
close: () => store.set({ tmdbId: undefined, type: undefined })
};
}
export let titlePageModal = createTitlePageModalStore();