Add navigation action system
This commit is contained in:
@@ -1,56 +1,58 @@
|
||||
<script lang="ts">
|
||||
import Carousel from "./lib/components/Carousel/Carousel.svelte";
|
||||
import I18n from "./lib/components/Lang/I18n.svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import CarouselPlaceholderItems from "./lib/components/Carousel/CarouselPlaceholderItems.svelte";
|
||||
import { Link, navigate, Route, Router } from "svelte-navigator";
|
||||
import { fade } from "svelte/transition";
|
||||
import { networks } from "./lib/discover";
|
||||
import NetworkCard from "./lib/components/NetworkCard.svelte";
|
||||
import I18n from './lib/components/Lang/I18n.svelte';
|
||||
import { Link, navigate, Route, Router } from 'svelte-navigator';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { handleKeyboardNavigation, navigationContainers } from './lib/actions/focusAction';
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === "ArrowRight") {
|
||||
console.log("right");
|
||||
navigate("/about");
|
||||
}
|
||||
if (event.key === "ArrowLeft") {
|
||||
console.log("left");
|
||||
navigate("/");
|
||||
}
|
||||
}
|
||||
let focusableElements: HTMLElement[] = [];
|
||||
|
||||
function registerArrayFocus(node: HTMLElement) {
|
||||
focusableElements.push(node);
|
||||
console.log('Added node', node);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
focusableElements = focusableElements.filter((el) => el !== node);
|
||||
console.log('Removed node', node);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const navBarContainer = navigationContainers.navBar.getRegisterer();
|
||||
const homeContainer = navigationContainers.home.getRegisterer();
|
||||
</script>
|
||||
|
||||
<I18n />
|
||||
<main class="bg-stone-950 text-white">
|
||||
<Router>
|
||||
<nav>
|
||||
<Link to="/">Home</Link>
|
||||
<Link to="about">About</Link>
|
||||
</nav>
|
||||
<main class="bg-stone-950 text-white flex">
|
||||
<Router>
|
||||
<nav class="border">
|
||||
<Link to="/">
|
||||
<div use:navBarContainer tabindex="0">Home</div>
|
||||
</Link>
|
||||
<Link to="about">
|
||||
<div use:navBarContainer tabindex="0">About</div>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<Carousel>
|
||||
<div slot="title" class="text-lg font-semibold text-zinc-300">
|
||||
{$_("discover.upcomingSeries")}
|
||||
</div>
|
||||
<CarouselPlaceholderItems />
|
||||
</Carousel>
|
||||
|
||||
<Carousel>
|
||||
<div slot="title" class="text-lg font-semibold text-zinc-300">
|
||||
{$_("discover.TVNetworks")}
|
||||
</div>
|
||||
{#each Object.values(networks) as network (network.tmdbNetworkId)}
|
||||
<NetworkCard {network} />
|
||||
{/each}
|
||||
</Carousel>
|
||||
|
||||
<Route path="/">
|
||||
<div transition:fade|global>Home path</div>
|
||||
</Route>
|
||||
<Route path="about">
|
||||
<div transition:fade|global>about path</div>
|
||||
</Route>
|
||||
</Router>
|
||||
<div class="flex-1">
|
||||
<Route path="/">
|
||||
<div class="flex flex-row">
|
||||
<div use:homeContainer tabindex="0" transition:fade|global class="focus:ring">
|
||||
Home path
|
||||
</div>
|
||||
<div use:homeContainer tabindex="0" transition:fade|global class="focus:ring">
|
||||
Another item
|
||||
</div>
|
||||
<div use:homeContainer tabindex="0" transition:fade|global class="focus:ring">
|
||||
Button perhaps?
|
||||
</div>
|
||||
</div>
|
||||
</Route>
|
||||
<Route path="about">
|
||||
<div transition:fade|global>about path</div>
|
||||
</Route>
|
||||
</div>
|
||||
</Router>
|
||||
</main>
|
||||
|
||||
<svelte:window on:keydown={handleKeyDown} />
|
||||
<svelte:window on:keydown={handleKeyboardNavigation} />
|
||||
|
||||
184
src/lib/actions/focusAction.ts
Normal file
184
src/lib/actions/focusAction.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
export class Container {
|
||||
id: symbol;
|
||||
name: string;
|
||||
parent?: Container;
|
||||
htmlElements: HTMLElement[] = [];
|
||||
private upNeighbor?: Container;
|
||||
private downNeighbor?: Container;
|
||||
private leftNeighbor?: Container;
|
||||
private rightNeighbor?: Container;
|
||||
|
||||
direction: 'horizontal' | 'vertical' = 'horizontal';
|
||||
|
||||
focusIndex: number = 0;
|
||||
|
||||
static focusedObject: Container;
|
||||
static objects = new Map<symbol, Container>();
|
||||
|
||||
constructor(name: string = '') {
|
||||
this.id = Symbol();
|
||||
this.name = name;
|
||||
Container.objects.set(this.id, this);
|
||||
|
||||
if (!Container.focusedObject) {
|
||||
Container.focusedObject = this;
|
||||
}
|
||||
}
|
||||
|
||||
getRegisterer() {
|
||||
return (htmlElement: HTMLElement) => {
|
||||
this.addHtmlElement(htmlElement);
|
||||
|
||||
return {
|
||||
destroy: () => {
|
||||
this.removeHtmlElement(htmlElement);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
giveFocus(direction: 'up' | 'down' | 'left' | 'right') {
|
||||
const shouldCycle =
|
||||
this.direction === 'horizontal'
|
||||
? (direction === 'left' && this.focusIndex !== 0) ||
|
||||
(direction === 'right' && this.focusIndex !== this.htmlElements.length - 1)
|
||||
: (direction === 'up' && this.focusIndex !== 0) ||
|
||||
(direction === 'down' && this.focusIndex !== this.htmlElements.length - 1);
|
||||
|
||||
if (shouldCycle) {
|
||||
console.log('Cycling through', this.htmlElements);
|
||||
if (direction === 'up') {
|
||||
this.focusIndex--;
|
||||
} else if (direction === 'down') {
|
||||
this.focusIndex++;
|
||||
} else if (direction === 'left') {
|
||||
this.focusIndex--;
|
||||
} else if (direction === 'right') {
|
||||
this.focusIndex++;
|
||||
}
|
||||
this.focusElement();
|
||||
} else {
|
||||
console.log("Giving focus to neighbor's", direction, 'neighbor');
|
||||
const upNeighbor = this.getUpNeighbor();
|
||||
const downNeighbor = this.getDownNeighbor();
|
||||
const leftNeighbor = this.getLeftNeighbor();
|
||||
const rightNeighbor = this.getRightNeighbor();
|
||||
if (direction === 'up' && upNeighbor) {
|
||||
if (upNeighbor.direction === 'vertical')
|
||||
upNeighbor.focusIndex = upNeighbor.htmlElements.length - 1;
|
||||
|
||||
Container.focusedObject = upNeighbor;
|
||||
upNeighbor.focusElement();
|
||||
} else if (direction === 'down' && downNeighbor) {
|
||||
if (downNeighbor.direction === 'vertical') downNeighbor.focusIndex = 0;
|
||||
|
||||
Container.focusedObject = downNeighbor;
|
||||
downNeighbor.focusElement();
|
||||
} else if (direction === 'left' && leftNeighbor) {
|
||||
if (leftNeighbor.direction === 'horizontal')
|
||||
leftNeighbor.focusIndex = leftNeighbor.htmlElements.length - 1;
|
||||
|
||||
Container.focusedObject = leftNeighbor;
|
||||
leftNeighbor.focusElement();
|
||||
} else if (direction === 'right' && rightNeighbor) {
|
||||
if (rightNeighbor.direction === 'horizontal') rightNeighbor.focusIndex = 0;
|
||||
|
||||
Container.focusedObject = rightNeighbor;
|
||||
rightNeighbor.focusElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getUpNeighbor() {
|
||||
return this.upNeighbor;
|
||||
}
|
||||
|
||||
getDownNeighbor() {
|
||||
return this.downNeighbor;
|
||||
}
|
||||
|
||||
getLeftNeighbor() {
|
||||
return this.leftNeighbor;
|
||||
}
|
||||
|
||||
getRightNeighbor() {
|
||||
return this.rightNeighbor;
|
||||
}
|
||||
|
||||
focusElement() {
|
||||
this.htmlElements[this.focusIndex].focus();
|
||||
}
|
||||
|
||||
setParent(parent: Container) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
addHtmlElement(htmlElement: HTMLElement) {
|
||||
this.htmlElements.push(htmlElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
removeHtmlElement(htmlElement: HTMLElement) {
|
||||
this.htmlElements = this.htmlElements.filter((element) => element !== htmlElement);
|
||||
return this;
|
||||
}
|
||||
|
||||
setUpNeighbor(upNeighbor: Container) {
|
||||
this.upNeighbor = upNeighbor;
|
||||
upNeighbor.downNeighbor = this;
|
||||
return this;
|
||||
}
|
||||
|
||||
setDownNeighbor(downNeighbor: Container) {
|
||||
this.downNeighbor = downNeighbor;
|
||||
downNeighbor.upNeighbor = this;
|
||||
return this;
|
||||
}
|
||||
|
||||
setLeftNeighbor(leftNeighbor: Container) {
|
||||
this.leftNeighbor = leftNeighbor;
|
||||
leftNeighbor.rightNeighbor = this;
|
||||
return this;
|
||||
}
|
||||
|
||||
setRightNeighbor(rightNeighbor: Container) {
|
||||
this.rightNeighbor = rightNeighbor;
|
||||
rightNeighbor.leftNeighbor = this;
|
||||
return this;
|
||||
}
|
||||
|
||||
setDirection(direction: 'horizontal' | 'vertical') {
|
||||
this.direction = direction;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export function handleKeyboardNavigation(event: KeyboardEvent) {
|
||||
const currentlyFocusedObject = Container.focusedObject;
|
||||
|
||||
if (!currentlyFocusedObject) {
|
||||
console.error('No focused object!!!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowUp') {
|
||||
currentlyFocusedObject.giveFocus('up');
|
||||
} else if (event.key === 'ArrowDown') {
|
||||
currentlyFocusedObject.giveFocus('down');
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
currentlyFocusedObject.giveFocus('left');
|
||||
} else if (event.key === 'ArrowRight') {
|
||||
currentlyFocusedObject.giveFocus('right');
|
||||
}
|
||||
}
|
||||
|
||||
const navBar = new Container().setDirection('vertical');
|
||||
const home = new Container();
|
||||
|
||||
home.setLeftNeighbor(navBar);
|
||||
|
||||
export const navigationContainers = {
|
||||
home,
|
||||
navBar
|
||||
};
|
||||
Reference in New Issue
Block a user