in src/focus.service.ts [654:723]
private findNextFocusByRaycast(
direction: Direction,
root: HTMLElement,
referenceRect: ClientRect,
) {
if (!this.selected) {
this.setDefaultFocus();
}
if (!this.selected) {
return null;
}
let maxDistance =
scoringConstants.maxFastSearchSize *
(isHorizontal(direction) ? referenceRect.width : referenceRect.height);
if (maxDistance < scoringConstants.fastSearchMinimumDistance) {
maxDistance = scoringConstants.fastSearchMinimumDistance;
}
// Sanity check so that we don't freeze if we get some insanely big element
let searchPointDistance = scoringConstants.fastSearchPointDistance;
if (maxDistance / searchPointDistance > scoringConstants.fastSearchMaxPointChecks) {
searchPointDistance = maxDistance / scoringConstants.fastSearchMaxPointChecks;
}
let baseX: number;
let baseY: number;
let seekX = 0;
let seekY = 0;
switch (direction) {
case Direction.LEFT:
baseX = referenceRect.left - 1;
baseY = referenceRect.top + referenceRect.height / 2;
seekX = -1;
break;
case Direction.RIGHT:
baseX = referenceRect.left + referenceRect.width + 1;
baseY = referenceRect.top + referenceRect.height / 2;
seekX = 1;
break;
case Direction.UP:
baseX = referenceRect.left + referenceRect.width / 2;
baseY = referenceRect.top - 1;
seekY = -1;
break;
case Direction.DOWN:
baseX = referenceRect.left + referenceRect.width / 2;
baseY = referenceRect.top + referenceRect.height + 1;
seekY = 1;
break;
default:
throw new Error('Invalid direction');
}
for (let i = 0; i < maxDistance; i += searchPointDistance) {
const el = <HTMLElement>document.elementFromPoint(baseX + seekX * i, baseY + seekY * i);
if (!el || el === this.selected) {
continue;
}
if (!isNodeAttached(el, root) || !this.isFocusable(el) || !this.checkFinalFocusable(el)) {
continue;
}
return el;
}
return null;
}