src/ai-label/ai-label.component.ts
AI-branded toggletip control (cds-ai-label). Renders an "AI" badge that opens a
popover; projected content and optional actions use ng-content.
Get started with importing the module:
Example :import { AILabelModule } from 'carbon-components-angular';<cds-ai-label size="md">
<div>
<p>AI Explained</p>
<h2>84%</h2>
<p>Confidence score</p>
</div>
<div cdsAILabelActions>
<button cdsButton="ghost" size="sm">View details</button>
</div>
</cds-ai-label>[cdsAILabelActions] adds cds--toggletip-actions and cds--ai-label-actions
to its host. Place it as a sibling of the body content, both direct
children of <cds-ai-label>. [cdsAILabelContent] is an optional marker; the
cds--ai-label-content / cds--toggletip-content classes come from this
component’s template.
AfterViewInit
OnChanges
OnDestroy
| changeDetection | ChangeDetectionStrategy.OnPush |
| selector | cds-ai-label, ibm-ai-label |
| template | |
Properties |
|
Methods |
Inputs |
Outputs |
HostBindings |
HostListeners |
Accessors |
constructor(elementRef: ElementRef)
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:233
|
||||||
|
Parameters :
|
| aiText | |
Type : string
|
|
Default value : "AI"
|
|
|
Defined in src/ai-label/ai-label.component.ts:184
|
|
|
Text inside the AI badge. |
|
| align | |
Type : DeprecatedAILabelAlign | Placement
|
|
|
Defined in src/ai-label/ai-label.component.ts:133
|
|
|
Set alignment of popover. Deprecated Carbon alignments are mapped to floating-ui placements. |
|
| ariaLabel | |
Type : string
|
|
Default value : "Show information"
|
|
|
Defined in src/ai-label/ai-label.component.ts:223
|
|
|
|
|
| autoAlign | |
Type : boolean
|
|
Default value : false
|
|
|
Defined in src/ai-label/ai-label.component.ts:153
|
|
|
Experimental: Use floating-ui to position the tooltip. |
|
| caret | |
Type : boolean
|
|
Default value : true
|
|
|
Defined in src/ai-label/ai-label.component.ts:138
|
|
|
Show caret at the alignment position. |
|
| dropShadow | |
Type : boolean
|
|
Default value : false
|
|
|
Defined in src/ai-label/ai-label.component.ts:143
|
|
|
Enable drop shadow around the popover container. |
|
| highContrast | |
Type : boolean
|
|
Default value : true
|
|
|
Defined in src/ai-label/ai-label.component.ts:148
|
|
|
Enable high contrast for popover container. |
|
| id | |
Type : string
|
|
Default value : `ai-label-${AILabelComponent.labelCounter++}`
|
|
|
Defined in src/ai-label/ai-label.component.ts:179
|
|
|
Unique id used to associate the trigger button with the popover panel
via |
|
| isOpen | |
Type : boolean
|
|
Default value : false
|
|
|
Defined in src/ai-label/ai-label.component.ts:158
|
|
|
Whether the callout is open. |
|
| kind | |
Type : "default" | "inline"
|
|
Default value : "default"
|
|
|
Defined in src/ai-label/ai-label.component.ts:194
|
|
|
Set badge shape: |
|
| revertActive | |
Type : boolean
|
|
Default value : false
|
|
|
Defined in src/ai-label/ai-label.component.ts:213
|
|
|
When |
|
| revertLabel | |
Type : string
|
|
Default value : "Revert to AI input"
|
|
|
Defined in src/ai-label/ai-label.component.ts:218
|
|
|
Accessible label / tooltip for the revert icon button. |
|
| size | |
Type : "mini" | "2xs" | "xs" | "sm" | "md" | "lg" | "xl"
|
|
Default value : "xs"
|
|
|
Defined in src/ai-label/ai-label.component.ts:199
|
|
|
Set badge size |
|
| textLabel | |
Type : string
|
|
|
Defined in src/ai-label/ai-label.component.ts:189
|
|
|
Extra text beside the badge when |
|
| isOpenChange | |
Type : EventEmitter
|
|
|
Defined in src/ai-label/ai-label.component.ts:173
|
|
|
Emits when |
|
| onClose | |
Type : EventEmitter
|
|
|
Defined in src/ai-label/ai-label.component.ts:163
|
|
|
Emits when the callout is closed. |
|
| onOpen | |
Type : EventEmitter
|
|
|
Defined in src/ai-label/ai-label.component.ts:168
|
|
|
Emits when the callout is opened. |
|
| revertClick | |
Type : EventEmitter
|
|
|
Defined in src/ai-label/ai-label.component.ts:228
|
|
|
Emitted when the revert icon is clicked. |
|
| class.cds--ai-label |
Type : boolean
|
Default value : true
|
|
Defined in src/ai-label/ai-label.component.ts:124
|
| class.cds--ai-label--revert |
Type : boolean
|
|
Defined in src/ai-label/ai-label.component.ts:125
|
| keyup |
Arguments : '$event'
|
keyup(event: KeyboardEvent)
|
|
Defined in src/ai-label/ai-label.component.ts:293
|
| Private handleOutsideClick | ||||||
handleOutsideClick(event: MouseEvent)
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:304
|
||||||
|
Dismisses the popover when a click lands outside the host element.
Parameters :
Returns :
void
|
| hostkeys | ||||||
hostkeys(event: KeyboardEvent)
|
||||||
Decorators :
@HostListener('keyup', ['$event'])
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:293
|
||||||
|
Parameters :
Returns :
void
|
| ngAfterViewInit |
ngAfterViewInit()
|
|
Defined in src/ai-label/ai-label.component.ts:261
|
|
Returns :
void
|
| ngOnChanges | ||||||
ngOnChanges(changes: SimpleChanges)
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:267
|
||||||
|
Parameters :
Returns :
void
|
| ngOnDestroy |
ngOnDestroy()
|
|
Defined in src/ai-label/ai-label.component.ts:274
|
|
Returns :
void
|
| onPopoverIsOpenChange | ||||||
onPopoverIsOpenChange(open: boolean)
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:237
|
||||||
|
Parameters :
Returns :
void
|
| onRevertButtonClick | ||||||
onRevertButtonClick(event: MouseEvent)
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:288
|
||||||
|
Parameters :
Returns :
void
|
| onTriggerClick | ||||||
onTriggerClick(event: MouseEvent)
|
||||||
|
Defined in src/ai-label/ai-label.component.ts:278
|
||||||
|
Parameters :
Returns :
void
|
| aiLabelClass |
Default value : true
|
Decorators :
@HostBinding('class.cds--ai-label')
|
|
Defined in src/ai-label/ai-label.component.ts:124
|
| Private aiLabelPopover |
Type : AILabelPopoverDirective
|
Decorators :
@ViewChild('aiLabelPopoverHost', {read: AILabelPopoverDirective})
|
|
Defined in src/ai-label/ai-label.component.ts:231
|
| Private Readonly documentClick |
Default value : this.handleOutsideClick.bind(this)
|
|
Defined in src/ai-label/ai-label.component.ts:233
|
| Static labelCounter |
Type : number
|
Default value : 0
|
|
Defined in src/ai-label/ai-label.component.ts:122
|
| revertClass |
getrevertClass()
|
|
Defined in src/ai-label/ai-label.component.ts:125
|
| alignmentAxisOffset |
getalignmentAxisOffset()
|
|
Defined in src/ai-label/ai-label.component.ts:205
|
|
Horizontal shift along the alignment axis when
Returns :
number
|
| triggerClasses |
gettriggerClasses()
|
|
Defined in src/ai-label/ai-label.component.ts:242
|
| computedAriaLabel |
getcomputedAriaLabel()
|
|
Defined in src/ai-label/ai-label.component.ts:256
|
|
Trigger
Returns :
string
|
import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
ElementRef,
EventEmitter,
HostBinding,
HostListener,
Input,
OnChanges,
OnDestroy,
Output,
SimpleChanges,
ViewChild
} from "@angular/core";
import { Placement } from "@floating-ui/dom";
import { AILabelPopoverDirective } from "./ai-label-popover.directive";
/**
* @deprecated alignments — use `Placement` names
*/
type DeprecatedAILabelAlign =
| "top-left"
| "top-right"
| "bottom-left"
| "bottom-right"
| "left-bottom"
| "left-top"
| "right-bottom"
| "right-top";
/**
* AI-branded toggletip control (`cds-ai-label`). Renders an "AI" badge that opens a
* popover; projected content and optional actions use `ng-content`.
*
* Get started with importing the module:
*
* ```typescript
* import { AILabelModule } from 'carbon-components-angular';
* ```
*
* ```html
* <cds-ai-label size="md">
* <div>
* <p>AI Explained</p>
* <h2>84%</h2>
* <p>Confidence score</p>
* </div>
* <div cdsAILabelActions>
* <button cdsButton="ghost" size="sm">View details</button>
* </div>
* </cds-ai-label>
* ```
*
* `[cdsAILabelActions]` adds `cds--toggletip-actions` and `cds--ai-label-actions`
* to its host. Place it as a **sibling** of the body content, both direct
* children of `<cds-ai-label>`. `[cdsAILabelContent]` is an optional marker; the
* `cds--ai-label-content` / `cds--toggletip-content` classes come from this
* component’s template.
*
* [See demo](../../?path=/story/components-ai-label--default)
*/
@Component({
selector: "cds-ai-label, ibm-ai-label",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<ng-container *ngIf="!revertActive">
<span
#aiLabelPopoverHost
cdsAILabelPopover
class="cds--toggletip"
[isOpen]="isOpen"
(isOpenChange)="onPopoverIsOpenChange($event)"
(onOpen)="onOpen.emit($event)"
(onClose)="onClose.emit($event)"
[align]="align"
[caret]="caret"
[dropShadow]="dropShadow"
[highContrast]="highContrast"
[autoAlign]="autoAlign"
[alignmentAxisOffset]="alignmentAxisOffset">
<button
type="button"
[attr.aria-label]="computedAriaLabel"
[attr.aria-expanded]="isOpen"
[attr.aria-controls]="id"
[ngClass]="triggerClasses"
(click)="onTriggerClick($event)">
<span class="cds--ai-label__text">{{aiText}}</span>
<span *ngIf="kind === 'inline' && textLabel" class="cds--ai-label__additional-text">{{textLabel}}</span>
</button>
<span
[id]="id"
class="cds--popover"
aria-live="polite">
<span class="cds--popover-content cds--ai-label-content">
<div class="cds--toggletip-content">
<ng-content></ng-content>
</div>
<span *ngIf="autoAlign" class="cds--popover-caret cds--popover--auto-align"></span>
</span>
<span *ngIf="!autoAlign" class="cds--popover-caret"></span>
</span>
</span>
</ng-container>
<cds-icon-button
*ngIf="revertActive"
kind="ghost"
size="sm"
[description]="revertLabel"
[autoAlign]="autoAlign"
[buttonAttributes]="{ 'aria-label': revertLabel }"
(click)="onRevertButtonClick($event)">
<svg cdsIcon="undo" size="16"></svg>
</cds-icon-button>
`
})
export class AILabelComponent implements AfterViewInit, OnChanges, OnDestroy {
static labelCounter = 0;
@HostBinding("class.cds--ai-label") aiLabelClass = true;
@HostBinding("class.cds--ai-label--revert") get revertClass() {
return this.revertActive;
}
/**
* Set alignment of popover. Deprecated Carbon alignments are mapped to
* floating-ui placements.
*/
@Input() align: DeprecatedAILabelAlign | Placement;
/**
* Show caret at the alignment position.
*/
@Input() caret = true;
/**
* Enable drop shadow around the popover container.
*/
@Input() dropShadow = false;
/**
* Enable high contrast for popover container.
*/
@Input() highContrast = true;
/**
* **Experimental**: Use floating-ui to position the tooltip.
*/
@Input() autoAlign = false;
/**
* Whether the callout is open.
*/
@Input() isOpen = false;
/**
* Emits when the callout is closed.
*/
@Output() onClose = new EventEmitter<Event>();
/**
* Emits when the callout is opened.
*/
@Output() onOpen = new EventEmitter<Event>();
/**
* Emits when `isOpen` changes (two-way binding).
*/
@Output() isOpenChange = new EventEmitter<boolean>();
/**
* Unique id used to associate the trigger button with the popover panel
* via `aria-controls` / `id`.
*/
@Input() id = `ai-label-${AILabelComponent.labelCounter++}`;
/**
* Text inside the AI badge.
*/
@Input() aiText = "AI";
/**
* Extra text beside the badge when `kind` is `"inline"`.
*/
@Input() textLabel: string;
/**
* Set badge shape: `"default"` (circular) or `"inline"` (pill, optional `textLabel`).
*/
@Input() kind: "default" | "inline" = "default";
/**
* Set badge size
*/
@Input() size: "mini" | "2xs" | "xs" | "sm" | "md" | "lg" | "xl" = "xs";
/**
* Horizontal shift along the alignment axis when `autoAlign` is on, matching
* React `AILabel` (`alignmentAxisOffset={isSmallIcon ? -24 : 0}` on `Toggletip`).
*/
get alignmentAxisOffset(): number {
return ["mini", "2xs", "xs"].includes(this.size) ? -24 : 0;
}
/**
* When `true`, shows the revert icon instead of the badge (AI-generated value
* is active and can be reverted).
*/
@Input() revertActive = false;
/**
* Accessible label / tooltip for the revert icon button.
*/
@Input() revertLabel = "Revert to AI input";
/**
* `aria-label` for the AI badge trigger (combined with `aiText` in `computedAriaLabel`).
*/
@Input() ariaLabel = "Show information";
/**
* Emitted when the revert icon is clicked.
*/
@Output() revertClick = new EventEmitter<MouseEvent>();
@ViewChild("aiLabelPopoverHost", { read: AILabelPopoverDirective })
private aiLabelPopover: AILabelPopoverDirective;
private readonly documentClick = this.handleOutsideClick.bind(this);
constructor(private elementRef: ElementRef) {}
onPopoverIsOpenChange(open: boolean): void {
this.isOpen = open;
this.isOpenChange.emit(open);
}
get triggerClasses(): Record<string, boolean> {
return {
"cds--toggletip-button": true,
"cds--ai-label__button": true,
[`cds--ai-label__button--${this.size}`]: true,
[`cds--ai-label__button--${this.kind}`]: true,
"cds--ai-label__button--inline-with-content": this.kind === "inline" && !!this.textLabel
};
}
/**
* Trigger `aria-label`: `"${aiText} ${ariaLabel}"`, or
* `"${aiText} ${textLabel}"` when `kind` is `"inline"` and `textLabel` is set.
*/
get computedAriaLabel(): string {
const suffix = (this.kind === "inline" && this.textLabel) ? this.textLabel : this.ariaLabel;
return `${this.aiText} ${suffix}`;
}
ngAfterViewInit(): void {
if (this.isOpen) {
document.addEventListener("click", this.documentClick);
}
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.revertActive && !changes.revertActive.firstChange && changes.revertActive.currentValue) {
this.isOpen = false;
document.removeEventListener("click", this.documentClick);
}
}
ngOnDestroy(): void {
document.removeEventListener("click", this.documentClick);
}
onTriggerClick(event: MouseEvent): void {
const opening = !this.isOpen;
if (opening) {
document.addEventListener("click", this.documentClick);
} else {
document.removeEventListener("click", this.documentClick);
}
this.aiLabelPopover?.handleChange(opening, event);
}
onRevertButtonClick(event: MouseEvent): void {
this.revertClick.emit(event);
}
@HostListener("keyup", ["$event"])
hostkeys(event: KeyboardEvent): void {
if (this.isOpen && event.key === "Escape") {
event.stopPropagation();
document.removeEventListener("click", this.documentClick);
this.aiLabelPopover?.handleChange(false, event);
}
}
/**
* Dismisses the popover when a click lands outside the host element.
*/
private handleOutsideClick(event: MouseEvent): void {
if (!this.elementRef.nativeElement.contains(event.target as Node)) {
this.aiLabelPopover?.handleChange(false, event);
document.removeEventListener("click", this.documentClick);
}
}
}