feat: library page sections, sorting, view options

fix: tmdb library items not being cached
This commit is contained in:
Aleksi Lassila
2025-02-15 05:03:20 +02:00
parent 8f83a34b35
commit ef4fa1d13a
21 changed files with 575 additions and 178 deletions

View File

@@ -52,6 +52,7 @@
{/if}
<AnimatedSelection hasFocus={$hasFocus}>
<Container
{...$$restProps}
{disabled}
on:clickOrSelect={() => {
if (tmdbId || tvdbId) navigate(`/${type}/${tmdbId || tvdbId}`);

View File

@@ -48,6 +48,7 @@
/>
<Container
{...$$restProps}
direction="grid"
gridCols={cols}
class={classNames(

View File

@@ -0,0 +1,66 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import Container from './Container.svelte';
import classNames from 'classnames';
import { Check } from 'radix-icons-svelte';
type Option = {
label: string;
value?: string;
disabled?: boolean;
};
export let options: Option[] = [];
export let selected: string | undefined = undefined;
export let name: string | undefined = undefined;
export let style: 'primary' | 'secondary' = 'secondary';
const dispatch = createEventDispatcher<{
select: string;
}>();
</script>
<div>
{#if name}
<h2 class="font-semibold text-sm text-secondary-400 mb-2">{name}</h2>
{/if}
<Container
direction="vertical"
class={classNames(
'rounded-xl',
{
'bg-primary-900': style === 'secondary',
'bg-secondary-800': style === 'primary'
},
$$restProps.class
)}
>
{#each options as option, index}
{@const first = index === 0}
{@const last = index === options.length - 1}
{#if !first}
<div class="w-0.5 h-full bg-secondary-700" />
{/if}
<Container
on:clickOrSelect={() => dispatch('select', option.value ?? option.label)}
let:hasFocus
>
<div
class={classNames('h-12 px-6 py-3 border-2 flex items-center justify-between', {
'cursor-pointer hover:border-primary-500': !option.disabled,
'cursor-not-allowed pointer-events-none opacity-40': option.disabled,
'border-primary-500': hasFocus,
'border-transparent': !hasFocus,
'rounded-t-xl': first,
'rounded-b-xl': last
})}
>
<span class="font-medium">{option.label}</span>
{#if selected === option.value}
<Check size={24} />
{/if}
</div>
</Container>
{/each}
</Container>
</div>

View File

@@ -1,6 +1,6 @@
import { type ComponentType } from 'svelte';
import { derived, get, writable } from 'svelte/store';
import LibraryPage from '../../pages/LibraryPage.svelte';
import LibraryPage from '../../pages/LibraryPage/LibraryPage.svelte';
import ManagePage from '../../pages/ManagePage/ManagePage.svelte';
import MoviesHomePage from '../../pages/MoviesHomePage.svelte';
import PageNotFound from '../../pages/PageNotFound.svelte';

View File

@@ -7,6 +7,7 @@
}>();
export let checked: boolean;
export let label: string | undefined = undefined;
let input: HTMLInputElement;
const handleChange = (e: Event) => {
@@ -17,22 +18,32 @@
};
</script>
<Container
class="relative inline-flex items-center cursor-pointer w-min h-min"
on:enter={(e) => {
e.detail.options.setFocusedElement = input;
}}
on:clickOrSelect={() => input?.click()}
>
<input
type="checkbox"
bind:checked
class="sr-only peer"
bind:this={input}
on:input={handleChange}
/>
<div
class="w-[3.25rem] h-7 rounded-full bg-secondary-700 peer-checked:bg-primary-500 peer-selectable
<div class="flex items-center justify-between">
<slot>
{#if label}
<label class="mr-2">
{label}
</label>
{/if}
</slot>
<Container
class="relative inline-flex items-center cursor-pointer w-min h-min"
on:enter={(e) => {
e.detail.options.setFocusedElement = input;
}}
on:clickOrSelect={() => input?.click()}
>
<input
type="checkbox"
bind:checked
class="sr-only peer"
bind:this={input}
on:input={handleChange}
/>
<div
class="w-[3.25rem] h-7 rounded-full bg-secondary-700 peer-checked:bg-primary-500 peer-selectable
after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 after:w-6 after:transition-all peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px]"
/>
</Container>
/>
</Container>
</div>