mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-25 18:25:12 +02:00
feat: Container focusOnClick and improvements to onFocus options
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
|
||||
<Container
|
||||
class={classNames(
|
||||
'rounded-xl overflow-hidden',
|
||||
'rounded-xl overflow-hidden cursor-pointer',
|
||||
'h-56 w-96 px-4 py-3',
|
||||
'flex flex-col shrink-0 relative selectable'
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { JellyfinItem } from '../../apis/jellyfin/jellyfin-api';
|
||||
import EpisodeCard from './EpisodeCard.svelte';
|
||||
import { useDependantRequest } from '../../stores/data.store';
|
||||
import { get, type Readable } from 'svelte/store';
|
||||
import { derived, get, type Readable } from 'svelte/store';
|
||||
import {
|
||||
tmdbApi,
|
||||
type TmdbEpisode,
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
export let id: number;
|
||||
export let tmdbSeries: Readable<TmdbSeriesFull2 | undefined>;
|
||||
export let nextEpisode: JellyfinItem | undefined = undefined;
|
||||
export let nextJellyfinEpisode: Readable<JellyfinItem | undefined>;
|
||||
export let selectedTmdbEpisode: TmdbEpisode | undefined = undefined;
|
||||
|
||||
const { data: tmdbSeasons, isLoading: isTmdbSeasonsLoading } = useDependantRequest(
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
const containers: Record<string, Container> = {};
|
||||
|
||||
function handleSelectSeason(season: TmdbSeason) {
|
||||
function focusFirstEpisodeOf(season: TmdbSeason) {
|
||||
let isAlreadySelected = false;
|
||||
|
||||
for (const episode of season.episodes || []) {
|
||||
@@ -42,18 +42,40 @@
|
||||
const episode = season.episodes?.[0];
|
||||
if (episode && !isAlreadySelected) {
|
||||
const selectable = containers[`episode-${episode.id}`]?.container;
|
||||
if (selectable) selectable.focus(false);
|
||||
if (selectable) selectable.focus({ setFocusedElement: false });
|
||||
}
|
||||
}
|
||||
|
||||
function handleFocusEpisode(episode: TmdbEpisode) {
|
||||
function focusSeasonOf(episode: TmdbEpisode) {
|
||||
const seasonSelectable = containers[`season-${episode.season_number}`]?.container;
|
||||
if (seasonSelectable) seasonSelectable.focus(false);
|
||||
if (seasonSelectable) seasonSelectable.focus({ setFocusedElement: false });
|
||||
}
|
||||
|
||||
tmdbSeasons.subscribe((seasons) => {
|
||||
selectedTmdbEpisode = seasons?.[0]?.episodes?.[0];
|
||||
});
|
||||
|
||||
// Handle focus next episode
|
||||
derived([tmdbSeasons, nextJellyfinEpisode], (r) => r).subscribe(
|
||||
([$seasons, $jellyfinEpisode]) => {
|
||||
const tmdbEpisode = $seasons
|
||||
?.flatMap((s) => s.episodes)
|
||||
.find(
|
||||
(e) =>
|
||||
e?.episode_number === $jellyfinEpisode?.IndexNumber &&
|
||||
e?.season_number === $jellyfinEpisode?.ParentIndexNumber
|
||||
);
|
||||
if (tmdbEpisode) {
|
||||
const container = containers[`episode-${tmdbEpisode.id}`]?.container;
|
||||
container?.focus({
|
||||
setFocusedElement: false,
|
||||
propagate: false
|
||||
});
|
||||
const element = container?.getHtmlElement();
|
||||
if (element) scrollElementIntoView(element, { horizontal: 64 + 16 });
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if $isTmdbSeasonsLoading}
|
||||
@@ -65,19 +87,21 @@
|
||||
<Container
|
||||
let:hasFocus
|
||||
class="mx-2 text-nowrap"
|
||||
on:click={() => handleSelectSeason(season)}
|
||||
on:click={() => focusFirstEpisodeOf(season)}
|
||||
handleFocus={(s, options) => {
|
||||
const element = s.getHtmlElement();
|
||||
if (element) scrollElementIntoView(element, { horizontal: 64 });
|
||||
if (options.didNavigate) handleSelectSeason(season);
|
||||
scrollIntoView({ horizontal: 64 })(s);
|
||||
if (options.setFocusedElement) focusFirstEpisodeOf(season);
|
||||
}}
|
||||
bind:this={containers[`season-${season.season_number}`]}
|
||||
>
|
||||
<div
|
||||
class={classNames({
|
||||
'font-semibold tracking-wide': hasFocus,
|
||||
'text-zinc-300 font-medium': !hasFocus
|
||||
})}
|
||||
class={classNames(
|
||||
'cursor-pointer hover:font-semibold hover:tracking-wide hover:text-white',
|
||||
{
|
||||
'font-semibold tracking-wide': hasFocus,
|
||||
'text-zinc-300 font-medium': !hasFocus
|
||||
}
|
||||
)}
|
||||
>
|
||||
Season {season.season_number}
|
||||
</div>
|
||||
@@ -93,8 +117,9 @@
|
||||
handleFocus={(s, options) => {
|
||||
scrollIntoView({ left: 64 + 16 })(s);
|
||||
selectedTmdbEpisode = episode;
|
||||
if (options.didNavigate) handleFocusEpisode(episode);
|
||||
if (options.setFocusedElement) focusSeasonOf(episode);
|
||||
}}
|
||||
focusOnClick
|
||||
>
|
||||
<EpisodeCard {episode} />
|
||||
</Container>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
let episodesSelectable: Selectable;
|
||||
|
||||
let scrollTop: number;
|
||||
$: showEpisodeInfo = scrollTop > 200;
|
||||
$: showEpisodeInfo = scrollTop > 140;
|
||||
</script>
|
||||
|
||||
<DetachedPage>
|
||||
@@ -58,11 +58,7 @@
|
||||
class="h-screen flex flex-col py-12 px-20 relative"
|
||||
handleFocus={scrollIntoView({ top: 0 })}
|
||||
handleNavigateOut={{
|
||||
down: () => {
|
||||
console.log('Here', episodesSelectable);
|
||||
episodesSelectable?.focusChildren(1);
|
||||
return true;
|
||||
}
|
||||
down: () => episodesSelectable?.focusChildren(1)
|
||||
}}
|
||||
>
|
||||
<HeroCarousel
|
||||
@@ -194,6 +190,11 @@
|
||||
</HeroCarousel>
|
||||
</Container>
|
||||
<Container handleFocus={scrollIntoView({ vertical: 64 })} bind:container={episodesSelectable}>
|
||||
<EpisodeCarousel id={Number(id)} tmdbSeries={tmdbSeriesData} bind:selectedTmdbEpisode />
|
||||
<EpisodeCarousel
|
||||
id={Number(id)}
|
||||
tmdbSeries={tmdbSeriesData}
|
||||
{nextJellyfinEpisode}
|
||||
bind:selectedTmdbEpisode
|
||||
/>
|
||||
</Container>
|
||||
</DetachedPage>
|
||||
|
||||
Reference in New Issue
Block a user