Я вот так реализовал в моем компоненте это циклическое перемещение с помощью Tab'а
/** удержание фокуса внутри шаблона компонента */
@HostListener('document:keydown', ['$event'])
public tabCycle(event: KeyboardEvent): void {
// выделенный элемент в шаблоне
this.lastestFocused = event.target as HTMLElement;
// крайний первый элемент, такая конструкция нужна потому что стрелка влево может быть скрыта
const firstEdge = this.firstElement ? this.firstElement : this.secondElement;
// флаг того, что выделенный элемент находится внутри шаблона компонента
let isElementInsideComponent: boolean;
// если это первый граничный элемент и мы пытаемся выделить элемент вне шаблона компонента
if (this.lastestFocused.isEqualNode(firstEdge.nativeElement) && event.shiftKey && event.code === 'Tab') {
event.preventDefault();
this.lastElement.nativeElement.focus();
return;
}
// если это последний граничный элемент и мы пытаемся выделить элемент вне шаблона компонента
if (this.lastestFocused.isEqualNode(this.lastElement.nativeElement) && !event.shiftKey && event.code === 'Tab') {
event.preventDefault();
firstEdge.nativeElement.focus();
return;
}
// если это не один из граничных элементов
// определяем, что выделенный элемент находится внутри шаблона компонента
event.composedPath().forEach((item: HTMLElement) => {
if (item.tagName && item.tagName.toLowerCase() === this.host.nativeElement.tagName.toLowerCase()) {
isElementInsideComponent = true;
}
});
// если выделенный элемент вне шаблона компонента, то переводим фокус на первый граничный элемент
if (!isElementInsideComponent) {
firstEdge.nativeElement.focus();
}
}
Если убрать фокусировку в первых двух if'ах, то цикличности не будет, фокус будет просто останавливаться на граничных элементах.
Надеюсь кому-то этот код поможет.