Functions
Index
forms/field/form-control-adapter.ts
CngxFieldAccessoradaptFormControl(control: AbstractControl, name: string, destroyRef: DestroyRef)Adapts an Angular Reactive Forms AbstractControl (FormControl, FormGroup, FormArray)
to the CngxFieldAccessor interface expected by cngx-form-field.
This enables using cngx-form-field without Signal Forms - for teams that haven't
migrated yet or for forms that use Reactive Forms by design.
The Reactive Forms control to adapt.
A unique field name for deterministic ID generation.
A DestroyRef for automatic subscription cleanup. Required -
without it the three RxJS subscriptions on the control would leak for the
lifetime of the parent injector. Pass inject(DestroyRef) from a component
field initialiser, or wrap the call in runInInjectionContext.
A CngxFieldAccessor compatible with [field] input on cngx-form-field.
readonly emailControl = new FormControl('', [Validators.required, Validators.email]);
readonly emailField = adaptFormControl(this.emailControl, 'email', inject(DestroyRef));forms/filter-builder/filter-builder.utils.ts
FilterGroupappendAtPath(root: FilterGroup, path, child: FilterNode)Append child to the group at path. Throws when path does not resolve to a group.
filterTreeEqual(a: FilterGroup, b: FilterGroup)Structural equality between two filter trees. Intended as the equal
option on computed / linkedSignal exposing a FilterGroup, per
the cngx signal-architecture equality rule (object/array computeds
MUST pass an explicit equal fn). Identity short-circuits.
FilterNode | nullgetNodeAtPath(root: FilterGroup, path)Pure tree utilities for the filter-builder data model. Every mutator
returns a new tree; the originals are never modified. Identity is
preserved when no descendant changed - feeds the filterTreeEqual
short-circuit on computed / linkedSignal consumers (Pillar 1).
Paths are arrays of child indices. The empty path [] addresses the
root group; [2, 0] addresses the first child of the root's third
child.
FilterGroupremoveAtPath(root: FilterGroup, path)Remove the node at path. Throws on empty path - the root cannot be removed.
FilterGroupupdateAtPath(root: FilterGroup, path, updater)Replace the node at path via updater. Returns the original root when no descendant changed (identity-preserving).
ui/collection/incremental-list-config.ts
injectIncrementalListConfigOpen detail pageprovideIncrementalListConfigOpen detail pageprovideIncrementalListConfigAtOpen detail pagewithIncrementalListAriaLabelsOpen detail pagewithIncrementalListTemplatesOpen detail page
injectIncrementalListConfig Injectv0.1.0provideIncrementalListConfig Providerv0.1.0provideIncrementalListConfigAt Providerv0.1.0withIncrementalListAriaLabels Featurev0.1.0withIncrementalListTemplates Featurev0.1.0CngxIncrementalListConfigapplyFeatures(base: CngxIncrementalListConfig, features)Reduce a feature list onto a base config, merging each sub-tree in isolation.
CngxIncrementalListConfigConvenience accessor for the incremental-list configuration. Runs in
injection context; resolves through the cascade. Equivalent to
inject(CNGX_INCREMENTAL_LIST_CONFIG).
EnvironmentProvidersprovideIncrementalListConfig(...features: undefined)Application-root configuration cascade for the incremental list. Pass any
combination of with* features in bootstrapApplication's providers.
Supplied features merge with the library defaults, so consumers only declare
the keys they override.
Provider[]provideIncrementalListConfigAt(...features: undefined)Component-scoped incremental-list configuration override. Pass into a
component's or directive's viewProviders; features merge on top of the
parent config (an enclosing scope or the application root), so a region can
re-phrase its labels without disturbing the rest of the app.
CngxIncrementalListConfigFeaturewithIncrementalListAriaLabels(payload: Partial)Override any subset of the incremental-list labels. The override applies to both the visible built-in views and the live-region announcements.
provideIncrementalListConfig(
withIncrementalListAriaLabels({
empty: 'Noch nichts hier',
endReached: (total) => `Alle ${total} geladen`,
}),
);PartialCngxIncrementalListConfigFeaturewithIncrementalListTemplates(payload: Partial)Supply application-wide template-slot defaults. A per-instance projected slot still wins over this tier; it only applies where no instance slot is present.
provideIncrementalListConfig(
withIncrementalListTemplates({ empty: myBrandedEmptyTemplate }),
);Partialui/paginator/paginator-config.ts
injectPaginatorConfigOpen detail pageprovideCngxPaginatorConfigOpen detail pageprovideCngxPaginatorConfigAtOpen detail pagewithPaginatorAnnouncementsOpen detail pagewithPaginatorAriaLabelsOpen detail pagewithPaginatorPageSizeOptionsOpen detail pagewithPaginatorPageStatusFormatOpen detail pagewithPaginatorRangeFormatOpen detail pagewithPaginatorTemplatesOpen detail page
injectPaginatorConfig Injectv0.1.0provideCngxPaginatorConfig Providerv0.1.0provideCngxPaginatorConfigAt Providerv0.1.0withPaginatorAnnouncements Featurev0.1.0withPaginatorAriaLabels Featurev0.1.0withPaginatorPageSizeOptions Featurev0.1.0withPaginatorPageStatusFormat Featurev0.1.0withPaginatorRangeFormat Featurev0.1.0withPaginatorTemplates Featurev0.1.0CngxPaginatorConfigapplyFeatures(base: CngxPaginatorConfig, features)Reduce a feature list onto a base config, merging each sub-tree in isolation.
CngxPaginatorConfigConvenience accessor for the paginator configuration. Runs in injection
context; resolves through the cascade. Equivalent to
inject(CNGX_PAGINATOR_CONFIG).
EnvironmentProvidersprovideCngxPaginatorConfig(...features: undefined)Application-root configuration cascade for the paginator. Pass any
combination of with* features in bootstrapApplication's providers.
Supplied features deep-merge with the library defaults, so consumers only
declare the keys they want to override.
Provider[]provideCngxPaginatorConfigAt(...features: undefined)Component-scoped paginator configuration override. Pass into a component's
or directive's viewProviders; features merge on top of the parent config
(an enclosing scope or the application root), so a region can re-phrase its
announcements or labels without disturbing the rest of the app.
CngxPaginatorConfigFeaturewithPaginatorAnnouncements(payload: Partial)Override any subset of the paginator live-region announcement phrasing.
provideCngxPaginatorConfig(
withPaginatorAnnouncements({
pageChange: (page, total) => `Seite ${page} von ${total}`,
loading: 'Wird geladen',
updated: 'Aktualisiert',
}),
);PartialCngxPaginatorConfigFeaturewithPaginatorAriaLabels(payload: Partial)Override any subset of the paginator accessible-name strings. Per-instance
aria-label bindings still win over the cascade for the landmark name.
provideCngxPaginatorConfig(
withPaginatorAriaLabels({ next: 'Nächste Seite', previous: 'Vorige Seite' }),
);PartialCngxPaginatorConfigFeaturewithPaginatorPageSizeOptions(payload)Supply the default items-per-page choices for the cngx-pgn-page-size
dropdown app-wide. The segment renders these when no per-instance [options]
is bound; a non-empty [options] input still wins. The list replaces the
library default wholesale - it is one atomic value, not a merged sub-tree.
provideCngxPaginatorConfig(
withPaginatorPageSizeOptions([12, 24, 48]),
);CngxPaginatorConfigFeaturewithPaginatorPageStatusFormat(pageStatus)Override the page-status format. The cngx-pgn-status segment renders the
returned string verbatim, so this localises the "Page n of m" readout that
the responsive collapse reveals.
provideCngxPaginatorConfig(
withPaginatorPageStatusFormat((page, totalPages) => `Seite ${page} von ${totalPages}`),
);CngxPaginatorConfigFeaturewithPaginatorRangeFormat(range)Override the range-readout format. The cngx-pgn-range segment renders the
returned string verbatim, so this also localises the of connector.
provideCngxPaginatorConfig(
withPaginatorRangeFormat((start, end, total) => `${start}-${end} von ${total}`),
);CngxPaginatorConfigFeaturewithPaginatorTemplates(payload: Partial)Supply application-wide template-slot defaults for the paginator. The
per-instance *cngxPaginatorLoading slot still wins over this tier; it only
applies where no instance slot is projected.
provideCngxPaginatorConfig(
withPaginatorTemplates({ loading: myBrandedSpinnerTemplate }),
);Partialforms/input/input-mask.directive.ts
selectPattern(patterns, rawLength: number, customTokens?: MaskTokenMap)slotCount(pattern: string, customTokens?: MaskTokenMap)projects/utils/equality.ts
arrayEqual(a, b)Shallow positional equality for two readonly arrays. Returns true when
both arrays reference the same object, or when they have the same length
and every index holds a Object.is-equal value.
Intended as an equal arg for computed() / linkedSignal returning
readonly T[], so consumers downstream do not re-render when the
computed re-runs but produces a positionally-identical array. Two arrays
with the same values in a different order compare unequal.
setEqual(a: ReadonlySet, b: ReadonlySet)Shallow structural equality for two readonly Sets. Returns true when
both sets reference the same object, or when they have the same size and
every element of a is present in b. Element comparison uses Set.has
(SameValueZero semantics).
Intended as an equal arg for computed() / linkedSignal returning
ReadonlySet<T>, so consumers downstream do not re-render when the
computed re-runs but produces the same element set.
ReadonlySetReadonlySetselect/shared/provide-cngx-select.ts
provideCngxSelect ProviderprovideCngxSelectAt ProviderEnvironmentProviders[]provideCngxSelect(...features: undefined)App-wide entry point for the Select-family configuration surfaces.
Routes mixed features from provideSelectConfig,
provideActionSelectConfig, and provideReorderableSelectConfig to
the correct underlying provider. The three individual providers stay
exported.
bootstrapApplication(App, {
providers: [
provideCngxSelect(
// CngxSelectConfig features
withPanelWidth('trigger'),
withVirtualization({ estimateSize: 36 }),
withAriaLabels({ clearButton: 'Clear', chipRemove: 'Remove' }),
// CngxActionSelectConfig features
withFocusTrapBehavior('strict'),
withCloseOnCreate(true),
// CngxReorderableSelectConfig features
withReorderKeyboardModifier('alt'),
withReorderStripFreeze(true),
),
],
});Provider[]provideCngxSelectAt(...features: undefined)Component-scoped twin of provideCngxSelect. Returns
Provider[] because viewProviders rejects EnvironmentProviders.
core/utils/build-async-state-view.ts
CngxAsyncStatebuildAsyncStateView(sources: AsyncStateViewSources)Build a read-only CngxAsyncState<T> view from source signals.
All derived fields (isLoading, isPending, isEmpty, etc.) are computed()
from the provided sources — the result is a consistent, single-source-of-truth
state object that cannot become inconsistent.
This is the shared kernel used by all async state factories and state producers.
No injection context required — uses only computed().
ui/breadcrumb/breadcrumb-router-sync.directive.ts
CngxBreadcrumbCrumb[]buildCrumbs(router: Router, dataKey: string, iconKey: string)Walks the activated route tree from the root down the firstChild chain,
accumulating the URL and emitting one crumb per route whose data[dataKey]
is a non-empty string. A non-empty data[iconKey] string rides onto the
crumb's opaque icon (the leading icon slot renders it; deepest wins on the
componentless-collapse branch, like the label).
crumbsEqual(a, b)Positional shape equality for two crumb trails. The router source maps a
fresh crumb literal per navigation, so reference equality (Object.is)
would treat every NavigationEnd as a change; comparing label/href/icon
lets a same-shape navigation keep the previous signal reference and stops
it cascading the bar (reference_signal_architecture Equality Rule).
The router source populates label, href, and (from data[iconKey])
icon, so those three fully describe a crumb's identity here - icon must be
compared or an icon-only route-data change never propagates. When SPA-link
emission lands, its equality is defined alongside it.
chart/path/curve.ts
buildCurvePath(points, curve: CngxCurve)Build the SVG d attribute for a sequence of points.
Pixel-coordinate points to connect.
Interpolation strategy. 'linear' joins points with
straight L commands; 'monotone' uses cubic Béziers with the
monotone-X tangent rule (Fritsch-Carlson) so the curve never
overshoots between data points.
The full path data starting with M. Returns '' when
the input is empty; M x y when the input has one point.
paginator/segments/paginator-dots.component.ts
buildDots(current: number, total: number)Build the full dot sequence plus the viewport anchor. Every page gets a dot;
for large counts the dots that fall outside the centred VISIBLE window are
shrunk to small (they sit off-screen behind the viewport clip and shrink as
they glide out), and the two dots at each truncated visible edge step down
medium -> small (iOS page-control edge-shrink). The consumer translates the
track by firstVisible so navigation slides the strip instead of reshuffling
the DOM - the active dot stays centred and the row glides under it.
ui/breadcrumb/breadcrumb-siblings-router-sync.directive.ts
CngxBreadcrumbSibling[]buildSiblings(router: Router, depth: number, dataKey: string)Enumerates the sibling routes at depth in the activated route chain: the
children of that level's parent (or the root config at depth 0) whose
data[dataKey] is a non-empty string, marking the active child current.
Sibling configs come from the static route configuration, not the activated
snapshot (which only holds the one active child), so the whole set of
alternatives is visible. Duplicate hrefs are collapsed like buildCrumbs.
siblingsEqual(a, b)Positional shape equality for two sibling sets. The router source maps a
fresh sibling literal per navigation, so reference equality (Object.is)
would treat every NavigationEnd as a change; comparing
label/href/current lets a same-shape navigation keep the previous
signal reference and stops it cascading the dropdown
(reference_signal_architecture Equality Rule). Mirrors crumbsEqual.
current is compared because the same set of siblings with a different
active member is a genuine change - the aria-current marker moves.
interactive/guard/can-deactivate.ts
canDeactivateWhenClean(isDirty, message: string)Creates a functional route guard that blocks navigation when the form is dirty.
Works with Angular's CanDeactivateFn. The isDirty callback is evaluated
on each navigation attempt. When dirty, shows a confirm dialog.
Uses DOCUMENT injection for SSR safety — returns true (allow) when
no window is available.
Pair with CngxBeforeUnload for full coverage (browser close + route change):
// Route config
{
path: 'edit',
component: EditComponent,
canDeactivate: [canDeactivateWhenClean(() => inject(EditComponent).isDirty())]
}- Callback that returns
truewhen there are unsaved changes.
- Confirmation message. Default:
'You have unsaved changes. Leave anyway?'
A functional guard compatible with Angular's canDeactivate.
forms/input/input-filter.directive.ts
projects/utils/clamp.ts
clamp(value: number, min, max)Clamp a number into the inclusive range [min, max]. null or
undefined bounds read as -Infinity / +Infinity respectively, so
either side can be left open. NaN value propagates and is returned
unchanged. When the bounds invert (min > max after coercion), min
wins and the returned value is min.
interactive/slider/slider-core.ts
createSliderCore Factoryv0.1.0CngxSliderCorecreateSliderCore(options: CngxSliderCoreOptions)Pure factory for a slider's value derivation - the brain shared by
CngxSlider and each CngxSliderThumb of a range slider. It
owns no DI and no DOM: hand it the source signals, get back the clamped
value, the track fraction, the aria-valuetext string, and the four
mutation helpers the keyboard / pointer handlers call. Snapping and
bound-clamping live in CngxSliderCore.setValue, so every write
path stays valid without an effect syncing state (Pillar 1).
ui/accordion/accordion-group.component.ts
ui/data-grid-accordion/data-grid-accordion.component.ts
clampHeadingLevel(value, fallback: number)Coerce a bound value to a number (via coerceNumberProperty, falling back to
fallback) and clamp it into the ARIA heading-level range 2-6.
trackFor(track, isPrimary: boolean)Map a cell's col track intent to one CSS grid-template-columns track. Unset
(undefined) falls back to the derived default: the primary column grows
(minmax(0, 1fr)), every other column fits its content (auto). The named sizes
resolve against the registered --cngx-dga-col-sm|-md|-lg tokens, each with a rem
fallback so the track stays valid even before the token surface loads (an invalid
var() would collapse the whole grid-template-columns to one column).
audio/engine/audio-engine.ts
scaleGain(gain, scale: number)Apply a per-call [0, 1] scale to a tone's peak gain. Centralised here so
every play path (play / tone / sequence) scales identically and an
engine override sees per-element volume uniformly, rather than each caller
pre-baking its own gain.
projects/utils/array.ts
T[]coerceArray(value)Coerce a single value or an array to an array. Returns the input unchanged when already an array; wraps a scalar value in a single-element array otherwise.
core/utils/coerce.util.ts
coerceBooleanProperty(value)Coerces a value to a boolean.
Strings are truthy unless they equal 'false'.
All other falsy values return false.
ui/layout/grid.component.ts
projects/utils/tree.ts
collectDescendantValuesOpen detail pagefilterTreeOpen detail pageflattenTreeOpen detail pageisNodeVisibleOpen detail pagesortTreeOpen detail pagewalkTreeOpen detail page
T[]collectDescendantValues(node: CngxTreeNode)Collect all descendant values of a node (exclusive of the node itself), in DFS order. For cascade-select: toggling a parent flips all entries in this list atomically.
CngxTreeNode[]filterTree(nodes, predicate)Return a new tree containing only nodes whose value matches predicate
or have at least one matching descendant. Ancestors of any match are
preserved so the path is never broken; branches with zero matches are
dropped entirely.
Fresh implementation — not ported from @cngx/data-display/treetable.
FlatTreeNode[]flattenTree(nodes, idFn: IdFn, labelFn: LabelFn)Flatten a tree in DFS order. Each emitted FlatTreeNode carries
aria-level (depth + 1), aria-posinset, and aria-setsize data so the
rendering layer can bind them directly.
Root-level tree nodes.
IdFn= defaultIdFn as IdFnDerives a stable id from (value, path). Defaults to
path.join('.') — adequate for static trees; supply a key-based idFn
for data that may be re-ordered without changing identity.
LabelFn= defaultLabelFn as LabelFnDerives visible label. Defaults to String(value).
isNodeVisible(node: FlatTreeNode, expandedIds: ReadonlySet)A flat node is visible iff every ancestor in its parentIds chain is
present in expandedIds. Root nodes (empty chain) are always visible.
ReadonlySetCngxTreeNode[]sortTree(nodes, by, direction)Return a new tree where each level's siblings are sorted independently by
the by extractor. Child ordering is stable within its own level only —
the relative position of nodes across different parents is irrelevant.
Fresh implementation — not ported from @cngx/data-display/treetable.
'asc'forms/input/phone-metadata.ts
createPrefixPhoneMetadata Factoryv0.2.0providePhoneMetadata ProviderCngxPhoneMetadatacreatePrefixPhoneMetadata(prefixes: PhonePrefixMap)Builds a CngxPhoneMetadata adapter from a region keyed prefix map, so a consumer declares the prefixes that matter instead of hand-writing the region branch and the matching closure.
The adapter resolves the line type by longest matching prefix: the matcher
with the most matched leading digits wins, so a specific '0820' fixed-line
rule beats a broader '08' mobile rule. Ties resolve to mobile (it is the
decisive case auto mask alternation cares about). An unknown region or no
matching prefix returns 'unknown', keeping the length-based fallback.
It still ships no numbering data - the caller supplies every prefix - so it is
sugar over an inline adapter, not a replacement for a real metadata library
like libphonenumber-js.
providePhoneMetadata(
createPrefixPhoneMetadata({
DE: { mobile: [/^1[567]/] },
AT: { mobile: ['650', '660', '664', '676', '699'] },
}),
);providePhoneMetadata(adapter: CngxPhoneMetadata)Registers a CngxPhoneMetadata adapter for CngxPhoneInput's auto
line-type detection.
Returns a plain Provider, so it works app-wide in
ApplicationConfig.providers or scoped to a subtree via a component's
viewProviders - mirroring provideInputConfig. The nearest provider
wins for a subtree by token resolution.
// app.config.ts
providers: [providePhoneMetadata(libphonenumberAdapter)]data/recycler/range-computer.ts
data/paginate/paginate-emit.ts
connectPaginateEmit v0.1.0connectPaginateEmit(paginate: CngxPaginate, handlers: CngxPaginateEmitHandlers)Wires the CngxPaginate brain's page / page-size changes onto a host's two-way outputs with exactly-once emit semantics. Two paths feed each handler, sharing a last-emitted guard:
- a subscription forwards the brain's nav-only
pageChange/pageSizeChange- the only signal that captures a controlled-mode
setPage, because the brain'spageIndex()stays pinned to the input until the consumer feeds it back;
- the only signal that captures a controlled-mode
- an
effect()reads the effectivepageIndex()/pageSize()and covers atotal-shrink clamp the nav-only output misses.
The shared guard makes a value one path already emitted a no-op on the other, so each change emits exactly once. Seeded with the current effective values, so wiring emits no initial change.
Shared verbatim by the CngxPaginator shell and the CngxIncrementalList
organism, so the two-way emit behaviour is byte-identical. Call inside an
injection context (constructor or field initialiser) - it reads DestroyRef
and creates effects.
data/paginate/paginate-reset.directive.ts
connectPaginateResetOn v0.1.0connectPaginateResetOn(paginate: CngxPaginate, key)Wires the reset-on-change behaviour onto a CngxPaginate brain: when
key() changes (after the initial run) the paginator jumps to the first
page. The first run captures the mounting value without resetting, and an
already-first paginator emits nothing.
Shared by [cngxPaginateResetOn], the CngxPaginator shell's resetOn
input, and the CngxMatPaginator bridge's resetOn input, so the behaviour
is byte-identical across all three. Call inside an injection context
(constructor or field initialiser).
data/recycler/connect-recycler-active-descendant.ts
connectRecyclerToActiveDescendant(recycler: CngxRecycler, ad: CngxActiveDescendant)Wires a CngxRecycler to a CngxActiveDescendant in virtual mode.
When the AD directive navigates to an item whose index is not in the
recycler's rendered range, it sets pendingHighlight (via the
virtualCount path). This helper watches that signal and scrolls
the recycler to the target index; AD's own effect observes the
resulting DOM update and clears the pending state automatically
(see the pendingHighlightState.set(null) branch in
CngxActiveDescendant after a re-render brings the target into the
rendered range).
Unlike * connectRecyclerToRoving}, we don't re-focus a DOM element after the
scroll — AD doesn't move real focus, it only rebinds
aria-activedescendant. Once the target index enters the rendered
range, the [attr.aria-activedescendant] binding on the combobox
trigger resolves to the now-present option element for free.
Must be called in an injection context (typically a component constructor) on the same component that owns both the recycler and the AD directive.
data/recycler/connect-recycler-roving.ts
connectRecyclerToRoving(recycler: CngxRecycler, roving: CngxRovingTabindex)Wires a CngxRecycler to a CngxRovingTabindex in virtual mode.
When the roving tabindex navigates to an item that is not in the DOM (out of rendered range), this function scrolls the recycler to that item and focuses it after rendering.
Must be called in an injection context (constructor or field initializer)
on the component that hosts both the recycler's scroll container and the
CngxRovingTabindex directive. The injected ElementRef is used to query
[data-cngx-recycle-index] — calling from a child component would query the
wrong subtree.
interactive/accordion/accordion-keyboard-nav.ts
createAccordionKeyboardNav Factoryv0.1.0select/shared/action-host-bridge.ts
createActionHostBridge FactoryActionHostBridgecreateActionHostBridge(options: ActionHostBridgeOptions)Action-host bridge for a select-family variant: dirty signal + stable
callbacks bundle + focus-trap policy + dismiss-block signal. dirty is
the only writable slot; everything else is computed. Installs a
capture-phase Escape listener on the host element via DestroyRef.
Injection context required.
select/shared/ad-activation-dispatcher.ts
createADActivationDispatcher FactorycreateADActivationDispatcher(options: ADActivationDispatcherOptions)Wires listbox.ad.activated into the variant's commit / non-commit
callbacks. Single effect(onCleanup) - subscribes on ref resolve,
unsubscribes on teardown. Payload runs inside untracked; activations
for unknown values are dropped. Value-shape agnostic - rollback
ownership stays in the consumer.
select/shared/array-commit-handler.ts
createArrayCommitHandler FactoryArrayCommitHandlercreateArrayCommitHandler(opts: ArrayCommitHandlerOptions)Array-shape commit flow shared by CngxMultiSelect and CngxCombobox.
Owns commit-controller lifecycle, reconciliation via
sameArrayContents, togglingOption.set(null) on success,
optimistic rollback on error, live-region "removed" announce. Consumer
owns change-event payloads via the finalize callbacks.
No scalar twin: this handler is array-only by design.
data/async-state/create-async-state.ts
createAsyncState FactoryMutableAsyncStateCreate a mutation async state — for explicit user-triggered actions.
Must be called in an injection context (field initializer or constructor)
because it uses inject(DestroyRef) for cleanup.
readonly saveResident = createAsyncState<Resident>();
async handleSave(): Promise<void> {
await this.saveResident.execute(
() => this.api.save(this.form().value())
);
}audio/autoplay-gate/autoplay-gate.ts
createAutoplayGate Factoryv0.1.0CngxAutoplayGatecreateAutoplayGate(deps)Create the autoplay gate: a one-shot latch that flips armed to true on
the first pointerdown / keydown / touchstart and then removes its own
listeners. The engine consults armed() before resuming the shared
AudioContext, honouring the browser autoplay policy without a permission
prompt.
Pure factory — takes its target and destroyRef as arguments rather than
calling inject(), so it composes inside the engine's injection context yet
stays testable with a fake target. No DI token: the gate has no independent
swap consumer (severity ladder concern: over-abstraction), so it ships
token-less and the engine owns it.
chart/scales/band.ts
createBandScale Factoryinteractive/breadcrumb/breadcrumb-collapse.ts
createBreadcrumbCollapse Factoryv0.1.0CngxBreadcrumbCollapseStrategyThe default collapse rule: keep the first crumb and the last
maxVisible - 1, folding the middle into the overflow menu. Returns an empty
set when maxVisible is unset/invalid (< 1) or the trail already fits
(total <= maxVisible), so a short trail never collapses.
select/shared/chip-removal-handler.ts
createChipRemovalHandler FactoryCngxChipRemovalHandlercreateChipRemovalHandler(opts: CngxChipRemovalHandlerOptions)Builds a CngxChipRemovalHandler. Owns disabled-guard, snapshot,
filter, branch dispatch, WeakMap closure stability. Consumer owns the
commit dispatch (beginCommit), rollback snapshotting
(onBeforeCommit), and change-event emission (onSyncFinalize).
interactive/reorder/chip-strip-roving.ts
createChipStripRoving Factoryv0.1.0CngxChipStripRovingControllercreateChipStripRoving(opts: CngxChipStripRovingOptions)Plain factory for the chip-strip roving-tabindex controller shared by CngxReorderableMultiSelect today and by any future reorder-aware chip trigger (e.g. a tag-input with user-defined ordering). Extracted from the component so the same focus-state machine doesn't reappear inline in every variant.
Why not CngxRovingTabindex. That directive uses a host
(keydown) listener that doesn't check modifier keys. Co-located
with CngxReorder on the same chip-strip element it
double-fires on Ctrl+Arrow (the reorder emits, then roving also
moves focus - racy). This controller deliberately skips
modifier-pressed events so the paired reorder directive owns that
gesture.
Injection context. Must be called in an injection context
(component constructor / field init) because it installs an * effect() for the active-index clamp on count shrink.
mat-tabs/overflow/mat-tab-overflow-dom-adapter.ts
createCngxMatTabOverflowDomAdapter Factoryv0.1.0CngxTabOverflowDomAdapterMaterial variant of CngxTabOverflowDomAdapter.
resolveStripRoot walks from host up to <mat-tab-header> and
locates .mat-mdc-tab-label-container via any rendered
.mat-mdc-tab descendant — Material's IO-friendly scroll
container.
resolveTabButton indexes positionally; Material owns the DOM
and cngx handle ids never reach the button elements. Index
correlates against presenter.tabs() registration order.
Wire it via the directive's
CNGX_TAB_OVERFLOW_DOM_ADAPTER_FACTORY provider:
providers: [
{ provide: CNGX_TAB_OVERFLOW_DOM_ADAPTER_FACTORY,
useValue: createCngxMatTabOverflowDomAdapter }
]tabs/overflow/dom-adapter.ts
createCngxTabOverflowDefaultDomAdapter FactoryCngxTabOverflowDomAdapterDefault adapter for the cngx-native <cngx-tab-group> strip:
walks host.closest('.cngx-tabs__strip-wrapper') for the IO root,
then [id="${handle.id}-header"] for each button.
data/commit/commit-controller.ts
createCommitController FactoryCngxCommitControllerFactory for the commit controller.
Plain function, not a class — matches the rest of the repo
(createManualState, createAsyncState, createTransitionTracker).
See reference_api_prefix_convention.md.
select/shared/commit-controller.token.ts
createCommitController FactoryCngxCommitControllerDirect (non-DI) factory for a select-side commit controller. Wraps a fresh generic controller with the action-shape adapter; bypasses CNGX_COMMIT_CONTROLLER_FACTORY. Use CNGX_SELECT_COMMIT_CONTROLLER_FACTORY for DI-aware resolution.
select/shared/commit-error-announcer.ts
createCommitErrorAnnouncer FactorycreateCommitErrorAnnouncer(opts: CngxCommitErrorAnnouncerOptions)Default factory for the scalar commit-error announcer. Dispatches
via policy.kind: 'verbose' announces the formatted error message
at the configured severity; 'soft' calls the soft-announce hook
(CngxTypeahead removal pattern).
Override the CNGX_COMMIT_ERROR_ANNOUNCER_FACTORY token to swap in telemetry or locale-aware variants.
core/utils/controlled-source.ts
createControlledSource Factoryv0.1.0SignalcreateControlledSource(priority, fallback: Signal)Derives a controlled/uncontrolled source signal: a higher-precedence
priority source wins over a lower-precedence fallback source. Precedence
is the only contract - either argument may be an injected token signal, a
component input(), or a projected contentChild query, and which role a
given source plays flips per seam (the bar lets an injected source win over
its [items] input; the overflow lets a forwarded input() win over its
projected slot query). Collapses the repeated priority?.() ?? fallback()
seam - the "controlled source wins via computed" pattern - into one factory
beside its create* siblings.
Pure pass-through: it returns whichever underlying signal's own value, never a
fresh literal, so an equal fn is unnecessary and no downstream cascade
fires. priority absent (no source injected) or yielding undefined (an
unbound input, an unmatched query) both fall through to fallback - one
expression covers the accessor-absent seam (source?.crumbs) and the
value-undefined seam (itemTemplateInput()).
Optional higher-precedence source. undefined when no source
is present; a signal yielding undefined when the source is bound but empty.
Lower-precedence source, read when priority is absent or
yields undefined.
A computed reading priority?.() ?? fallback().
// an injected source wins over the [items] input, else the input shows
protected readonly items = createControlledSource(
this.itemsSource?.crumbs, // injected source signal, may be absent
this.itemsInput, // [items] input - the fallback
);select/shared/create-commit-handler.ts
createCreateCommitHandler FactoryCreateCommitHandlercreateCreateCommitHandler(opts: CreateCommitHandlerOptions)Plain factory for the quick-create commit flow. Shared by
CngxActionSelect and CngxActionMultiSelect. Two separate
factories vs createReorderCommitHandler because create and
reorder have different value-shape contracts (materialise new T vs
reorder existing T[]).
audio/debouncer/debouncer.ts
createDebouncer Factoryv0.1.0CngxDebouncercreateDebouncer(options?)Per-name time-window debouncer: suppresses repeated fires of the same earcon
within windowMs. A separate composed factory rather than logic buried in
the engine (Pillar 3). Plain factory, no DI token — withDebounceMs covers
configuration and there is no independent swap consumer.
windowMs accepts a getter so a caller whose window is a reactive input can
hold ONE debouncer instance for its lifetime and still track changes: the
window is resolved per shouldFire call, not captured at construction. That
keeps the (stateful) debouncer out of the signal graph — minting one inside a
computed would return a fresh object per evaluation and break the equality
rule. CngxAudioPitch is the getter consumer; the engine passes a number.
The clock is injectable (now) purely so specs are deterministic; production
defaults to Date.now.
select/shared/flat-nav-strategy.ts
createDefaultFlatNavStrategy Factorytabs/registry/directive-by-id-map.ts
createDirectiveByIdMap FactorySignalcreateDirectiveByIdMap(opts: CngxDirectiveByIdMapOptions)Build a Signal<Map<string, T>> from a Signal<readonly T[]> of
directives keyed by id(). Structural equal prevents cascade when
contentChildren re-emits an unchanged child set. Shared by
<cngx-tab-group>, <cngx-stepper>, and <cngx-mat-stepper>.
select/shared/dismiss-handler.ts
createDismissHandler FactoryDismissHandlercreateDismissHandler(opts: DismissHandlerOptions)Click-outside handler. Pure closure over opts - no Angular DI.
Override via CNGX_DISMISS_HANDLER_FACTORY for telemetry or
conditional-dismiss prompts.
select/shared/display-binding.ts
createDisplayBinding FactoryDisplayBindingcreateDisplayBinding(opts: DisplayBindingOptions)Bidirectional binding between a scalar value signal and the visible
text of a co-located <input> running CngxListboxSearch. Two
effect()s - value→input (gated on focused) and search-term→callback
(gated on writingFlag + skipInitial).
tabs/overflow/dom-anchor-retry.ts
createDomAnchorRetry FactoryCngxDomAnchorRetryHandlecreateDomAnchorRetry(options: CngxDomAnchorRetryOptions)Bounded retry loop for DOM-anchoring patterns - shared
attempt-counter and give-up and cancellation contract.
Used by
<cngx-tab-overflow>'s rAF strip-attach loop and [cngxMatTabs]'s
afterNextRender header-anchor loop.
Consumer-supplied scheduler lets different timing primitives flow through one counter.
const retry = createDomAnchorRetry({
attempt: () => {
const root = host.closest('.strip-wrapper');
if (!root) return null;
observer.observe(root);
return true;
},
maxAttempts: 60,
schedule: (cb) => {
const h = requestAnimationFrame(cb);
return () => cancelAnimationFrame(h);
},
onGiveUp: () => console.warn('strip wrapper not found'),
});
afterNextRender(() => retry.start());
destroyRef.onDestroy(() => retry.cancel());common/stepper/strip-density.ts
createElementWidthSignal FactorycreateStripDensity FactorySignalcreateElementWidthSignal(element: HTMLElement, destroyRef: DestroyRef)Tracks an element's content-box width as a reactive signal via the
ResizeObserver API. Mirrors createMobileViewportSignal's
matchMedia wrapper: the API is wrapped directly (not via the
CngxResizeObserver directive, which can only attach through
hostDirectives). In SSR / non-DOM environments the signal stays
0 and no observer is wired.
HTMLElementcreateStripDensity(options: CngxStripDensityOptions)Resolves the classic strip's density rung from its own container
width against the step count and two per-step px thresholds. Pure
create* factory, sibling to createStepperDisplayMode - it owns a
ResizeObserver the way the display-mode factory owns a
matchMedia listener, and returns a single derived Signal.
'comfortable' density short-circuits to 'full' (no measurement
dependency). Before the first measurement (width === 0) and for an
empty strip the rung is 'full', so the strip never flashes
'minimal' on mount.
forms/filter-builder/filter-builder.helpers.ts
createEmptyFilterRootOpen detail pagecreateFilterExpressionOpen detail pagecreateFilterGroupOpen detail pageevaluateExpressionOpen detail pagetoFilterPredicateOpen detail page
createEmptyFilterRoot FactorycreateFilterExpression FactorycreateFilterGroup FactoryFilterGroupFrozen empty root used as the presenter's model<FilterGroup> default and
by CngxFilterBuilderState.clear(). Always returns the same frozen
reference so consumers comparing tree identity short-circuit correctly.
FilterExpressioncreateFilterExpression(field: string, operator: string, value?: TValue)Build a fresh FilterExpression with a generated id.
FilterGroupcreateFilterGroup(logic: FilterLogic, filters, opts: CreateFilterGroupOptions)Build a fresh FilterGroup with a generated id. Defaults to and logic, no children, not negated.
FilterGroupensureFilterTreeIds(tree: FilterGroup)Normalises a tree by assigning a stable id to every node missing one.
Identity-preserving short-circuit - when every node already carries an id,
the same tree reference is returned. Consumers who hand-construct trees
(deserialised JSON, presets, persisted snapshots) run this once at the
boundary; the presenter already invokes it on initial read and on every
external write through value.
evaluateExpression(expr: FilterExpression, item: TItem, fieldDef)Evaluate a single FilterExpression against item. Unfilled expressions short-circuit to true (except isEmpty/isNotEmpty).
unknown | nulltoFilterPredicate(tree, fields)Build an item-level predicate from a FilterGroup. Returns null when
the tree itself is null - the consumer typically interprets null as
"no filtering, accept every item". For an empty root group, the returned
predicate evaluates true for every item (vacuous truth on and).
forms/field/field-sync.ts
createFieldSync Factoryv0.1.0createFieldSync(options: FieldSyncOptions)Bidirectional Field <-> control value sync via CngxFormFieldPresenter.
Two effect()s, each reading the opposite branch inside untracked() with
valueEquals as the cycle break - the canonical bridge every model()-based
cngx control reuses instead of re-implementing field write-back. Both the
field and the control's model() are writable sources of truth, so this is
coordination, not a single computed(). Requires an injection context;
no-op without a surrounding cngx-form-field.
Keeps the create* prefix despite calling inject() in its body: it is a
blessed exception (see architecture-summary, mirrors adaptFormControl),
not an oversight. The name is also the public @cngx/forms/select export;
renaming would break that contract.
createFieldSync<number>({
componentValue: this.value,
valueEquals: Object.is,
coerceFromField: (v) => (typeof v === 'number' ? v : 0),
});select/shared/field-sync.ts
createFieldSync FactorycreateFieldSync(options: FieldSyncOptions)Gate-aware wrapper over the canonical createFieldSync from
@cngx/forms/field. Returns early when CNGX_SELECT_DISABLE_FIELD_SYNC
is provided truthy in the injection context; otherwise delegates to the one
bridge implementation. Array-shape callers keep working unchanged: the field
createFieldSync<V> is generic in V and the composites already pass their
own valueEquals / coerceFromField / toFieldValue.
Kept as the select-local export so the 9 select composites and
cngx-filter-builder import the gate and the sync from one module.
forms/filter-builder/filter-builder-announcer.ts
createFilterBuilderAnnouncer FactoryinjectFilterBuilderAnnouncerFactory InjectCngxFilterBuilderAnnouncercreateFilterBuilderAnnouncer(sources: CngxFilterBuilderAnnouncerSources)Default announcer - derives the live-region string from lastMutation through the i18n formatter bundle.
CngxFilterBuilderAnnouncerFactoryInject-context helper that resolves CNGX_FILTER_BUILDER_ANNOUNCER_FACTORY.
forms/filter-builder/filter-builder-state.ts
createFilterBuilderState FactoryCngxFilterBuilderStatecreateFilterBuilderState(opts: CngxFilterBuilderStateOptions)Default factory behind CNGX_FILTER_BUILDER_STATE_FACTORY. Wraps one writable
FilterGroup signal as the canonical tree and returns the
CngxFilterBuilderState the presenter drives:
- read-only
tree/fieldMap/isEmptysignals - path-keyed mutators (
addExpression,setLogic,removeNode,clear, ...) - the
lastMutationevent the announcer formats into live-region text
Two-way binding: pass the presenter's model<FilterGroup>() as source, so
every mutator write emits through the consumer's [(value)]. Uncontrolled
callers omit source and the factory creates its own signal from
initial ?? EMPTY_ROOT.
Plain TS, no inject() - testable without TestBed. Override the token to
wrap this (logging, undo) rather than reimplementing the mutators.
forms/filter-builder/filter-builder-template-registry.ts
createFilterBuilderTemplateRegistryOpen detail pageinjectFilterBuilderTemplateRegistryOpen detail page
createFilterBuilderTemplateRegistry FactoryinjectFilterBuilderTemplateRegistry InjectCngxFilterBuilderTemplateRegistrycreateFilterBuilderTemplateRegistry(queries: CngxFilterBuilderTemplateRegistryQueries)Wires every slot query through the documented three-stage cascade.
Must be called inside an Angular injection context (the helper resolves
CNGX_FILTER_BUILDER_CONFIG lazily and the contentChild signals were
already created in the caller's context).
The default factory is registered behind
CNGX_FILTER_BUILDER_TEMPLATE_REGISTRY_FACTORY so consumers can wrap
the resolution path (telemetry, dynamic theme swapping, etc.) without
forking the component.
CngxFilterBuilderTemplateRegistryinjectFilterBuilderTemplateRegistry(queries: CngxFilterBuilderTemplateRegistryQueries)Inject-context helper that resolves the registry factory through the DI
token and invokes it with the caller's contentChild queries. The
<cngx-filter-builder> component is the canonical caller.
select/shared/panel-renderer.ts
createIdentityPanelRenderer FactoryPanelRenderercreateIdentityPanelRenderer(input: PanelRendererInput)Pass-through renderer: every option enters the DOM. Comfortable to ~500 options; beyond that, wire a virtualising renderer via CNGX_PANEL_RENDERER_FACTORY.
chart/scales/linear.ts
createLinearScale FactorycreateLinearScale(domain, range)Pure-TS linear scale. Maps a numeric domain [d0, d1] to a numeric
range [r0, r1] via standard linear interpolation. Domain may be
inverted (d0 > d1) for SVG Y-axes where the top of the chart is the
highest data value but the lowest pixel coordinate.
Values outside the domain extrapolate. Charts that need overflow clamping clamp at the data layer, not the scale.
[start, end] data range. Equal endpoints collapse the
scale to a constant function returning range[0].
[start, end] output range (typically pixel coordinates).
(v: number) => number mapping domain values to range values.
select/shared/local-items-buffer.ts
createLocalItemsBuffer FactoryLocalItemsBuffercreateLocalItemsBuffer(compareWith: Signal)Builds a LocalItemsBuffer. Reads compareWith lazily so
mid-flight comparator swaps are honoured.
data/async-state/create-manual-state.ts
createManualState FactoryManualAsyncStateCreate a fully manual async state — no HTTP, no automatic loading.
Use for local operations: heavy computations, Web Workers, complex local processes.
Does not require an injection context — uses only signal() and computed().
readonly processState = createManualState<ProcessResult>();
async handleProcess(): Promise<void> {
this.processState.set('loading');
this.processState.setProgress(0);
const result = await heavyComputation((p) => this.processState.setProgress(p));
this.processState.setSuccess(result);
}data/material-bridge/bidirectional-sync.ts
createMaterialBidirectionalSync FactoryCngxMaterialBidirectionalSyncHandlecreateMaterialBidirectionalSync(opts: CngxMaterialBidirectionalSyncOptions)Single shared bidirectional-sync factory for cngx organisms /
directives that bridge a cngx presenter against a Material parent
(<mat-tab-group>, <mat-stepper>, etc.).
Lives at Level 2 in @cngx/common/data parallel to
createCommitController. Material types never enter the signature —
the caller maps Material-specific events and property accessors to a
host-agnostic shape at the directive boundary.
Installs:
- A presenter→Material
effect()that trackspresenterIndexand writes throughwriteSelectedIndex, equality-guarded againstreadSelectedIndex()to suppress redundant writes. The Material read+write pair runs insideuntracked()perreference_signal_architecturerule 2. - A Material→presenter subscription that forwards each
selectionChange$emission toonMaterialSelection(equality-guarded againstpresenterIndex()so a Material event whose value already matches the presenter is dropped — closes the re-entrancy loop).
Both sides clean up via destroyRef.
Material-eager-advance reconciliation. Material's MDC click
handler advances selectedIndex before the Material→presenter
subscription forwards the click. When the presenter HOLDS its index
(pessimistic mode + bound commitAction), the subscriber does NOT
write Material back: on a SUCCESSFUL commit the presenter later
advances to the clicked target and Material is already there, so the
mirror effect is a no-op (no flash); on a REJECTED commit the presenter
never moves, leaving Material eager-advanced on the refused tab. The
host detects the rejection through its own commit lifecycle and calls
CngxMaterialBidirectionalSyncHandle.reconcile to snap Material
back. Writing from inside the subscriber instead is unsafe: Material's
selectedIndex read-back lags a programmatic write, so the mirror
effect's equality guard reads a stale value and skips the corrective
write, sticking the visual on the wrong tab.
Self-echo suppression (loop-safety). Every programmatic
selectedIndex write makes Material re-emit selectionChange. The
factory records each value it writes and drops the matching echo
(indexOf + splice, which also prunes earlier writes whose echo
Material coalesced away) before any other processing. This is
load-bearing for an ASYNC commit-action: Material emits the echo a
microtask later, by which point the commit may have advanced
presenterIndex, so an equality-only guard (presenterIndex() === idx) would FAIL to drop the echo — it would re-enter the subscriber
and ping-pong against the in-flight navigation (the demo freezes).
Matching the echo by the value we wrote drops it regardless of where
the presenter has moved.
mat-accordion/material-bridge/set-sync.ts
createMatExpansionSetSync FactorycreateMatExpansionSetSync(opts: CngxMatExpansionSetSyncOptions)Set-based bidirectional sync between a <mat-accordion>'s projected
<mat-expansion-panel> tree and the headless CngxAccordion
open-set. The accordion open-set is a multi-membership
Set<string>, so the index-based createMaterialBidirectionalSync
(@cngx/common/data) does not fit — this helper syncs per-panel
expanded against set membership instead.
Installs two effects and one listener-set:
- brain→Material — an
effect()trackingpanels()and, per panel,accordion.isOpen(id). When the open-set (or the panel list) changes it writespanel.expandedto match, guarded by a re-entrancy flag so the resulting synchronousexpandedChangeecho does not loop back. The Material writes run insideuntracked()perreference_signal_architecturerule 2. - subscription reconcile — an
effect()tracking onlypanels(); insideuntracked()it subscribes freshly-added panels'expandedChangeand drops removed ones (diff-only churn, aMap<P, subscription>). - Material→brain — each
expandedChangelistener flips the brain viaaccordion.toggle(id), but only when the incomingexpandeddiffers from the brain's current (clamped) membership, so a programmatic echo (or a redundant Material emit) is a no-op. Routing throughtoggle(not a raw set-write) means single-mode arbitration and the exposedopenIdsmodel stay identical to the nativeCngxAccordionPanelDOM path.
The idempotent membership check plus the re-entrancy flag together
close the loop: the flag drops the synchronous echo CdkAccordionItem
emits from its expanded setter during our own write, and the
membership check drops any Material emit that already agrees with the
brain.
mat-stepper/material-bridge/handle.ts
createMatStepHandle FactoryCngxMatStepHandleSetupcreateMatStepHandle(matStep: MatStep, idSeed)Translates a Material MatStep into a cngx
CngxStepRegistration.
id- always a freshidSeed()value. Mirrors the tabs instrumentation handle: a label-keyed id would collide when two steps share a label.kind- fixed at'step'. The instrumentation path does not project nested<mat-step>groups (Material's stepper has no group-of-steps concept; group-aware semantics belong to the<cngx-stepper>thin-wrapper organism).label- snapshot signal resolved through a four-tier fallback at registration time so cngx-side phrases (announcements,aria-labelcomposition, telemetry) never read empty when Material consumers project a<ng-template matStepLabel>:MatStep.labelwhen it is a plain string - the canonical shape and the only one that emits a runtime change Material itself observes.MatStep.ariaLabelwhen the consumer set the input - designed exactly as the substitute for template labels.- Static-text read from
MatStep.stepLabel.templatevia a throwaway detachedEmbeddedViewRef(readMatStepLabelTemplateText). Captures literalmatStepLabelmarkup; dynamic interpolation bails through to (4). Step <id>- deterministic, derived from the cngx handle id. Always non-empty. Documented limitation: runtime label changes do not propagate. CDK'sCdkStepdoes not expose a_stateChangesSubject analogous toMatTab._stateChanges, so cngx cannot re-trigger the snapshot when Material flips the input later. Surface the same Material-internal coupling family typed inMaterialPrivateSurfaces.CompletedOverrideSource.
disabled- fixedfalse. Material owns step gating vialinear+editable+completed; surfacing a cngx-sidedisabledwould duplicate Material's own click-time enforcement and is ignored by<mat-stepper>itself.state-computed()overMatStep.hasError/MatStep.completed.CdkStep.completed's getter reads_completedOverride()- aWritableSignal<boolean | null>typed inMaterialPrivateSurfaces.CompletedOverrideSource. The cngx computed transitively tracks that signal through the getter and re-fires whenever Material flips completion.hasErroris a plain property setter onCdkStep, NOT a Signal - ahasErrorwrite that is not paired with acompletedchange does not re-trigger this computed. In practice Material wizards write the two together (step.hasError = true; step.completed = falsein error-handler patterns and inside Material's own error-state matchers) so the limitation is benign for the documented usage pattern.errorAggregator- points at the shared NO_ERROR_AGGREGATOR constant.
MatStepmat-tabs/material-bridge/handle.ts
createMatTabHandle FactoryCngxMatTabHandleSetupcreateMatTabHandle(matTab: MatTab, idSeed, injector: Injector)Translates a Material MatTab into a cngx CngxTabHandle
plus a writable errorAggregator slot the [cngxMatTabError]
directive binds.
id- freshidSeed()value; a label-keyed id would collide when two tabs share a label.label/disabled-computedsignals retriggered bytoSignal(matTab._stateChanges). Bridge lifetime is tied to the suppliedinjector(typically a per-tab childEnvironmentInjector)._stateChangesis a Material-internal surface.errorAggregator- writable seeded atundefined;[cngxMatTabError]writes its bound aggregator in and resets on teardown. The handle exposes.asReadonly()to preserve theCngxTabHandlecontract.directError- writable seeded atfalse;[cngxMatTabErrorFlag]writes itsstring | booleanvalue in and resets on teardown. Folds intohasError/errorMessage.
interactive/menu/menu-announcer.ts
createMenuAnnouncer FactoryinjectMenuAnnouncer InjectCngxMenuAnnouncerLikeDefault factory that hands out the root-scoped CngxMenuAnnouncer singleton. Consumers wire a custom announcer by replacing CNGX_MENU_ANNOUNCER_FACTORY.
Must run inside an injection context.
CngxMenuAnnouncerLikeResolve the CngxMenuAnnouncerLike from the current injection scope via the factory token. Must run inside an injection context.
interactive/menu/dismiss-handler.ts
createMenuDismissHandler Factoryv0.1.0createMenuTriggerDismissBinding Factoryv0.1.0CngxMenuDismissHandlercreateMenuDismissHandler(opts: CngxMenuDismissHandlerOptions)Default factory. Pure closure over opts - no Angular DI. Mirrors the
shape of @cngx/forms/select's createDismissHandler so future
cross-family consolidation has a clean target.
Override via CNGX_MENU_DISMISS_HANDLER_FACTORY for telemetry-wrapped or test-doubled dismissal.
CngxMenuTriggerDismissBindingcreateMenuTriggerDismissBinding(opts: CngxMenuTriggerDismissBindingOptions)Build the dismiss lifecycle for a menu-bearing trigger directive.
Lazily instantiates the handler on first attach() so the trigger's
popover input is bound before the factory reads it. The returned
lastSource signal is owned by this binding - the factory writes it
via its onDismiss callback, which runs from DOM event handlers
(never inside an Angular effect()).
common/stepper/mobile-viewport.ts
createMobileViewportSignal FactorycreateStepperDisplayMode FactorySignalcreateMobileViewportSignal(mediaQuery: string, destroyRef: DestroyRef)Returns a reactive Signal<boolean> that reflects whether the host
window matches a CSS media query (e.g. (max-width: 480px)). Used by
<cngx-stepper> to drive its mobile auto-collapse template branch.
In SSR / non-DOM environments the signal stays false and the
listener is never wired.
Signal<"classic" | "text" | "dots">createStepperDisplayMode(mediaQuery: string, mobileCollapse, destroyRef: DestroyRef)Resolves the active stepper display mode by combining a viewport
media-query with the configured mobileCollapse policy.
'classic' keeps the full strip; 'text' / 'dots' swap to the
matching compact variant.
field/testing/mock-field.ts
createMockField Factoryliteral typecreateMockField(opts: MockFieldOptions)Creates a mock CngxFieldAccessor that returns a fully writable MockFieldRef.
Tests can mutate any signal to simulate field state changes.
const { accessor, ref } = createMockField({ name: 'email', required: true });
// Pass `accessor` to [field] input
// Mutate ref.touched.set(true) to simulate interactionchart/scales/ordinal.ts
createOrdinalScale FactorycreateOrdinalScale(domain, colors)Pure-TS ordinal scale. Maps a discrete categorical domain to a cycling palette of values (typically colours). When the domain is longer than the palette, mappings wrap modulo palette length.
Ordered list of categorical values. Values are matched by reference equality on lookup.
Palette to cycle through. Must have at least one entry; an empty palette throws synchronously at construction time.
Callable (v: T) => string returning the palette entry for
v. Lookup of an unknown value returns the palette's first entry.
tabs/scroll-sync/organism-scroll-sync.ts
createOrganismScrollSync Factoryv0.1.0createOrganismScrollSync(opts: CngxOrganismScrollSyncOptions)Scroll-into-view effect for any strip-based organism (tabs,
stepper headers, future scrolling lists).
Tracks activeId and scrolls the matching [id="<itemId>-header"] into view.
The DOM call sits in untracked() (only activeId is tracked). scrollIntoView is missing in jsdom - guarded with optional chain.
UX / a11y
- The active tab is never stranded off-screen: when activation moves to a tab clipped by overflow (keyboard arrow, deep link, programmatic select), its header scrolls into view, so the focused and selected tab is always visible (WCAG 2.4.7 Focus Visible).
- Pairs with the overflow surface: picking a hidden tab from the "More" popover scrolls it back into the strip, and the overflow recompute then self-trims - keyboard navigation and the overflow list stay in sync.
- Motion is overridable for reduced motion: the default is smooth-center
along the inline axis; pass
scrollOptions(e.g.behavior: 'auto') to honourprefers-reduced-motionor to scroll vertically.
// Install once from the organism's field-init (needs an injection context).
createOrganismScrollSync({
activeId: this.presenter.activeId, // Signal<string | null>
hostElement: this.hostElement, // holds the [id="<tabId>-header"] buttons
injector: this.injector,
// scrollOptions omitted -> smooth-center; override for reduced motion:
// scrollOptions: { behavior: 'auto' },
});tabs/overflow/overflow-template-cascade.ts
createOverflowPopoverHighlightSyncOpen detail pagecreateTabOverflowTemplateBindingsOpen detail pagetabOverflowOptionIdOpen detail page
createOverflowPopoverHighlightSync FactorycreateTabOverflowTemplateBindings FactorycreateOverflowPopoverHighlightSync(popover: Signal, ad: Signal)Resets the AD highlight on popover close.
Keyboard-open paths
(ArrowDown / End / typeahead on the closed trigger) set
activeIndex via AD's own keydown listener before opening -
unaffected. Mouse-open leaves activeIndex === -1 so the popover
renders unhighlighted.
Without this reset, the next open would
inherit a stale index from the prior keyboard session.
Must run in injection context.
UX / a11y
- Closing the popover resets the highlight, so a keyboard session never
leaves a stale
aria-activedescendantfor the next open. - Each row has a stable descendant id (
tabOverflowOptionId), so the SR focus reference never dangles across re-renders. - Highlight is virtual focus, not DOM focus: keyboard nav moves
aria-activedescendantwhile real focus stays on the trigger (APG menu-button); the rows are never tab stops.
CngxTabOverflowTemplateBindingscreateTabOverflowTemplateBindings(opts: CngxTabOverflowTemplateBindingsOptions)Wires the 3-stage template cascade for the overflow molecule's
two visible regions:
per-instance directive > CNGX_TABS_CONFIG.templates.overflow* > built-in markup
(template-outlet returns null). \
Pure - no DI, no side effects, no destroy hooks. Safe to call
from a component's field-init block. Mirrors the select-family
createTemplateRegistry pattern.
tabOverflowOptionId(tab: CngxTabHandle)DOM id for a hidden-tab option row in the overflow listbox.
Stable across CD passes so aria-activedescendant resolves to
the same <li>. -overflow-option suffix avoids collision with
the strip-button (-header) and per-tab descriptor (-desc).
ui/paginator/paginator-announcer.ts
createPaginatorAnnouncer Factoryv0.1.0CngxPaginatorAnnouncerBuilds the paginator live-region message as a single derived signal - no
class logic baked into the shell, so the skin still ejects cleanly. The
message is a linkedSignal over [pageIndex, totalPages, isBusy] from
CNGX_PAGINATOR_HOST: it speaks "Page N of M" on every effective-page
change (navigation OR a total-shrink clamp, so the clamp is never silent),
"Loading" while busy, and "Updated" on the first settle after busy. Phrasing
comes from injectPaginatorConfig.
The previous source value (held by linkedSignal) is what distinguishes a
settle from a steady state, so there is no signal write in an effect and no
imperative previous tracking. Identical consecutive messages dedupe through
the signal's value equality, so the live region never re-announces a no-op.
Must run in an injection context (call as a field initialiser on the shell).
paginator/segments/paginator-nav.component.ts
createPaginatorNavCore Factoryselect/shared/panel-lifecycle-emitter.ts
createPanelLifecycleEmitter FactorycreatePanelLifecycleEmitter(opts: PanelLifecycleEmitterOptions)One effect() that emits openedChange/opened/closed on
panelOpen flips and restores focus to the trigger after close.
Output emits + focus call wrapped in untracked. Injection context
required.
forms/input/password-strength.factory.ts
createPasswordStrength FactoryCngxPasswordStrengthFactoryBuilds the dependency-less default password-strength estimator.
The heuristic scores length tiers (>= 8 / 12 / 16) plus character-class diversity (lower, upper, digit, symbol) and subtracts a point for a run of three or more identical characters, then clamps to 0..4. It ships no dictionary - enterprises swap in zxcvbn or similar via CNGX_PASSWORD_STRENGTH_FACTORY.
chart/path/path-builder.ts
createPathBuilder FactoryPathBuildercreatePathBuilder(opts: PathBuilderOptions)Pure-TS path builder with single-slot LRU memo on
(data, xScale, yScale) reference identity. Pure TS, no Angular
dep. Compute guard only - does not know about signals or equal
functions; the d computed in <cngx-line> carries the cascade
guard separately.
The cache returns the previous result when all three inputs are reference-equal to the previous call. Any reference mismatch triggers a rebuild and updates the slot.
Each call to createPathBuilder returns a fresh builder with its
own lastData / lastX / lastY slots - there is no cross-call /
cross-consumer state. The cascade guard for layer atoms is the
equal: (a, b) => a === b on the builder computed; combined
with Angular signals' default behaviour of skipping re-emissions
when the inputs to the computed are unchanged, two consecutive
cascade ticks with the same (y, x, curve) produce the same
builder instance.
select/shared/projected-option-model.ts
createProjectedOptionModel FactoryProjectedOptionModelcreateProjectedOptionModel(input: ProjectedOptionModelInput)Hierarchy-preserving option model derived from projected DOM. Leaves
stay leaves, groups stay groups; createSelectCore reflattens for AD
lookup. Labels are plain-text via {{ }} interpolation.
select/shared/recycler-panel-renderer.ts
createRecyclerPanelRendererFactory FactoryCngxPanelRendererFactorycreateRecyclerPanelRendererFactory(recycler: CngxRecycler)Builds a CngxPanelRendererFactory backed by a consumer-owned
CngxRecycler. Slices flatOptions to the recycler window and
forwards spacer heights + setsize. Consumer wires
connectRecyclerToActiveDescendant separately; this factory doesn't
touch AD state.
select/shared/reorder-commit-handler.ts
createReorderCommitHandler FactoryReorderCommitHandlercreateReorderCommitHandler(opts: ReorderCommitHandlerOptions)Plain factory for the reorder-commit flow used by
CngxReorderableMultiSelect. Operates on ordered arrays with
same-membership semantics.
Why a separate factory (not a flag on createArrayCommitHandler):
the array handler's reconcileValues uses sameArrayContents to
short-circuit writes when the target matches current state - a pure
reorder would silently skip. A bypassReconcile flag would
complicate every other call-site; a dedicated factory keeps the
array handler's hot path small.
CngxTreeSelect.dispatchValueChange follows the same pattern
inline; a future refactor could lift it here.
select/shared/scalar-commit-handler.ts
createScalarCommitHandler FactoryScalarCommitHandlercreateScalarCommitHandler(opts: ScalarCommitHandlerOptions)Scalar-shape commit flow shared by scalar select variants. Owns
commit-controller lifecycle, reconciliation, togglingOption.set(null)
on success, optimistic rollback on error. Consumer owns change-event
emission, announcer severity (onCommitError), input-text mirroring
(onValueWrite), and popover-close timing - handler never closes the
panel.
select/shared/search-effects.ts
createSearchEffects FactorycreateSearchEffects(opts: SearchEffectsOptions)One or two effect()s for input-trigger variants: skipInitial-gated
searchTermChange forward (when emit is set) and auto-open-on-typing.
External calls wrapped in untracked. Injection context required.
core/utils/selection-controller.ts
createSelectionController FactorySelectionControllercreateSelectionController(values: WritableSignal, options?: SelectionControllerOptions)Create a signal-based selection engine that reads and writes an external
WritableSignal<T[]>.
const values = signal<User[]>([]);
const selection = createSelectionController(values, { keyFn: (u) => u.id });
selection.select(alice);
selection.isSelected(alice)(); // true
selection.isSelected(alice) === selection.isSelected(alice); // stableinteractive/slider/slider-interaction.ts
createSliderInteraction Factoryv0.1.0pointerFraction v0.1.0CngxSliderInteractioncreateSliderInteraction(options: CngxSliderInteractionOptions)Pure factory for a slider's keyboard + pointer-drag behaviour - the shared
interaction brain for both CngxSlider (single) and each
CngxSliderThumb (range). Arrow / Page / Home / End map onto the core's
step helpers; pointer-down captures the pointer and drags via the supplied
fractionFromPointer. Keeping this in one factory means the track and the
thumb cannot drift apart.
pointerFraction(el: HTMLElement, orientation, clientX: number, clientY: number)Maps a pointer position to a 0..1 track fraction against el's geometry.
Vertical sliders measure bottom-up (fraction 0 at the lower edge) to match the
skin's bottom-anchored fill. Shared by the single slider and the range host so
the geometry math lives in exactly one place.
interactive/slider/slider-ticks.ts
createSliderTicks Factoryv0.1.0CngxSliderTicksViewcreateSliderTicks(options)Derives a slider's tick marks + labels from its bounds, shared by
CngxSlider and CngxRangeSlider so the math lives once. marks
gates the repeating-gradient interval; labels gates the numeric stops; both
are independent. The values signal is arrayEqual-guarded.
data/sort/sort-header-state.ts
createSortHeaderState Factoryv0.1.0SortHeaderStatecreateSortHeaderState(sort, field)Derives the shared sort-header state for one column from a CngxSort getter.
Both CngxSortHeader (table context, aria-sort) and CngxDgaSortHeader
(disclosure context, role="button" + aria-describedby) compose this factory
and keep only their own a11y presentation. The factory is a11y-agnostic - it
returns the plain derived signals each header maps to its own DOM/ARIA surface.
Getter for the owning CngxSort engine (() => sortRef, () => grid.sort).
Getter for the column's field key.
The derived { entry, isActive, isAsc, isDesc, priority, toggle } bundle.
common/stepper/commit-handler.ts
createStepperCommitHandler FactoryCngxStepperCommitHandlercreateStepperCommitHandler(opts: CngxStepperCommitHandlerOptions)Build a stepper commit handler over an existing CngxCommitController.
Resolves Observable<boolean> / Promise<boolean> / boolean returns into
a unified accept: boolean outcome.
common/stepper/group-navigation.ts
createStepperGroupNavigation Factorycommon/stepper/group-summary.ts
createStepperGroupSummary FactoryCngxStepperGroupSummaryViewcreateStepperGroupSummary(options: CngxStepperGroupSummaryOptions)Builds the collapsed-group summary view. Level-2 helper so the
organism stays a thin renderer. status mode defers its SR phrasing
to the group's existing status-phrase span, so srText returns
null there. English defaults (locale via the i18n cascade is a
follow-up).
literal typesubtreeStats(node: CngxStepNode)Terminal-step totals for a group's subtree; reads each step state().
common/stepper/create-stepper-host-proxy.ts
createStepperHostProxy Factoryv0.1.0CngxStepperHostcreateStepperHostProxy(supplier)Builds a live delegating CngxStepperHost proxy over a supplier
signal. Every signal member is a computed() reading
supplier()?.<member>(), every method forwards to
supplier()?.<method>(...). This is the spelled-out shape for
re-providing an input-derived host through DI - you cannot
useExisting an input() value, so the footer provides this proxy
once and it tracks whichever host the supplier currently resolves.
Null-supplier neutral set (the disabled-by-default contract). When
the supplier resolves null (a standalone footer with neither [host]
nor an ambient stepper), every navigation affordance must render
inert, never falsely enabled: canGoPrevious / canGoNext collapse
to false so Back / Next render disabled, and every method is a no-op.
create* pure factory - no injection context required (computed()
only). Single consumer (the footer); the surface is load-bearing, not
speculative.
common/stepper/stepper-state-view.ts
createStepperStateView Factoryv0.1.0resolveStepperErrorSummary v0.1.0CngxStepperStateViewcreateStepperStateView(inputs)Build the shared CngxStepperStateView over a presenter host and its
step-only projection. Pure factory - computed() only, no injection
context required, so any skin can allocate it in a field initializer.
resolveStepperErrorSummary(view: Pick, stepsOnly: Signal, i18n: CngxStepperI18n, messageOf?)Aggregate error phrase shared by the minimal skins (text / progress-bar)
and the classic mobile-collapse text branch. A single errored step names
itself ("Payment: Errored"); several collapse to the i18n count phrase
("2 errors"). Returns '' when no step errored - callers gate on
view.hasAnyError() so the empty string never reaches the DOM.
The optional messageOf resolver lets callers surface a per-step
reason (a direct [error] string or the first aggregator label) in the
single-error case instead of the generic errored status word. When
omitted - or when it returns undefined/'' - the helper produces
byte-identical output to the no-resolver form, so existing callers
(the classic mobile-collapse summary) are a guaranteed no-op.
Pure helper - reads signals at call time, intended to be wrapped in the
caller's computed().
stepper/slots/stepper-template-cascade.ts
createStepperTemplateBindings FactoryCngxStepperTemplateBindingscreateStepperTemplateBindings(opts: CngxStepperTemplateBindingsOptions)Wires the 3-stage template cascade for the <cngx-stepper> skin
slots: per-instance directive > CNGX_STEPPER_CONFIG.templates.<key>
null(built-in default).
Pure - no DI, no side effects. Safe to call from a field-init
block. Sibling of createTabOverflowTemplateBindings and the
family-wide createTemplateRegistry.
Tabs has no parallel slot surface yet - see
stepper-accepted-debt §3 for the planned Phase-4 closure.
tabs/dismissals/tab-dismissals.ts
createTabDismissals Factoryv0.1.0CngxTabDismissalscreateTabDismissals(opts: CngxTabDismissalsOptions)Helper resolving the dismissable/addable affordances for
<cngx-tab-group>.
Keeps the close/add cascade + interaction off the
organism class (LOC guard).
Each cascade is one computed().
The actual tab removal is the consumer's -
handleClose only routes through the presenter's requestClose, which
moves the active index, and restores focus once the consumer's removal
has rendered.
// The organism builds it once from field-init; the template reads it.
protected readonly dismiss = createTabDismissals({
host: this.host, // CNGX_TAB_GROUP_HOST
config: this.config, // injectTabsConfig()
i18n: this.i18n, // injectTabsI18n()
closable: this.closable, // input<boolean | undefined>('closable')
addable: this.addable, // input<boolean | undefined>('addable')
hostElement: this.hostElement,
injector: this.injector,
});
// Per-tab close affordance, read from the header template:
dismiss.isTabClosable(tab); // per-tab override > group resolution
dismiss.closeButtonLabel(tab); // i18n accessible name for the close button
dismiss.handleClose(tab, clickEvent); // routes through presenter.requestClose, restores focus
dismiss.handleTabKeydown(tab, keyEvent); // Delete on a focused closable tab closes it (APG)
// Group-level add affordance:
dismiss.resolvedAddable(); // input ?? config ?? false
dismiss.handleAdd(); // routes through presenter.requestAddtabs/announcements/tab-group-announcements.ts
createTabGroupAnnouncements Factoryv0.1.0CngxTabGroupAnnouncementscreateTabGroupAnnouncements(options: CngxTabGroupAnnouncementsOptions)Pure factory bundling the <cngx-tab-group> AT-announcement +
descriptor surfaces. Owns one internal linkedSignal
(prior-active-index, drives the success-arm direction prefix) —
lazy by linkedSignal semantics, so the organism reads
liveAnnouncement once at construction to seed it.
UX / a11y
- The live region is declarative, never imperative:
liveAnnouncementis a signal the polite region renders, empty between transitions so AT stays silent on no-op ticks. - Direction is spoken on every change: a move carries a previous/next prefix plus the landing tab; a rollback announces the safe-harbour tab or a retry, so the outcome is never silent.
- Accessible names carry position ("Tab 2 of 5: Settings") so AT does not infer it from tablist enumeration.
- Role descriptions stay distinct from the label: the tablist
aria-roledescriptionis separate from the regionaria-label, so AT never reads the same word twice. aria-labelandaria-labelledbystay mutually exclusive (resolvedAriaLabelreturnsnullwhen labelledby is bound).- The per-tab descriptor is ARIA-by-value: the
cngx-sr-onlyspan is always in the DOM;statusPhraseonly fills its content, keeping thearia-describedbyreference stable.
tabs/slots/tab-group-template-cascade.ts
createTabGroupTemplateBindings Factoryv0.1.0CngxTabGroupTemplateBindingscreateTabGroupTemplateBindings(opts: CngxTabGroupTemplateBindingsOptions)Wires the 3-stage template cascade for the <cngx-tab-group> skin
slots (errorBadge / rejectionIcon / busySpinner / icon / closeIcon /
addIcon):
per-instance directive >
CNGX_TABS_CONFIG.templates.<key> >
null.
null means the organism renders its built-in default - a default
span for the state decorations, the CNGX_TABS_GLYPHS glyph for
closeIcon / addIcon, and nothing for icon.
Pure — no DI, no side effects. Safe in field-init. Sibling to
createStepperTemplateBindings and createTabOverflowTemplateBindings.
Single-consumer today: [cngxMatTabs] does not consume this —
Material owns the rendered tab-button chrome via its own MDC
template, leaving no DOM seam. See tabs-accepted-debt §9.
UX / a11y
- The cascade is presentation-only; the accessibility contract is invariant under it. Overriding a slot never strips its accessible default (fallthrough to a built-in or the organism default).
- The screen-reader channel (descriptor /
aria-busy/ live region) lives on the organism, not the slot template, so swapping a decoration template changes only the visual. - An unbound
iconslot resolves to nothing, which is correct - the icon is decorative and the label carries the accessible name.
tabs/keyboard/tab-keyboard-nav.ts
createTabKeyboardNav Factoryv0.1.0common/tabs/router-commit.ts
createTabRouterCommit Factoryv0.1.0CngxTabsCommitActioncreateTabRouterCommit(opts: CngxTabRouterCommitOptions)Builds a CngxTabsCommitAction that gates a tab switch through
@angular/router. The action navigates to the target tab's route and
resolves on the router's own outcome:
NavigationEnd→true(commit the switch)NavigationCancel(aCanDeactivateguard blocked) →falseNavigationError(guard/resolver threw) →false
Routed tabs reuse the presenter's commit lifecycle verbatim - the
router navigation is simply the async op.
In pessimistic mode the active tab follows the resolved route, so a cancelled guard keeps
the old tab with zero extra gate machinery. The events subscription
is opened before navigate(...) so a synchronously-resolving
navigation cannot emit before the action is listening; the commit
controller's cancel() unsubscribes on supersede.
common/tabs/commit-handler.ts
createTabsCommitHandler FactoryCngxTabsCommitHandlercreateTabsCommitHandler(opts: CngxTabsCommitHandlerOptions)Wraps a CngxCommitController with the action-shape adapter
that collapses Observable<boolean> / Promise<boolean> /
boolean returns into a single accept: boolean outcome.
CngxCommitHandlerunTabsAction(action: CngxTabsCommitAction, fromIndex: number, toIndex: number, handlers)select/shared/template-registry.ts
createTemplateRegistry FactoryCngxSelectTemplateRegistrycreateTemplateRegistry(queries: CngxSelectTemplateRegistryQueries)Build a resolved CngxSelectTemplateRegistry from raw
contentChild directive queries. Runs each slot through the
3-stage cascade (instance directive → CNGX_SELECT_CONFIG.templates
default → null). Must be called in an injection context.
Used by every select-family variant to replace ~13 inline
injectResolvedTemplate(...) cascade blocks. See
CngxSelectTemplateRegistryQueries for the input shape.
interactive/slider/slider-thumb.directive.ts
createThumbValue FactoryWritableSignalcreateThumbValue(range: CngxSliderRangeHost, position: Signal)Builds a WritableSignal<number> view over one end of the range tuple:
reads range.value()[index] reactively, routes writes through
range.commit(position, …). Lets a thumb run an ordinary
createSliderCore (which writes a WritableSignal) while the tuple
stays the single source of truth - no local state, no effect sync.
chart/scales/time.ts
createTimeScale FactorycreateTimeScale(domain, range)Pure-TS time scale. Reuses createLinearScale after coercing
Date endpoints (and inputs) to epoch milliseconds. Supports
inverted domains and bare-number timestamps interchangeably.
[start, end] time range. Date and number are
interchangeable on either endpoint.
[start, end] output range (typically pixel coordinates).
(v: Date | number) => number mapping time values to range
values.
core/utils/transition-tracker.ts
createTransitionTracker FactoryStatusTransitioncreateTransitionTracker(source)Creates a reactive transition tracker for an AsyncStatus source.
Uses linkedSignal internally — when source() changes, previous holds
the prior value and current holds the new one. Both are memoized signals.
Reactive function that reads the current AsyncStatus.
const tracker = createTransitionTracker(() => this.state().status());
effect(() => {
const { current, previous } = tracker;
if (current() === previous()) return; // no change — deduplicated by linkedSignal
if (current() === 'success') { ... }
});interactive/tree-controller/tree-ad-items.ts
createTreeAdItems Factoryv0.1.0createTreeAdItems(ctrl: CngxTreeController)Projects a tree controller's visibleNodes into the
ActiveDescendantItem[] shape consumed by CngxActiveDescendant.items.
Kept as a helper (not a method on the controller) so
CngxTreeController stays free of the @cngx/common/a11y import and
can be reused from contexts that do not render through AD.
Returns a structurally-equal memoized computed - consumers can pass the
signal straight into [items]="adItems()" without worrying about cascade
re-renders on irrelevant tree re-emissions.
interactive/tree-controller/tree-controller.ts
createTreeController Factoryv0.1.0CngxTreeControllercreateTreeController(opts: CngxTreeControllerOptions)Plain factory for a signal-native tree controller. Reads opts.nodes
reactively, derives flat / visible projections via computed, and
tracks expanded-id state internally. Must be called in an injection
context (reads CNGX_TREE_CONFIG for defaults).
See CngxTreeController for the returned surface and CngxTreeControllerOptions for the configuration cascade.
select/shared/trigger-focus.ts
createTriggerFocusState FactoryCngxTriggerFocusStateBuilds the focus-state slot.
private readonly focus = inject(CNGX_TRIGGER_FOCUS_FACTORY)();
readonly focused = this.focus.focused;
protected handleFocus(): void {
this.focus.markFocused();
if (this.config.openOn === 'focus') this.open();
}select/shared/typeahead-controller.ts
createTypeaheadController FactoryTypeaheadControllercreateTypeaheadController(options: TypeaheadControllerOptions)<select> keyboard-typeahead: printable-key guard, lower-case match,
disabled skip, round-robin walk, debounced buffer reset. State-holding
(buffer + timer) but no DI refs; caller owns the lifetime.
interactive/menu/menu-nav-strategy.ts
createW3CMenuStrategy FactoryCngxMenuNavStrategyDefault W3C APG menu keyboard policy:
- ArrowRight on a submenu parent that is currently closed →
open-submenu. On an already-open submenu or a leaf item →noop(item-level navigation inside the open submenu is owned by the submenu's own active-descendant). - ArrowLeft when a submenu is open at the current level →
close-submenu. Otherwise →move-to-parent(an enclosing menubar interprets that; a standalone menu trigger treats it as noop).
interactive/hierarchical-nav/hierarchical-nav-strategy.ts
createW3CTreeStrategy FactoryCngxHierarchicalNavStrategyDefault W3C APG treeview keyboard policy:
- ArrowRight on a collapsed parent expands it. On an already-open parent it moves the active-descendant to the first child. On a leaf it is a no-op.
- ArrowLeft on an open node collapses it. On a closed node (or leaf) with a parent it moves the active-descendant to the parent. On a root leaf it is a no-op.
Move actions internally verify that ad.highlightByValue actually
changed activeId (e.g. disabled skip rejection), and downgrade to
'noop' when it didn't - so consumers bound to (movedToChild) /
(movedToParent) only see state-change-truthful emissions.
projects/utils/decimal-places.ts
decimalPlaces v0.1.0decimalPlaces(n: number)Count the decimal places in a finite number's shortest decimal string.
Used to round float-drift artefacts back to the precision a step or origin
carries (e.g. 0.1 * 3 = 0.30000000000000004 snaps back to 0.3). Reads
the places off String(n) rather than a fixed epsilon so 0.125 reports 3,
not a guessed constant. Non-finite input returns 0.
select/tree-select/tree-select.component.ts
mat-tabs/decorations/decoration-projectors.ts
data/data-source/smart-data-source.ts
injectSmartDataSource InjectdefaultSortFn(a: T, b: T, field: string, dir)CngxSmartDataSourceinjectSmartDataSource(source, options?: CngxSmartDataSourceOptions)Factory function for CngxSmartDataSource.
Must be called within an injection context (constructor or field initializer).
Accepts either a plain Signal<T[]> or a CngxAsyncState<T[]> for
full UX state integration (loading, error, refresh, empty).
// Plain signal
readonly dataSource = injectSmartDataSource(this.items);
// With async state - table shows skeleton, error, loading bar
readonly residents = injectAsyncState(() => this.api.getAll());
readonly dataSource = injectSmartDataSource(this.residents);display/shared/delta-format.ts
DeltaSentimentdeltaSentiment(direction: DeltaDirection, polarity: DeltaPolarity)Combine movement with the caller's polarity. flat or neutral polarity
collapse to neutral; otherwise up is positive under higher-is-better
and negative under lower-is-better (and the reverse for down).
formatDelta(value: number, mode: DeltaMode, locale: string, format?)Format the magnitude. The sign is carried by the arrow and colour, not the
digits: a positive value gains a leading +, everything else prints its
absolute value unsigned. Percent mode appends a narrow-no-break-space + %
and defaults to one fraction digit; absolute mode uses the locale grouping.
A supplied Intl.NumberFormatOptions overrides the default digit handling
in both modes.
chart/chart/equal-helpers.ts
dimensionsEqual(a, b)Field-wise equality on the chart's { width, height } dimension
shape. Used by the dimensions computed; every ResizeObserver tick
produces a fresh literal even when the numeric pair is unchanged, so
an equal fn on this signal is the foundation of the chart-graph
cascade short-circuit.
utils/rxjs-interop/rxjs-interop.ts
ui/mat-tabs/mat-tabs-config.ts
injectMatTabsConfigOpen detail pageprovideMatTabsConfigOpen detail pageprovideMatTabsConfigAtOpen detail pagewithAnchorRetryAttemptsOpen detail pagewithHalfWiredSlotSinkOpen detail page
injectMatTabsConfig InjectprovideMatTabsConfig ProviderprovideMatTabsConfigAt ProviderwithAnchorRetryAttempts FeaturewithHalfWiredSlotSink FeatureResolve the effective [cngxMatTabs] configuration for the current
injector. Always returns a fully populated, non-undefined object
so call sites do not need null-coalescing.
Resolution order per key:
CNGX_MAT_TABS_CONFIGvalue (set via provideMatTabsConfig / provideMatTabsConfigAt).- CNGX_MAT_TABS_CONFIG_DEFAULTS (library default).
EnvironmentProvidersprovideMatTabsConfig(...features: undefined)App-wide [cngxMatTabs] configuration. Returns
EnvironmentProviders for use inside bootstrapApplication's
providers array.
bootstrapApplication(App, {
providers: [
provideMatTabsConfig(
withAnchorRetryAttempts(10),
withHalfWiredSlotSink((missing) => Sentry.captureMessage(`half-wired ${missing}`)),
),
],
});Provider[]provideMatTabsConfigAt(...features: undefined)Component-scope [cngxMatTabs] configuration. Returns a
Provider[] so the result can be spread into a component's
providers or viewProviders (which cannot accept opaque
environment providers).
CngxMatTabsConfigFeaturewithAnchorRetryAttempts(n: number)Cap on the [cngxMatTabs] overflow-anchor retry loop.
See CngxMatTabsConfig.anchorMaxAttempts for default + rationale.
CngxMatTabsConfigFeaturewithHalfWiredSlotSink(sink: CngxMatTabHalfWiredSlotSink)Override the half-wired-slot diagnostic sink. See CngxMatTabsConfig.halfWiredSlotSink.
select/shared/action-select-config.ts
provideActionSelectConfigOpen detail pageprovideActionSelectConfigAtOpen detail pagewithActionAriaLabelOpen detail pagewithActionPopoverPlacementOpen detail pagewithActionPositionOpen detail pagewithCloseOnCreateOpen detail pagewithFocusTrapBehaviorOpen detail pagewithLiveInputFallbackOpen detail page
provideActionSelectConfig ProviderprovideActionSelectConfigAt ProviderwithActionAriaLabel FeaturewithActionPopoverPlacement FeaturewithActionPosition FeaturewithCloseOnCreate FeaturewithFocusTrapBehavior FeaturewithLiveInputFallback FeatureEnvironmentProvidersprovideActionSelectConfig(...features: undefined)App-wide action-select config. provideActionSelectConfigAt wins.
bootstrapApplication(App, {
providers: [
provideActionSelectConfig(
withFocusTrapBehavior('always'),
withActionAriaLabel('Quick action'),
withCloseOnCreate(true),
withActionPosition('top'),
withLiveInputFallback(false),
),
],
});Provider[]provideActionSelectConfigAt(...features: undefined)Component-scoped action-select config. Returns Provider[] because
viewProviders rejects EnvironmentProviders.
CngxActionSelectConfigFeaturewithActionAriaLabel(label: string)Sets the action-slot ARIA label.
CngxActionSelectConfigFeaturewithActionPopoverPlacement(placement: PopoverPlacement)Sets the popover placement for the action organisms.
CngxActionSelectConfigFeaturewithActionPosition(position: CngxActionPosition)Sets the default *cngxSelectAction slot position.
CngxActionSelectConfigFeaturewithCloseOnCreate(closeOnCreate)Forces closeOnCreate across both action organisms. Pass null to
restore the variant baselines.
CngxActionSelectConfigFeaturewithFocusTrapBehavior(behavior: CngxActionFocusTrapBehavior)CngxActionSelectConfigFeaturewithLiveInputFallback(enabled: boolean)Sets the live-input fallback policy. Disable when consumer owns debouncing.
select/shared/config.ts
provideSelectConfig ProviderprovideSelectConfigAt ProviderwithAnnouncer FeaturewithAriaLabels FeaturewithCaret FeaturewithChipOverflow FeaturewithCommitErrorAnnouncePolicy FeaturewithCommitErrorDisplay FeaturewithDismissOn FeaturewithEnterKeyHint FeaturewithFallbackLabels FeaturewithInputMode FeaturewithLoadingVariant FeaturewithMaxVisibleChips FeaturewithOpenOn FeaturewithPanelClass FeaturewithPanelWidth FeaturewithPopoverPlacement FeaturewithRefreshingVariant FeaturewithRestoreFocus FeaturewithSelectionIndicator FeaturewithSelectionIndicatorPosition FeaturewithSelectionIndicatorVariant FeaturewithSkeletonRowCount FeaturewithTypeaheadDebounce FeaturewithTypeaheadWhileClosed FeaturewithVirtualization FeatureEnvironmentProvidersprovideSelectConfig(...features: undefined)App-wide defaults for the Select family. provideSelectConfigAt and
per-instance inputs win.
Provider[]provideSelectConfigAt(...features: undefined)Component-scoped config. Returns Provider[] because viewProviders
rejects EnvironmentProviders.
CngxSelectConfigFeaturewithAnnouncer(config: CngxSelectAnnouncerConfig)Configure the live-region announcer used for selection changes.
CngxSelectConfigFeaturewithAriaLabels(labels: CngxSelectAriaLabels)Sets ARIA-label overrides. Partial. Per-instance inputs win.
bootstrapApplication(App, {
providers: [
provideSelectConfig(
withAriaLabels({
clearButton: 'Clear selection',
chipRemove: 'Remove',
}),
),
],
});CngxSelectConfigFeaturewithCaret(enabled: boolean)Whether the trigger's dropdown caret glyph is rendered. Default true.
CngxSelectConfigFeaturewithChipOverflow(mode: NonNullable)Sets the chip-strip overflow mode. Default 'wrap'.
NonNullableCngxSelectConfigFeaturewithCommitErrorAnnouncePolicy(policy)Forces the scalar-commit error-announce policy. null restores each
variant's baseline.
CngxSelectConfigFeaturewithCommitErrorDisplay(display: CngxSelectCommitErrorDisplay)Default [commitAction] error surface. Default 'banner'.
CngxSelectConfigFeaturewithDismissOn(mode)Dismiss strategy for the panel. Default 'both'.
CngxSelectConfigFeaturewithEnterKeyHint(hint)Forces enterkeyhint across input-trigger variants. null restores
each variant's baseline.
CngxSelectConfigFeaturewithFallbackLabels(labels: CngxSelectFallbackLabels)Sets the panel-shell visible-fallback labels. Partial. Per-instance template projection wins.
bootstrapApplication(App, {
providers: [
provideSelectConfig(
withFallbackLabels({
loadFailed: 'Échec du chargement',
loadFailedRetry: 'Réessayer',
empty: 'Aucune option',
}),
),
],
});CngxSelectConfigFeaturewithInputMode(mode: NonNullable)Sets inputmode for input-trigger variants. Default 'search'.
NonNullableCngxSelectConfigFeaturewithLoadingVariant(variant: CngxSelectLoadingVariant)First-load indicator variant. Default 'spinner'.
CngxSelectConfigFeaturewithMaxVisibleChips(count: number)Sets maxVisibleChips for truncate mode. Coerces ≤ 0 to 1. Default 3.
CngxSelectConfigFeaturewithOpenOn(mode)Open strategy for the trigger. Default 'click'.
CngxSelectConfigFeaturewithPanelClass(panelClass)Class list applied to every select panel.
CngxSelectConfigFeaturewithPanelWidth(width)Panel width: 'trigger' (match), fixed px number, or null (natural).
CngxSelectConfigFeaturewithPopoverPlacement(placement: PopoverPlacement)Default popover placement for flat variants. Default 'bottom'.
Action organisms use withActionPopoverPlacement.
CngxSelectConfigFeaturewithRefreshingVariant(variant: CngxSelectRefreshingVariant)Sets the refreshing indicator. Default 'bar'. 'none' suppresses.
CngxSelectConfigFeaturewithRestoreFocus(enabled: boolean)Whether the trigger is re-focused after the panel closes. Default true.
CngxSelectConfigFeaturewithSelectionIndicator(enabled: boolean)Whether the selected-option checkmark is rendered at all. Default true.
CngxSelectConfigFeaturewithSelectionIndicatorPosition(position: CngxSelectSelectionIndicatorPosition)Sets the selection-indicator position. Default 'before'.
CngxSelectConfigFeaturewithSelectionIndicatorVariant(variant: CngxSelectSelectionIndicatorVariant)Sets the selection-indicator glyph. Default 'auto'.
CngxSelectConfigFeaturewithSkeletonRowCount(count: number)Sets skeletonRowCount. Default 3.
CngxSelectConfigFeaturewithTypeaheadDebounce(ms: number)Typeahead buffer debounce in ms. Default 300.
CngxSelectConfigFeaturewithTypeaheadWhileClosed(enabled: boolean)Typeahead-while-closed (native <select> parity). Default true.
CngxSelectConfigFeaturewithVirtualization(config)Opts in to recycler virtualisation. {} / true uses defaults;
null / false falls back to identity. Custom pipelines provide
CNGX_PANEL_RENDERER_FACTORY directly.
provideSelectConfig(
withVirtualization({ estimateSize: 36, overscan: 8, threshold: 500 }),
)trueselect/shared/reorderable-select-config.ts
provideReorderableSelectConfigOpen detail pageprovideReorderableSelectConfigAtOpen detail pagewithDefaultDragHandleOpen detail pagewithReorderAriaLabelOpen detail pagewithReorderKeyboardModifierOpen detail pagewithReorderStripFreezeOpen detail page
provideReorderableSelectConfig ProviderprovideReorderableSelectConfigAt ProviderwithDefaultDragHandle FeaturewithReorderAriaLabel FeaturewithReorderKeyboardModifier FeaturewithReorderStripFreeze FeatureEnvironmentProvidersprovideReorderableSelectConfig(...features: undefined)App-wide defaults for reorder-aware select variants.
provideReorderableSelectConfigAt and per-instance inputs win.
bootstrapApplication(App, {
providers: [
provideReorderableSelectConfig(
withReorderKeyboardModifier('alt'),
withReorderAriaLabel('Reorder with Alt + arrow keys'),
withReorderStripFreeze(false),
),
],
});Provider[]provideReorderableSelectConfigAt(...features: undefined)Component-scoped reorderable-select config. Returns Provider[]
because viewProviders rejects EnvironmentProviders.
CngxReorderableSelectConfigFeaturewithDefaultDragHandle(template)Sets the default drag-handle glyph app-wide via TemplateRef<void>.
CngxReorderableSelectConfigFeaturewithReorderAriaLabel(label: string)Sets the chip-strip ARIA label.
CngxReorderableSelectConfigFeaturewithReorderKeyboardModifier(modifier: CngxReorderModifier)Sets the keyboard modifier gating reorder moves.
CngxReorderableSelectConfigFeaturewithReorderStripFreeze(freeze: boolean)Sets strip-freeze-on-commit. false lets reorders supersede in-flight
commits via the commit-controller's supersede semantics.
forms/input/file-drop.directive.ts
select/shared/option.model.ts
filterSelectOptionsOpen detail pageflattenSelectOptionsOpen detail pageisCngxSelectOptionGroupDefOpen detail pagemergeLocalItemsOpen detail page
CngxSelectOptionsInputfilterSelectOptions(input: CngxSelectOptionsInput, term: string, match)Filters by term using a listbox match fn. Preserves group shape;
empty groups dropped. The matcher payload's id: '' is synthetic; real
DOM ids come from CngxOption, and in-tree matchers ignore the field.
CngxSelectOptionDef[]flattenSelectOptions(input: CngxSelectOptionsInput)Flattens grouped + flat input. Used by keyboard nav and compare lookups.
CngxSelectOptionGroupDefisCngxSelectOptionGroupDef(item)Group / flat-option type guard.
isOptionDisabled(option)Disabled-check across both option shapes:
CngxSelectOptionDef.disabled- plainboolean(data-driven)CngxOption.disabled-InputSignal<boolean>(element-driven, callable)
Direct .disabled access would treat every signal as truthy (TS2774).
CngxSelectOptionsInputmergeLocalItems(provided: CngxSelectOptionsInput, localItems, compareWith)Folds a local-items buffer onto server-provided options, deduped by
value via compareWith. Group shape preserved; locals appended flat
after groups. Server wins on collision - locals matching a provided
value drop silently.
Identity-stable when localItems is empty (returns provided).
data-display/treetable/tree.utils.ts
filterTreeOpen detail pageflattenTreeOpen detail pagenodeMatchesSearchOpen detail pagesortTreeOpen detail page
filterTree(nodes, predicate)Filters a tree recursively. A parent node is kept if it matches the predicate OR if at least one of its descendants matches.
FlatNode[]flattenTree(input, nodeId?)Flattens a tree (or forest) into a depth-first ordered array of FlatNodes.
- A single root node or an array of root nodes.
- Optional function to derive a stable ID from the node value and
its path in the tree. When omitted, IDs are the path indices joined by
"-"(e.g."0","0-1","0-1-2").
A flat array where every node contains its depth, parent ID chain, and a flag indicating whether it has children.
nodeMatchesSearch(value: T, term: string)Simple full-text search across all primitive fields of a node value. Used as the default search implementation in CngxSmartDataSource.
Tforms/field/focus-first-error.ts
focusFirstError(tree: FieldTree)Focuses the first invalid leaf field's bound control after form submission.
Uses errorSummary() from the root FieldState to find all descendant errors,
then iterates to find the first one with a bound UI control and calls
focusBoundControl() on it. Skips group-level validators that have no
bound control (where focusBoundControl() would be a no-op).
Call after submit() fails or after manually touching all fields.
The root FieldTree of the form.
true if a field was focused, false if no focusable error found.
async handleSubmit() {
const success = await submit(this.loginForm, async () => { ... });
if (!success) {
focusFirstError(this.loginForm);
}
}data/async-state/from-http-resource.ts
CngxAsyncStatefromHttpResource(ref: HttpResourceLike)Bridge that projects an Angular httpResource() ref onto CngxAsyncState<T>.
Identical to fromResource but additionally maps the HTTP progress
signal to CngxAsyncState.progress (0–100 scale).
Must be called in an injection context.
private readonly res = httpResource<Item[]>(() => ({
url: '/api/items',
params: { q: this.filter() },
}));
readonly items = fromHttpResource(this.res);
// items.progress() tracks upload/download progress
// <cngx-progress [state]="items"> — auto-wired progress barinterop/query/from-query.ts
CngxAsyncStatefromQuery(query: CngxQueryLike)Bridge that projects a TanStack Query result onto CngxAsyncState<T>.
Reads the query's signal-bag and maps TanStack's status / fetchStatus
pair onto the cngx AsyncStatus union, then hands the derived signals to
buildAsyncStateView - the same single-source-of-truth kernel every other
producer uses. No injection context is required (only computed()), and no
boolean view is re-derived here.
Status mapping:
error->errorsuccess+fetching->refreshing(background refetch, data visible)success+ idle/paused ->successpending+fetching->loading(first load, no data yet)pending+ idle/paused ->idle(disabled or paused query)
private readonly query = injectQuery(() => ({
queryKey: ['users', this.filter()],
queryFn: () => fetchUsers(this.filter()),
}));
readonly users = fromQuery(this.query);
// users.status(), users.data(), users.isFirstLoad() - all work
// <cngx-async-container [state]="users"> - direct bindingdata/async-state/from-resource.ts
CngxAsyncStatefromResource(ref: Resource)Bridge that projects an Angular Resource<T> onto CngxAsyncState<T>.
Must be called in an injection context (the effect() that tracks
hadSuccess requires one).
All signals are derived reactively from the resource — no manual synchronization. The resource stays the single source of truth.
private readonly res = resource({
request: () => ({ filter: this.filter() }),
loader: ({ request, abortSignal }) =>
fetch(`/api/items?q=${request.filter}`, { signal: abortSignal })
.then(r => r.json()),
});
readonly items = fromResource(this.res);
// items.status(), items.data(), items.isFirstLoad() — all work
// <cngx-async-container [state]="items"> — direct bindingcore/utils/transition.util.ts
hasTransition(el: HTMLElement)Checks whether an element has any CSS transition applied.
HTMLElementtrue if at least one transition-duration value is greater than 0.
onTransitionDone(el: HTMLElement, onDone)Listens for the longest CSS transition on an element, then invokes onDone.
Automatically falls back to a timeout if transitionend never fires.
HTMLElementA cleanup function that removes the listener and clears the fallback timer.
interactive/accordion/accordion.directive.ts
headersEqual(a, b)Order-independent identity-set equality for the header registry. Two arrays
are equal when they hold the same handle references, regardless of
registration order. Handles are stable per panel (carrying disabled by
signal reference), so this comparator only suppresses re-fires from a
re-register of the same set; it does not gate the roving derivation on
disabled state - rovingActiveId reads each handle's disabled() directly,
so a disabled flip still re-runs it (which is what keeps the tab stop off a
newly-disabled header).
accordion/config/inject-accordion-config.ts
injectAccordionConfig Injectv0.1.0CngxAccordionConfigConvenience accessor for the accordion configuration cascade. Runs in
injection context; resolves through the priority chain (per-instance Input
-> provideAccordionConfigAt -> provideAccordionConfig -> library
defaults). Equivalent to inject(CNGX_ACCORDION_CONFIG) - the helper exists
so consumers don't import the token directly. Mirrors injectBreadcrumbConfig
in @cngx/ui/breadcrumb.
export class CngxAccordionItem {
private readonly config = injectAccordionConfig();
readonly disabledReason = input<string>(this.config.disabledReason);
}data/async-registry/provide-async-registry.ts
injectAsyncRegistry InjectprovideAsyncRegistry ProviderCngxAsyncRegistry | nullReturns the ambient CngxAsyncRegistry, or null when the consumer
did not opt in via provideAsyncRegistry. Must run in an injection
context.
EnvironmentProvidersProvides CngxAsyncRegistry in the host environment.
Opt-in - not providedIn: 'root'. Add it to bootstrapApplication
providers to enable app-wide async observability; producers that opt in
(injectAsyncState({ register: true }), provideAsyncHttpObservability())
then surface in isAnythingLoading() / activeOperations().
bootstrapApplication(AppComponent, {
providers: [provideAsyncRegistry()],
});data/async-state/inject-async-state.ts
injectAsyncState InjectReactiveAsyncStateinjectAsyncState(fn, options?: InjectAsyncStateOptions)Create a reactive async state that auto-loads when signal dependencies change.
Must be called in an injection context (field initializer or constructor).
The computation function is tracked by Angular's effect() — any signal
read inside fn will cause a re-load when it changes.
readonly residents = injectAsyncState(
() => this.api.getResidents(this.filter())
);
// Loads automatically when filter() changes.
// Caches last result during refresh (refreshing state).
// Deduplicates parallel requests.audio/config/audio-config.ts
injectAudioConfigOpen detail pageprovideCngxAudioOpen detail pagewithDebounceMsOpen detail pagewithEarconsOpen detail pagewithMutedOpen detail pagewithRespectReducedMotionOpen detail pagewithVolumeOpen detail page
injectAudioConfig Injectv0.1.0provideCngxAudio Providerv0.1.0withDebounceMs Featurev0.1.0withEarcons Featurev0.1.0withMuted Featurev0.1.0withRespectReducedMotion Featurev0.1.0withVolume Featurev0.1.0CngxAudioConfigResolve the effective audio config: library defaults merged with whatever
provideCngxAudio(...) contributed. Runs in an injection context.
EnvironmentProvidersprovideCngxAudio(...features: undefined)Register global audio defaults. Mirrors provideFeedback — a single small
config object folded through with* features.
bootstrapApplication(AppComponent, {
providers: [
provideCngxAudio(
withVolume(0.6),
withEarcons({ send: { sequence: [{ freq: 880, duration: 60 }] } }),
),
],
});Register this at application bootstrap. The shared engine is
providedIn: 'root' and reads its configuration once, when it is first built,
so config registered in a lazy route's providers never reaches it. To
configure a subtree, use provideCngxAudioAt(...) in viewProviders — it
layers over this one and re-provides the engine so the scope actually applies.
CngxAudioFeaturewithDebounceMs(ms: number)Set the same-name suppression window in milliseconds.
CngxAudioFeaturewithEarcons(earcons: Record)Register or override earcons globally. Merged over the six built-ins and over
any earlier withEarcons in the same provideCngxAudio call.
RecordCngxAudioFeaturewithMuted(muted: unknown)Start muted (or explicitly unmuted with withMuted(false)).
unknown= trueCngxAudioFeaturewithRespectReducedMotion(respect: boolean)Toggle the prefers-reduced-motion mute gate. true by default in the
library; pass false to keep audio playing under reduced-motion.
breadcrumb/config/inject-breadcrumb-config.ts
injectBreadcrumbConfig Injectv0.1.0CngxBreadcrumbConfigConvenience accessor for the breadcrumb configuration cascade. Runs in
injection context; resolves through the priority chain (per-instance Input
-> provideBreadcrumbConfigAt -> provideBreadcrumbConfig -> library
defaults). Equivalent to inject(CNGX_BREADCRUMB_CONFIG) - the helper
exists so consumers don't import the token directly. Mirrors
injectTagConfig in @cngx/common/display.
export class CngxBreadcrumbBar {
private readonly cfg = injectBreadcrumbConfig();
readonly label = input<string>(this.cfg.ariaLabels?.bar ?? 'Breadcrumb');
}chart/chart/chart-context.ts
injectChartContext InjectCngxChartContextinjectChartContext(consumerName: string)Inject the parent chart's reactive context. Throws a clear dev-mode
error when the consumer is not mounted as a content child of
<cngx-chart>. The consumerName argument is interpolated into the
error message so the consumer-class name surfaces at the call site
rather than a generic guard string.
Replaces six verbatim copies of the same six-line helper that previously lived inline in every layer atom.
common/audio/inject-audio.ts
injectCngxAudio Injectv0.1.0provideCngxAudioAt Providerv0.1.0CngxAudioHandleResolve the shared audio handle. Must run in an injection context (field initialiser or constructor).
export class MyToggle {
private readonly audio = injectCngxAudio();
protected readonly muted = this.audio.muted;
protected toggle(): void {
this.audio.setMuted(!this.muted());
}
}Provider[]provideCngxAudioAt(...features: undefined)Component-scope audio config — place in a component's viewProviders so a
subtree runs its own muted / volume / earcon set, independent of the app-wide
provideCngxAudio(...).
data-grid-accordion/config/inject-data-grid-accordion-config.ts
injectDataGridAccordionConfig Injectv0.1.0CngxDataGridAccordionConfigConvenience accessor for the data-grid-accordion configuration cascade. Runs in
injection context; resolves through the priority chain (per-instance Input
-> provideDataGridAccordionConfigAt -> provideDataGridAccordionConfig ->
library defaults). Equivalent to inject(CNGX_DATA_GRID_ACCORDION_CONFIG) -
the helper exists so consumers don't import the token directly. Mirrors
injectAccordionConfig in @cngx/ui/accordion.
export class CngxDataGridAccordion {
private readonly config = injectDataGridAccordionConfig();
readonly skin = input<CngxDataGridSkin | undefined>(undefined);
}data/data-source/data-source.ts
injectDataSource InjectCngxDataSourceinjectDataSource(data: Signal)Factory function for CngxDataSource.
Must be called within an injection context (constructor or field initializer).
readonly dataSource = injectDataSource(this.items);core/theming/density.ts
injectDensity Injectv0.1.0provideDensity Providerv0.1.0WritableSignalRead the app-wide density signal in an injection context. The
returned signal is writable, so injectDensity().set('spacious')
re-densifies the whole document reactively.
EnvironmentProvidersprovideDensity(initial: CngxDensityValue)Install the density preference at app root and reflect it onto
<html data-density>, mirroring how the colour ramp is applied by
attribute. The reflector is an effect (not afterNextRender) so it
re-runs on every runtime injectDensity().set(...); the DOM write is
wrapped in untracked() per the signal-architecture rules.
bootstrapApplication(AppComponent, {
providers: [provideDensity('compact')],
});forms/filter-builder/filter-builder.config.ts
injectFilterBuilderConfigOpen detail pageisNativeEditorOpen detail pageprovideFilterBuilderConfigOpen detail pageprovideFilterBuilderConfigAtOpen detail pagewithDefaultOperatorsOpen detail pagewithFilterBuilderI18nOpen detail pagewithLogicOptionsOpen detail pagewithMaxNestingDepthOpen detail pagewithNegationOpen detail pagewithSkeletonCountOpen detail pagewithTemplatesOpen detail page
injectFilterBuilderConfig InjectprovideFilterBuilderConfig ProviderprovideFilterBuilderConfigAt ProviderwithDefaultOperators FeaturewithFilterBuilderI18n FeaturewithLogicOptions FeaturewithMaxNestingDepth FeaturewithNegation FeaturewithSkeletonCount FeaturewithTemplates FeatureCngxFilterBuilderConfigInject-context helper that resolves CNGX_FILTER_BUILDER_CONFIG.
CngxFilterNativeEditorisNativeEditor(value: CngxFilterEditor)Narrowing helper for CngxFilterEditor.
EnvironmentProvidersprovideFilterBuilderConfig(...features: undefined)Root / environment-level config. Compose with withFilterBuilderI18n(...), withNegation(true), etc.
Provider[]provideFilterBuilderConfigAt(...features: undefined)Component/route-level config - same shape as provideFilterBuilderConfig but for non-environment injectors.
CngxFilterBuilderConfigFeaturewithDefaultOperators(operators: Readonly)Extend or override the default operator lists keyed by editor type.
ReadonlyCngxFilterBuilderConfigFeaturewithFilterBuilderI18n(partial: Partial)Override any subset of the i18n bundle. operators is shallow-merged.
PartialCngxFilterBuilderConfigFeaturewithLogicOptions(logics)Restrict which logic operators (and / or / xor) appear in the group toggle.
CngxFilterBuilderConfigFeaturewithMaxNestingDepth(depth: number)Cap the nesting depth of the builder tree. Default 8.
CngxFilterBuilderConfigFeaturewithNegation(enabled: boolean)Reveal the per-group negation toggle. Off by default.
CngxFilterBuilderConfigFeaturewithSkeletonCount(count: number)Number of skeleton rows the builder renders while async fields are loading.
CngxFilterBuilderConfigFeaturewithTemplates(templates: CngxFilterBuilderTemplates)Register global template overrides - keyed fallback below per-instance content-child slots.
forms/filter-builder/filter-builder.tokens.ts
injectFilterEditors InjectReadonlyMapInject the resolved editor registry. Sugar over
inject(CNGX_FILTER_EDITORS). Exists so consumers can read editors
without importing the token directly when they already inject other
filter-builder helpers (e.g. inside a slot directive context).
Snapshot semantics. The returned ReadonlyMap is captured at
injection time. Runtime swaps of CNGX_FILTER_EDITORS in nested DI
scopes are NOT observed by an already-injected consumer. If a
consumer needs reactive editor swap behaviour, re-inject() the
token at the swap-boundary's injector instead of cached references.
layout/observers/inject-media-query.ts
injectMediaQuery Injectv0.1.0SignalinjectMediaQuery(query: string)Inject-form of CngxMediaQuery: returns a reactive
Signal<boolean> that reflects whether the host window currently matches
a CSS media query, without needing a host element.
Use it where the directive form has nowhere to attach - inside a route
guard, a store, a computed(), or any injection context that reacts to a
viewport or preference query. For host-bound templates, prefer the
[cngxMediaQuery] directive; for pure styling, prefer CSS @media /
@container.
Wraps window.matchMedia(): seeds the signal from MediaQueryList.matches,
updates on the change event, and removes the listener via DestroyRef
when the injection scope is destroyed. In SSR / non-DOM environments (no
defaultView or no matchMedia) it returns a static false signal and
wires no listener, so it never throws off the browser.
Returns a Signal<boolean> - never an Observable; the reactive boundary
stays inside cngx.
A CSS media query string, e.g. (max-width: 640px).
A reactive Signal<boolean> tracking the query's match state.
interactive/menu/menu-config.ts
injectMenuConfigOpen detail pageprovideMenuConfigOpen detail pageprovideMenuConfigAtOpen detail page
injectMenuConfig InjectprovideMenuConfig ProviderprovideMenuConfigAt ProviderCngxMenuConfigResolves the CngxMenuConfig from the current injection scope. Must run inside an injection context.
EnvironmentProvidersprovideMenuConfig(...features: undefined)Provide a menu configuration at app root. Each feature is a partial
override produced by with* helpers (see menu-config-features.ts).
bootstrapApplication(AppComponent, {
providers: [
provideMenuConfig(
withAriaLabels({ submenuOpened: 'Untermenü geöffnet' }),
withTypeaheadDebounce(500),
),
],
});Provider[]provideMenuConfigAt(...features: undefined)Component-scoped menu configuration override. Pass into a directive or
component's viewProviders; features merge on top of the parent
config (root or an enclosing scope).
interactive/nav/nav-config.ts
injectNavConfigOpen detail pageprovideNavConfigOpen detail pagewithNavAnimationOpen detail pagewithNavIndentOpen detail pagewithSingleAccordionOpen detail page
injectNavConfig InjectprovideNavConfig ProviderwithNavAnimation FeaturewithNavIndent FeaturewithSingleAccordion FeatureNavConfigFeatureEnables single-accordion mode - only one nav group can be open at a time.
layout/router/inject-query-param-sync.ts
injectQueryParamSync Injectv0.1.0injectQueryParamSync(state: WritableSignal, opts: QueryParamSyncOptions)Bidirectional sync between a caller-owned WritableSignal<T> and a named
query param. URL wins on initial load (deep-link intent); after hydrate the
signal is the source and reflects outward. Re-hydrates on browser
back/forward. Dev-warns once and no-ops when @angular/router is absent.
Loop-safety (the core of this helper): the reflect effect writes the URL in
untracked() and skips navigate when the serialized state() equals the
last value it wrote - a local closure seeded from the initial URL read, not
the live URL param (the live param races the navigate-settle window). The
hydrate path updates that same closure whenever it accepts a URL value, so
back/forward re-hydration never triggers a reflex navigate. A param rename
is migrated in one navigate ({ [old]: null, [new]: serialize(state()) })
by a dedicated effect whose only tracked source is the normalized param
signal; its first run seeds bookkeeping and never navigates.
The state.set inside the hydrate effect is a deliberate signal-write-in-
effect: the URL is an external system and state is a caller-owned signal
the consumer must stay able to toggle, so hydrate cannot be a computed. It
is edge-guarded by serialized equality and wrapped in untracked().
data/recycler/recycler.ts
injectRecycler InjectprovideRecyclerI18n ProviderCngxRecyclerinjectRecycler(config: RecyclerConfig)Creates a Signal-based virtualizer for DOM recycling.
Must be called in an injection context (field initializer or constructor).
Internally injects DestroyRef and DOCUMENT for cleanup and selector resolution.
Basic list
readonly recycler = injectRecycler({
scrollElement: '.scroll-container',
totalCount: () => this.items().length,
estimateSize: 48,
});
readonly visibleItems = this.recycler.sliced(this.items);With async state
readonly state = injectAsyncState(() => this.api.getAll());
readonly recycler = injectRecycler({
scrollElement: '.scroll-container',
totalCount: () => (this.state.data() ?? []).length,
estimateSize: 64,
state: this.state,
});provideRecyclerI18n(i18n: RecyclerI18n)Provider function for custom recycler i18n texts.
providers: [provideRecyclerI18n({
loaded: (n, t) => `${n} weitere Einträge. ${t} gesamt.`,
filtered: (c) => `${c} Ergebnisse.`,
empty: () => 'Keine Ergebnisse.',
error: () => 'Fehler beim Laden.',
})]select/shared/inject-helpers.ts
injectSelectAnnouncer InjectinjectSelectConfig InjectCngxSelectAnnouncerRoot-scoped CngxSelectAnnouncer for custom composites that want to share the family live-region.
const announcer = injectSelectAnnouncer();
announcer.announce('Filter applied: Red', 'polite');ReturnTypeEffective select config for the current injector, merged with library
defaults. Always fully populated - never null. Injection context required.
import { injectSelectConfig } from '@cngx/forms/select';
export class MyComposite {
private readonly config = injectSelectConfig();
protected readonly panelWidth = this.config.panelWidth;
}sidenav/config/inject-sidenav-config.ts
injectSidenavConfig Injectv0.1.0common/stepper/stepper-config.ts
injectStepperConfigOpen detail pageprovideStepperConfigOpen detail pageprovideStepperConfigAtOpen detail pagewithDotStepperDotTemplateOpen detail pagewithStepBadgeTemplateOpen detail pagewithStepBusySpinnerTemplateOpen detail pagewithStepErrorTemplateOpen detail pagewithStepGroupHeaderTemplateOpen detail pagewithStepIndicatorTemplateOpen detail pagewithStepperAriaLabelsOpen detail pagewithStepperCommitModeOpen detail pagewithStepperConnectorsOpen detail pagewithStepperDefaultOrientationOpen detail pagewithStepperDensityOpen detail pagewithStepperEmptyTemplateOpen detail pagewithStepperFallbackLabelsOpen detail pagewithStepperGroupCollapseOpen detail pagewithStepperGroupCollapseSummaryOpen detail pagewithStepperHeaderNavigationOpen detail pagewithStepperLinearOpen detail pagewithStepperMobileBreakpointOpen detail pagewithStepperMobileCollapseOpen detail pagewithStepperMobileIndicatorPositionOpen detail pagewithStepperMobileSwipeOpen detail pagewithStepperRouterSyncOpen detail pagewithStepperSkinOpen detail pagewithStepRejectionTemplateOpen detail page
injectStepperConfig InjectprovideStepperConfig ProviderprovideStepperConfigAt ProviderwithDotStepperDotTemplate FeaturewithStepBadgeTemplate FeaturewithStepBusySpinnerTemplate FeaturewithStepErrorTemplate FeaturewithStepGroupHeaderTemplate FeaturewithStepIndicatorTemplate FeaturewithStepperAriaLabels FeaturewithStepperCommitMode FeaturewithStepperConnectors FeaturewithStepperDefaultOrientation FeaturewithStepperDensity FeaturewithStepperEmptyTemplate FeaturewithStepperFallbackLabels FeaturewithStepperGroupCollapse FeaturewithStepperGroupCollapseSummary FeaturewithStepperHeaderNavigation FeaturewithStepperLinear FeaturewithStepperMobileBreakpoint FeaturewithStepperMobileCollapse FeaturewithStepperMobileIndicatorPosition FeaturewithStepperMobileSwipe FeaturewithStepperRouterSync FeaturewithStepperSkin FeaturewithStepRejectionTemplate FeatureCngxStepperConfigInject the resolved stepper config in an injection context.
EnvironmentProvidersprovideStepperConfig(...features: undefined)Root-level provider for the stepper config. Apply once in the
application providers array. Sibling of provideTabsConfig /
provideSelectConfig.
Provider[]provideStepperConfigAt(...features: undefined)Component-scoped config override. Spread the returned Provider[]
into providers or viewProviders - viewProviders cannot accept
opaque EnvironmentProviders, so the twin returns a list.
Sibling of provideTabsConfigAt.
Resolution priority: per-instance Input > viewProviders (At) >
root provider > library default.
CngxStepperConfigFeaturewithDotStepperDotTemplate(template: TemplateRef)Override the default *cngxDotStepperDot template app-wide. The
<cngx-dot-stepper> variant resolves the cascade per dot:
per-instance directive > this feature > built-in empty body
(the dot fill is painted by .cngx-dot-stepper__dot regardless).
CngxStepperConfigFeaturewithStepBadgeTemplate(template: TemplateRef)Override the default *cngxStepBadge template app-wide.
CngxStepperConfigFeaturewithStepBusySpinnerTemplate(template: TemplateRef)Override the default *cngxStepBusySpinner template app-wide.
CngxStepperConfigFeaturewithStepErrorTemplate(template: TemplateRef)Override the default *cngxStepError template app-wide. The slot
renders the per-step validation reason on the skins with a label area
(classic / stripe-status-rich). Per-instance directive still wins;
this moves the cascade middle tier.
CngxStepperConfigFeaturewithStepGroupHeaderTemplate(template: TemplateRef)Override the default *cngxStepGroupHeader template app-wide.
CngxStepperConfigFeaturewithStepIndicatorTemplate(template: TemplateRef)Override the default *cngxStepIndicator template app-wide.
Per-instance directive still wins; this only moves the cascade
middle tier.
CngxStepperConfigFeaturewithStepperAriaLabels(labels: CngxStepperAriaLabels)Merge ARIA labels into the cascade. Keys not provided keep their library defaults; per-instance overrides on the stepper still win.
CngxStepperConfigFeaturewithStepperCommitMode(mode)Override the default commit mode for async step transitions.
'optimistic' advances on action dispatch and rolls back on error;
'pessimistic' waits for success. Per-instance [commitMode]
Input still wins.
CngxStepperConfigFeaturewithStepperConnectors(on: boolean)Opt the classic <cngx-stepper> skin into the connector-rail
presentation: a solid completed/upcoming rail between adjacent step
indicators, horizontal and vertical, with per-segment coloring driven
by the preceding step's [data-state]. The rule is double-scoped on
[data-skin='classic']; the four other skins each ship their own
inter-step decoration and ignore this flag. Per-instance
[connectors] Input still wins.
CngxStepperConfigFeaturewithStepperDefaultOrientation(orientation)Override the default orientation. Per-instance [orientation]
Input still wins; this only moves the cascade default.
CngxStepperConfigFeaturewithStepperDensity(mode: CngxStepperDensity, breakpoints?: CngxStepperDensityBreakpoints)Set the app-wide space-driven density policy for the classic
<cngx-stepper> strip. 'auto' degrades labels on the strip's own
container width (full -> compact -> minimal); 'comfortable'
(library default) keeps full labels at every width. The optional
second argument overrides the per-step px thresholds (default
STEPPER_DEFAULT_DENSITY_BREAKPOINTS). Orthogonal to the
mobileCollapse axis; the Material twin ignores this setting.
CngxStepperConfigFeaturewithStepperEmptyTemplate(template: TemplateRef)Override the default *cngxStepperEmpty template app-wide.
CngxStepperConfigFeaturewithStepperFallbackLabels(labels: CngxStepperFallbackLabels)Merge text fallback labels (button captions, error strings) into the cascade. Used when a slot directive is not present.
CngxStepperConfigFeaturewithStepperGroupCollapse(mode: CngxStepperGroupCollapse)Set the app-wide focus-driven group-collapse policy for grouped
<cngx-stepper> flows. 'expand-active' collapses every non-active
cngxStepGroup branch to its header node and expands only the group
holding the active step; 'off' (library default) keeps the full
strip. Orthogonal to the mobileCollapse axis. The Material twin
ignores this setting.
CngxStepperConfigFeaturewithStepperGroupCollapseSummary(mode: CngxStepperGroupSummary)Choose what a collapsed cngxStepGroup header shows beside its label
under groupCollapse: 'expand-active'. 'progress' (library default)
shows completed/total steps; 'count' the step count; 'status' a
status dot coloured by the group's rolled-up state; 'off' the bare
label. No effect unless groupCollapse is 'expand-active'.
CngxStepperConfigFeaturewithStepperLinear(linear: boolean)Override the default linear-progression setting. Per-instance
[linear] Input still wins.
CngxStepperConfigFeaturewithStepperMobileBreakpoint(query: string)Override the media query the mobile auto-collapse policy reacts to.
Default '(max-width: 480px)'. Useful for tablet-tier consumers
who want the collapse to engage at 768px or for design systems
aligned with --mat-sys-breakpoint-* tokens.
CngxStepperConfigFeaturewithStepperMobileCollapse(mode: CngxStepperMobileCollapse)Configure the mobile auto-collapse target for <cngx-stepper>. Under
a narrow viewport (max-width: 480px), the classic strip swaps to
the chosen variant - 'text' (default) renders <cngx-text-stepper>,
'dots' renders <cngx-dot-stepper>, 'off' keeps the classic
strip. The Material twin <cngx-mat-stepper> ignores this setting.
CngxStepperConfigFeaturewithStepperMobileIndicatorPosition(position: CngxStepperMobileIndicatorPosition)Set the app-wide default position of the mobile auto-collapse
indicator relative to panel content. 'top' keeps the indicator
above (library default); 'bottom' flips it below so panel content
reads first and the navigation cue sits at thumb height. Per-
instance [mobileIndicatorPosition] on <cngx-stepper> still
wins.
CngxStepperConfigFeaturewithStepperMobileSwipe(enabled: boolean)Toggle the built-in horizontal-swipe navigation on <cngx-stepper>
in mobile-collapse mode. Default true - the 'dots' and 'text'
variants read as carousels, so users expect to swipe. Per-instance
[mobileSwipe] Input still wins; the classic strip is unaffected.
CngxStepperConfigFeaturewithStepperRouterSync(mode, param: string)Configure router synchronisation for the active step. mode chooses
the URL surface (URL fragment or query parameter); param names the
key (default 'step').
CngxStepperConfigFeaturewithStepperSkin(skin: CngxStepperSkin)Select the visual skin for the cngx-standard <cngx-stepper>. The
default is 'classic'. Per-instance [skin] Input still wins; this
moves the cascade default. Structure, slots, ARIA, and keyboard
behaviour are identical across skins - only the [data-skin] host
attribute changes the CSS layer that paints the strip.
CngxStepperConfigFeaturewithStepRejectionTemplate(template: TemplateRef)Override the default *cngxStepRejection template app-wide.
Symmetric with the upcoming tabs cngxTabRejectionIcon (Phase 4).
stepper/i18n/stepper-i18n.ts
injectStepperI18nOpen detail pageprovideStepperI18nOpen detail pagewithStepperI18nLabelsOpen detail page
injectStepperI18n InjectprovideStepperI18n ProviderwithStepperI18nLabels FeatureCngxStepperI18nInject the resolved stepper i18n bundle in an injection context.
provideStepperI18n(...features: undefined)Provider for the stepper i18n bundle. Compose withStepperI18nLabels(...)
(plus any future i18n with*) - unset keys fall back to English.
bootstrapApplication(AppComponent, {
providers: [
provideStepperI18n(
withStepperI18nLabels({ stepperLabel: 'Schrittfolge', previousStep: 'Vorheriger' }),
),
],
});CngxStepperI18nFeaturewithStepperI18nLabels(overrides: CngxStepperI18nOverrides)Override stepper i18n labels. Partial override - unset keys keep
the English default. CngxStepperStatusLabels is merged
key-by-key so consumers can override one pill label without
restating the rest. Sibling of withStepperAriaLabels /
withStepperFallbackLabels.
common/tabs/tabs-config.ts
injectTabsConfigOpen detail pageprovideTabsConfigOpen detail pageprovideTabsConfigAtOpen detail pagewithTabAddIconTemplateOpen detail pagewithTabBusySpinnerTemplateOpen detail pagewithTabCloseIconTemplateOpen detail pagewithTabErrorBadgeTemplateOpen detail pagewithTabIconTemplateOpen detail pagewithTabOverflowItemTemplateOpen detail pagewithTabOverflowMaxDeferMsOpen detail pagewithTabOverflowStabilizeMsOpen detail pagewithTabOverflowTriggerTemplateOpen detail pagewithTabRejectionIconTemplateOpen detail pagewithTabsAddableOpen detail pagewithTabsAlignOpen detail pagewithTabsAriaLabelsOpen detail pagewithTabsClosableOpen detail pagewithTabsCommitModeOpen detail pagewithTabsDefaultOrientationOpen detail pagewithTabsFallbackLabelsOpen detail pagewithTabsFittedOpen detail pagewithTabsFragmentSyncOpen detail pagewithTabsIconLayoutOpen detail pagewithTabsPanelModeOpen detail pagewithTabsRovingLoopOpen detail pagewithTabsSkinOpen detail page
injectTabsConfig InjectprovideTabsConfig ProviderprovideTabsConfigAt ProviderwithTabAddIconTemplate FeaturewithTabBusySpinnerTemplate FeaturewithTabCloseIconTemplate FeaturewithTabErrorBadgeTemplate FeaturewithTabIconTemplate FeaturewithTabOverflowItemTemplate FeaturewithTabOverflowMaxDeferMs FeaturewithTabOverflowStabilizeMs FeaturewithTabOverflowTriggerTemplate FeaturewithTabRejectionIconTemplate FeaturewithTabsAddable FeaturewithTabsAlign FeaturewithTabsAriaLabels FeaturewithTabsClosable FeaturewithTabsCommitMode FeaturewithTabsDefaultOrientation FeaturewithTabsFallbackLabels FeaturewithTabsFitted FeaturewithTabsFragmentSync FeaturewithTabsIconLayout FeaturewithTabsPanelMode FeaturewithTabsRovingLoop FeaturewithTabsSkin FeatureCngxTabsConfigInject the resolved tabs config in an injection context.
EnvironmentProvidersprovideTabsConfig(...features: undefined)Root-level provider. Apply once in bootstrapApplication /
appConfig.providers. Returns EnvironmentProviders per
the canonical cngx config-cascade signature.
Provider[]provideTabsConfigAt(...features: undefined)Component-scoped override. Returns Provider[] (not
EnvironmentProviders) because viewProviders rejects
opaque environment providers. Resolution priority: per-instance
Input > viewProviders (At) > root > default.
CngxTabsConfigFeaturewithTabAddIconTemplate(template: TemplateRef)App-wide override for the add-tab button glyph. Middle tier;
per-instance *cngxTabAddIcon still wins.
CngxTabsConfigFeaturewithTabBusySpinnerTemplate(template: TemplateRef)App-wide override for the commit-pending busy-spinner overlay.
Middle tier; per-instance *cngxTabBusySpinner still wins.
Sibling of withStepBusySpinnerTemplate.
CngxTabsConfigFeaturewithTabCloseIconTemplate(template: TemplateRef)App-wide override for a tab's close-button glyph. Middle tier;
per-instance *cngxTabCloseIcon still wins.
CngxTabsConfigFeaturewithTabErrorBadgeTemplate(template: TemplateRef)App-wide override for the error-badge decoration. Middle tier;
per-instance *cngxTabErrorBadge still wins. Sibling of the
stepper family's withStepBadgeTemplate.
CngxTabsConfigFeaturewithTabIconTemplate(template: TemplateRef)App-wide override for the tab-icon slot. Middle tier of the 3-stage
cascade; per-instance *cngxTabIcon still wins. Sibling of
withStepIndicatorTemplate.
CngxTabsConfigFeaturewithTabOverflowItemTemplate(template: TemplateRef)App-wide override for each row inside the overflow popover.
Middle tier; per-instance *cngxTabOverflowItem still wins.
CngxTabsConfigFeaturewithTabOverflowMaxDeferMs(ms: number)Override the worst-case staleness ceiling on the visibility-map commit. See CngxTabsConfig.overflowMaxDeferMs.
CngxTabsConfigFeaturewithTabOverflowStabilizeMs(ms: number)Override the IO-debounce window (ms). See CngxTabsConfig.overflowStabilizeMs.
CngxTabsConfigFeaturewithTabOverflowTriggerTemplate(template: TemplateRef)App-wide override for the More-button label. Middle tier;
per-instance *cngxTabOverflowTrigger still wins.
readonly moreTrigger = viewChild.required<TemplateRef<CngxTabOverflowTriggerContext>>(
'moreTrigger',
{ read: TemplateRef },
);
providers: [provideTabsConfig(withTabOverflowTriggerTemplate(this.moreTrigger()))]CngxTabsConfigFeaturewithTabRejectionIconTemplate(template: TemplateRef)App-wide override for the rejection-icon decoration. Middle
tier;
per-instance *cngxTabRejectionIcon still wins. Sibling
of withStepRejectionTemplate.
CngxTabsConfigFeaturewithTabsAddable(addable: boolean)Set the app-wide default for whether the group renders an add-tab
button.
Default false. Per-instance [addable] Input wins.
CngxTabsConfigFeaturewithTabsAlign(align: CngxTabAlign)Set the app-wide default tab-cluster alignment (horizontal only).
Default 'start'. Per-instance [tabAlign] Input still wins. Ignored
under orientation="vertical" and when the group is fitted.
CngxTabsConfigFeaturewithTabsAriaLabels(labels: CngxTabsAriaLabels)Merge ARIA labels into the cascade. Keys not provided keep their library defaults; per-instance overrides on the tab group still win.
CngxTabsConfigFeaturewithTabsClosable(closable: boolean)Set the app-wide default for whether tabs render a close affordance.
Default false. Per-instance [closable] Input wins; a per-CngxTab
[closable] override wins over both.
CngxTabsConfigFeaturewithTabsCommitMode(mode)Override the default commit mode for async tab transitions.
'optimistic' activates the tab on action dispatch and rolls back
on error; 'pessimistic' waits for success. Per-instance
[commitMode] Input still wins.
CngxTabsConfigFeaturewithTabsDefaultOrientation(orientation)Override the default orientation. Per-instance [orientation]
still wins; this changes the cascade default only.
CngxTabsConfigFeaturewithTabsFallbackLabels(labels: CngxTabsFallbackLabels)Merge text fallback labels (overflow trigger, busy / rejection text) into the cascade. Used when a slot directive is not present.
CngxTabsConfigFeaturewithTabsFitted(fitted: boolean)Set the app-wide default for stretching tabs to the full strip width
(horizontal only).
Default false. Per-instance [fitted] Input still
wins. No effect under orientation="vertical".
CngxTabsConfigFeaturewithTabsFragmentSync(mode, param: string)Configure URL fragment / query-param deep-linking for the active tab
(the CngxTabsFragmentSync directive). mode chooses the URL surface (URL fragment or query parameter); param names the key
(default 'tab').
Not to be confused with [cngxTabsRouteSync], the router-outlet
integration - this feature configures fragment deep-linking only.
CngxTabsConfigFeaturewithTabsIconLayout(layout: CngxTabIconLayout)Set the app-wide default icon layout for <cngx-tab-group>.
Default'start'. Per-instance [iconLayout] Input still wins. Orthogonal to
skin and orientation - only the [data-icon-layout] host attribute
changes.
CngxTabsConfigFeaturewithTabsPanelMode(mode: CngxTabsPanelMode)Set the app-wide default panel render strategy for <cngx-tab-group>.
Default 'eager' (every panel's content rendered up front, toggled
via [hidden]). 'lazy' keep-alives content after first activation;
'lazy-destroy' renders only the active panel's content. Per-instance
[panelMode] Input still wins. The panel <div> always stays in the
DOM regardless of mode (the aria-controls target).
CngxTabsConfigFeaturewithTabsRovingLoop(loop: boolean)Override whether roving-tabindex navigation loops from last back to
first (and vice versa). Per-instance [loop] Input still wins.
CngxTabsConfigFeaturewithTabsSkin(skin: CngxTabsSkin)Select the app-wide default visual skin for <cngx-tab-group>.
Default 'line'. Per-instance [skin] Input still wins; this moves
the cascade default. Structure, slots, ARIA, and keyboard behaviour
are identical across skins - only the [data-skin] host attribute
changes the CSS layer. The Material twin ([cngxMatTabs]) ignores it.
tabs/i18n/tabs-i18n.ts
injectTabsI18n InjectprovideTabsI18n ProviderwithTabsI18nLabels FeatureCngxTabsI18nInject the resolved tabs i18n bundle.
provideTabsI18n(...features: undefined)Provider for the tabs i18n bundle. Compose withTabsI18nLabels(...)
(and future i18n with* features); unset keys fall back to the
English default.
bootstrapApplication(AppComponent, {
providers: [
provideTabsI18n(
withTabsI18nLabels({ tabsLabel: 'Reiter', previousTab: 'Vorheriger' }),
),
],
});CngxTabsI18nFeaturewithTabsI18nLabels(overrides: Partial)Override i18n labels via a partial bundle - unset keys keep the
English default. Same shape as withTabsAriaLabels /
withTabsFallbackLabels so provideCngxTabs composes both
surfaces uniformly.
Partialtag/config/inject-tag-config.ts
injectTagConfig InjectCngxTagConfigConvenience accessor for the tag-family configuration cascade.
Runs in injection context; resolves through the priority chain
(per-instance Input → provideTagConfigAt → provideTagConfig
→ library defaults).
Equivalent to inject(CNGX_TAG_CONFIG) — the helper exists so
consumers don't need to import the token directly. Mirrors
injectSelectConfig in @cngx/forms/select.
export class MyDirective {
private readonly cfg = injectTagConfig();
readonly variant = input<CngxTagVariant>(
this.cfg.defaults?.variant ?? 'filled',
);
}interactive/tree-controller/tree-config.ts
injectTreeConfigOpen detail pageprovideTreeConfigOpen detail pageprovideTreeConfigAtOpen detail pagewithDefaultInitiallyExpandedOpen detail pagewithDefaultKeyFnOpen detail pagewithDefaultLabelFnOpen detail pagewithDefaultNodeIdFnOpen detail pagewithTreeCacheLimitOpen detail page
injectTreeConfig Injectv0.1.0provideTreeConfig Providerv0.1.0provideTreeConfigAt Providerv0.1.0withDefaultInitiallyExpanded Featurev0.1.0withDefaultKeyFn Featurev0.1.0withDefaultLabelFn Featurev0.1.0withDefaultNodeIdFn Featurev0.1.0withTreeCacheLimit Featurev0.1.0CngxTreeConfigInject the currently-resolved config. Safe to call in any injection
context; returns {} if no provideTreeConfig is registered.
Provider[]provideTreeConfig(...features: undefined)Register an app-wide CngxTreeConfig composed from with*
features.
bootstrapApplication(AppComponent, {
providers: [
provideTreeConfig(
withDefaultNodeIdFn<MyDomain>((v) => v.uuid),
withDefaultKeyFn<MyDomain>((v) => v.uuid),
withTreeCacheLimit(5000),
),
],
});Provider[]provideTreeConfigAt(...features: undefined)Sub-tree / component-scoped variant - use in viewProviders so the
tree config only applies to descendants of this component.
CngxTreeConfigFeaturewithDefaultInitiallyExpanded(mode)App-wide default initiallyExpanded seed.
CngxTreeConfigFeaturewithDefaultKeyFn(fn)App-wide default keyFn. Library default is identity.
CngxTreeConfigFeaturewithDefaultLabelFn(fn)App-wide default labelFn. Library default is String(value).
CngxTreeConfigFeaturewithDefaultNodeIdFn(fn)App-wide default nodeIdFn. Per-controller options still override.
Use this to enforce a domain id convention across the whole app
(e.g. (v) => (v as { uuid: string }).uuid) without repeating it
at every call site.
CngxTreeConfigFeaturewithTreeCacheLimit(limit: number)Bound the isExpanded(id) signal cache. See
CngxTreeConfig.cacheLimit for the trade-off.
core/tokens/window.token.ts
injectWindow InjectprovideWindow ProviderWindow | nullInjects the WINDOW token. Returns null in SSR contexts.
input/mask-presets/registry.ts
provideEagerMaskPresets Providerv0.1.0EnvironmentProvidersprovideEagerMaskPresets(...keys: undefined)Eagerly loads the lazily code-split mask preset tables at application start,
instead of on first use. Reach for it when the on-demand import() is a
problem - chiefly offline / PWA apps that must have every mask pattern
cached before connectivity drops, or to avoid the one-frame generic-fallback
mask on first focus.
Pass specific keys to warm only the tables you ship; omit them to load all
four. For strict offline-first apps, also list the emitted mask-presets-*
chunks in your service-worker precache so the eager fetch is cached.
bootstrapApplication(App, {
providers: [provideEagerMaskPresets()], // all tables
});
// or: provideEagerMaskPresets('phone', 'date') // only theseprojects/utils/version.ts
core/utils/keyboard.util.ts
matchesKeyCombo(event: KeyboardEvent, combo: KeyCombo, isMac: boolean)Tests whether a KeyboardEvent matches a parsed KeyCombo.
parseKeyCombo(combo: string)Parses a keyboard shortcut string like 'ctrl+shift+k' or 'mod+b' into
a structured KeyCombo.
The mod modifier resolves to meta on macOS and ctrl elsewhere.
Modifier names are case-insensitive.
const combo = parseKeyCombo('mod+b');
// { key: 'b', ctrl: false, meta: false, mod: true, shift: false, alt: false }core/utils/memo.util.ts
Vmemoize(fn)Creates a memoized version of a single-argument pure function.
Each call to memoize() produces an independent cache.
const expensive = memoize((id: string) => computeHeavy(id)); expensive('a'); // computes expensive('a'); // cached
core/utils/uid.util.ts
interactive/optimistic/optimistic.ts
unknownoptimistic(current: WritableSignal, action)Creates an optimistic update function for a signal.
Sets the new value immediately (optimistic), then confirms via the async action. On success, applies the server-confirmed value. On failure, rolls back to the previous value.
This is a utility function, not a directive — it composes with any signal.
readonly name = signal('Alice');
readonly [updateName, nameState] = optimistic(
this.name,
(value) => this.http.put('/api/name', { name: value })
);
// In template:
<input [value]="name()" (change)="updateName($event.target.value)" />
<ng-container [cngxToastOn]="nameState.state" toastError="Update failed" />- The writable signal to update optimistically.
- Async action that confirms the value. Should return the confirmed value.
Tuple of [applyFn, state].
paginator/segments/page-model.ts
PageWindowpageWindow(current: number, total: number, siblingCount: number, boundaryCount: number)Compute the windowed page sequence around current for total pages.
A run of more than one hidden page collapses into a gap carrying the hidden
0-based indices (the ellipsis menu's options); a single hidden page renders
as a plain page button instead of a gap.
Derived from the MUI usePagination
range algorithm, 0-based. siblingCount is how many pages flank current on
each side; boundaryCount is how many pages are pinned at each end. Both
default to 1, reproducing the v1 window byte-for-byte; the cngx-pgn-pages
inputs and CNGX_PAGINATOR_PAGE_WINDOW_FACTORY feed non-default values.
pageWindowEqual(a: PageWindow, b: PageWindow)Structural equality for two page windows. The equal fn for the segment's
computed() - keeps the reference stable across recomputes that yield an
identical window, so downstream @for does not churn.
audio/event-mode/parse-bindings.ts
ParsedEventBindingsparseEventBindings(spec: string)Parse the event:earcon grammar ('click:tap, focus:notification') into a
DOM-event map. Pure — no DOM, no inject(). Keys are categorised so the
directive can dev-warn on unknown earcon keys and, separately, on lifecycle
keys that should have used [cngxAudioStatus].
audio/status-mode/parse-status-bindings.ts
ParsedStatusBindingsparseStatusBindings(spec: string)Parse the status:earcon grammar ('pending:tap, succeeded:success') into a
status map. Pure — no DOM, no inject(). Keys are categorised so the
directive can dev-error on DOM-event keys that should have used [cngxAudio]
and dev-warn on unknown keys. succeeded/failed normalise to
success/error so a lookup by the tracker's raw AsyncStatus hits.
common/tabs/provide-cngx-tabs.ts
provideCngxTabs Providerv0.1.0provideCngxTabsAt Providerv0.1.0EnvironmentProvidersprovideCngxTabs(...features: undefined)Unified aggregator for tabs configuration.
Routes features by _target to provideTabsConfig and provideTabsI18n.
Sibling to
provideCngxMenuandprovideCngxSelect; apply once in the app providers array.
Returns EnvironmentProviders; forviewProvidersuse provideCngxTabsAt — opaqueEnvironmentProviderscannot live there.
bootstrapApplication(AppComponent, {
providers: [
provideCngxTabs(
withTabsDefaultOrientation('vertical'),
withTabsAriaLabels({ tabsRegion: 'Bereiche' }),
withTabsI18nLabels({ tabsLabel: 'Bereiche', moreTabsLabel: (n) => `${n} mehr` }),
withTabOverflowStabilizeMs(150),
),
],
});Provider[]provideCngxTabsAt(...features: undefined)Component-scoped twin of provideCngxTabs.
Returns Provider[] for viewProviders/providers; same dispatch
semantics, different scope.
forms/validators/pattern-match.validator.ts
ValidatorFnpatternMatch(pattern: RegExp)Validates that the string control value matches the given regular expression.
Returns null when valid, { patternMatch: { pattern, actual } } otherwise.
Returns null (valid) when the control value is empty or falsy —
pair with Validators.required when an empty value is not acceptable.
RegExpaccordion/config/provide-accordion-config.ts
provideAccordionConfig Providerv0.1.0provideAccordionConfigAt Providerv0.1.0EnvironmentProvidersprovideAccordionConfig(...features: undefined)Application-root configuration cascade for the accordion. Pass any
combination of withAccordionLabels / withDefaultHeadingLevel features in
bootstrapApplication's providers array.
Resolution priority (high -> low):
- Per-instance Input binding.
provideAccordionConfigAt(...)in a parent component'sviewProviders.provideAccordionConfig(...)at the application root.- Library defaults (
CNGX_ACCORDION_DEFAULTS).
The provider merges supplied features with the library defaults so consumers only declare keys they want to override.
bootstrapApplication(AppComponent, {
providers: [
provideAccordionConfig(
withAccordionLabels({ disabledReason: 'Dieser Abschnitt ist gesperrt.' }),
withDefaultHeadingLevel(2),
withAccordionSkin('categorized'),
),
],
});Provider[]provideAccordionConfigAt(...features: undefined)Component-scoped configuration cascade for the accordion. Pass any
combination of feature factories in a parent component's viewProviders
array.
Unlike provideAccordionConfig (root-only), provideAccordionConfigAt
injects the parent injector's CNGX_ACCORDION_CONFIG value (resolves through
the priority chain - root provider, library defaults, or another
provideAccordionConfigAt further up) and merges the supplied features on
top. Descendant accordion instances see the merged config; sibling sub-trees
keep the inherited value untouched.
data/async-registry/http-interceptor.ts
provideAsyncHttpObservability ProviderEnvironmentProvidersSets up HttpClient with cngxAsyncInterceptor so every request
surfaces in CngxAsyncRegistry. Opt-in - add it to
bootstrapApplication providers alongside provideAsyncRegistry().
bootstrapApplication(AppComponent, {
providers: [
provideAsyncRegistry(),
provideAsyncHttpObservability(),
],
});Use this only when cngx owns the HttpClient setup. It calls
provideHttpClient(withInterceptors([cngxAsyncInterceptor])) internally, so
an app that already calls provideHttpClient (especially with withFetch()
or other features) would get a second, feature-less HttpClient
configuration - last-provider-wins on HttpBackend can silently revert
withFetch() to the XHR backend. In that case do not call this; add the
exported cngxAsyncInterceptor to your own withInterceptors instead:
provideHttpClient(withFetch(), withInterceptors([authInterceptor, cngxAsyncInterceptor]))breadcrumb/config/provide-breadcrumb-config.ts
provideBreadcrumbConfig Providerv0.1.0provideBreadcrumbConfigAt Providerv0.1.0EnvironmentProvidersprovideBreadcrumbConfig(...features: undefined)Application-root configuration cascade for the breadcrumb family. Pass any
combination of withBreadcrumbAriaLabels, withBreadcrumbDataKey,
withBreadcrumbIconKey, and withBreadcrumbSkin features in
bootstrapApplication's providers array.
Resolution priority (high -> low):
- Per-instance Input binding.
provideBreadcrumbConfigAt(...)in a parent component'sviewProviders.provideBreadcrumbConfig(...)at the application root.- Library defaults (
CNGX_BREADCRUMB_DEFAULTS).
The provider deep-merges supplied features with the library defaults so consumers only declare keys they want to override.
bootstrapApplication(AppComponent, {
providers: [
provideBreadcrumbConfig(
withBreadcrumbAriaLabels({ bar: 'Navigation trail' }),
withBreadcrumbDataKey('crumb'),
),
],
});Provider[]provideBreadcrumbConfigAt(...features: undefined)Component-scoped configuration cascade for the breadcrumb family. Pass any
combination of feature factories in a parent component's viewProviders
array.
Unlike provideBreadcrumbConfig (root-only), provideBreadcrumbConfigAt
injects the parent injector's CNGX_BREADCRUMB_CONFIG value (resolves
through the priority chain - root provider, library defaults, or another
provideBreadcrumbConfigAt further up) and deep-merges the supplied
features on top. Descendant breadcrumb instances see the merged config;
sibling sub-trees keep the inherited value untouched.
chart/i18n/chart-i18n.ts
provideChartI18n Providerliteral typeprovideChartI18n(i18n: CngxChartI18n)Provider helper for custom chart i18n strings.
providers: [provideChartI18n({
summary: ({ trend, min, max, current, thresholds }) =>
`${trend === 'up' ? 'Aufwärtstrend' : 'Abwärtstrend'}. Min ${min}, Max ${max}, aktuell ${current}.`,
dataTable: () => 'Datentabelle',
// ...remaining keys
})]interactive/menu/provide-cngx-menu.ts
provideCngxMenu ProviderEnvironmentProvidersprovideCngxMenu(...features: undefined)Unified aggregator for the menu family's configuration. Filters features
by _target and forwards to the matching provide*Config function.
Today there is exactly one config surface (CNGX_MENU_CONFIG), so all
features dispatch to provideMenuConfig. The discriminator
scaffolding is in place so adding a future surface does not break the
public API of existing with* features.
Mirrors provideCngxSelect from the select-family A+ closure.
bootstrapApplication(AppComponent, {
providers: [
provideCngxMenu(
withAriaLabels({ submenuOpened: 'Untermenü geöffnet' }),
withTypeaheadDebounce(500),
),
],
});common/stepper/provide-cngx-stepper.ts
provideCngxStepper ProviderprovideCngxStepperAt ProviderEnvironmentProvidersprovideCngxStepper(...features: undefined)Unified aggregator for the stepper family. Filters by _target and
forwards: config features to provideStepperConfig, i18n
features to provideStepperI18n. Mirrors provideCngxTabs /
provideCngxSelect.
Returns EnvironmentProviders for app-root use; the
component-scoped twin provideCngxStepperAt returns
Provider[] because viewProviders cannot accept opaque
EnvironmentProviders.
bootstrapApplication(AppComponent, {
providers: [
provideCngxStepper(
withStepperDefaultOrientation('vertical'),
withStepperLinear(true),
withStepperAriaLabels({ stepperRegion: 'Schrittfolge' }),
withStepperI18nLabels({ stepperLabel: 'Schrittfolge', previousStep: 'Vorheriger' }),
),
],
});Provider[]provideCngxStepperAt(...features: undefined)Component-scoped twin of provideCngxStepper. Spread into
viewProviders or providers. Same dispatch semantics - only the
provider scope differs.
data-grid-accordion/config/provide-data-grid-accordion-config.ts
provideDataGridAccordionConfig Providerv0.1.0provideDataGridAccordionConfigAt Providerv0.1.0EnvironmentProvidersprovideDataGridAccordionConfig(...features: undefined)Application-root configuration cascade for the data-grid-accordion. Pass a
withDataGridSkin feature in bootstrapApplication's providers array to move
the app-wide default skin.
Resolution priority (high -> low):
- Per-instance
[skin]Input binding. provideDataGridAccordionConfigAt(...)in a parent component'sviewProviders.provideDataGridAccordionConfig(...)at the application root.- Library defaults (
CNGX_DATA_GRID_ACCORDION_DEFAULTS).
bootstrapApplication(AppComponent, {
providers: [provideDataGridAccordionConfig(withDataGridSkin('ledger'))],
});Provider[]provideDataGridAccordionConfigAt(...features: undefined)Component-scoped configuration cascade for the data-grid-accordion. Pass a
withDataGridSkin feature in a parent component's viewProviders array.
Unlike provideDataGridAccordionConfig (root-only),
provideDataGridAccordionConfigAt injects the parent injector's
CNGX_DATA_GRID_ACCORDION_CONFIG value (resolves through the priority chain)
and merges the supplied features on top. Descendant grid instances see the
merged config; sibling sub-trees keep the inherited value untouched.
dialog/dialog/dialog.service.ts
provideDialog ProviderProvider[]Provide CngxDialogOpener for programmatic dialog opening.
Must be called in the application's providers array or a route's
providers for CngxDialogOpener to be injectable.
bootstrapApplication(AppComponent, {
providers: [provideDialog()],
});dialog/dialog/dialog-stack.ts
provideDialogStack ProviderProvide a scoped CngxDialogStack instance for nested dialog backdrop management.
By default, CngxDialogStack is providedIn: 'root', so all dialogs share
one stack. Call provideDialogStack() in a component's providers array to
create an isolated stack scope (e.g., for a sub-application or dialog group).
core/tokens/environment.token.ts
provideEnvironment Providerforms/field/form-field.token.ts
provideErrorMessagesOpen detail pageprovideFormFieldOpen detail pagewithAutocompleteMappingsOpen detail pagewithConstraintHintsOpen detail pagewithErrorMessagesOpen detail pagewithErrorStrategyOpen detail pagewithNoSpellcheckOpen detail pagewithRequiredMarkerOpen detail page
provideErrorMessages ProviderprovideFormField ProviderwithAutocompleteMappings FeaturewithConstraintHints FeaturewithErrorMessages FeaturewithErrorStrategy FeaturewithNoSpellcheck FeaturewithRequiredMarker FeatureEnvironmentProvidersprovideErrorMessages(messages: ErrorMessageMap)Binds only the validation message map, for apps that need no other form-field
config. Equivalent to provideFormField(withErrorMessages(messages)), but
provides CNGX_ERROR_MESSAGES directly without the config wrapper.
Returns EnvironmentProviders - same placement as provideFormField: an
environment injector (app bootstrap or a route's providers), never a
component.
providers: [provideErrorMessages({ required: () => 'Required.' })]EnvironmentProvidersprovideFormField(...features: undefined)Registers application-wide defaults for every CngxFormField in scope.
Each with* feature contributes one slice of FormFieldConfig; features
apply left to right, so a later feature overrides an earlier one on the
same key.
Returns EnvironmentProviders, so it sits at an environment injector -
bootstrapApplication's providers, or a lazy route's providers. It
cannot be placed on a component. Resolution is nearest-wins and replace,
not merge: a route-level provideFormField shadows the root one for that
subtree rather than deep-merging into it.
bootstrapApplication(AppComponent, {
providers: [
provideFormField(
withErrorMessages({ required: () => 'Required.' }),
withConstraintHints(),
),
],
});FormFieldFeaturewithAutocompleteMappings(mappings: Record)Override or extend the autocomplete mappings for CngxInput.
RecordAdditional or replacement field-name-to-autocomplete entries.
Merged with built-in defaults. Pass a key with value '' to remove a mapping.
provideFormField(withAutocompleteMappings({
iban: 'cc-number',
taxid: 'off',
}))FormFieldFeaturewithConstraintHints(formatters?: Partial)Enable auto-generated constraint hints for all form fields.
Pass true for English defaults, or a ConstraintHintFormatters object for i18n.
English defaults
provideFormField(withConstraintHints())German
provideFormField(withConstraintHints({
lengthRange: (min, max) => `${min}–${max} Zeichen`,
minLength: (min) => `Mind. ${min} Zeichen`,
maxLength: (max) => `Max. ${max} Zeichen`,
valueRange: (min, max) => `${min}–${max}`,
minValue: (min) => `Mind. ${min}`,
maxValue: (max) => `Max. ${max}`,
}))PartialFormFieldFeaturewithErrorMessages(messages: ErrorMessageMap)Register the application-wide validation error message map. Each entry maps an
error kind to a function that renders its display string; CngxFieldErrors
and CngxFormErrors resolve messages against it.
Merges into any messages already on the config, so later features add to or
override earlier ones by kind.
provideFormField(withErrorMessages({
required: () => 'Required.',
minLength: (e) => `Min ${(e as { minLength: number }).minLength} chars.`,
}))FormFieldFeaturewithErrorStrategy(strategy)Configures the error visibility strategy used by CngxFormFieldPresenter.showError.
Pass a built-in name ('onTouched', 'onDirty', 'onSubmit',
'onTouchedOrSubmit', 'always') or a custom ErrorStrategyFn.
The strategy fully overrides the default gate
(touched OR errorScope.showErrors).
built-in
provideFormField(withErrorStrategy('onSubmit'))custom
provideFormField(withErrorStrategy(
(c) => c.invalid && (c.dirty || c.submitted),
))FormFieldFeaturewithNoSpellcheck(fields)Override or extend the set of field names where spellcheck="false" is auto-applied.
Additional field names to disable spellcheck for.
provideFormField(withNoSpellcheck(['iban', 'accountnumber', 'serialnumber']))FormFieldFeaturewithRequiredMarker(marker: string)Auto-render a required marker (e.g. *) inside every CngxLabel for required fields.
Individual labels can opt out via [showRequired]="false".
The marker text. Defaults to '*'.
provideFormField(withRequiredMarker()) // shows '*'
provideFormField(withRequiredMarker('(required)'))feedback/config/feedback-config.ts
provideFeedbackOpen detail pagewithAlertIconsOpen detail pagewithAlertsOpen detail pagewithBannersOpen detail pagewithCloseIconOpen detail pagewithLoadingDefaultsOpen detail pagewithSpinnerTemplateOpen detail pagewithToastsOpen detail page
provideFeedback ProviderwithAlertIcons FeaturewithAlerts FeaturewithBanners FeaturewithCloseIcon FeaturewithLoadingDefaults FeaturewithSpinnerTemplate FeaturewithToasts FeatureEnvironmentProvidersprovideFeedback(...features: undefined)Register global defaults for all feedback components.
bootstrapApplication(AppComponent, {
providers: [
provideFeedback(
withSpinnerTemplate(MyLucideSpinner),
withAlertIcons({ error: MyErrorIcon, warning: MyWarningIcon }),
withLoadingDefaults({ delay: 300, minDuration: 600 }),
),
],
});FeedbackFeaturewithAlertIcons(icons: Partial)Replace the built-in SVG icons in CngxAlert per severity.
Each component receives no inputs - it should render a single icon. Only specified severities are replaced; others keep the built-in SVG.
provideFeedback(withAlertIcons({
error: MyErrorIcon,
warning: MyWarningIcon,
success: MyCheckIcon,
info: MyInfoIcon,
}))PartialFeedbackFeaturewithAlerts(opts?)Enable the scoped alert system within provideFeedback().
Provides CngxAlerter at the environment level for root-level injection.
Without this feature, CngxAlerter is only available via CngxAlertStack's
viewProviders (scoped injection).
The token is opaque - always use provideFeedback() with feature functions,
never construct the config object manually.
Optional alert defaults.
provideFeedback(
withToasts(),
withAlerts(),
)FeedbackFeatureEnable the global banner system within provideFeedback().
Provides CngxBanner at the environment level.
Without this feature, CngxBannerOutlet will throw a NullInjectorError.
Banners are always persistent - no duration. Dismiss programmatically
via banner.dismiss(id) when the condition resolves.
provideFeedback(
withToasts(),
withAlerts(),
withBanners(),
)FeedbackFeaturewithCloseIcon(component: Type)Replace the built-in X SVG in all close/dismiss buttons globally.
The component receives no inputs - it should render a single icon.
Per-instance override via content projection on CngxCloseButton takes precedence.
provideFeedback(withCloseIcon(MyLucideXIcon))FeedbackFeaturewithLoadingDefaults(opts)Set default timing for all loading indicators and overlays.
Individual components can still override via their [delay] and [minDuration] inputs.
provideFeedback(withLoadingDefaults({ delay: 300, minDuration: 600 }))FeedbackFeaturewithSpinnerTemplate(component: Type)Replace the built-in SVG spinner in CngxLoadingIndicator and CngxLoadingOverlay.
The component receives no inputs - it should be a self-contained spinner
(e.g. a Lucide <loader-2> with CSS animation, or a Material <mat-spinner>).
provideFeedback(withSpinnerTemplate(MatProgressSpinner))FeedbackFeaturewithToasts(opts?)Enable the toast system within provideFeedback().
Provides CngxToaster at the environment level.
Without this feature, CngxToastOn and CngxToastOutlet will throw
a NullInjectorError at runtime.
Optional toast defaults.
provideFeedback(
withToasts(),
withAlertIcons({ error: MyErrorIcon }),
)common/popover/floating-fallback.ts
provideFloatingFallback ProviderprovideFloatingFallback(computePosition: ComputePositionFn, middleware?)Provides the Floating UI positioning fallback for browsers without CSS Anchor Positioning support.
The consumer must install @floating-ui/dom themselves - this library
never imports it directly, keeping the bundle at zero cost for modern browsers.
import { computePosition, flip, offset, shift } from '@floating-ui/dom';
// In app.config.ts or component providers:
providers: [
provideFloatingFallback(computePosition, [offset(8), flip(), shift()]),
]forms/input/input-config.ts
provideInputConfigOpen detail pagewithCopyResetDelayOpen detail pagewithCustomTokensOpen detail pagewithDateFormatsOpen detail pagewithFileMaxFilesOpen detail pagewithFileMaxSizeOpen detail pagewithIbanPatternsOpen detail pagewithInputAriaLabelsOpen detail pagewithMaskGuideOpen detail pagewithMaskPlaceholderOpen detail pagewithNumericDefaultsOpen detail pagewithPhoneDefaultRegionOpen detail pagewithPhonePatternsOpen detail pagewithZipPatternsOpen detail page
provideInputConfig ProviderwithCopyResetDelay FeaturewithCustomTokens FeaturewithDateFormats FeaturewithFileMaxFiles FeaturewithFileMaxSize FeaturewithIbanPatterns FeaturewithInputAriaLabels FeaturewithMaskGuide FeaturewithMaskPlaceholder FeaturewithNumericDefaults FeaturewithPhoneDefaultRegion FeaturewithPhonePatterns FeaturewithZipPatterns FeatureprovideInputConfig(...features: undefined)Provides global configuration for @cngx/forms/input directives.
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideInputConfig(
withPhonePatterns({ LI: '+000 000 00 00' }),
withMaskPlaceholder('·'),
withNumericDefaults({ decimals: 2, locale: 'de-CH' }),
withCopyResetDelay(3000),
),
],
};Returns a plain Provider, so it works app-wide in
ApplicationConfig.providers or scoped to a subtree via a component's
viewProviders. The nearest provideInputConfig wins for a subtree by
token resolution - it replaces an ancestor config, it does not deep-merge.
Compose with the feature functions:
InputConfigFeaturewithCopyResetDelay(ms: number)Sets the global delay in milliseconds before CngxCopyValue reverts its
copied state - the window where the button reads as copied before snapping
back to idle. Library default is 2000.
- A per-input binding overrides it.
- Maps to the
copyResetDelayconfig key.
provideInputConfig(withCopyResetDelay(3000));InputConfigFeaturewithCustomTokens(tokens: MaskTokenMap)Registers mask tokens globally, beyond the built-in 0 9 A a *.
- Each token maps one mask character to a
patternregex, anoptionalflag, and an optional per-chartransform. - Use it when a pattern needs a character class the built-ins lack (hex digit, restricted letter set) across the app rather than per input.
- Merged as
{ ...config.customTokens, ...input.customTokens }, so a per-input[customTokens]entry overrides the global one of the same key.
provideInputConfig(withCustomTokens({
H: { pattern: /[0-9a-fA-F]/, transform: (c) => c.toUpperCase() },
}));
// <input cngxInputMask="#HHHHHH" />InputConfigFeaturewithDateFormats(formats: Record)Adds or overrides date mask patterns keyed by BCP-47 locale (e.g. de-CH).
- Built-ins ship per-locale separators (de-* dots, fr-CH dots, sv-SE/lt-LT/
en-CA ISO, en-US/en-GB slashes, ...) for the
dateanddatetimepresets. - Resolution for
date/datetime: exact locale (case-insensitive), then a bare language key (so a language-keyed override like{ sv: '...' }still applies), then any same-language locale, thenen-US. - Masks capture separators and group count only, NOT field order:
en-US(MM/DD) anden-GB(DD/MM) share00/00/0000. - Tokens:
0= required digit, separators are literals. - Does not feed
date:short- that uses a separate built-in table. - Consumer entries merge per key and win on collision.
provideInputConfig(withDateFormats({ 'en-NZ': '00/00/0000' }));
// LOCALE_ID 'en-NZ' + <input cngxInputMask="date" />RecordInputConfigFeaturewithFileMaxFiles(count: number)Set the default maxFiles for CngxFileDrop.
InputConfigFeaturewithFileMaxSize(bytes: number)Sets the global maximum accepted file size in bytes for CngxFileDrop.
- No library default; unset means the directive enforces no size cap.
- Value is raw bytes, not KB/MB.
- A per-input binding overrides it.
- Maps to the
fileMaxSizeconfig key.
provideInputConfig(withFileMaxSize(5 * 1024 * 1024)); // 5 MiBInputConfigFeaturewithIbanPatterns(patterns: Record)Adds or overrides IBAN mask patterns keyed by country code.
- Built-in: ~30 IBAN-using countries (Europe + parts of the Middle East), keyed by ISO 3166-1 alpha-2.
- Patterns encode length and 4-char grouping with tokens (
A= required letter,0= required digit). - Resolved against the lazily-loaded built-in table merged with
config.ibanPatterns; an unknown region falls back toAA00 0000 0000 0000 0000 00. - Consumer entries merge per key and win on collision.
- Region comes from
iban:<REGION>orLOCALE_ID.
provideInputConfig(withIbanPatterns({ BE: 'AA00 0000 0000 0000' }));
// <input cngxInputMask="iban:BE" />RecordInputConfigFeaturewithInputAriaLabels(labels: Partial)Overrides the built-in ARIA label strings the input directives announce.
- Unset keys fall back to
DEFAULT_INPUT_ARIA_LABELSper key, so a partial override is safe. - Library defaults are English; German (or any locale) is consumer-supplied
here. Mirrors the select family's
withAriaLabels. clear->CngxInputClearbutton label.copySuccess/copyError->CngxCopyValuelive-region announcements.otpGroup/otpSlot(index, length)/otpComplete->CngxOtpInputgroup, per-slot labels, and completion announcement.capsLockOn->CngxCapsLockassertive warning.passwordStrength(label)->CngxPasswordStrengthpolite announcement.inputRejected->CngxInputFilterassertive rejection.sensitiveReveal/sensitiveHide->CngxSensitiveValuereveal/hide announcements.ratingValue(value, max)->CngxRatingpolite committed-value announcement.ratingItem(step, max)->CngxRatingper-star radioaria-label.phoneCountry->CngxPhoneInputcountry-pickeraria-label.
provideInputConfig(
withInputAriaLabels({ clear: 'Leeren', copySuccess: 'Kopiert' }),
);PartialInputConfigFeaturewithMaskGuide(guide: boolean)Sets the global guide mode for CngxInputMask. Library default is true.
true: placeholder characters fill unfilled slots and the cursor snaps to the next empty slot on focus.false: a bare mask showing only typed characters and their literals.- Resolution order:
[guide]input, thenconfig.maskGuide, thentrue.
provideInputConfig(withMaskGuide(false));InputConfigFeaturewithMaskPlaceholder(char: string)Sets the global placeholder character CngxInputMask shows for unfilled
slots in guide mode. Library default is '_'.
- Resolution order:
[placeholder]input, thenconfig.maskPlaceholder, then'_'. - Sets the app-wide baseline for every masked input that does not bind its
own
[placeholder]. - Also fills the
aria-placeholderthe mask exposes.
provideInputConfig(withMaskPlaceholder('·')); // middle dotInputConfigFeaturewithNumericDefaults(defaults)Sets app-wide defaults for CngxNumericInput.
localeoverridesLOCALE_IDfor numeric inputs only.decimalsandstepset formatting (stepdefault1).- Each key applies only when supplied; a partial override leaves the rest at the directive's defaults.
- Per-input bindings still win over these.
- Maps to the
numericLocale/numericDecimals/numericStepkeys.
provideInputConfig(withNumericDefaults({ decimals: 2, locale: 'de-CH' }));InputConfigFeaturewithPhoneDefaultRegion(region: string)Sets the app-wide default selected region (ISO key, e.g. 'AT') for
CngxPhoneInput, instead of the first country in the list.
- The region must match a
CngxPhoneInputcountryregion; an unknown region is ignored (the first country stays selected). - A per-instance
[country]binding overrides it.
provideInputConfig(withPhoneDefaultRegion('AT'));InputConfigFeaturewithPhonePatterns(patterns: Record)Adds or overrides phone mask patterns keyed by region code.
Reach for it when a cngxInputMask="phone" / "phone:<REGION>" needs a
region the library does not ship, or to override one it does.
- Built-in regions: ~45 ISO 3166-1 alpha-2 keys across Europe, the Americas,
East Asia, and Oceania (
UKkept as a back-compat alias ofGB). - Regions whose landline and mobile national-number lengths differ ship
|-separated landline+mobile alternates picked by length; uniform-length plans ship a single pattern. Masks are coarse approximations, not validators - reach forlibphonenumber-jswhen you need real validation. - Consumer entries merge per key onto the built-ins and win on collision.
- Resolved as
{ ...PHONE_PATTERNS, ...config.phonePatterns }[region], falling back to US when the region is absent. - Region comes from
phone:<REGION>orLOCALE_ID. - Pattern tokens:
0= required digit, literals pass through.
provideInputConfig(withPhonePatterns({ LI: '+000 000 00 00' }));
// <input cngxInputMask="phone:LI" />RecordInputConfigFeaturewithZipPatterns(patterns: Record)Adds or overrides postal-code mask patterns keyed by country code.
- Built-in: ~50 countries keyed by ISO 3166-1 alpha-2 (
GB, withUKkept as an alias). Countries with no postal-code system (HK, AE, ...) are intentionally absent. - A
|in a pattern declares alternates picked by input length (the GB entry:A0A 0AA|AA0 0AA|...). - Resolved against the lazily-loaded built-in table merged with
config.zipPatterns; an unknown region falls back to00000. - Consumer entries merge per key and win on collision.
- Region comes from
zip:<REGION>orLOCALE_ID.
provideInputConfig(withZipPatterns({ NL: '0000 AA' }));
// <input cngxInputMask="zip:NL" />Recordui/overlay/overlay.service.ts
provideOverlay ProviderEnvironmentProvidersProvides CngxOverlay as an environment-scoped service.
common/popover/popover-panel.config.ts
providePopoverPanelOpen detail pagewithArrowOpen detail pagewithArrowTemplateOpen detail pagewithAutoDismissOpen detail pagewithCloseButtonOpen detail pagewithCloseOnSuccessOpen detail pagewithDefaultVariantOpen detail page
providePopoverPanel ProviderwithArrow FeaturewithArrowTemplate FeaturewithAutoDismiss FeaturewithCloseButton FeaturewithCloseOnSuccess FeaturewithDefaultVariant FeatureprovidePopoverPanel(...features: undefined)Provides configuration for CngxPopoverPanel instances.
providers: [
providePopoverPanel(
withAutoDismiss({ info: 5000, success: 3000 }),
withCloseOnSuccess(300),
),
]PopoverPanelFeaturewithArrow(show: unknown)Show the arrow on all panels by default.
unknown= truePopoverPanelFeaturewithArrowTemplate(tpl: TemplateRef)Register an app-wide default template for the panel's arrow ornament.
The template receives a CngxPopoverArrowContext with edge and
offsetPx so the consumer's glyph can match the resolved placement.
Per-instance *cngxPopoverArrow still wins over this default; the
library's rotated-diamond fallback only renders when neither tier is
set.
providers: [
providePopoverPanel(withArrowTemplate(brandArrowTpl)),
]PopoverPanelFeaturewithAutoDismiss(timing: Record)Auto-dismiss the panel after a timeout, per variant.
Record- Map of variant name to dismiss duration in ms.
withAutoDismiss({ info: 5000, success: 3000 })PopoverPanelFeaturewithCloseButton(show: unknown)Show the close button on all panels by default.
unknown= truePopoverPanelFeaturewithCloseOnSuccess(delay: number)Auto-close the panel after an async action in the footer succeeds.
- Delay in ms before closing. Defaults to
300.
PopoverPanelFeaturewithDefaultVariant(variant: string)Set the default variant applied when no variant input is set.
sidenav/config/provide-sidenav-config.ts
provideSidenavConfig Providerv0.1.0provideSidenavConfigAt Providerv0.1.0tag/config/provide-tag-config.ts
provideTagConfig ProviderprovideTagConfigAt ProviderEnvironmentProvidersprovideTagConfig(...features: undefined)Application-root configuration cascade for the tag family. Pass
any combination of withTagDefaults / withTagGroupDefaults /
withTagColors / withTagSlots features in
bootstrapApplication's providers array.
Resolution priority (high → low):
- Per-instance Input binding.
provideTagConfigAt(...)in a parent component'sviewProviders.provideTagConfig(...)at the application root.- Library defaults (
CNGX_TAG_DEFAULTS).
The provider deep-merges supplied features with the library defaults so consumers only declare keys they want to override.
bootstrapApplication(AppComponent, {
providers: [
provideTagConfig(
withTagDefaults({ variant: 'subtle' }),
withTagColors({
'my-brand': {
bg: '#4f46e5',
color: '#ffffff',
border: 'transparent',
},
}),
),
],
});Provider[]provideTagConfigAt(...features: undefined)Component-scoped configuration cascade for the tag family. Pass
any combination of feature factories in a parent component's
viewProviders array.
Unlike provideTagConfig (root-only), provideTagConfigAt injects
the parent injector's CNGX_TAG_CONFIG value (resolves through
the priority chain — root provider, library defaults, or another
provideTagConfigAt further up) and deep-merges the supplied
features on top. This produces cumulative cascade behaviour:
descendant tag instances see the merged config, sibling sub-trees
keep the inherited value untouched.
feedback/toast/toast.service.ts
provideToasts ProviderEnvironmentProvidersStandalone provider for the toast system - use when not using provideFeedback().
bootstrapApplication(AppComponent, {
providers: [provideToasts()],
});data-display/treetable/treetable.token.ts
provideTreetableOpen detail pagewithCapitaliseHeadersOpen detail pagewithHighlightOnHoverOpen detail page
provideTreetable ProviderwithCapitaliseHeaders FeaturewithHighlightOnHover FeatureEnvironmentProvidersprovideTreetable(...features: undefined)Registers application-wide defaults for every CngxTreetable in the
injection scope. Composes any number of TreetableFeature
helpers via left-to-right reduction; later features can override
earlier ones if you call the same withXxx() twice (rare).
Calling with no arguments is valid and produces an empty
TreetableConfig - identical to not calling provideTreetable at
all. Per-instance [options] input always wins over whatever lands
here.
Application bootstrap:
bootstrapApplication(AppComponent, {
providers: [
provideTreetable(
withHighlightOnHover(), // turn hover-highlight on app-wide
withCapitaliseHeaders(false), // keep raw header keys app-wide
),
],
});Per-route scope (Angular 16+ provide* works inside Route.providers):
const routes: Routes = [{
path: 'admin',
providers: [provideTreetable(withHighlightOnHover())],
children: adminChildren,
}];TreetableFeaturewithCapitaliseHeaders(enabled: unknown)Feature: auto-capitalisation of column header labels.
The library default for capitaliseHeader is true, so headers
already capitalise without this helper. Use
withCapitaliseHeaders(false) to opt out and render the raw
column-key strings (useful for snake_case domain keys you want to
keep verbatim, or for fully custom *cngxHeader slot rendering).
unknown= true- Capitalise on/off. Default
true.
TreetableFeaturewithHighlightOnHover(enabled: unknown)Feature: row highlight on mouse-hover.
The library default for highlightRowOnHover is false, so calling
withHighlightOnHover() opts in across the app. Pass false
explicitly to be loud about the off state (e.g. when overlaying on
top of an earlier provideTreetable(withHighlightOnHover()) in a
nested scope).
unknown= true- Hover-highlight on/off. Default
true.
interactive/async-status/async-status.directive.ts
CngxAsyncDisplayStatusreflectAsyncDisplayStatus(state)Single source for collapsing a CngxAsyncState into its display
bucket. Owned by the CngxAsyncStatus reflector so every consumer
(the reflector directive, CngxActionButton's [externalState] branch)
maps an externally-owned state the same way - one reflection code path.
forms/validators/required-true.validator.ts
ValidatorFnValidates that the control value is strictly true.
Returns { requiredTrue: { actual } } for any value other than true.
Intended for checkbox agreement fields.
data/async-state/resolve-view.ts
resolveAsyncView(status: AsyncStatus, firstLoad: boolean, empty: boolean)Pure function that resolves which view to show based on async state.
Encodes the state machine as a lookup table — no branching:
| status | firstLoad | empty | view |
|---|---|---|---|
| idle | true | * | none |
| loading | true | * | skeleton |
| refreshing | true | * | skeleton |
| pending | true | * | skeleton |
| error | true | * | error |
| success | * | true | empty |
| error | false | * | content+error |
| (all other) | * | * | content |
interactive/breadcrumb/breadcrumb-responsive.ts
resolveBreadcrumbTier v0.1.0resolveBreadcrumbTier(width: number, tiers)Pure width->count resolver: returns the maxVisible of the tier with the
highest minWidth that is still <= width. Order-independent - the tier list
is normalised (sorted by descending minWidth) before matching, so a consumer
may pass tiers in any order. When width sits below every tier's minWidth
the narrowest tier's maxVisible is the floor; an empty tier list yields
Number.POSITIVE_INFINITY (never collapse), leaving the trail intact.
No DOM, no Angular - the width source (a CngxResizeObserver) lives in the
bar; this stays jsdom-testable and shared by the bar and the headless recipe.
data-display/treetable/column-template.utils.ts
data/recycler/scroll-observer.ts
interactive/button-toggle/button-toggle.directive.ts
ParentResolutioncommon/stepper/status-label.ts
resolveStepperStatusLabel(node: CngxStepNode, i18n: CngxStepperI18n, isActive: boolean)Resolve the status-pill label for a step node against the resolved
i18n bundle. Used by skins that surface a per-step status pill
(e.g. stripe-status-rich). Pure factory - no injection context,
no signal graph involvement.
Resolution order:
successdata-state ->statusLabels.doneerrordata-state ->statusLabels.erroredaria-current="step"->statusLabels.inProgress- otherwise ->
statusLabels.upNext
Group nodes (non-step) return an empty string.
data/paginate/bucket-paginate.directive.ts
data/async-state/operators.ts
MonoTypeOperatorFunctiontapAsyncProgress(state: Pick)RxJS operator that extracts upload/download progress from an
HttpEvent stream and reports it to a ManualAsyncState.
Filters progress events, calculates percentage, calls setProgress().
Non-progress events pass through unchanged.
Use with { observe: 'events', reportProgress: true } on HttpClient.
readonly upload = createManualState<UploadResult>();
handleUpload(file: File): void {
this.http.post('/api/upload', file, {
reportProgress: true,
observe: 'events',
}).pipe(
tapAsyncProgress(this.upload),
takeUntilDestroyed(this.destroyRef),
).subscribe();
}PickMonoTypeOperatorFunctiontapAsyncState(state: AsyncStateSink, options?)RxJS operator that wires an Observable's lifecycle to a ManualAsyncState.
On subscribe: sets loading (or refreshing if data was already loaded).
On next: calls setSuccess(value).
On error: calls setError(err) and re-throws (does not swallow).
The Observable passes through unchanged — tapAsyncState is a side-effect operator.
readonly residents = createManualState<Resident[]>();
load(): void {
this.http.get<Resident[]>('/api/residents').pipe(
tapAsyncState(this.residents),
takeUntilDestroyed(this.destroyRef),
).subscribe();
}OperatorFunctiontapHttpAsyncState(state: AsyncStateSink, options?)RxJS operator that combines tapAsyncState + tapAsyncProgress for HTTP event streams.
Pipe this onto an HttpClient call with { observe: 'events', reportProgress: true }.
It will:
- Set
loadingon subscribe - Report upload/download progress via
setProgress() - Extract the response body on
HttpEventType.Response - Call
setSuccess(body)with the extracted body - Call
setError(err)on error
The output Observable emits the response body (not HttpEvents).
Throws if the response body is null.
readonly upload = createManualState<UploadResult>();
handleUpload(file: File): void {
this.http.post('/api/upload', file, {
reportProgress: true,
observe: 'events',
}).pipe(
tapHttpAsyncState(this.upload),
takeUntilDestroyed(this.destroyRef),
).subscribe();
// upload.status(), upload.progress(), upload.data() — all wired
}display/stat/stat-slots.ts
useStatSlot(kind: CngxStatSlotKind)Generate a stable id, register it with the enclosing CngxStat (if any),
and withdraw it on destroy. Shared by the four slot directives via plain
composition - no base class, no inheritance (Pillar 3).
accordion/config/features.ts
withAccordionLabelsOpen detail pagewithAccordionSkinOpen detail pagewithAccordionTemplatesOpen detail pagewithDefaultHeadingLevelOpen detail page
withAccordionLabels Featurev0.1.0withAccordionSkin Featurev0.1.0withAccordionTemplates Featurev0.1.0withDefaultHeadingLevel Featurev0.1.0CngxAccordionConfigFeaturewithAccordionLabels(payload)Override the accordion's locale-sensitive announced strings - disabledReason
(spoken when an item is disabled) and errorMessage (spoken via role="alert"
when an item's [state] is error and no error slot is given). Pass either or
both. Per-instance [disabledReason] / [errorMessage] still win over the
cascade; this only sets the fallback.
provideAccordionConfig(
withAccordionLabels({
disabledReason: 'This section is locked.',
errorMessage: 'This section failed to load.',
}),
);CngxAccordionConfigFeaturewithAccordionSkin(skin: CngxAccordionSkin)Set the app-wide default visual skin for <cngx-accordion-group>. Unset by
default (the base flat look). Per-instance [skin] Input still wins; this
moves the cascade default. Structure, slots, ARIA, and keyboard behaviour are
identical across skins - only the [data-skin] host attribute changes the
CSS. Typed class-sugar, not a mode flag.
provideAccordionConfig(withAccordionSkin('categorized'));CngxAccordionConfigFeaturewithAccordionTemplates(payload: NonNullable)Set app-wide slot templates - the config tier of the slot cascade
(*cngxAccordionItemXxx per-instance -> this -> CSS default). Three slots
carry a config tier: icon (chevron; $implicit is the item's expanded
state), busySpinner (loading/refreshing visual), and error (error
affordance). Hand a TemplateRef per key here and every item renders it
unless a per-instance slot overrides it. Partial payloads compose - repeated
calls merge per key.
provideAccordionConfig(
withAccordionTemplates({ icon: myChevronTpl, busySpinner: mySpinnerTpl }),
);NonNullableCngxAccordionConfigFeaturewithDefaultHeadingLevel(headingLevel: number)Override the default heading level (aria-level) a CngxAccordionGroup
applies when [headingLevel] is not bound. Per-instance [headingLevel]
still wins; the group clamps the resolved value into the ARIA 2-6 range.
provideAccordionConfig(withDefaultHeadingLevel(2));interactive/menu/menu-config-features.ts
withAriaLabelsOpen detail pagewithCloseOnSelectOpen detail pagewithDismissOnBlurOpen detail pagewithDismissOnOutsideClickOpen detail pagewithDismissOnScrollOpen detail pagewithSubmenuCloseDelayOpen detail pagewithSubmenuOpenDelayOpen detail pagewithTypeaheadDebounceOpen detail page
withAriaLabels FeaturewithCloseOnSelect FeaturewithDismissOnBlur FeaturewithDismissOnOutsideClick FeaturewithDismissOnScroll FeaturewithSubmenuCloseDelay FeaturewithSubmenuOpenDelay FeaturewithTypeaheadDebounce FeatureCngxMenuConfigFeaturewithAriaLabels(partial: Partial)Override one or more ARIA strings (English defaults). Unset keys keep their default value, so consumers can supply only the locale strings they care about.
PartialCngxMenuConfigFeaturewithCloseOnSelect(close: boolean)Whether activating a leaf item closes the menu. Default: true
(menu semantics — distinct from listbox/combobox which stay open).
CngxMenuConfigFeaturewithDismissOnBlur(dismiss: boolean)Whether the "context lost" bundle dismisses the menu. The bundle
covers BOTH window blur (system notification, OS-native menu
overlaying, tab switch) AND document pointercancel outside the
popover and trigger host (palm rejection on touch, gesture cancelled
by the browser). The two sources share one toggle because consumers
who want one rarely want the other off; consumers needing one
without the other replace the entire handler via
CNGX_MENU_DISMISS_HANDLER_FACTORY. Set to false to keep
both listeners off; lastDismissSource will then never report
'blur' or 'pointer-cancel'.
Default: true.
CngxMenuConfigFeaturewithDismissOnOutsideClick(dismiss: boolean)Whether pointerdown outside the menu's popover and the trigger host
dismisses the menu. Default: true. Pass false for ESC-only
semantics (e.g. a tutorial overlay that wants to keep the menu open
while the user clicks through other UI).
CngxMenuConfigFeaturewithDismissOnScroll(dismiss: boolean)Whether window scroll while the menu is open dismisses it. Default:
false. Opt in when the menu should follow native context-menu
behaviour and close as soon as the page scrolls. Scroll-dismiss
listens to window only - the nearest scrollable ancestor is not
traversed.
CngxMenuConfigFeaturewithTypeaheadDebounce(ms: number)Override the typeahead debounce window (milliseconds) for menu
navigation. Default: 300.
data/async-registry/http-context.ts
withAsyncLabel FeaturewithAsyncSkip FeatureHttpContextwithAsyncLabel(label: string, context: HttpContext)Returns an HttpContext that labels the request for async observability.
Pass an existing context to chain with other tokens.
this.http.get('/api/users', { context: withAsyncLabel('users') });HttpContextwithAsyncSkip(context: HttpContext)Returns an HttpContext that opts the request out of async observability.
Pass an existing context to chain with other tokens.
this.http.get('/api/ping', { context: withAsyncSkip() });breadcrumb/config/features.ts
withBreadcrumbAriaLabelsOpen detail pagewithBreadcrumbDataKeyOpen detail pagewithBreadcrumbIconKeyOpen detail pagewithBreadcrumbSkinOpen detail page
withBreadcrumbAriaLabels Featurev0.1.0withBreadcrumbDataKey Featurev0.1.0withBreadcrumbIconKey Featurev0.1.0withBreadcrumbSkin Featurev0.1.0CngxBreadcrumbConfigFeaturewithBreadcrumbAriaLabels(payload: NonNullable)Override the breadcrumb family's ARIA-string fallbacks - the bar landmark
name and the overflow/siblings trigger + list labels. Per-instance
[label] / [triggerLabel] / [menuLabel] bindings still win over the
cascade; this only sets the fallback.
provideBreadcrumbConfig(
withBreadcrumbAriaLabels({ bar: 'Navigation trail' }),
);NonNullableCngxBreadcrumbConfigFeaturewithBreadcrumbDataKey(dataKey: string)Override the route-data key the breadcrumb router-sync directives
(CngxBreadcrumbRouterSync, CngxBreadcrumbSiblingsRouterSync) read
crumbs and siblings from. Per-instance [dataKey] still wins.
provideBreadcrumbConfig(withBreadcrumbDataKey('crumb'));CngxBreadcrumbConfigFeaturewithBreadcrumbIconKey(iconKey: string)Override the route-data key CngxBreadcrumbRouterSync reads the per-crumb
opaque icon token from (default 'icon'). Per-instance [iconKey] still
wins. Mirrors withBreadcrumbDataKey for cascade parity.
provideBreadcrumbConfig(withBreadcrumbIconKey('glyph'));CngxBreadcrumbConfigFeaturewithBreadcrumbSkin(skin: CngxBreadcrumbSkin)Select the app-wide default visual skin for CngxBreadcrumbBar. Per-instance
[skin] still wins; this only moves the cascade default. Structure, slots,
and ARIA are identical across skins - only the [data-skin] host attribute
changes which @scope paint applies.
provideBreadcrumbConfig(withBreadcrumbSkin('pill'));interop/signals/with-cngx-async-state.ts
withCngxAsyncState Feature@ngrx/signals store feature that grants a store a CngxAsyncState-shaped
slice with zero hand-written status flags.
The element type is supplied up front and the key is inferred from the
argument, so the two contributed members carry the literal key:
withCngxAsyncState<User[]>()('users') adds usersState (the read-only
CngxAsyncState<User[]> view) and usersSink (the writable
ManualAsyncState<User[]>). Drive the status with the existing
tapAsyncState operator - no new state machine, no manual isLoading
boolean. Both members are the same underlying createManualState instance,
exposed under a read type and a write type.
The <T>() / (key) split is deliberate: TypeScript cannot infer the
literal key while T is given explicitly in one call, so a single-call
form collapses the key to a string index signature. Currying keeps the
key literal - and therefore the store members concretely named.
export const UsersStore = signalStore(
withCngxAsyncState<User[]>()('users'),
withMethods((store) => {
const http = inject(HttpClient);
return {
load: () =>
http.get<User[]>('/api/users').pipe(
tapAsyncState(store.usersSink),
takeUntilDestroyed(),
).subscribe(),
};
}),
);
// store.usersState.status(), store.usersState.data() - derived, no flagsforms/input/currency.feature.ts
withCurrency FeatureInputConfigFeaturewithCurrency(options: CurrencyOptions)Configures CngxNumericInput to format its value with a currency's grouping
and standard fraction digits (USD -> 2, JPY -> 0) - the currency code
drives the number; the symbol is never baked into the editable value. Render
the symbol with a CngxPrefix / CngxSuffix affix instead, so a screen
reader hears the field label rather than "dollar sign" and the editable text
stays a plain number (Pillar 3: configuration over a new organism).
provideInputConfig(withCurrency({ code: 'CHF', locale: 'de-CH' }));<span class="row">
<span cngxPrefix>CHF</span>
<input cngxInput cngxNumericInput [field]="f.price" />
</span>data-grid-accordion/config/features.ts
withDataGridSkin Featurev0.1.0CngxDataGridAccordionConfigFeaturewithDataGridSkin(skin: CngxDataGridSkin)Set the app-wide default visual skin for <cngx-data-grid-accordion>. Unset by
default (the base flat grid look). Per-instance [skin] Input still wins; this
moves the cascade default. Structure, cells, ARIA, and keyboard behaviour are
identical across skins - only the [data-skin] host attribute changes the CSS.
Typed class-sugar, not a mode flag.
provideDataGridAccordionConfig(withDataGridSkin('ledger'));interactive/retry/with-retry.ts
withRetry FeatureunknownwithRetry(action: AsyncAction, config?: RetryConfig)Wraps an AsyncAction with automatic retry logic.
Returns a tuple: [action, retryState] where action is a new AsyncAction
that retries on failure, and retryState exposes attempt/retry signals and
a state: CngxAsyncState for feedback system integration.
Composes naturally with CngxAsyncClick and CngxActionButton:
const [saveWithRetry, retryState] = withRetry(
() => this.http.post('/api/save', data),
{ maxAttempts: 3, delay: 1000, backoff: 'exponential' }
);
// Use with CngxAsyncClick + toast
<button [cngxAsyncClick]="saveWithRetry">Save</button>
<ng-container [cngxToastOn]="retryState.state"
toastSuccess="Saved" toastError="All retries failed" />sidenav/config/features.ts
withSidenavDimensionsOpen detail pagewithSidenavHoverDwellOpen detail pagewithSidenavResponsiveOpen detail pagewithSidenavRouterSyncOpen detail pagewithSidenavShortcutOpen detail page
withSidenavDimensions Featurev0.1.0withSidenavHoverDwell Featurev0.1.0withSidenavResponsive Featurev0.1.0withSidenavRouterSync Featurev0.1.0withSidenavShortcut Featurev0.1.0tag/config/features.ts
withTagColorsOpen detail pagewithTagDefaultsOpen detail pagewithTagGroupDefaultsOpen detail pagewithTagSlotsOpen detail page
withTagColors FeaturewithTagDefaults FeaturewithTagGroupDefaults FeaturewithTagSlots FeatureCngxTagConfigFeaturewithTagColors(payload: NonNullable)Register consumer-defined colour entries. Each key adds a
[data-color="<key>"] cascade entry resolved through
--cngx-tag-bg/-color/-border custom properties at the consumer's
own CSS layer.
The five predefined keys (neutral/success/warning/error/
info) ship in tag.css and are NOT part of this map; passing
them here is a no-op against the predefined cascade. Consumer
keys composed via data-color="my-brand" resolve through the
registered entry.
provideTagConfig(
withTagColors({
'my-brand': {
bg: '#4f46e5',
color: '#ffffff',
border: 'transparent',
},
}),
);NonNullableCngxTagConfigFeaturewithTagDefaults(payload: NonNullable)Override the default CngxTag input values (variant, color,
size, truncate, maxWidth). Per-instance bindings still win
over the cascade — this only sets the fallback per directive.
provideTagConfig(
withTagDefaults({ variant: 'subtle', size: 'sm' }),
);NonNullableCngxTagConfigFeaturewithTagGroupDefaults(payload: NonNullable)Override the default CngxTagGroup input values (gap, align,
semanticList). Per-instance bindings still win.
provideTagConfig(
withTagGroupDefaults({ gap: 'md', semanticList: true }),
);NonNullableCngxTagConfigFeaturewithTagSlots(payload: NonNullable)Register app-wide template overrides for the five Tag-family
slots. Resolved in tier 2 of the slot cascade — instance
directives still win, the host's <ng-template> default body is
the floor.
Staging note. This factory ships ahead of a documented second
consumer — the headless CngxTag + CngxTagGroup are the only
current consumers. The staging is intentional and tracked in
display-accepted-debt.md §2 (Material organism deferral): a
future cngx-mat-tag / cngx-mat-tag-group wrapper inherits the
cascade for free without forking. If §2 is rejected outright,
withTagSlots re-evaluates as a sunsetting candidate at the same
time. Per the cngx-review architecture lens, the surface is
tracked rather than left silent.
NonNullable