create a layout for person page

This commit is contained in:
Francisco Zorat
2023-09-04 23:34:33 -03:00
parent d38a529670
commit 150b20f15c
2 changed files with 234 additions and 9 deletions

View File

@@ -0,0 +1,225 @@
<script lang="ts">
import { TMDB_IMAGES_ORIGINAL, TMDB_POSTER_SMALL } from '$lib/constants';
import type { TitleId, TitleType } from '$lib/types';
import classNames from 'classnames';
import { ChevronLeft, Cross2, DotFilled, ExternalLink } from 'radix-icons-svelte';
import Carousel from '../Carousel/Carousel.svelte';
import CarouselPlaceholderItems from '../Carousel/CarouselPlaceholderItems.svelte';
import IconButton from '../IconButton.svelte';
import LazyImg from '../LazyImg.svelte';
export let isModal = false;
export let handleCloseModal: () => void = () => {};
export let titleInformation:
| {
tmdbId: number;
type: TitleType;
title: string;
tagline: string;
overview: string;
backdropUriCandidates: string[];
posterPath: string;
}
| undefined = undefined;
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} />
<!-- Desktop -->
<div
style={'height: ' + imageHeight.toFixed() + 'px'}
class={classNames('hidden sm:block inset-x-0 bg-center bg-cover bg-stone-950', {
absolute: isModal,
fixed: !isModal
})}
>
{#if titleInformation}
<LazyImg
src={TMDB_IMAGES_ORIGINAL + getBackdropUri(titleInformation.backdropUriCandidates)}
class="h-full"
>
<div class="absolute inset-0 bg-darken" />
</LazyImg>
{/if}
</div>
<!-- Mobile -->
<div
style={'height: ' + imageHeight.toFixed() + 'px'}
class="sm:hidden fixed inset-x-0 bg-center bg-cover bg-stone-950"
>
{#if titleInformation}
<LazyImg src={TMDB_IMAGES_ORIGINAL + titleInformation.posterPath} class="h-full">
<div class="absolute inset-0 bg-darken" />
</LazyImg>
{/if}
</div>
<div
class={classNames('flex flex-col relative z-[1]', {
'h-[85vh] sm:h-screen': !isModal,
'': isModal
})}
>
<div
class={classNames('flex-1 relative flex pt-24 px-2 sm:px-4 lg:px-8 pb-6', {
'min-h-[60vh]': isModal
})}
bind:clientHeight={topHeight}
>
{#if isModal}
{#if titleInformation}
<a
href={`/${titleInformation.type}/${titleInformation.tmdbId}`}
class="absolute top-8 right-4 sm:right-8 z-10"
>
<IconButton>
<ExternalLink size={20} />
</IconButton>
</a>
{/if}
<div class="absolute top-8 left-4 sm:left-8 z-10">
<button class="flex items-center sm:hidden font-medium" on:click={handleCloseModal}>
<ChevronLeft size={20} />
Back
</button>
<div class="hidden sm:block">
<IconButton on:click={handleCloseModal}>
<Cross2 size={20} />
</IconButton>
</div>
</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">
{#if titleInformation}
<div
class="aspect-[2/3] w-52 bg-center bg-cover rounded-md hidden sm:block"
style={"background-image: url('" + TMDB_POSTER_SMALL + titleInformation.posterPath + "')"}
/>
{:else}
<div class="aspect-[2/3] w-52 bg-center bg-cover rounded-md hidden sm:block placeholder" />
{/if}
<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">
<slot name="title-info">
<div class="placeholder-text">Placeholder Long</div>
</slot>
</div>
{#if titleInformation}
<h1 class="text-4xl sm:text-5xl md:text-6xl font-semibold">{titleInformation.title}</h1>
{:else}
<h1 class="text-4xl sm:text-5xl md:text-6xl placeholder-text mt-2">Placeholder</h1>
{/if}
</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 relative', {
'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"
>
{#if titleInformation}
<div class="flex gap-4 justify-between">
<h1 class="font-semibold text-xl sm:text-2xl">{titleInformation.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">{titleInformation.overview}</p>
{:else}
<div class="flex gap-4 justify-between">
<h1 class="font-semibold text-xl sm:text-2xl placeholder-text">Placeholder</h1>
</div>
<div class="flex">
<div class="mr-4 placeholder w-1 flex-shrink-0 rounded" />
<p class="text-sm sm:text-base placeholder-text">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sit amet sem eget
dolor lobortis mollis. Aliquam semper imperdiet mi nec viverra. Praesent ac ligula
congue, aliquam diam nec, ullamcorper libero. Nunc mattis rhoncus justo, ac pretium
urna vehicula et.
</p>
</div>
{/if}
</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="movie-carousel-title" slot="title" />
<slot name="movie-carousel">
<CarouselPlaceholderItems />
</slot>
</Carousel>
</div>
<div class="max-w-screen-2xl 2xl:mx-auto w-full">
<Carousel gradientFromColor="from-stone-950">
<slot name="tv-carousel-title" slot="title" />
<slot name="tv-carousel">
<CarouselPlaceholderItems />
</slot>
</Carousel>
</div>
</div>

View File

@@ -4,7 +4,7 @@
import Card from '$lib/components/Card/Card.svelte';
import { fetchCardTmdbProps } from '$lib/components/Card/card';
import CarouselPlaceholderItems from '$lib/components/Carousel/CarouselPlaceholderItems.svelte';
import TitlePageLayout from '$lib/components/TitlePageLayout/TitlePageLayout.svelte';
import PersonPageLayout from '$lib/components/PersonPageLayout/PersonPageLayout.svelte';
import { Archive, ChevronRight, DotFilled, Plus } from 'radix-icons-svelte';
const GenderDescription = ['Not set', 'Female', 'Male', 'Non-binary'];
@@ -38,10 +38,10 @@
</script>
{#await data}
<TitlePageLayout {isModal} {handleCloseModal} />
<PersonPageLayout {isModal} {handleCloseModal} />
{:then { tmdbPerson, tmdbMoviesOn, tmdbSeriesOn }}
{@const person = tmdbPerson}
<TitlePageLayout
<PersonPageLayout
titleInformation={{
tmdbId: Number(person?.id),
type: 'person',
@@ -59,7 +59,7 @@
<p>{person?.homepage}</p>
<DotFilled />
{/if}
<a href={tmdbUrl} target="_blank">Popularity index: {person?.popularity?.toFixed(1)} TMDB</a>
<a href={tmdbUrl} target="_blank">Popularity: {person?.popularity?.toFixed(1)} on TMDB</a>
</svelte:fragment>
<svelte:fragment slot="title-right" />
@@ -104,8 +104,8 @@
{/if}
</svelte:fragment>
<div slot="cast-crew-carousel-title" class="font-medium text-lg">Appeared on movies like</div>
<svelte:fragment slot="cast-crew-carousel">
<div slot="movie-carousel-title" class="font-medium text-lg">Appeared on movies like</div>
<svelte:fragment slot="movie-carousel">
{#await tmdbMoviesOn}
<CarouselPlaceholderItems />
{:then props}
@@ -114,8 +114,8 @@
{/each}
{/await}
</svelte:fragment>
<div slot="recommendations-carousel-title" class="font-medium text-lg">And tv shows like</div>
<svelte:fragment slot="recommendations-carousel">
<div slot="tv-carousel-title" class="font-medium text-lg">And tv shows like</div>
<svelte:fragment slot="tv-carousel">
{#await tmdbSeriesOn}
<CarouselPlaceholderItems />
{:then props}
@@ -124,5 +124,5 @@
{/each}
{/await}
</svelte:fragment>
</TitlePageLayout>
</PersonPageLayout>
{/await}