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 {SelectModule} from "@qualcomm-ui/angular/select"
import {TooltipModule} from "@qualcomm-ui/angular/tooltip"
import {PortalDirective} from "@qualcomm-ui/angular-core/portal"
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"
>
<span q-tooltip-trigger>
<button q-select-trigger>
<span q-select-value-text></span>
<span q-select-indicator></span>
</button>
</span>
<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>Hello World!</q-tooltip-floating-portal>
</div>
</div>
`,
})
export class TooltipPlacementDemo {
readonly placement = signal<Placement[]>(["top"])
readonly collection = signal(
selectCollection({
items: [
"top",
"bottom",
"right",
"left",
"top-end",
"bottom-end",
"right-end",
"left-end",
"top-start",
"bottom-start",
"right-start",
"left-start",
],
}),
)
}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
Set the initial visibility using the defaultOpen prop, or use open and openChanged to control the 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:
| Prop | Type |
|---|---|
If true, the tooltip will not be rendered in a DOM portal. | boolean |
Whether to hide the arrow. | boolean |
booleanbooleanComposite API
q-tooltip-root
| Prop | Type |
|---|---|
Whether the tooltip should close when the trigger is clicked. | boolean |
Whether to close the tooltip when the escape key is pressed. | boolean |
The document's text/writing direction. | 'ltr' | 'rtl' |
The disabled state of the tooltip | boolean |
HTML id attribute. If
omitted, a unique identifier will be generated for accessibility.) | string |
The open state of the tooltip. | boolean |
The user provided options used to position the popover content | |
Function called when the tooltip is opened/closed. | boolean |
booleanboolean'ltr' | 'rtl'
booleanstringbooleanbooleanq-tooltip-trigger
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be generated for accessibility.) | string |
string| Attribute / Property | Value |
|---|---|
data-expanded | |
data-part | 'trigger' |
data-scope | 'tooltip' |
data-state | | 'open' |
data-expandeddata-part'trigger'data-scope'tooltip'data-state| 'open'
| 'closed'
q-tooltip-positioner
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be generated for accessibility.) | string |
string| Attribute / Property | Value |
|---|---|
class | 'qui-tooltip__positioner' |
data-part | 'positioner' |
data-scope | 'tooltip' |
style |
class'qui-tooltip__positioner'data-part'positioner'data-scope'tooltip'styleq-tooltip-content
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be generated for accessibility.) | string |
string| Attribute / Property | Value |
|---|---|
class | 'qui-tooltip__content' |
data-part | 'content' |
data-placement | | 'bottom' |
data-scope | 'tooltip' |
data-state | | 'open' |
hidden | boolean |
class'qui-tooltip__content'data-part'content'data-placement| 'bottom'
| 'bottom-end'
| 'bottom-start'
| 'left'
| 'left-end'
| 'left-start'
| 'right'
| 'right-end'
| 'right-start'
| 'top'
| 'top-end'
| 'top-start'
data-scope'tooltip'data-state| 'open'
| 'closed'
hiddenbooleanq-tooltip-arrow
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be generated for accessibility.) | string |
string| Attribute / Property | Value |
|---|---|
class | 'qui-tooltip__arrow' |
data-part | 'arrow' |
data-scope | 'tooltip' |
style |
q-tooltip-arrow-tip
| Attribute / Property | Value |
|---|---|
class | 'qui-tooltip__arrow-tip' |
data-part | 'arrowTip' |
data-scope | 'tooltip' |
style |
class'qui-tooltip__arrow-tip'data-part'arrowTip'data-scope'tooltip'styleTooltipPositioningOptions
| Prop | Type | Default |
|---|---|---|
The minimum padding between the arrow and the floating element's corner. | number | 10 |
The overflow boundary of the reference element | () => | |
Whether the popover should fit the viewport. | boolean | |
Whether to flip the placement when the floating element overflows the boundary. | | boolean | true |
Function that returns the anchor rect | ( | |
The main axis offset or gap between the reference and floating element | number | 8 |
Whether the popover should be hidden when the reference element is detached | boolean | |
Options to activate auto-update listeners | | boolean | true |
The offset of the floating element | { | |
Function called when the placement is computed | ( | |
Function called when the floating element is positioned or not | (data: { | |
The virtual padding around the viewport edges to check for overflow | number | |
Whether the floating element can overlap the reference element | boolean | false |
The initial placement of the floating element | | 'bottom' | 'top' |
Whether to make the floating element same width as the reference element | boolean | |
The secondary axis offset or gap between the reference and floating elements | number | |
Whether the popover should slide when it overflows. | boolean | |
The strategy to use for positioning | | 'absolute' | 'absolute' |
A callback that will be called when the popover needs to calculate its
position. | (data: { |
number() =>
| '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>