Tooltip
Tooltips appear on demand to explain UI elements, clarify functionality, or provide helpful context without permanently occupying screen space. They automatically position themselves relative to their trigger element.
import {TooltipModule} from "@qualcomm-ui/angular/tooltip"Overview
The tooltip should be used to provide additional non-essential information. It is linked to an interactive element (the trigger) and will be shown when this element is focused or hovered. Because the tooltip itself can't be focused or tabbed to, it should not contain interactive elements. Only one tooltip can be open at a time.
In general, tooltips should be used sparingly and only contain succinct information. Here are guidelines to help you decide when to use one:
- When using controls that lack visual labels, like icon buttons.
- When defining a term or inline item.
- When additional information may help a user make decisions.
- When providing more context to an element.
Examples
Simple
The simple API provides a standalone component with built-in layout. The trigger is supplied as a child directive, and the content of the tooltip is the children.
<div q-tooltip>
<button emphasis="primary" q-button q-tooltip-trigger variant="fill">
Hover me
</button>
Hello World!
</div>
Composite
Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.
<div q-tooltip-root>
<button emphasis="primary" q-button q-tooltip-trigger variant="fill">
Hover me
</button>
<q-portal>
<div q-tooltip-positioner>
<div q-tooltip-content>
<div q-tooltip-arrow>
<div q-tooltip-arrow-tip></div>
</div>
Hello World!
</div>
</div>
</q-portal>
</div>
Placement
The tooltip's position, relative to the trigger element, can be set using the placement option of the positioning prop. Its default value is top.
Change the values in the example below and hover over the select to see the tooltip position change.
import {Component, signal} from "@angular/core"
import {FormsModule} from "@angular/forms"
import {PortalDirective} from "@qualcomm-ui/angular-core/portal"
import {SelectModule} from "@qualcomm-ui/angular/select"
import {TooltipModule} from "@qualcomm-ui/angular/tooltip"
import {selectCollection} from "@qualcomm-ui/core/select"
import type {Placement} from "@qualcomm-ui/dom/floating-ui"
@Component({
imports: [TooltipModule, SelectModule, FormsModule, PortalDirective],
selector: "tooltip-placement",
template: `
<div class="flex flex-col">
<div q-tooltip-root [positioning]="{placement: placement()[0]}">
<div
class="w-48"
q-select-root
[collection]="collection()"
[(ngModel)]="placement"
>
<div q-tooltip-trigger>
<div aria-label="Select placement" q-select-control>
<span q-select-value-text></span>
<button q-select-indicator></button>
</div>
</div>
<select q-select-hidden-select></select>
<ng-template qPortal>
<div q-select-positioner>
<div q-select-content>
<q-select-items />
</div>
</div>
</ng-template>
</div>
<q-tooltip-floating-portal>
{{ placement()[0] }}
</q-tooltip-floating-portal>
</div>
</div>
`,
})
export class TooltipPlacementDemo {
readonly placement = signal<Placement[]>(["top"])
readonly collection = signal(
selectCollection({
items: [
"top-start",
"top",
"top-end",
"right-start",
"right",
"right-end",
"bottom-start",
"bottom",
"bottom-end",
"left-start",
"left",
"left-end",
],
}),
)
}Close On Click / Escape
By default, the tooltip will close when the Escape key is pressed or a click is detected. You can customize this behavior using the closeOnClick and closeOnEscape props.
<div closeOnClick="false" closeOnEscape="false" q-tooltip>
<button emphasis="primary" q-button q-tooltip-trigger variant="fill">
Hover me
</button>
Hello World!
</div>
Controlled Visibility
Use open and openChanged to control the component's visibility manually. These props follow our controlled state pattern.
import {Component, signal} from "@angular/core"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {TooltipModule} from "@qualcomm-ui/angular/tooltip"
@Component({
imports: [TooltipModule, ButtonModule],
selector: "tooltip-controlled-state-demo",
template: `
<div class="flex flex-col items-center gap-4">
<div q-tooltip [open]="open()" (openChanged)="handleOpenChange($event)">
<button emphasis="primary" q-button q-tooltip-trigger variant="fill">
Hover me
</button>
Hello World!
</div>
<output class="text-neutral-primary block">
the tooltip is {{ open() ? "open" : "closed" }}
</output>
</div>
`,
})
export class TooltipControlledStateDemo {
readonly open = signal(false)
handleOpenChange(open: boolean): void {
this.open.set(open)
}
}Disabled
You can disable the tooltip by using the disabled prop.
<div q-tooltip [disabled]="disabled()">
<button emphasis="primary" q-button q-tooltip-trigger variant="fill">
Hover me
</button>
Hello World!
</div>
Shortcuts
<q-tooltip-floating-portal>
A helper component that combines the portal, positioner, content, and arrow components. This shortcut is equivalent to:
<q-portal>
<div q-tooltip-positioner>
<div q-tooltip-content>
<div q-tooltip-arrow>
<div q-tooltip-arrow-tip></div>
</div>
<ng-content />
</div>
</div>
</q-portal>API
q-tooltip
The simple tooltip extends the q-tooltip-root directive with the following props:
booleanbooleanComposite API
q-tooltip-root
booleanboolean'ltr' | 'rtl'
booleanstringbooleanbooleanq-tooltip-trigger
stringdata-expandeddata-state| 'open'
| 'closed'
data-tooltip-part'trigger'q-tooltip-positioner
stringq-tooltip-content
stringclass'qui-tooltip__content'data-placement| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
data-state| 'open'
| 'closed'
data-tooltip-part'content'hiddenbooleanq-tooltip-arrow
stringq-tooltip-arrow-tip
TooltipPositioningOptions
numberstring[data-menu-part=arrow]).() =>
| 'clippingAncestors'
| Element
| Array<Element>
| {
height: number
width: number
x: number
y: number
}
boolean| boolean
| Array<
| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
>
(
element:
| HTMLElement
| VirtualElement,
) => {
height?: number
width?: number
x?: number
y?: number
}
numberboolean| boolean
| {
ancestorResize?: boolean
ancestorScroll?: boolean
animationFrame?: boolean
elementResize?: boolean
layoutShift?: boolean
}
{
crossAxis?: number
mainAxis?: number
}
(
data: ComputePositionReturn,
) => void
(data: {
placed: boolean
}) => void
numberboolean| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
booleannumberboolean| 'absolute'
| 'fixed'
(data: {
updatePosition: () => Promise<void>
}) => void | Promise<void>