Skip to main content
cngx-src documentation

CngxButtonToggle

button[cngxButtonToggle]

DirectivePrimaryv0.1.0WCAG AA

projects/common/interactive/button-toggle/button-toggle.directive.ts

Import#

import { CngxButtonToggle } from '@cngx/common/interactive'

Description#

Button-toggle leaf. Restricted to <button> elements via the button[cngxButtonToggle] selector so consumers cannot accidentally apply the directive to a <div> and lose the native button semantics (Space + Enter activation, focus ring, disabled propagation to the form-submission engine).

Injects EITHER CNGX_BUTTON_TOGGLE_GROUP (single, radiogroup) OR CNGX_BUTTON_MULTI_TOGGLE_GROUP (multi, toolbar) - never the concrete group class - both with { optional: true }.

Exactly one parent must be present; the constructor throws when both or neither resolve. The resolved parent is stored as a discriminated union ({ mode: 'single' | 'multi'; group }) so every downstream branch narrows without non-null assertions.

The present token determines:

  • the ARIA pattern (aria-checked for single, aria-selected for multi), bound on the host AT INJECTION TIME,
  • the activation contract (single: group.value.set(...); multi: group.toggle(...)),
  • whether (focus) consumes the parent's pending arrow-select flag (single only - multi follows toolbar APG, which does not auto-select on arrow nav).

Mode is static per atom instance. There is no runtime [selectionMode] flag. Per feedback_select_family_split, single + multi are two distinct groups reusing this leaf; the decision is made by the consumer at template-authoring time.

Composes CngxRovingItem as a host directive with input forwarding ('cngxRovingItemDisabled: disabled') so arrow-key navigation in either parent group skips per-toggle-disabled leaves automatically.

Group-disabled cascade vs roving. Thev group-level [disabled] cascade short-circuits handleSelect / handleKeydown / handleFocus (see toggleDisabled computed), but does NOT propagate into CngxRovingItem.disabled. Angular forbids re-binding a host-directive's read-only InputSignal input from a wrapping host. As a result, a fully-disabled group lets visual focus transit through its toggles via Arrow keys; every selection pathway short-circuits silently. Re-evaluation is gated on CngxRovingItem.disabled becoming a writable surface in @cngx/common/a11y.

Disabled "why" via aria-describedby. Pillar 2 requires disabled state to communicate a reason. The leaf exposes [describedBy] (alias cngxDescribedBy). A reactive input that binds to the host's aria-describedby. Consumers render the description element themselves and pass its id; the directive does not own a sr-only span (it is a @Directive on a native <button> - no template to inject siblings into). Native <button aria-describedby> semantics apply.

https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/multi-group/basic-multi-filter-toolbar https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/multi-group/disabled-group-cascade-vs-per-toggle https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/group/basic-view-switcher https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/group/disabled-group-cascade-vs-per-toggle https://cngxjs.github.io/cngx/examples/#/common/interactive/button-toggle/group/vertical-orientation

Metadata#

Host#

Index#

Inputs#

describedBy#string | null
input()
default null, { alias: 'cngxDescribedBy', }
disabled#boolean
input()
default false
value#T
input()Required

Methods#

handleFocus#void
Protected
handleKeydown#void
Protected
handleKeydown(event: Event)
@parameventEvent
handleSelect#void
Protected

HostBindings#

BindingExpression
[attr.aria-checked]ariaChecked()
[attr.aria-selected]ariaSelected()
[attr.aria-disabled]toggleDisabled() ? "true" : null
[attr.aria-describedby]describedBy()
[attr.disabled]toggleDisabled() ? "" : null
[class.cngx-button-toggle--checked]toggleChecked()
[class.cngx-button-toggle--disabled]toggleDisabled()

HostListeners#

EventHandler
(focus)focus()
(click)click()
(keydown.space)keydown.space()
(keydown.enter)keydown.enter()