fix: Issues with grid item sizing

This commit is contained in:
Aleksi Lassila
2024-05-31 19:53:39 +03:00
parent 8acfd0f4d9
commit f6b9ac41ba
6 changed files with 78 additions and 76 deletions

View File

@@ -4,46 +4,49 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { getCardDimensions } from '../utils'; import { getCardDimensions } from '../utils';
export let direction: 'horizontal' | 'vertical' = 'vertical'; // export let direction: 'horizontal' | 'vertical' = 'vertical';
export let type: 'portrait' | 'landscape' = 'portrait';
let cols = getCardDimensions(window.innerWidth).columns; let cols = getCardDimensions(window.innerWidth, type).columns;
$: console.log('cols', cols); $: console.log('cols', cols);
// let cols: number = 1; // let cols: number = 1;
const calculateRows = () => { // const calculateRows = () => {
const width = window.innerWidth; // const width = window.innerWidth;
if (direction === 'vertical') { // if (direction === 'vertical') {
if (width >= 1536) { // if (width >= 1536) {
cols = 6; // cols = 6;
} else if (width >= 1280) { // } else if (width >= 1280) {
cols = 5; // cols = 5;
} else if (width >= 768) { // } else if (width >= 768) {
cols = 4; // cols = 4;
} else { // } else {
cols = 3; // cols = 3;
} // }
} else { // } else {
// if (width >= 1920) { // // if (width >= 1920) {
// cols = 4; // // cols = 4;
// } else // // } else
if (width >= 1536) { // if (width >= 1536) {
cols = 3; // cols = 3;
} else if (width >= 1280) { // } else if (width >= 1280) {
cols = 2; // cols = 2;
} else if (width >= 768) { // } else if (width >= 768) {
cols = 1; // cols = 1;
} else { // } else {
cols = 1; // cols = 1;
} // }
} // }
}; // };
// onMount(() => { // onMount(() => {
// calculateRows(); // calculateRows();
// }); // });
</script> </script>
<svelte:window on:resize={(e) => (cols = getCardDimensions(e.currentTarget.innerWidth).columns)} /> <svelte:window
on:resize={(e) => (cols = getCardDimensions(e.currentTarget.innerWidth, type).columns)}
/>
<Container <Container
direction="grid" direction="grid"
@@ -51,8 +54,8 @@
class={classNames( class={classNames(
'grid gap-x-8 gap-y-8', 'grid gap-x-8 gap-y-8',
{ {
'grid-cols-1 md:grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 3xl:grid-cols-4': // 'grid-cols-1 md:grid-cols-1 xl:grid-cols-2 2xl:grid-cols-3 3xl:grid-cols-4':
direction === 'horizontal' // direction === 'horizontal'
// 'grid-cols-4 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6': direction === 'vertical' // 'grid-cols-4 md:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6': direction === 'vertical'
}, },

View File

@@ -4,6 +4,7 @@
import { ArrowDown, Check, TriangleRight } from 'radix-icons-svelte'; import { ArrowDown, Check, TriangleRight } from 'radix-icons-svelte';
import type { Readable } from 'svelte/store'; import type { Readable } from 'svelte/store';
import AnimateScale from '../AnimateScale.svelte'; import AnimateScale from '../AnimateScale.svelte';
import { getCardDimensions } from '../../utils';
export let episodeNumber: number; export let episodeNumber: number;
export let episodeName: string; export let episodeName: string;
@@ -16,36 +17,20 @@
let hasFocus: Readable<boolean>; let hasFocus: Readable<boolean>;
let dimensions = getDimensions(window.innerWidth); let dimensions = getCardDimensions(window.innerWidth, 'landscape');
function getDimensions(viewportWidth: number) {
const minWidth = 240;
const margin = 128;
const gap = 32;
const cols = Math.floor((gap - 2 * margin + viewportWidth) / (minWidth + gap));
const scale = -(gap * (cols - 1) + 2 * margin - viewportWidth) / (cols * minWidth);
const newWidth = minWidth * scale;
const newHeight = (3 / 2) * newWidth;
return {
width: newWidth,
height: newHeight
};
}
</script> </script>
<svelte:window on:resize={(e) => (dimensions = getDimensions(e.currentTarget.innerWidth))} /> <svelte:window
on:resize={(e) => (dimensions = getCardDimensions(e.currentTarget.innerWidth, 'landscape'))}
/>
<AnimateScale hasFocus={$hasFocus}> <AnimateScale hasFocus={$hasFocus}>
<Container <Container
class={classNames( class={classNames(
'w-full h-72',
'flex flex-col shrink-0', 'flex flex-col shrink-0',
'overflow-hidden rounded-2xl cursor-pointer group relative px-4 py-3 selectable transition-opacity' 'overflow-hidden rounded-2xl cursor-pointer group relative px-4 py-3 selectable transition-opacity'
)} )}
style={`width: ${dimensions.width}px; height: ${dimensions.height}px`}
on:clickOrSelect on:clickOrSelect
on:enter on:enter
on:mount on:mount

View File

@@ -111,7 +111,7 @@
</Container> </Container>
{/each} {/each}
</UICarousel> </UICarousel>
<CardGrid direction="horizontal" on:mount> <CardGrid type="landscape" on:mount>
{#if $tmdbSeasons?.[seasonIndex]?.episodes?.length} {#if $tmdbSeasons?.[seasonIndex]?.episodes?.length}
{#each $tmdbSeasons?.[seasonIndex]?.episodes || [] as episode} {#each $tmdbSeasons?.[seasonIndex]?.episodes || [] as episode}
{@const jellyfinEpisode = awaitedJellyfinEpisodes?.find( {@const jellyfinEpisode = awaitedJellyfinEpisodes?.find(

View File

@@ -4,19 +4,25 @@
import AnimateScale from '../AnimateScale.svelte'; import AnimateScale from '../AnimateScale.svelte';
import classNames from 'classnames'; import classNames from 'classnames';
import { Plus, PlusCircled } from 'radix-icons-svelte'; import { Plus, PlusCircled } from 'radix-icons-svelte';
import { getCardDimensions } from '../../utils';
export let backdropUrl: string; export let backdropUrl: string;
let hasFocus: Readable<boolean>; let hasFocus: Readable<boolean>;
let dimensions = getCardDimensions(window.innerWidth, 'landscape');
</script> </script>
<svelte:window
on:resize={(e) => (dimensions = getCardDimensions(e.currentTarget.innerWidth, 'landscape'))}
/>
<AnimateScale hasFocus={$hasFocus}> <AnimateScale hasFocus={$hasFocus}>
<Container <Container
class={classNames( class={classNames(
'w-full h-64',
'flex flex-col shrink-0', 'flex flex-col shrink-0',
'overflow-hidden rounded-2xl cursor-pointer group relative selectable transition-opacity' 'overflow-hidden rounded-2xl cursor-pointer group relative selectable transition-opacity'
)} )}
style={`width: ${dimensions.width}px; height: ${dimensions.height}px`}
on:clickOrSelect on:clickOrSelect
on:enter on:enter
bind:hasFocus bind:hasFocus

View File

@@ -11,23 +11,28 @@
import { radarrApi } from '../apis/radarr/radarr-api'; import { radarrApi } from '../apis/radarr/radarr-api';
import Card from '../components/Card/Card.svelte'; import Card from '../components/Card/Card.svelte';
import type { ComponentProps } from 'svelte'; import type { ComponentProps } from 'svelte';
import TmdbCard from '../components/Card/TmdbCard.svelte';
import { tmdbApi, type TmdbMovie2, type TmdbSeries2 } from '../apis/tmdb/tmdb-api';
const libraryItemsP = jellyfinApi.getLibraryItems(); const libraryItemsP = jellyfinApi.getLibraryItems();
let sonarrDownloads: Promise<ComponentProps<Card>[]> = sonarrApi.getDownloads().then((items) => const sonarrDownloads: Promise<TmdbSeries2[]> = sonarrApi
items .getDownloads()
.filter( .then((items) =>
(value, index, self) => index === self.findIndex((t) => t.seriesId === value.seriesId) Promise.all(
items
.filter(
(value, index, self) => index === self.findIndex((t) => t.seriesId === value.seriesId)
)
.map((i) => tmdbApi.getTmdbSeriesFromTvdbId(String(i.series.tvdbId)))
).then((i) => i.filter((i) => !!i) as TmdbSeries2[])
);
let radarrDownloads: Promise<TmdbMovie2[]> = radarrApi
.getDownloads()
.then((items) =>
Promise.all(items.map((i) => tmdbApi.getTmdbMovie(i.movie.tmdbId || -1))).then(
(i) => i.filter((i) => !!i) as TmdbMovie2[]
) )
.map((i) => ({ );
backdropUrl: i.series.images?.find((i) => i.coverType === 'poster')?.remoteUrl || '',
group: true
}))
);
let radarrDownloads: Promise<ComponentProps<Card>[]> = radarrApi.getDownloads().then((items) =>
items.map((i) => ({
backdropUrl: i.movie.images?.find((i) => i.coverType === 'poster')?.remoteUrl || ''
}))
);
settings.update((prev) => ({ settings.update((prev) => ({
...prev, ...prev,
@@ -46,12 +51,12 @@
{#if sonarrDownloads?.length || radarrDownloads?.length} {#if sonarrDownloads?.length || radarrDownloads?.length}
<Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}> <Carousel scrollClass="px-32" on:enter={scrollIntoView({ vertical: 128 })}>
<span slot="header">Downloading</span> <span slot="header">Downloading</span>
{#each sonarrDownloads as props} {#each sonarrDownloads as item}
<Card on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {...props} /> <TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} group />
{/each} {/each}
{#each radarrDownloads as props} {#each radarrDownloads as item}
<Card on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {...props} /> <TmdbCard on:enter={scrollIntoView({ horizontal: 128 })} size="lg" {item} />
{/each} {/each}
</Carousel> </Carousel>
{/if} {/if}

View File

@@ -134,8 +134,11 @@ export function subscribeUntil<T>(store: Readable<T>, fn: (value: T) => boolean)
}); });
} }
export function getCardDimensions(viewportWidth: number) { export function getCardDimensions(
const minWidth = 240; viewportWidth: number,
type: 'portrait' | 'landscape' = 'portrait'
) {
const minWidth = type === 'portrait' ? 240 : 400;
const margin = 128; const margin = 128;
const gap = 32; const gap = 32;
@@ -144,7 +147,7 @@ export function getCardDimensions(viewportWidth: number) {
const scale = -(gap * (cols - 1) + 2 * margin - viewportWidth) / (cols * minWidth); const scale = -(gap * (cols - 1) + 2 * margin - viewportWidth) / (cols * minWidth);
const newWidth = minWidth * scale; const newWidth = minWidth * scale;
const newHeight = (3 / 2) * newWidth; const newHeight = (type === 'portrait' ? 3 / 2 : 9 / 16) * newWidth;
return { return {
width: newWidth, width: newWidth,