diff --git a/src/lib/components/SeriesPage/EpisodeCarousel.svelte b/src/lib/components/SeriesPage/EpisodeCarousel.svelte index 61af60b..4dfd481 100644 --- a/src/lib/components/SeriesPage/EpisodeCarousel.svelte +++ b/src/lib/components/SeriesPage/EpisodeCarousel.svelte @@ -66,10 +66,10 @@ let:hasFocus class="mx-2 text-nowrap" on:click={() => handleSelectSeason(season)} - handleFocus={(s, didNavigate) => { + handleFocus={(s, options) => { const element = s.getHtmlElement(); if (element) scrollElementIntoView(element, { horizontal: 64 }); - if (didNavigate) handleSelectSeason(season); + if (options.didNavigate) handleSelectSeason(season); }} bind:this={containers[`season-${season.season_number}`]} > @@ -90,10 +90,10 @@ { + handleFocus={(s, options) => { scrollIntoView({ left: 64 + 16 })(s); selectedTmdbEpisode = episode; - if (didNavigate) handleFocusEpisode(episode); + if (options.didNavigate) handleFocusEpisode(episode); }} > diff --git a/src/lib/selectable.ts b/src/lib/selectable.ts index 4f041fb..54c3cdf 100644 --- a/src/lib/selectable.ts +++ b/src/lib/selectable.ts @@ -12,7 +12,25 @@ export type NavigationActions = { enter?: (selectable: Selectable) => boolean; }; -export type FocusHandler = (selectable: Selectable, didNavigate: boolean) => void; +type FocusHandlerOptions = { + didNavigate: boolean; + propagate: boolean; + stopPropagation: () => void; +}; + +const createFocusHandlerOptions = (): FocusHandlerOptions => { + const options: Partial = { + didNavigate: true, + propagate: true + }; + + options.stopPropagation = () => { + options.propagate = false; + }; + + return options as FocusHandlerOptions; +}; +export type FocusHandler = (selectable: Selectable, options: FocusHandlerOptions) => void; export class Selectable { id: symbol; @@ -76,16 +94,20 @@ export class Selectable { return this; } - focus(didNavigate: boolean = true) { - function updateFocusIndex(parent: Selectable, child?: Selectable) { - if (!get(parent.hasFocusWithin)) parent.onFocus?.(parent, didNavigate); + focus(didNavigate = true) { + function propagateFocusUpdates( + options: FocusHandlerOptions, + parent: Selectable, + child?: Selectable + ) { + if (!get(parent.hasFocusWithin) && options.propagate) parent.onFocus?.(parent, options); if (child) { const index = parent.children.indexOf(child); parent.focusIndex.update((prev) => (index === -1 ? prev : index)); } if (parent.parent) { - updateFocusIndex(parent.parent, parent); + propagateFocusUpdates(options, parent.parent, parent); } } @@ -115,7 +137,9 @@ export class Selectable { } } } else if (this.htmlElement) { - updateFocusIndex(this); + const options = createFocusHandlerOptions(); + options.didNavigate = didNavigate; + propagateFocusUpdates(options, this); if (didNavigate) { this.htmlElement.focus({ preventScroll: true });