Для использования Floating UI в проекте на Angular необходимо сначала установить библиотеку через npm:
npm install @floating-ui/dom
После установки важно импортировать необходимые функции из пакета:
import { computePosition, offset, flip, shift, autoUpdate } from '@floating-ui/dom';
Эти функции позволяют вычислять позиционирование всплывающих элементов и динамически их корректировать в зависимости от положения целевого элемента и доступного пространства.
В Angular-компоненте создаются два ключевых элемента: reference (ссылка, к которой привязывается всплывающий элемент) и floating (сам всплывающий элемент). Например:
@Component({
selector: 'app-tooltip',
template: `
Подсказка
`,
styles: [`
.tooltip {
position: absolute;
background: #333;
color: #fff;
padding: 5px 10px;
border-radius: 4px;
display: none;
}
`]
})
export class TooltipComponent implements AfterViewInit, OnDestroy {
@ViewChild('button') button!: ElementRef;
@ViewChild('tooltip') tooltip!: ElementRef;
private cleanup?: () => void;
ngAfterViewInit() {
this.cleanup = autoUpdate(
this.button.nativeElement,
this.tooltip.nativeElement,
() => this.updatePosition()
);
this.button.nativeElement.addEventListener('mouseenter', () => {
this.tooltip.nativeElement.style.display = 'block';
});
this.button.nativeElement.addEventListener('mouseleave', () => {
this.tooltip.nativeElement.style.display = 'none';
});
}
updatePosition() {
computePosition(this.button.nativeElement, this.tooltip.nativeElement, {
placement: 'top',
middleware: [offset(8), flip(), shift({ padding: 5 })]
}).then(({ x, y }) => {
Object.assign(this.tooltip.nativeElement.style, {
left: `${x}px`,
top: `${y}px`
});
});
}
ngOnDestroy() {
this.cleanup?.();
}
}
Ключевые моменты:
computePosition — вычисляет координаты всплывающего
элемента.offset — задаёт расстояние между reference и floating
элементом.flip — автоматически меняет сторону, если элемент не
помещается.shift — корректирует позицию, чтобы элемент оставался в
видимой области.autoUpdate — подписывает floating элемент на изменения
размеров и скролла, чтобы позиция оставалась актуальной.В Angular часто встречаются динамические элементы, например, меню
автодополнения. Для корректного позиционирования Floating UI можно
использовать ngAfterViewChecked, чтобы гарантировать, что
элементы уже рендерятся перед вызовом computePosition.
ngAfterViewChecked() {
if (this.isDropdownVisible) {
this.updatePosition();
}
}
Также важно корректно очищать подписки, чтобы избежать утечек памяти:
ngOnDestroy() {
this.cleanup?.();
}
Для интеграции Floating UI с Angular CDK Overlay можно использовать кастомные стратегии позиционирования:
import { ConnectedPosition, Overlay, OverlayRef } from '@angular/cdk/overlay';
const overlayRef: OverlayRef = this.overlay.create({
positionStrategy: this.overlay.position()
.flexibleConnectedTo(this.button)
.withPositions([{ originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top' }])
});
Однако Floating UI обеспечивает более гибкое управление поведением middleware, что особенно полезно при создании сложных всплывающих компонентов с динамическим содержимым.
1. offset() — отступы между элементами:
offset(10) // 10 пикселей от reference
2. flip() — автоматический поворот, если элемент выходит за границы viewport:
flip({ fallbackPlacements: ['bottom', 'right'] })
3. shift() — подстраивание позиции под границы экрана:
shift({ padding: 5 })
4. arrow() — позиционирование стрелки подсказки:
import { arrow } from '@floating-ui/dom';
arrow({ element: arrowElement })
В Angular можно обновлять координаты стрелки аналогично обновлению позиции floating элемента.
Если Angular-компонент использует
ViewEncapsulation.ShadowDom, Floating UI автоматически
поддерживает расчёт позиции через computePosition, но важно
указывать правильный контейнер для autoUpdate, чтобы
слушатели скролла и resize работали корректно.
autoUpdate(
this.button.nativeElement,
this.tooltip.nativeElement,
() => this.updatePosition(),
{ ancestorScroll: true }
);
requestAnimationFrame
или RxJS auditTime.Floating UI легко интегрируется с @angular/animations.
Например, анимация появления tooltip:
animations: [
trigger('tooltipFade', [
state('hidden', style({ opacity: 0 })),
state('visible', style({ opacity: 1 })),
transition('hidden => visible', animate('150ms ease-in')),
transition('visible => hidden', animate('100ms ease-out')),
])
]
Применение состояния может быть синхронизировано с показом tooltip и позиционированием через Floating UI.