mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-22 00:35:12 +02:00
Add Tizen 5.5 Polyfills, implement dpad navigation with specified components
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { derived, get, type Readable, type Writable, writable } from 'svelte/store';
|
||||
import { navigate } from 'svelte-navigator';
|
||||
|
||||
export type Registerer = (htmlElement: HTMLElement) => { destroy: () => void };
|
||||
|
||||
@@ -19,6 +18,7 @@ export class Container {
|
||||
right: undefined
|
||||
};
|
||||
private focusByDefault: boolean = false;
|
||||
private isInitialized: boolean = false;
|
||||
|
||||
private direction: FlowDirection = 'vertical';
|
||||
|
||||
@@ -42,12 +42,13 @@ export class Container {
|
||||
return false;
|
||||
});
|
||||
|
||||
static objects = new Map<symbol, Container>();
|
||||
static objects = new Map<HTMLElement, Container>();
|
||||
|
||||
constructor(name: string = '') {
|
||||
this.id = Symbol();
|
||||
this.name = name;
|
||||
Container.objects.set(this.id, this);
|
||||
|
||||
// Find parents
|
||||
}
|
||||
|
||||
setDirection(direction: FlowDirection) {
|
||||
@@ -60,12 +61,18 @@ export class Container {
|
||||
return this;
|
||||
}
|
||||
|
||||
createChild(name: string = '') {
|
||||
const child = new Container(name);
|
||||
this.addChild(child);
|
||||
return child;
|
||||
setHtmlElement(htmlElement: HTMLElement) {
|
||||
this.htmlElement = htmlElement;
|
||||
Container.objects.set(htmlElement, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
// createChild(htmlElement: HTMLElement, name: string = '') {
|
||||
// const child = new Container(htmlElement, name);
|
||||
// this.addChild(child);
|
||||
// return child;
|
||||
// }
|
||||
|
||||
focus() {
|
||||
if (this.children.length > 0) {
|
||||
this.children[get(this.focusIndex)]?.focus();
|
||||
@@ -89,7 +96,7 @@ export class Container {
|
||||
|
||||
isFocusable() {
|
||||
if (this.htmlElement) {
|
||||
return true;
|
||||
return this.htmlElement.tabIndex >= 0;
|
||||
} else {
|
||||
for (const child of this.children) {
|
||||
if (child.isFocusable()) {
|
||||
@@ -143,57 +150,135 @@ export class Container {
|
||||
}
|
||||
}
|
||||
|
||||
getChildRegisterer(): Registerer {
|
||||
return (htmlElement: HTMLElement) => {
|
||||
if (this.htmlElement) console.warn('Registering to a container that has an element.');
|
||||
// getChildRegisterer(): Registerer {
|
||||
// return (htmlElement: HTMLElement) => {
|
||||
// if (this.htmlElement) console.warn('Registering to a container that has an element.');
|
||||
//
|
||||
// this.createChild().addHtmlElement(htmlElement);
|
||||
//
|
||||
// if (!get(Container.focusedObject) && this.shouldFocusByDefault()) {
|
||||
// this.focus();
|
||||
// }
|
||||
//
|
||||
// return {
|
||||
// destroy: () => {
|
||||
// this.removeHtmlElement();
|
||||
// }
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// getHtmlElementRegisterer(): Registerer {
|
||||
// return (htmlElement: HTMLElement) => {
|
||||
// if (this.children.length > 0) {
|
||||
// console.warn('Registering an html element to a container that has children.');
|
||||
// for (const child of this.children) {
|
||||
// this.removeChild(child);
|
||||
// }
|
||||
// }
|
||||
// this.addHtmlElement(htmlElement);
|
||||
// return {
|
||||
// destroy: () => {
|
||||
// this.removeHtmlElement();
|
||||
// }
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
|
||||
this.createChild().addHtmlElement(htmlElement);
|
||||
_initializeContainer() {
|
||||
const getParentContainer = (htmlElement: HTMLElement): Container | undefined => {
|
||||
if (Container.objects.get(htmlElement)) return Container.objects.get(htmlElement);
|
||||
else if (htmlElement.parentElement) return getParentContainer(htmlElement.parentElement);
|
||||
else return undefined;
|
||||
};
|
||||
|
||||
if (!get(Container.focusedObject) && this.shouldFocusByDefault()) {
|
||||
this.focus();
|
||||
if (!this.htmlElement) {
|
||||
console.error('No html element found for', this);
|
||||
return;
|
||||
} else if (this.isInitialized) {
|
||||
console.warn('Container already initialized', this);
|
||||
}
|
||||
|
||||
const parentContainer = this.htmlElement.parentElement
|
||||
? getParentContainer(this.htmlElement.parentElement)
|
||||
: undefined;
|
||||
if (parentContainer) {
|
||||
parentContainer.addChild(this);
|
||||
} else {
|
||||
console.error('No parent container found for', this.htmlElement);
|
||||
}
|
||||
|
||||
if (!get(Container.focusedObject) && this.shouldFocusByDefault()) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
_unmountContainer() {
|
||||
console.log('Unmounting container', this);
|
||||
const isFocusedWithin = get(this.hasFocusWithin);
|
||||
|
||||
if (this.htmlElement) {
|
||||
Container.objects.delete(this.htmlElement);
|
||||
}
|
||||
|
||||
const parent = this.parent;
|
||||
if (parent) {
|
||||
parent.removeChild(this);
|
||||
if (isFocusedWithin) {
|
||||
parent.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static createRegisterer(
|
||||
_container?: Container,
|
||||
flowDirection: FlowDirection = 'vertical'
|
||||
): Registerer {
|
||||
const container = _container || new Container().setDirection(flowDirection);
|
||||
|
||||
return (htmlElement: HTMLElement) => {
|
||||
console.log('Registering', htmlElement, container);
|
||||
container.setHtmlElement(htmlElement);
|
||||
|
||||
return {
|
||||
destroy: () => {
|
||||
this.removeHtmlElement();
|
||||
container.parent?.removeChild(container);
|
||||
Container.objects.delete(htmlElement);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
getHtmlElementRegisterer(): Registerer {
|
||||
return (htmlElement: HTMLElement) => {
|
||||
if (this.children.length > 0) {
|
||||
console.warn('Registering an html element to a container that has children.');
|
||||
for (const child of this.children) {
|
||||
this.removeChild(child);
|
||||
}
|
||||
}
|
||||
this.addHtmlElement(htmlElement);
|
||||
return {
|
||||
destroy: () => {
|
||||
this.removeHtmlElement();
|
||||
}
|
||||
};
|
||||
};
|
||||
static getRegisterer(flowDirection: FlowDirection = 'vertical'): Registerer {
|
||||
return (htmlElement: HTMLElement) =>
|
||||
this.createRegisterer(undefined, flowDirection)(htmlElement);
|
||||
}
|
||||
|
||||
getRegisterer(): Registerer {
|
||||
return (htmlElement: HTMLElement) => Container.createRegisterer(this)(htmlElement);
|
||||
}
|
||||
|
||||
static getStores(element: HTMLElement) {
|
||||
return Container.objects.get(element)?.getStores();
|
||||
}
|
||||
|
||||
getStores(): {
|
||||
container: Container;
|
||||
hasFocus: Readable<boolean>;
|
||||
hasFocusWithin: Readable<boolean>;
|
||||
registerer: Registerer;
|
||||
} {
|
||||
return {
|
||||
container: this,
|
||||
hasFocus: this.hasFocus,
|
||||
hasFocusWithin: this.hasFocusWithin
|
||||
hasFocusWithin: this.hasFocusWithin,
|
||||
registerer: this.getRegisterer()
|
||||
};
|
||||
}
|
||||
|
||||
private addChild(child: Container) {
|
||||
this.children.push(child);
|
||||
child.parent = this;
|
||||
this.htmlElement = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -203,21 +288,16 @@ export class Container {
|
||||
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;
|
||||
}
|
||||
|
||||
private removeHtmlElement() {
|
||||
this.htmlElement = 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;
|
||||
// }
|
||||
|
||||
private shouldFocusByDefault(): boolean {
|
||||
return this.focusByDefault || this.parent?.shouldFocusByDefault() || false;
|
||||
@@ -229,7 +309,12 @@ export function handleKeyboardNavigation(event: KeyboardEvent) {
|
||||
|
||||
if (!currentlyFocusedObject) {
|
||||
console.error('No focused object!!!');
|
||||
mainContainer.focus();
|
||||
// Find object that can be focused
|
||||
Container.objects.forEach((container) => {
|
||||
if (container.isFocusable()) {
|
||||
container.focus();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -247,6 +332,8 @@ export function handleKeyboardNavigation(event: KeyboardEvent) {
|
||||
}
|
||||
|
||||
export const focusedObject = Container.focusedObject;
|
||||
export const mainContainer = new Container('main')
|
||||
.setDirection('horizontal')
|
||||
.setFocusByDefault(true);
|
||||
// export const mainContainer = new Container('main')
|
||||
// .setDirection('horizontal')
|
||||
// .setFocusByDefault(true);
|
||||
|
||||
export const registerer = Container.getRegisterer();
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
<script lang="ts">
|
||||
import Selectable from '../components/Selectable.svelte';
|
||||
import classNames from 'classnames';
|
||||
import { useNavigate } from 'svelte-navigator';
|
||||
import { get } from 'svelte/store';
|
||||
import { Container } from '../actions/focusAction';
|
||||
import { get, type Readable } from 'svelte/store';
|
||||
import Container from '../../Container.svelte';
|
||||
import { Container as Cont } from '../../lib/actions/focusAction';
|
||||
|
||||
export let to: string;
|
||||
export let parentContainer: Container;
|
||||
const { container, hasFocus } = parentContainer.createChild('navBarItem').getStores();
|
||||
let hasFocus: Readable<boolean>;
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
function handleClick() {
|
||||
navigate(to);
|
||||
get(Container.focusedObject)?.giveFocus('right');
|
||||
get(Cont.focusedObject)?.giveFocus('right');
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={handleClick}>
|
||||
<Selectable {container}>
|
||||
<div
|
||||
class={classNames('flex items-center my-2', {
|
||||
'text-amber-200': $hasFocus
|
||||
})}
|
||||
>
|
||||
<slot name="icon" />
|
||||
<slot name="text" />
|
||||
</div>
|
||||
</Selectable>
|
||||
</button>
|
||||
<Container tag="button" on:click={handleClick} bind:hasFocus>
|
||||
<div
|
||||
class={classNames('flex items-center my-2', {
|
||||
'text-amber-200': $hasFocus
|
||||
})}
|
||||
>
|
||||
<slot name="icon" />
|
||||
<slot name="text" />
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -1,37 +1,31 @@
|
||||
<script lang="ts">
|
||||
import CardPlaceholder from '../Card/CardPlaceholder.svelte';
|
||||
import { Container } from '../../actions/focusAction';
|
||||
import classNames from 'classnames';
|
||||
import Container from '../../../Container.svelte';
|
||||
import type { Readable } from 'svelte/store';
|
||||
export let size: 'dynamic' | 'md' | 'lg' = 'md';
|
||||
export let orientation: 'landscape' | 'portrait' = 'landscape';
|
||||
|
||||
export let container: Container;
|
||||
let carousel = container.createChild('carousel').setDirection('horizontal');
|
||||
let focusIndexStore = carousel.focusIndex;
|
||||
let focusWithinStore = carousel.hasFocusWithin;
|
||||
|
||||
Container.focusedObject.subscribe((fo) => console.log('focusedObject', fo));
|
||||
carousel.hasFocus.subscribe((hf) => console.log('hasFocus', hf));
|
||||
|
||||
let registerer = carousel.getChildRegisterer();
|
||||
let focusIndex: Readable<number>;
|
||||
let focusWithin: Readable<boolean>;
|
||||
</script>
|
||||
|
||||
<p
|
||||
class={classNames({
|
||||
'bg-blue-500': $focusWithinStore
|
||||
'bg-blue-500': $focusWithin
|
||||
})}
|
||||
>
|
||||
Index: {$focusIndexStore}
|
||||
Index: {$focusIndex}
|
||||
</p>
|
||||
|
||||
{#each Array(10) as _, i (i)}
|
||||
<div
|
||||
tabindex="0"
|
||||
use:registerer
|
||||
<Container
|
||||
bind:focusIndex
|
||||
bind:focusWithin
|
||||
class={classNames({
|
||||
'bg-red-500': $focusIndexStore === i && $focusWithinStore
|
||||
'bg-red-500': $focusIndex === i && $focusWithin
|
||||
})}
|
||||
>
|
||||
<CardPlaceholder {size} index={i} {orientation} />
|
||||
</div>
|
||||
</Container>
|
||||
{/each}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { Container } from '../actions/focusAction';
|
||||
|
||||
export let container: Container;
|
||||
const registerer = container.getHtmlElementRegisterer();
|
||||
const registerer = container.getRegisterer();
|
||||
|
||||
export let handleClick = () => {
|
||||
container.focus();
|
||||
};
|
||||
</script>
|
||||
|
||||
<button use:registerer tabindex="0" on:click={handleClick} class="outline-none ring-0">
|
||||
<button use:registerer on:click={handleClick} class="outline-none ring-0">
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { Container } from '../actions/focusAction';
|
||||
import { settings } from '../stores/settings.store';
|
||||
import { jellyfinItemsStore } from '../stores/data.store';
|
||||
import Carousel from '../components/Carousel/Carousel.svelte';
|
||||
import CarouselPlaceholderItems from '../components/Carousel/CarouselPlaceholderItems.svelte';
|
||||
|
||||
export let parent: Container;
|
||||
let registerer = parent.getChildRegisterer();
|
||||
import Container from '../../Container.svelte';
|
||||
|
||||
settings.update((prev) => ({
|
||||
...prev,
|
||||
@@ -22,13 +19,13 @@
|
||||
jellyfinItemsStore.subscribe((items) => {
|
||||
console.warn('GOT ITEMS', items.data);
|
||||
});
|
||||
let asd: HTMLDivElement;
|
||||
$: console.log('asd', asd);
|
||||
</script>
|
||||
|
||||
<div use:registerer bind:this={asd}>
|
||||
<Container>
|
||||
<div>LibraryPage</div>
|
||||
<Carousel>
|
||||
<CarouselPlaceholderItems container={parent} />
|
||||
</Carousel>
|
||||
</div>
|
||||
<Container horizontal>
|
||||
<Carousel>
|
||||
<CarouselPlaceholderItems />
|
||||
</Carousel>
|
||||
</Container>
|
||||
</Container>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Container } from '../actions/focusAction';
|
||||
|
||||
export let container: Container;
|
||||
let registerer = container.getChildRegisterer();
|
||||
import Container from '../../Container.svelte';
|
||||
</script>
|
||||
|
||||
<div use:registerer>ManagePage</div>
|
||||
<Container>ManagePage</Container>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Container } from '../actions/focusAction';
|
||||
|
||||
export let container: Container;
|
||||
let registerer = container.getChildRegisterer();
|
||||
import Container from '../../Container.svelte';
|
||||
</script>
|
||||
|
||||
<div use:registerer>MoviesPage</div>
|
||||
<Container>MoviesPage</Container>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Container } from '../actions/focusAction';
|
||||
|
||||
export let container: Container;
|
||||
let registerer = container.getChildRegisterer();
|
||||
import Container from '../../Container.svelte';
|
||||
</script>
|
||||
|
||||
<div use:registerer>SearchPage</div>
|
||||
<Container>SearchPage</Container>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Container } from '../actions/focusAction';
|
||||
|
||||
export let container: Container;
|
||||
let registerer = container.getChildRegisterer();
|
||||
import Container from '../../Container.svelte';
|
||||
</script>
|
||||
|
||||
<div use:registerer>SeriesPage</div>
|
||||
<Container>SeriesPage</Container>
|
||||
|
||||
Reference in New Issue
Block a user