CngxTabOverflow
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.
Metadata#
Host#
Dependencies#
CNGX_TAB_PANEL_HOSTinject()panelHostRelationships
Index#
Derived State
HostBindings
Instance Properties#
unknowncreateTabOverflowTemplateBindings({
triggerSlot: this.triggerSlot,
itemSlot: this.itemSlot,
config: this.tabsConfig,
hiddenCount: this.hiddenCount,
hiddenTabs: this.hiddenTabs,
pickTab: (tab) => this.pickTab(tab),
})Methods#
handleAdActivated(value: unknown)AD (activated) - Enter / Space on a highlighted option picks.
unknownhandleTriggerKeydown(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.
KeyboardEventHostBindings#
| Binding | Expression |
|---|---|
[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-ringoutline
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
Layout
State / Hover
Surface
<color>transparentBackground of the overflow wrapper. Falls back through
--cngx-tab-strip-bg.
*1px solid currentColorBorder shorthand of the popover panel.
Layout
*16remMaximum block size of the popover panel before vertical scrolling.
State / Hover
<color>oklch(0 0 0 / 0.05)Background of the item hover state.
State / Active
<color>oklch(0 0 0 / 0.08)Background of the AD-active item - APG combobox pattern requires visible indication of the highlighted option.
*2px solid currentColorOutline shorthand applied to the AD-active item.
<length>-2pxOutline 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 };