diff --git a/src/Container.svelte b/src/Container.svelte index 248cfe7..5f49a96 100644 --- a/src/Container.svelte +++ b/src/Container.svelte @@ -45,6 +45,18 @@ dispatch('enter', { selectable, options, stopPropagation }); }) + .setOnNavigate((selectable, options) => { + function preventNavigation() { + options.preventNavigation = true; + } + + dispatch('navigate', { + selectable, + options, + preventNavigation, + direction: options.direction + }); + }) .setOnSelect(() => { dispatch('select'); dispatch('clickOrSelect'); diff --git a/src/lib/components/DetachedPage/DetachedPage.svelte b/src/lib/components/DetachedPage/DetachedPage.svelte index 2692263..a8ff4c4 100644 --- a/src/lib/components/DetachedPage/DetachedPage.svelte +++ b/src/lib/components/DetachedPage/DetachedPage.svelte @@ -3,10 +3,10 @@ { + on:navigate={({ detail }) => { + if (detail.direction === 'left' && detail.options.willLeaveContainer) { history.back(); - return false; + detail.preventNavigation(); } }} focusOnMount diff --git a/src/lib/components/HeroCarousel/HeroCarousel.svelte b/src/lib/components/HeroCarousel/HeroCarousel.svelte index 0d2108c..3c2c6fe 100644 --- a/src/lib/components/HeroCarousel/HeroCarousel.svelte +++ b/src/lib/components/HeroCarousel/HeroCarousel.svelte @@ -42,10 +42,15 @@ Selectable.giveFocus('left', true) || true + on:navigate={({ detail }) => { + if (detail.options.direction === 'right') { + if (onNext()) detail.preventNavigation(); + } else if (detail.options.direction === 'left') { + if (onPrevious()) detail.preventNavigation(); + } else if (detail.options.direction === 'up') { + Selectable.giveFocus('left', true); + detail.preventNavigation(); + } }} />
diff --git a/src/lib/components/Modal/FullScreenModal.svelte b/src/lib/components/Modal/FullScreenModal.svelte index e7f222f..2c4e6c1 100644 --- a/src/lib/components/Modal/FullScreenModal.svelte +++ b/src/lib/components/Modal/FullScreenModal.svelte @@ -8,10 +8,10 @@ { + on:navigate={({ detail }) => { + if (detail.direction === 'left' && detail.options.willLeaveContainer) { modalStack.close(modalId); - return true; + detail.preventNavigation(); } }} focusOnMount diff --git a/src/lib/components/SeriesPage/SeriesPage.svelte b/src/lib/components/SeriesPage/SeriesPage.svelte index 324d3cb..43a2096 100644 --- a/src/lib/components/SeriesPage/SeriesPage.svelte +++ b/src/lib/components/SeriesPage/SeriesPage.svelte @@ -54,8 +54,10 @@ episodesSelectable?.focusChild(1) + on:navigate={({ detail }) => { + if (detail.direction === 'down') { + if (episodesSelectable?.focusChild(1)) detail.preventNavigation(); + } }} > void; }; -type NavigateEventOptions = {}; - -export type NavigateEvent = { - selectable: Selectable; - options: NavigateEventOptions; - preventDefault: () => void; -}; - const createFocusHandlerOptions = (): FocusEventOptions => ({ setFocusedElement: true, propagate: true }); +type NavigateEventOptions = { + willLeaveContainer: boolean; + preventNavigation: boolean; + direction: Direction; +}; + +export type NavigateEvent = { + selectable: Selectable; + direction: Direction; + options: NavigateEventOptions; + preventNavigation: () => void; +}; + +const createNavigateHandlerOptions = (direction: Direction): NavigateEventOptions => ({ + willLeaveContainer: false, + preventNavigation: false, + direction +}); + export type FocusHandler = (selectable: Selectable, options: FocusEventOptions) => void; +export type NavigationHandler = (selectable: Selectable, options: NavigateEventOptions) => void; export class Selectable { id: symbol; @@ -57,6 +69,7 @@ export class Selectable { private canFocusEmpty: boolean = true; private trapFocus: boolean = false; private navigationActions: NavigationActions = {}; + private onNavigate: NavigationHandler = () => {}; private isActive: boolean = true; private onFocus: FocusHandler = () => {}; private onSelect?: () => void; @@ -196,6 +209,7 @@ export class Selectable { private giveFocus(direction: Direction, bypassActions: boolean = false): boolean { const focusIndex = get(this.focusIndex); + const navigationEventOptions = createNavigateHandlerOptions(direction); const indexAddition = { up: this.direction === 'vertical' ? -1 : -this.gridColumns, @@ -220,6 +234,8 @@ export class Selectable { while (index >= 0 && index < this.children.length) { const children = this.children[index]; if (children && children.isFocusable()) { + this.onNavigate(this, navigationEventOptions); + if (navigationEventOptions.preventNavigation) return true; children.focus(); return true; } @@ -228,11 +244,12 @@ export class Selectable { } // About to leave this container (=coulnd't cycle siblings) - - const action = this.navigationActions[direction]; - if (action && !bypassActions && action(this)) { - return true; - } else if (this.neighbors[direction]?.isFocusable()) { + navigationEventOptions.willLeaveContainer = true; + if (!bypassActions) { + this.onNavigate(this, navigationEventOptions); + if (navigationEventOptions.preventNavigation) return true; + } + if (this.neighbors[direction]?.isFocusable()) { this.neighbors[direction]?.focus(); return true; } else if (!this.trapFocus) { @@ -572,6 +589,11 @@ export class Selectable { this.onSelect = onSelect; return this; } + + setOnNavigate(onNavigate: NavigationHandler) { + this.onNavigate = onNavigate; + return this; + } } export function handleKeyboardNavigation(event: KeyboardEvent) { @@ -603,6 +625,8 @@ export function handleKeyboardNavigation(event: KeyboardEvent) { else { currentlyFocusedObject.select(); } + } else if (event.key === 'Back') { + } else if (event.key === 'MediaPlayPause') { } }