Skip to main content
cngx-src documentation

CngxTabOverflow

ComponentPrimaryOnPushNo encapsulationv0.1.0WCAG AA

projects/ui/tabs/tab-overflow.component.ts

Import#

import { CngxTabOverflow } from '@cngx/ui/tabs'

Description#

Opt-in overflow indicator for <cngx-tab-group>. Surfaces clipped tab buttons through a "More" CngxPopover dropdown. Talks to the organism only via CNGX_TAB_PANEL_HOST.

Visibility is tracked through a native IntersectionObserver rooted on the parent strip - CngxIntersectionObserver doesn't fit (single-element scope, can't reach sibling viewChildren). threshold: 0 - only fully-clipped tabs surface. Combined with the organism's scrollIntoView effect this forms the self-healing loop: pick hidden tab -> strip scrolls -> IO fires -> hiddenTabs self-trims.

https://cngxjs.github.io/cngx/examples/#/ui/tabs/tab-overflow/8-tabs-in-a-narrow-container https://cngxjs.github.io/cngx/examples/#/ui/tabs/tab-overflow/all-skins-horizontal https://cngxjs.github.io/cngx/examples/#/ui/tabs/tab-overflow/all-skins-vertical

Metadata#

Host#

Dependencies#

CNGX_TAB_PANEL_HOSTinject()panelHost

Relationships

Index#

Instance Properties#

adItemId#unknown
ProtectedReadonly
tabOverflowOptionId
i18n#unknown
ProtectedReadonly
injectTabsI18n()
popover#unknown
ProtectedReadonly
viewChild.required(CngxPopover)
templates#unknown
ProtectedReadonly
createTabOverflowTemplateBindings({
  triggerSlot: this.triggerSlot,
  itemSlot: this.itemSlot,
  config: this.tabsConfig,
  hiddenCount: this.hiddenCount,
  hiddenTabs: this.hiddenTabs,
  pickTab: (tab) => this.pickTab(tab),
})

Methods#

handleAdActivated#void
Protected
handleAdActivated(value: unknown)

AD (activated) - Enter / Space on a highlighted option picks.

@paramvalueunknown
handleTriggerKeydown#void
Protected
handleTriggerKeydown(event: KeyboardEvent)

Auto-opens popover on nav-key when collapsed. AD's host listener fires first, so activeIndex is already set when this runs. No-op while open - AD owns navigation.

@parameventKeyboardEvent
pickTab#void
Protected
pickTab(tab: CngxTabHandle)
@paramtabCngxTabHandle

HostBindings#

BindingExpression
[class.cngx-tab-overflow]true

Thematic skin for <cngx-tab-overflow> - the trailing-edge popover surface that lists clipped tabs. Structural rules (flex / grid, sr-only) come from tabs-base.css in @cngx/common/tabs via the parent organism's styleUrls. This file owns trigger- button shape, popover panel layout, and per-row affordances. The host carries .cngx-tab-overflow and renders __trigger + __panel-surface + __list + __item children.

State modifiers

On .cngx-tab-overflow__trigger:

  • [hidden] - collapsed when no tabs are clipped
  • :focus-visible - shared --cngx-tab-focus-ring outline

On .cngx-tab-overflow__item:

  • :hover - tints via --cngx-tab-overflow-item-hover-bg
  • --active - APG combobox highlighted-option signal; tints background and draws an inset outline ring
  • [aria-disabled="true"] - cursor + opacity dim via --cngx-tab-disabled-opacity

Slots

  • .cngx-tab-overflow__trigger - chevron-style overflow button (consumes shared --cngx-tab-gap / --cngx-tab-padding / --cngx-tab-color)
  • .cngx-tab-overflow__panel-surface - scrollable popover panel with min-width / max-height caps
  • .cngx-tab-overflow__list - unstyled <ul> column flex
  • .cngx-tab-overflow__item - per-row entry with hover / active states

Inheritance

  • --cngx-tab-overflow-bg -> --cngx-tab-strip-bg
  • --cngx-tab-overflow-item-active-bg -> --cngx-tab-overflow- item-hover-bg

Index#

Surface

