Info

The <smart-info> component provides a small info icon that toggles a popover with a short tooltip text. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. It renders a default InfoStandardComponent which can be replaced via INFO_STANDARD_COMPONENT_TOKEN.


Usage

<span>Email address</span>
<smart-info [options]="options" />

Components

InfoComponent (<smart-info>)

Main wrapper component. Renders InfoStandardComponent by default. When INFO_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.

InfoStandardComponent (<smart-info-standard>)

Default concrete implementation. Renders:

  • a small SVG info icon (button),
  • a popover shown while isOpen() is true,
  • Tailwind-styled container with smart:dark:* dark-mode classes,
  • a document:click listener that closes the popover on outside click.

InfoPresetComponent (<smart-info-preset>)

Styled variation that extends InfoBaseComponent and is a drop-in replacement for InfoStandardComponent. Register it via INFO_STANDARD_COMPONENT_TOKEN to restyle every <smart-info>, or use the <smart-info-preset> selector directly. It renders the Preline Tooltip look: a circular icon toggle plus a small tooltip bubble. Visibility is driven by Angular hover/focus ((mouseenter)/(mouseleave) + (focus)/(blur)) using the inherited isOpen signal and @if — Preline's JS plugin is not installed. It keeps the translated Preline visual classes, exposes a placement input ('top' (default) | 'bottom' | 'left' | 'right') for tooltip position, and renders role="tooltip" with aria-describedby wiring on the toggle. All classes are smart:-prefixed Tailwind with explicit dark: variants. The class recipes live in preset/preset-classes.util.ts (getInfoContainerClasses, getInfoToggleClasses, getInfoTooltipClasses).

Because InfoComponent renders injected components via NgComponentOutlet (which passes inputs by canonical name), InfoPresetComponent overrides cssClass as input<string>('') without the class alias. Bind it as [cssClass] when using the <smart-info-preset> selector directly, or just pass class on <smart-info> (the wrapper forwards it). The placement input is only available on the preset selector directly — the <smart-info> wrapper forwards only options and cssClass, so through the token tooltips default to 'top'.

InfoBaseComponent (abstract)

Abstract base directive for extending custom info implementations. Exposes options, cssClass, isOpen signal, and toggle()/open()/close() methods.

API

Inputs

InputTypeDefaultDescription
optionsInputSignal<IInfoOptions>requiredInfo configuration
classInputSignal<string>''External CSS classes (alias for cssClass)

IInfoOptions

The text value is rendered inside the popover through TranslatePipe, so it may be a translation key.

INFO_STANDARD_COMPONENT_TOKEN

InjectionToken that allows replacing the default InfoStandardComponent with a custom implementation. Provide a Type<InfoBaseComponent> to override.

Extending the base class

import {
  ChangeDetectionStrategy,
  Component,
  computed,
  ElementRef,
  HostListener,
  inject,
  input,
  ViewEncapsulation,
} from '@angular/core';
import { TranslatePipe } from '@ngx-translate/core';

import {
  INFO_STANDARD_COMPONENT_TOKEN,
  IInfoOptions,
  InfoBaseComponent,
  InfoComponent,
} from '@smartsoft001/angular';

@Component({
  selector: 'docs-custom-info',
  template: `
    <div [class]="containerClasses()">
      <button
        type="button"
        class="docs-info__trigger"
        aria-label="More information"
        [attr.aria-expanded]="isOpen()"
        (click)="toggle(); $event.stopPropagation()"
      >
        ?
      </button>
      @if (isOpen()) {
        <div class="docs-info__popover" role="tooltip">
          {{ options().text | translate }}
        </div>
      }
    </div>
  `,
  imports: [TranslatePipe],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomInfoComponent extends InfoBaseComponent {
  private readonly elementRef = inject(ElementRef);

  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class'
  // alias, so an info component registered through the token declares it
  // explicitly.
  override cssClass = input<string>('');

  containerClasses = computed(() => {
    const classes = ['docs-info'];
    const extra = this.cssClass();
    if (extra) classes.push(extra);
    return classes.join(' ');
  });

  // InfoBaseComponent owns isOpen/toggle/open/close but does not close the
  // popover on an outside click - a custom implementation adds that itself.
  @HostListener('document:click', ['$event'])
  onDocumentClick(event: MouseEvent): void {
    if (!this.elementRef.nativeElement.contains(event.target)) {
      this.close();
    }
  }
}

@Component({
  selector: 'docs-info-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [InfoComponent],
  // The token swaps the standard info popover for the custom one everywhere
  // below this component, so consumers keep writing `<smart-info>`.
  providers: [
    { provide: INFO_STANDARD_COMPONENT_TOKEN, useValue: CustomInfoComponent },
  ],
  template: `
    <label class="docs-info__label">Email address</label>
    <smart-info [options]="options" />
  `,
})
export class InfoCustomExampleComponent {
  options: IInfoOptions = { text: 'Enter your primary email address.' };
}

Source

The component lives in packages/shared/angular/src/lib/components/info and is documented for Claude Code by the angular-components-info skill.