mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-05-19 07:52:02 +00:00
improve algo for finding items
This commit is contained in:
parent
5abe361d00
commit
831222f812
2 changed files with 54 additions and 48 deletions
|
|
@ -104,7 +104,7 @@ const StreamsList = ({ className, video, type, onEpisodeSearch, ...props }) => {
|
||||||
{
|
{
|
||||||
video ?
|
video ?
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Button className={classnames(styles['button-container'], styles['back-button-container'])} tabIndex={-1} onClick={backButtonOnClick}>
|
<Button className={classnames(styles['button-container'], styles['back-button-container'])} tabIndex={0} onClick={backButtonOnClick}>
|
||||||
<Icon className={styles['icon']} name={'chevron-back'} />
|
<Icon className={styles['icon']} name={'chevron-back'} />
|
||||||
</Button>
|
</Button>
|
||||||
<div className={styles['episode-title']}>
|
<div className={styles['episode-title']}>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright (C) 2017-2026 Smart code 203358507
|
// Copyright (C) 2017-2026 Smart code 203358507
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useGamepad } from '../GamepadContext';
|
import { useGamepad } from '../GamepadContext';
|
||||||
|
|
||||||
const FOCUSABLE = '[tabindex="0"]';
|
const FOCUSABLE = '[tabindex="0"]';
|
||||||
|
|
@ -20,12 +20,22 @@ const useContentGamepadNavigation = (
|
||||||
gamepadHandlerId: string
|
gamepadHandlerId: string
|
||||||
) => {
|
) => {
|
||||||
const gamepad = useGamepad();
|
const gamepad = useGamepad();
|
||||||
|
const lastFocused = useRef<HTMLDivElement | null>(null);
|
||||||
|
const wasInOverlay = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleGamepadNavigation = (
|
const handleGamepadNavigation = (
|
||||||
direction: 'left' | 'right' | 'up' | 'down'
|
direction: 'left' | 'right' | 'up' | 'down'
|
||||||
) => {
|
) => {
|
||||||
const scope = getActiveScope(sectionRef.current);
|
const scope = getActiveScope(sectionRef.current);
|
||||||
|
const inOverlay = scope !== sectionRef.current;
|
||||||
|
|
||||||
|
if (inOverlay && !wasInOverlay.current) {
|
||||||
|
const focused = sectionRef.current?.querySelector<HTMLDivElement>(':focus');
|
||||||
|
if (focused) lastFocused.current = focused;
|
||||||
|
}
|
||||||
|
wasInOverlay.current = inOverlay;
|
||||||
|
|
||||||
const elements = Array.from(
|
const elements = Array.from(
|
||||||
scope?.querySelectorAll<HTMLDivElement>(FOCUSABLE) || []
|
scope?.querySelectorAll<HTMLDivElement>(FOCUSABLE) || []
|
||||||
);
|
);
|
||||||
|
|
@ -39,57 +49,31 @@ const useContentGamepadNavigation = (
|
||||||
}
|
}
|
||||||
|
|
||||||
let closestElement: HTMLDivElement | null = null;
|
let closestElement: HTMLDivElement | null = null;
|
||||||
const currentRect = activeElement.getBoundingClientRect();
|
const cur = activeElement.getBoundingClientRect();
|
||||||
|
const cx = cur.left + cur.width / 2;
|
||||||
|
const cy = cur.top + cur.height / 2;
|
||||||
let closestDistance = Infinity;
|
let closestDistance = Infinity;
|
||||||
|
|
||||||
elements.forEach((el) => {
|
elements.forEach((el) => {
|
||||||
if (el === activeElement) return;
|
if (el === activeElement) return;
|
||||||
const rect = el.getBoundingClientRect();
|
const r = el.getBoundingClientRect();
|
||||||
|
const ex = r.left + r.width / 2;
|
||||||
|
const ey = r.top + r.height / 2;
|
||||||
|
|
||||||
let distance = Infinity;
|
const isCorrectDirection =
|
||||||
|
(direction === 'left' && ex < cx) ||
|
||||||
|
(direction === 'right' && ex > cx) ||
|
||||||
|
(direction === 'up' && ey < cy) ||
|
||||||
|
(direction === 'down' && ey > cy);
|
||||||
|
|
||||||
switch (direction) {
|
if (!isCorrectDirection) return;
|
||||||
case 'left':
|
|
||||||
if (
|
const dx = ex - cx;
|
||||||
rect.right <= currentRect.left &&
|
const dy = ey - cy;
|
||||||
(rect.top === currentRect.top ||
|
const isHorizontal = direction === 'left' || direction === 'right';
|
||||||
(rect.top < currentRect.top && rect.bottom > currentRect.top)
|
const primary = isHorizontal ? Math.abs(dx) : Math.abs(dy);
|
||||||
)
|
const secondary = isHorizontal ? Math.abs(dy) : Math.abs(dx);
|
||||||
) {
|
const distance = primary + secondary * 3;
|
||||||
distance = currentRect.left - rect.right;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
if (
|
|
||||||
currentRect.right <= rect.left &&
|
|
||||||
(rect.top === currentRect.top ||
|
|
||||||
(rect.top < currentRect.top && rect.bottom > currentRect.top)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
distance = rect.left - currentRect.right;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'up':
|
|
||||||
if (
|
|
||||||
rect.bottom <= currentRect.top &&
|
|
||||||
(rect.left === currentRect.left ||
|
|
||||||
(rect.left < currentRect.left && rect.right > currentRect.left)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
distance = currentRect.top - rect.bottom;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'down':
|
|
||||||
if (
|
|
||||||
rect.top >= currentRect.bottom &&
|
|
||||||
(rect.left === currentRect.left ||
|
|
||||||
(rect.left < currentRect.left && rect.right > currentRect.left)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
distance = rect.top - currentRect.bottom;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distance < closestDistance) {
|
if (distance < closestDistance) {
|
||||||
closestDistance = distance;
|
closestDistance = distance;
|
||||||
|
|
@ -104,10 +88,24 @@ const useContentGamepadNavigation = (
|
||||||
|
|
||||||
const onSelect = () => {
|
const onSelect = () => {
|
||||||
const scope = getActiveScope(sectionRef.current);
|
const scope = getActiveScope(sectionRef.current);
|
||||||
|
const inOverlay = scope !== sectionRef.current;
|
||||||
|
|
||||||
|
if (inOverlay && !wasInOverlay.current) {
|
||||||
|
const focused = sectionRef.current?.querySelector<HTMLDivElement>(':focus');
|
||||||
|
if (focused) lastFocused.current = focused;
|
||||||
|
}
|
||||||
|
wasInOverlay.current = inOverlay;
|
||||||
|
|
||||||
const elements = Array.from(
|
const elements = Array.from(
|
||||||
scope?.querySelectorAll<HTMLDivElement>(FOCUSABLE) || []
|
scope?.querySelectorAll<HTMLDivElement>(FOCUSABLE) || []
|
||||||
);
|
);
|
||||||
if (elements.length === 0) return;
|
if (elements.length === 0) {
|
||||||
|
if (lastFocused.current) {
|
||||||
|
lastFocused.current.focus();
|
||||||
|
wasInOverlay.current = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const activeElement = (scope ?? document)?.querySelector<HTMLDivElement>(':focus');
|
const activeElement = (scope ?? document)?.querySelector<HTMLDivElement>(':focus');
|
||||||
|
|
||||||
|
|
@ -118,6 +116,14 @@ const useContentGamepadNavigation = (
|
||||||
const isSelect = Array.from(activeElement.classList).some((cls) => cls.startsWith('select-input'));
|
const isSelect = Array.from(activeElement.classList).some((cls) => cls.startsWith('select-input'));
|
||||||
if (!isSelect) {
|
if (!isSelect) {
|
||||||
activeElement?.click();
|
activeElement?.click();
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const stillInOverlay = getActiveScope(sectionRef.current) !== sectionRef.current;
|
||||||
|
if (!stillInOverlay && wasInOverlay.current && lastFocused.current) {
|
||||||
|
lastFocused.current.focus();
|
||||||
|
wasInOverlay.current = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue