diff --git a/src/App.svelte b/src/App.svelte
index 47e379b..64024c1 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,27 +1,15 @@
@@ -29,16 +17,17 @@
-
+
-
+
about path
diff --git a/src/lib/actions/focusAction.ts b/src/lib/actions/focusAction.ts
index ad9729c..e23548e 100644
--- a/src/lib/actions/focusAction.ts
+++ b/src/lib/actions/focusAction.ts
@@ -1,19 +1,25 @@
export type Registerer = (htmlElement: HTMLElement) => { destroy: () => void };
+export type Direction = 'up' | 'down' | 'left' | 'right';
+export type FlowDirection = 'vertical' | 'horizontal';
+
export class Container {
id: symbol;
name: string;
- parent?: Container;
- children: Container[] = [];
- htmlElement?: HTMLElement;
- private upNeighbor?: Container;
- private downNeighbor?: Container;
- private leftNeighbor?: Container;
- private rightNeighbor?: Container;
+ private parent?: Container;
+ private children: Container[] = [];
+ private htmlElement?: HTMLElement;
+ private neighbors: Record = {
+ up: undefined,
+ down: undefined,
+ left: undefined,
+ right: undefined
+ };
+ private focusByDefault: boolean = false;
- direction: 'horizontal' | 'vertical' = 'horizontal';
+ private direction: FlowDirection = 'vertical';
- focusIndex: number = 0;
+ private focusIndex: number = 0;
static focusedObject: Container;
static objects = new Map();
@@ -22,191 +28,150 @@ export class Container {
this.id = Symbol();
this.name = name;
Container.objects.set(this.id, this);
+ }
- if (!Container.focusedObject) {
+ setDirection(direction: FlowDirection) {
+ this.direction = direction;
+ return this;
+ }
+
+ setFocusByDefault(focusByDefault: boolean) {
+ this.focusByDefault = focusByDefault;
+ return this;
+ }
+
+ createChild(name: string = '') {
+ const child = new Container(name);
+ this.addChild(child);
+ return child;
+ }
+
+ focus() {
+ if (this.children.length > 0) {
+ this.children[this.focusIndex]?.focus();
+ } else if (this.htmlElement) {
+ this.htmlElement.focus({ preventScroll: true });
+ this.htmlElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
Container.focusedObject = this;
+ this.updateFocusIndex();
+ }
+ }
+
+ updateFocusIndex(container?: Container) {
+ if (container) {
+ const index = this.children.indexOf(container);
+ this.focusIndex = index === -1 ? this.focusIndex : index;
+ }
+ if (this.parent) {
+ this.parent.updateFocusIndex(this);
+ }
+ }
+
+ isFocusable() {
+ if (this.htmlElement) {
+ return true;
+ } else {
+ for (const child of this.children) {
+ if (child.isFocusable()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ getFocusableNeighbor(direction: Direction): Container | undefined {
+ const canLoop =
+ (this.direction === 'vertical' &&
+ ((direction === 'up' && this.focusIndex !== 0) ||
+ (direction === 'down' && this.focusIndex !== this.children.length - 1))) ||
+ (this.direction === 'horizontal' &&
+ ((direction === 'left' && this.focusIndex !== 0) ||
+ (direction === 'right' && this.focusIndex !== this.children.length - 1)));
+ if (this.children.length > 0 && canLoop) {
+ if (direction === 'up' || direction === 'left') {
+ let index = this.focusIndex - 1;
+ while (index >= 0) {
+ if (this.children[index].isFocusable()) {
+ return this.children[index];
+ }
+ index--;
+ }
+ } else if (direction === 'down' || direction === 'right') {
+ let index = this.focusIndex + 1;
+ while (index < this.children.length) {
+ if (this.children[index].isFocusable()) {
+ return this.children[index];
+ }
+ index++;
+ }
+ }
+ } else if (this.neighbors[direction]?.isFocusable()) {
+ return this.neighbors[direction];
+ } else {
+ return this.parent?.getFocusableNeighbor(direction);
+ }
+ }
+
+ giveFocus(direction: Direction) {
+ const neighbor = this.getFocusableNeighbor(direction);
+ console.log('Giving focus to', direction, 'neighbor: ', neighbor?.name, neighbor);
+ if (neighbor) {
+ neighbor.focus();
+ return true;
+ } else {
+ return false;
}
}
getRegisterer(): Registerer {
return (htmlElement: HTMLElement) => {
- this.addChildElement(htmlElement);
+ if (this.htmlElement) console.warn('Registering to a container that has an element.');
+
+ this.createChild().addHtmlElement(htmlElement);
+
+ if (!Container.focusedObject && this.shouldFocusByDefault()) {
+ this.focus();
+ }
return {
destroy: () => {
- this.removeChildElement(htmlElement);
+ this.removeHtmlElement();
}
};
};
}
- giveFocus(direction: 'up' | 'down' | 'left' | 'right') {
- console.log('This: ');
- this.log();
-
- console.log('Giving focus to', 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.children.length - 1;
- }
-
- upNeighbor.focusElement();
- } else if (direction === 'down' && downNeighbor) {
- if (downNeighbor.direction === 'vertical') {
- downNeighbor.focusIndex = 0;
- }
-
- downNeighbor.focusElement();
- } else if (direction === 'left' && leftNeighbor) {
- if (leftNeighbor.direction === 'horizontal') {
- leftNeighbor.focusIndex = leftNeighbor.children.length - 1;
- }
-
- leftNeighbor.focusElement();
- } else if (direction === 'right' && rightNeighbor) {
- if (rightNeighbor.direction === 'horizontal') {
- rightNeighbor.focusIndex = 0;
- }
-
- rightNeighbor.focusElement();
- } else {
- return false;
- }
-
- return true;
- }
-
- private getUpNeighbor(): Container | undefined {
- return this.upNeighbor || this.parent?.getUpNeighbor();
- }
-
- private getDownNeighbor(): Container | undefined {
- return this.downNeighbor || this.parent?.getDownNeighbor();
- }
-
- private getLeftNeighbor(): Container | undefined {
- return this.leftNeighbor || this.parent?.getLeftNeighbor();
- }
-
- private getRightNeighbor(): Container | undefined {
- return this.rightNeighbor || this.parent?.getRightNeighbor();
- }
-
- focusElement() {
- Container.focusedObject = this;
- if (this.htmlElement) {
- this.htmlElement.focus({ preventScroll: true });
- this.htmlElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
-
- if (this.parent) {
- this.parent.focusIndex = this.parent.children.findIndex((child) => child === this);
- }
- } else {
- this.children[this.focusIndex].focusElement();
- }
- }
-
- private setParent(parent?: Container) {
- this.parent = parent;
+ private addChild(child: Container) {
+ this.children.push(child);
+ child.parent = this;
+ this.htmlElement = undefined;
return this;
}
- private setHtmlElement(htmlElement: HTMLElement) {
+ private removeChild(child: Container) {
+ this.children = this.children.filter((c) => c !== child);
+ child.parent = undefined;
+ return this;
+ }
+
+ private addHtmlElement(htmlElement: HTMLElement) {
+ if (this.children.length > 0) {
+ console.warn('Adding an html element to a container that has children.');
+ for (const child of this.children) {
+ this.removeChild(child);
+ }
+ }
this.htmlElement = htmlElement;
return this;
}
- addChild(child: Container) {
- console.log('Adding child', child.name, 'to', this.name);
- if (this.direction === 'vertical' && this.children.length >= 1) {
- child.setUpNeighbor(this.children[this.children.length - 1]);
- } else if (this.direction === 'horizontal' && this.children.length >= 1) {
- child.setLeftNeighbor(this.children[this.children.length - 1]);
- }
- this.children.push(child.setParent(this));
- console.log('After add', this);
+ private removeHtmlElement() {
+ this.htmlElement = undefined;
return this;
}
- private addChildElement(htmlElement: HTMLElement) {
- const childContainer = new Container().setHtmlElement(htmlElement).setParent(this);
-
- if (this.direction === 'vertical' && this.children.length >= 1) {
- childContainer.setUpNeighbor(this.children[this.children.length - 1]);
- } else if (this.direction === 'horizontal' && this.children.length >= 1) {
- childContainer.setLeftNeighbor(this.children[this.children.length - 1]);
- }
-
- this.children.push(childContainer);
- return this;
- }
-
- private removeChildElement(htmlElement: HTMLElement) {
- const child = this.children.find((child) => child.htmlElement === htmlElement);
- child?.setParent(undefined)?.removeNeighbors();
- this.children = this.children.filter((child) => child.htmlElement !== htmlElement);
- return this;
- }
-
- private removeNeighbors() {
- if (this.upNeighbor?.downNeighbor === this) {
- this.upNeighbor.downNeighbor = undefined;
- }
- if (this.downNeighbor?.upNeighbor === this) {
- this.downNeighbor.upNeighbor = undefined;
- }
- if (this.leftNeighbor?.rightNeighbor === this) {
- this.leftNeighbor.rightNeighbor = undefined;
- }
- if (this.rightNeighbor?.leftNeighbor === this) {
- this.rightNeighbor.leftNeighbor = undefined;
- }
- this.upNeighbor = undefined;
- this.downNeighbor = undefined;
- this.leftNeighbor = undefined;
- this.rightNeighbor = undefined;
- 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;
- }
-
- log() {
- console.log(this.name, this);
- if (this.parent) {
- console.log('With parent: ');
- this.parent.log();
- }
+ private shouldFocusByDefault(): boolean {
+ return this.focusByDefault || this.parent?.shouldFocusByDefault() || false;
}
}
@@ -218,6 +183,8 @@ export function handleKeyboardNavigation(event: KeyboardEvent) {
return;
}
+ console.log('Currently focused object: ', currentlyFocusedObject.name, currentlyFocusedObject);
+
if (event.key === 'ArrowUp') {
if (currentlyFocusedObject.giveFocus('up')) event.preventDefault();
} else if (event.key === 'ArrowDown') {
@@ -228,16 +195,3 @@ export function handleKeyboardNavigation(event: KeyboardEvent) {
if (currentlyFocusedObject.giveFocus('right')) event.preventDefault();
}
}
-
-const navBar = new Container('navBar').setDirection('vertical');
-const main = new Container('main').setDirection('vertical');
-const home = new Container('home').setDirection('vertical');
-
-main.setLeftNeighbor(navBar);
-main.addChild(home);
-
-export const navigationContainers = {
- home,
- main,
- navBar
-};
diff --git a/src/lib/components/Carousel/CarouselPlaceholderItems.svelte b/src/lib/components/Carousel/CarouselPlaceholderItems.svelte
index 047e7a8..43e1dc5 100644
--- a/src/lib/components/Carousel/CarouselPlaceholderItems.svelte
+++ b/src/lib/components/Carousel/CarouselPlaceholderItems.svelte
@@ -5,7 +5,7 @@
export let orientation: 'landscape' | 'portrait' = 'landscape';
export let container: Container;
- let registerer = container.getRegisterer();
+ let registerer = container.createChild('carousel').setDirection('horizontal').getRegisterer();
{#each Array(10) as _, i (i)}
diff --git a/src/lib/components/Selectable.svelte b/src/lib/components/Selectable.svelte
index 8063609..50cac16 100644
--- a/src/lib/components/Selectable.svelte
+++ b/src/lib/components/Selectable.svelte
@@ -1,3 +1,7 @@
+
+
diff --git a/src/lib/pages/HomePage.svelte b/src/lib/pages/HomePage.svelte
index 10faf1c..e82141c 100644
--- a/src/lib/pages/HomePage.svelte
+++ b/src/lib/pages/HomePage.svelte
@@ -1,20 +1,16 @@
-
+
-
+