--cngx-tab-overflow-bg#<color>
Default value transparent

Background of the overflow wrapper. Falls back through --cngx-tab-strip-bg.

--cngx-tab-overflow-button-bg#<color>
Default value transparent

Background of the trigger button.

--cngx-tab-overflow-popover-bg#<color>
Default value oklch(1 0 0)

Background of the popover panel.

--cngx-tab-overflow-popover-border#*
Default value 1px solid currentColor

Border shorthand of the popover panel.

Layout

--cngx-tab-overflow-button-radius#*
Default value 0.25rem

Corner radius of the trigger button.

--cngx-tab-overflow-popover-min-width#*
Default value 12rem

Minimum inline size of the popover panel.

--cngx-tab-overflow-popover-max-height#*
Default value 16rem

Maximum block size of the popover panel before vertical scrolling.

--cngx-tab-overflow-popover-radius#*
Default value 0.25rem

Corner radius of the popover panel.

--cngx-tab-overflow-popover-padding#*
Default value 0.25rem 0

Padding shorthand of the popover panel.

--cngx-tab-overflow-item-padding#*
Default value 0.5rem 0.75rem

Padding shorthand of each list item.

State / Hover

--cngx-tab-overflow-item-hover-bg#<color>
Default value oklch(0 0 0 / 0.05)

Background of the item hover state.

State / Active

--cngx-tab-overflow-item-active-bg#<color>
Default value oklch(0 0 0 / 0.08)

Background of the AD-active item - APG combobox pattern requires visible indication of the highlighted option.

--cngx-tab-overflow-item-active-outline#*
Default value 2px solid currentColor

Outline shorthand applied to the AD-active item.

--cngx-tab-overflow-item-active-outline-offset#<length>
Default value -2px

Outline offset of the AD-active item.

Overflow showcase

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { MatTabsModule } from '@angular/material/tabs';

import { CngxMatTabs } from '@cngx/ui/mat-tabs';

/**
 * Smart overflow under viewport constraint.
 *
 * Adds `cngxMatTabs` to a vanilla `<mat-tab-group>`. The directive
 * programmatically mounts a `<cngx-tab-overflow>` "More" button as a
 * flex sibling of `.mat-mdc-tab-label-container` inside `.mat-mdc-tab-header`.
 * An `IntersectionObserver` rooted on the tab strip re-derives the hidden
 * set whenever the container width changes. Clicking the More button opens
 * a popover with all clipped tabs; selecting one routes through
 * `presenter.selectById(...)` so the cngx commit-action lifecycle stays
 * coherent. Resize the preview pane to watch the button engage and
 * disengage as the strip width crosses the overflow threshold.
 */
@Component({
  selector: 'app-root',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [MatTabsModule, CngxMatTabs],
  template: `
    <p style="margin: 0 0 12px; opacity: 0.8; font-size: 0.875rem">
      Ten tabs inside a 520px container. <code>cngxMatTabs</code> auto-mounts
      a "More" popover for the clipped tabs. Resize the preview to see the
      threshold flip.
    </p>
    <div style="max-width: 520px; border: 1px dashed #ccc; padding: 8px">
      <mat-tab-group
        cngxMatTabs
        [(activeIndex)]="active"
        aria-label="Overflow showcase"
      >
        <mat-tab label="Profile">Profile</mat-tab>
        <mat-tab label="Account">Account</mat-tab>
        <mat-tab label="Notifications">Notifications</mat-tab>
        <mat-tab label="Privacy">Privacy</mat-tab>
        <mat-tab label="Billing">Billing</mat-tab>
        <mat-tab label="Connections">Connections</mat-tab>
        <mat-tab label="Devices">Devices</mat-tab>
        <mat-tab label="Sessions">Sessions</mat-tab>
        <mat-tab label="Audit">Audit</mat-tab>
        <mat-tab label="Advanced">Advanced</mat-tab>
      </mat-tab-group>
    </div>
    <p style="margin-top: 12px">Active: {{ active() }}</p>
  `,
})
export class OverflowShowcaseExample {
  protected readonly active = signal(0);
}
export { OverflowShowcaseExample as AppComponent };