improve algo for finding items

This commit is contained in:
Timothy Z. 2026-04-29 00:07:16 +03:00
parent 5abe361d00
commit 831222f812
2 changed files with 54 additions and 48 deletions

View file

@ -104,7 +104,7 @@ const StreamsList = ({ className, video, type, onEpisodeSearch, ...props }) => {
{
video ?
<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'} />
</Button>
<div className={styles['episode-title']}>

View file

@ -1,6 +1,6 @@
// Copyright (C) 2017-2026 Smart code 203358507
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { useGamepad } from '../GamepadContext';
const FOCUSABLE = '[tabindex="0"]';
@ -20,12 +20,22 @@ const useContentGamepadNavigation = (
gamepadHandlerId: string
) => {
const gamepad = useGamepad();
const lastFocused = useRef<HTMLDivElement | null>(null);
const wasInOverlay = useRef(false);
useEffect(() => {
const handleGamepadNavigation = (
direction: 'left' | 'right' | 'up' | 'down'
) => {
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(
scope?.querySelectorAll<HTMLDivElement>(FOCUSABLE) || []
);
@ -39,57 +49,31 @@ const useContentGamepadNavigation = (
}
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;
elements.forEach((el) => {
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) {
case 'left':
if (
rect.right <= currentRect.left &&
(rect.top === currentRect.top ||
(rect.top < currentRect.top && rect.bottom > currentRect.top)
)
) {
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 (!isCorrectDirection) return;
const dx = ex - cx;
const dy = ey - cy;
const isHorizontal = direction === 'left' || direction === 'right';
const primary = isHorizontal ? Math.abs(dx) : Math.abs(dy);
const secondary = isHorizontal ? Math.abs(dy) : Math.abs(dx);
const distance = primary + secondary * 3;
if (distance < closestDistance) {
closestDistance = distance;
@ -104,10 +88,24 @@ const useContentGamepadNavigation = (
const onSelect = () => {
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(
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');
@ -118,6 +116,14 @@ const useContentGamepadNavigation = (
const isSelect = Array.from(activeElement.classList).some((cls) => cls.startsWith('select-input'));
if (!isSelect) {
activeElement?.click();
requestAnimationFrame(() => {
const stillInOverlay = getActiveScope(sectionRef.current) !== sectionRef.current;
if (!stillInOverlay && wasInOverlay.current && lastFocused.current) {
lastFocused.current.focus();
wasInOverlay.current = false;
}
});
}
};