Skip to main content
cngx-src documentation
Miscellaneous > Functions

Functions

Function675

Index

adaptFormControlappendAtPathapplyFeaturesapplyFeaturesapplyFeaturesapplyFeaturesapplyMaskapplySrOnlyarrayEqualbucketbuildAsyncStateViewbuildCrumbsbuildCurvePathbuildDotsbuildSiblingscanDeactivateWhenCleancharMatcherclampclampclampHeadingLevelclampHeadingLevelclampVolumecoerceArraycoerceBooleanPropertycoerceColumnscoerceNumberPropertycollectDescendantValuescompileMatchercomputeFixedRangecomputeVariableRangeconnectPaginateEmitconnectPaginateResetOnconnectRecyclerToActiveDescendantconnectRecyclerToRovingconnectSubmenuHoverToFocusStackcreateAccordionKeyboardNavcreateActionHostBridgecreateADActivationDispatchercreateAggregateAsyncStatecreateAnnouncementPhrasecreateArrayCommitHandlercreateAsyncStatecreateAutoplayGatecreateBandScalecreateBreadcrumbCollapsecreateCanvasRenderercreateChartRendererControllercreateChipRemovalHandlercreateChipStripRovingcreateCngxMatTabOverflowDomAdaptercreateCngxTabOverflowDefaultDomAdaptercreateCommitControllercreateCommitControllercreateCommitErrorAnnouncercreateContainerSizecreateContextMenuItemSubmenuFacadecreateContextMenuTriggerCorecreateControlledSourcecreateCreateCommitHandlercreateDebouncercreateDefaultCommandMatchercreateDefaultFlatNavStrategycreateDirectiveByIdMapcreateDismissHandlercreateDisplayBindingcreateDomAnchorRetrycreateElementWidthSignalcreateEmptyFilterRootcreateFieldControlAriacreateFieldSynccreateFieldSynccreateFilterBuilderAnnouncercreateFilterBuilderStatecreateFilterBuilderTemplateRegistrycreateFilterExpressioncreateFilterGroupcreateFilterRowControllercreateForwardedAsyncStatecreateIdentityPanelRenderercreateKeyedRegistrycreateLatencyProbecreateLinearScalecreateLocalItemsBuffercreateManualStatecreateMaterialBidirectionalSynccreateMatStepHandlecreateMatTabHandlecreateMediaQuerySignalcreateMenuAnnouncercreateMenuDismissHandlercreateMenuFocusStackcreateMenuTriggerDismissBindingcreateMockFieldcreateOptimisticcreateOrdinalScalecreateOrganismScrollSynccreateOverflowPopoverHighlightSynccreatePaginatorAnnouncercreatePaginatorNavCorecreatePaletteKeybindingcreatePanelLifecycleEmittercreatePasswordStrengthcreatePathBuildercreatePrefixPhoneMetadatacreateProjectedOptionModelcreateRecyclerPanelRendererFactorycreateReorderCommitHandlercreateResizeSignalcreateRetrycreateRingBuffercreateScalarCommitHandlercreateScrollLockcreateSearchEffectscreateSelectionControllercreateSignificantChangeTrackercreateSliderCorecreateSliderDisabledReasoncreateSliderInteractioncreateSliderTickscreateSlotRegistrycreateSortHeaderStatecreateStepperCommitHandlercreateStepperDisplayModecreateStepperGroupNavigationcreateStepperGroupSummarycreateStepperHostProxycreateStepperStateViewcreateStepperTemplateBindingscreateStripDensitycreateSvgRenderercreateTabDismissalscreateTabGroupAnnouncementscreateTabGroupTemplateBindingscreateTabKeyboardNavcreateTabNavAnnouncementcreateTabOverflowTemplateBindingscreateTabRouterCommitcreateTabsCommitHandlercreateTabUrlMatchcreateTemplateRegistrycreateThumbValuecreateTimelineFallbackCopycreateTimelineGroupingcreateTimelineSlotBindingcreateTimelineSlotscreateTimelineViewcreateTimeScalecreateTransitionTrackercreateTreeAdItemscreateTreeControllercreateTriggerFocusStatecreateTypeaheadControllercreateVisibilityGatecreateW3CMenuStrategycreateW3CTreeStrategycrumbsEqualdateTimeFormatterFordecimalPlacesdedupdefaultRetryCeilingWarndefaultSearchFndefaultSortFndeltaDirectiondeltaSentimentdimensionsEqualdirectionGlyphdotsEqualdownsampleLTTBensureFilterTreeIdsensureObservableentriesEqualentryEqualestimateTotalSizeevaluateExpressionfeaturefeaturefeaturefeaturefileKeyfilterSelectOptionsfilterTreefilterTreefilterTreeEqualfirstEmptySlotflattenSelectOptionsflattenTreeflattenTreefocusFirstErrorformatDeltafromHttpResourcefromQueryfromResourcegetNodeAtPathgroupByDaygroupByMonthgroupByNonegroupByWeekgroupsEqualhasTransitionheadersEqualinjectA11yPanelConfiginjectA11yPreferencesinjectAccordionConfiginjectActionSelectConfiginjectAsyncRegistryinjectAsyncStateinjectAudioConfiginjectBreadcrumbConfiginjectCardI18ninjectChartBufferinjectChartContextinjectChartPanelConfiginjectCngxAudioinjectCommandPaletteConfiginjectCommandsinjectContainerSizeinjectContrastinjectDataGridAccordionConfiginjectDataSourceinjectDensityinjectDialogConfiginjectDirectioninjectErrorAggregatorinjectErrorScopeinjectFeedbackI18ninjectFilterBuilderAnnouncerFactoryinjectFilterBuilderConfiginjectFilterBuilderTemplateRegistryinjectFilterEditorsinjectFormFieldConfiginjectIncrementalListConfiginjectInputConfiginjectInteractiveGroupHostinjectLatencyProbeinjectLoadingConfiginjectMatTabsConfiginjectMediaQueryinjectMenuAnnouncerinjectMenuConfiginjectMenuItemCoreinjectMotioninjectNavConfiginjectPaginatorConfiginjectQueryParamSyncinjectRecyclerinjectReorderableSelectConfiginjectSelectAnnouncerinjectSelectConfiginjectSidenavConfiginjectSmartDataSourceinjectStatCardConfiginjectStepperCollapseinjectStepperConfiginjectStepperI18ninjectTabsConfiginjectTabsI18ninjectTagConfiginjectTextScaleinjectTimelineConfiginjectTocConfiginjectTouchTargetsinjectTreeConfiginjectWindowinsetEqualinstallAxisPersistenceisAsyncStateisCngxSelectOptionGroupDefisExpressionIncompleteisExpressionValueEmptyisNativeEditorisNodeVisibleisOptionDisabledisSuspendedLikeitemsEqualloadTablelocaleToRegionmakeSelectConfigmakeVersionmapAudioStatusmatchesKeyCombomatchesTypeaheadmemoizemergeFeaturesmergeLocalItemsmockValidationErrornextSlotIndexnextUidnodeMatchesSearchobserveMediaQueryobserveResizeonTransitionDonepad2pageWindowpageWindowEqualparseEventBindingsparseKeyComboparseMaskparsePxparseStatusBindingspartitionFeaturespatternMatchplotAreaEqualpointerFractionprevSlotIndexprimaryPathSegmentsprovideA11yPanelConfigprovideA11yPanelConfigAtprovideA11yPreferencesprovideAccordionConfigprovideAccordionConfigAtprovideActionSelectConfigprovideActionSelectConfigAtprovideAsyncHttpObservabilityprovideAsyncRegistryprovideBreadcrumbConfigprovideBreadcrumbConfigAtprovideCardI18nprovideChartI18nprovideChartPanelConfigprovideChartPanelConfigAtprovideChartRendererprovideCngxAudioprovideCngxAudioAtprovideCngxMenuprovideCngxPaginatorConfigprovideCngxPaginatorConfigAtprovideCngxSelectprovideCngxSelectAtprovideCngxStepperprovideCngxStepperAtprovideCngxTabsprovideCngxTabsAtprovideCommandPaletteConfigprovideCommandPaletteConfigAtprovideCommandsprovideContrastprovideDataGridAccordionConfigprovideDataGridAccordionConfigAtprovideDensityprovideDialogprovideDialogConfigprovideDialogConfigAtprovideDialogStackprovideDirectionprovideDirectionAtprovideEagerMaskPresetsprovideEnvironmentprovideErrorMessagesprovideErrorRegistryprovideFeedbackprovideFeedbackI18nprovideFilterBuilderConfigprovideFilterBuilderConfigAtprovideFloatingFallbackprovideFormFieldprovideFormFieldAtprovideIncrementalListConfigprovideIncrementalListConfigAtprovideInputConfigprovideInputConfigAtprovideLoadingConfigprovideLoadingConfigAtprovideMatTabsConfigprovideMatTabsConfigAtprovideMenuConfigprovideMenuConfigAtprovideMotionprovideNavConfigprovideNavConfigAtprovideOverlayprovidePersistenceprovidePhoneMetadataprovidePopoverPanelprovideRecyclerI18nprovideRecyclerPlaceholderRowprovideReorderableSelectConfigprovideReorderableSelectConfigAtprovideSelectConfigprovideSelectConfigAtprovideSidenavConfigprovideSidenavConfigAtprovideStatCardConfigprovideStatCardConfigAtprovideStepperConfigprovideStepperConfigAtprovideStepperI18nprovideTabsConfigprovideTabsConfigAtprovideTabsI18nprovideTagConfigprovideTagConfigAtprovideTextScaleprovideTimelineConfigprovideTimelineConfigAtprovideToastsprovideTocConfigprovideTocConfigAtprovideTouchTargetsprovideTreeConfigprovideTreeConfigAtprovideTreetableprovideTreetableAtprovideWindowrangereadStoredreflectAsyncDisplayStatusremoveAtPathrequiredTrueresolveAsyncViewresolveBoundaryStepresolveByPrefixresolveBySuffixresolveCellTplresolveElementresolveFeaturesresolveGrouperresolveHeaderTplresolveI18nFeaturesresolveInlineArrowKeyresolveInlineStepresolveLoadingConfigresolveLoadingTreatmentresolveOperatorDefresolvePageJumpTargetresolveParentresolveStepFromresolveStepperErrorSummaryresolveStepperStatusLabelresolveTreetableConfigresolveZiprunTabsActionsameItemssameItemsArrsameNumberArrsameSubmenuItemsscaleGainscaleStepsselectPatternsetEqualsetEqualssiblingsEqualsliderTickValuesslotContextEqualslotCountsnapToStepsortTreesortTreesubtreeStatstabOverflowOptionIdtapAsyncProgresstapAsyncStatetapHttpAsyncStatetoDatetoFilterPredicatetrackForupdateAtPathurlTreeSegmentCountuseStatSlotwalkTreewithA11yPanelAxeswithA11yPanelLabelswithAccordionLabelswithAccordionSkinwithAccordionTemplateswithActionAriaLabelwithActionPopoverPlacementwithActionPositionwithAlertIconswithAlertswithAnchorRetryAttemptswithAnnouncerwithAriaLabelswithAriaLabelswithArrowwithArrowTemplatewithAsyncLabelwithAsyncSkipwithAutocompleteMappingswithAutoDismisswithBannerswithBreadcrumbAriaLabelswithBreadcrumbDataKeywithBreadcrumbIconKeywithBreadcrumbSkinwithCapitaliseHeaderswithCardI18nLabelswithCaretwithCaseInsensitiveStringswithChartPanelAriaLabelswithChartPanelLegendPositionwithChartRendererFactorywithChartRendererThresholdwithChipOverflowwithCloseButtonwithCloseIconwithCloseOnCreatewithCloseOnSelectwithCloseOnSuccesswithCngxAsyncStatewithCommandPaletteLabelswithCommandPaletteTemplateswithCommitErrorAnnouncePolicywithCommitErrorDisplaywithConstraintHintswithContrastwithCopyResetDelaywithCurrencywithCustomTokenswithDataGridSkinwithDateFormatswithDebounceMswithDefaultDragHandlewithDefaultHeadingLevelwithDefaultInitiallyExpandedwithDefaultKeyFnwithDefaultLabelFnwithDefaultNodeIdFnwithDefaultOperatorswithDefaultVariantwithDensitywithDialogLabelswithDismissOnwithDismissOnBlurwithDismissOnOutsideClickwithDismissOnScrollwithDotStepperDotTemplatewithEarconswithEnterKeyHintwithErrorMessageswithErrorStrategywithFallbackLabelswithFeedbackI18nLabelswithFieldSkinwithFileMaxFileswithFileMaxSizewithFilterBuilderI18nwithFocusTrapBehaviorwithGlobalRevealOnSubmitwithHalfWiredSlotSinkwithHighlightOnHoverwithIbanPatternswithIncrementalListAriaLabelswithIncrementalListTemplateswithInputAriaLabelswithInputModewithKeyboardLegendwithLiveInputFallbackwithLoadingDefaultswithLoadingVariantwithLogicOptionswithMaskGuidewithMaskPlaceholderwithMatTabRejectionTemplatewithMaxNestingDepthwithMaxVisibleChipswithMinDwellwithMotionwithMutedwithNavAnimationwithNavIndentwithNegationwithNoSpellcheckwithNumericDefaultswithOpenOnwithOperatorswithPaginatorAnnouncementswithPaginatorAriaLabelswithPaginatorPageSizeOptionswithPaginatorPageStatusFormatwithPaginatorRangeFormatwithPaginatorTemplateswithPaletteShortcutwithPanelClasswithPanelWidthwithPersistencewithPhoneDefaultRegionwithPhonePatternswithPopoverPlacementwithRefreshingVariantwithReorderAriaLabelwithReorderKeyboardModifierwithReorderStripFreezewithRequiredMarkerwithRespectReducedMotionwithRestoreFocuswithResultCountFormatterwithRevealOnNavigatewithSelectionIndicatorwithSelectionIndicatorPositionwithSelectionIndicatorVariantwithShowDelaywithSidenavDimensionswithSidenavHoverDwellwithSidenavRouterSyncwithSidenavShortcutwithSingleAccordionwithSkeletonRowCountwithSpinnerTemplatewithSpinnerVsSkeletonCutoffwithStatCardAriaLabelswithStatCardLoadingTreatmentwithStepBadgeTemplatewithStepBusySpinnerTemplatewithStepErrorTemplatewithStepGroupHeaderTemplatewithStepIndicatorTemplatewithStepperAriaLabelswithStepperCommitModewithStepperConnectorswithStepperDefaultOrientationwithStepperDensitywithStepperEmptyTemplatewithStepperFallbackLabelswithStepperGroupCollapsewithStepperGroupCollapseSummarywithStepperHeaderNavigationwithStepperI18nLabelswithStepperLinearwithStepperMobileCollapsewithStepperMobileIndicatorPositionwithStepperMobileSwipewithStepperRouterSyncwithStepperSkinwithStepRejectionTemplatewithSubmenuCloseDelaywithSubmenuOpenDelaywithTabAddIconTemplatewithTabBusySpinnerTemplatewithTabCloseIconTemplatewithTabErrorBadgeTemplatewithTabIconTemplatewithTabOverflowItemTemplatewithTabOverflowMaxDeferMswithTabOverflowStabilizeMswithTabOverflowTriggerTemplatewithTabRejectionIconTemplatewithTabsAddablewithTabsAlignwithTabsAriaLabelswithTabsClosablewithTabsCommitModewithTabsDefaultOrientationwithTabsFallbackLabelswithTabsFittedwithTabsFragmentSyncwithTabsI18nLabelswithTabsIconLayoutwithTabsLinkAriaCurrentwithTabsPanelModewithTabsRouteMatchwithTabsRovingLoopwithTabsSkinwithTagColorswithTagDefaultswithTagGroupDefaultswithTagSlotswithTemplateswithTemplateswithTextScalewithTimelineLabelswithTimelineTemplateswithToastswithTocAriaLabelswithTocScrollBehaviorwithTocSpywithTocTemplateswithTreeCacheLimitwithTreetableLabelswithTreetableTemplateswithTypeaheadDebouncewithTypeaheadDebouncewithTypeaheadWhileClosedwithVirtualizationwithVolumewithZipPatterns
No matching entities

forms/field/form-control-adapter.ts

adaptFormControl#CngxFieldAccessor
adaptFormControl(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 adapter binds to the control instance passed at call time and never re-binds. Replacing that instance later (FormGroup.setControl(), rebuilding the form) goes unnoticed - the subscriptions stay on the old control and the accessor freezes on its last mirrored state. Call adaptFormControl again with the new instance and swap the [field] binding instead.

@paramcontrolAbstractControl

The Reactive Forms control to adapt.

@paramnamestring

A unique field name for deterministic ID generation.

@paramdestroyRefDestroyRef

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

appendAtPath#FilterGroup
appendAtPath(root: FilterGroup, path, child: FilterNode)

Append child to the group at path. Throws when path does not resolve to a group.

@paramrootFilterGroup
@parampath
@paramchildFilterNode
filterTreeEqual#boolean
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.

getNodeAtPath#FilterNode | null
getNodeAtPath(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.

@paramrootFilterGroup
@parampath
removeAtPath#FilterGroup
removeAtPath(root: FilterGroup, path)

Remove the node at path. Throws on empty path - the root cannot be removed.

@paramrootFilterGroup
@parampath
updateAtPath#FilterGroup
updateAtPath(root: FilterGroup, path, updater)

Replace the node at path via updater. Returns the original root when no descendant changed (identity-preserving).

@paramrootFilterGroup
@parampath
@paramupdater

common/timeline/timeline-config.ts

injectTimelineConfig Inject
provideTimelineConfig Provider
provideTimelineConfigAt Provider
withTimelineLabels Feature
withTimelineTemplates Feature
applyFeatures#CngxTimelineConfig
applyFeatures(base: CngxTimelineConfig, features)
@paramfeatures
injectTimelineConfig#CngxTimelineConfig

Read the resolved timeline config. Runs in an injection context.

provideTimelineConfig#EnvironmentProviders
provideTimelineConfig(...features: undefined)

Root-level provider. Apply once in bootstrapApplication / appConfig.providers.

@paramfeatures
provideTimelineConfigAt#Provider[]
provideTimelineConfigAt(...features: undefined)

Component-scoped override. Returns Provider[] rather than EnvironmentProviders because viewProviders rejects opaque environment providers.

Features merge onto the parent config - an enclosing scope, or the root provider, or the library defaults when neither is present - so a region can re-phrase one label without resetting every other one the app set.

@paramfeatures
withTimelineLabels#CngxTimelineConfigFeature
withTimelineLabels(labels: CngxTimelineLabels)

Merge label overrides into the cascade. Keys left out keep their English library default, so a consumer translates what they need and nothing more.

provideTimelineConfig(
  withTimelineLabels({
    retry: 'Erneut versuchen',
    emptyFallback: 'Noch keine Ereignisse.',
    groupLabel: (group) => group.start.toLocaleDateString('de-AT'),
  }),
)
@paramlabelsCngxTimelineLabels
withTimelineTemplates#CngxTimelineConfigFeature
withTimelineTemplates(templates: CngxTimelineTemplates)

Merge app-wide slot template defaults into the cascade. Keys left out keep whatever an earlier feature set, and a per-instance slot directive still wins over anything set here.

One bag rather than a with*Template builder per slot, matching how the select family carries its own slot defaults - the timeline has eight slots and near-identical builders would only add surface.

readonly emptyTpl = viewChild.required<TemplateRef<CngxTimelineEmptyContext>>('emptyTpl', {
  read: TemplateRef,
});

providers: [provideTimelineConfig(withTimelineTemplates({ empty: this.emptyTpl() }))]
@paramtemplatesCngxTimelineTemplates

ui/a11y/a11y-panel.config.ts

injectA11yPanelConfig Injectv0.1.0
provideA11yPanelConfig Providerv0.1.0
provideA11yPanelConfigAt Providerv0.1.0
withA11yPanelAxes Featurev0.1.0
withA11yPanelLabels Featurev0.1.0
applyFeatures(base: CngxA11yPanelConfig, features)

Reduce a feature list onto a base config, merging text and axes in isolation.

@paramfeatures
injectA11yPanelConfig#CngxA11yPanelConfig

Convenience accessor for the resolved panel configuration. Runs in an injection context; resolves through the cascade. Equivalent to inject(CNGX_A11Y_PANEL_CONFIG).

provideA11yPanelConfig#EnvironmentProviders
provideA11yPanelConfig(...features: undefined)

Application-root configuration cascade for the accessibility panel. Pass any combination of with* features in bootstrapApplication's providers; supplied features merge with the library defaults, so consumers only declare what they override.

@paramfeatures
provideA11yPanelConfigAt#Provider[]
provideA11yPanelConfigAt(...features: undefined)

Component-scoped 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 one panel can re-scope its axis subset or labels without disturbing the rest of the app. This is the component-scope tier of the resolution cascade: provideA11yPanelConfigAt (nearest) wins over provideA11yPanelConfig (root), which wins over the library defaults.

@paramfeatures
withA11yPanelAxes(payload)

Replace the rendered axis list - reorder groups, drop an axis, or restrict the options a group offers. Supplying a subset renders only those groups.

provideA11yPanelConfig(
  withA11yPanelAxes([
    { axis: 'textScale', reset: 'md', options: [
      { value: 'md', label: 'Default' },
      { value: 'lg', label: 'Large' },
    ] },
  ]),
);
@parampayload
withA11yPanelLabels#CngxA11yPanelConfigFeature
withA11yPanelLabels(payload: CngxA11yPanelLabelsOverride)

Override any subset of the panel text - axis group labels, the Reset label, the default heading, or the Reset announcement. The axes record merges key-by-key, so a single axis can be relabelled in isolation.

provideA11yPanelConfig(
  withA11yPanelLabels({
    heading: 'Barrierefreiheit',
    axes: { motion: 'Bewegung' },
    resetMessage: 'Einstellungen zurueckgesetzt',
  }),
);

ui/collection/incremental-list-config.ts

injectIncrementalListConfig Injectv0.1.0
provideIncrementalListConfig Providerv0.1.0
provideIncrementalListConfigAt Providerv0.1.0
withIncrementalListAriaLabels Featurev0.1.0
withIncrementalListTemplates Featurev0.1.0
applyFeatures(base: CngxIncrementalListConfig, features)

Reduce a feature list onto a base config, merging each sub-tree in isolation.

@paramfeatures
injectIncrementalListConfig#CngxIncrementalListConfig

Convenience accessor for the incremental-list configuration. Runs in injection context; resolves through the cascade. Equivalent to inject(CNGX_INCREMENTAL_LIST_CONFIG).

provideIncrementalListConfig#EnvironmentProviders
provideIncrementalListConfig(...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.

@paramfeatures
provideIncrementalListConfigAt#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.

@paramfeatures
withIncrementalListAriaLabels#CngxIncrementalListConfigFeature
withIncrementalListAriaLabels(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`,
  }),
);
@parampayloadPartial
withIncrementalListTemplates#CngxIncrementalListConfigFeature
withIncrementalListTemplates(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 }),
);
@parampayloadPartial

ui/paginator/paginator-config.ts

injectPaginatorConfig Injectv0.1.0
provideCngxPaginatorConfig Providerv0.1.0
provideCngxPaginatorConfigAt Providerv0.1.0
withPaginatorAnnouncements Featurev0.1.0
withPaginatorAriaLabels Featurev0.1.0
withPaginatorPageSizeOptions Featurev0.1.0
withPaginatorPageStatusFormat Featurev0.1.0
withPaginatorRangeFormat Featurev0.1.0
withPaginatorTemplates Featurev0.1.0
applyFeatures(base: CngxPaginatorConfig, features)

Reduce a feature list onto a base config, merging each sub-tree in isolation.

@paramfeatures
injectPaginatorConfig#CngxPaginatorConfig

Convenience accessor for the paginator configuration. Runs in injection context; resolves through the cascade. Equivalent to inject(CNGX_PAGINATOR_CONFIG).

provideCngxPaginatorConfig#EnvironmentProviders
provideCngxPaginatorConfig(...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.

@paramfeatures
provideCngxPaginatorConfigAt#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.

@paramfeatures
withPaginatorAnnouncements#CngxPaginatorConfigFeature
withPaginatorAnnouncements(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',
  }),
);
@parampayloadPartial
withPaginatorAriaLabels#CngxPaginatorConfigFeature
withPaginatorAriaLabels(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' }),
);
@parampayloadPartial
withPaginatorPageSizeOptions#CngxPaginatorConfigFeature
withPaginatorPageSizeOptions(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]),
);
@parampayload
withPaginatorPageStatusFormat#CngxPaginatorConfigFeature
withPaginatorPageStatusFormat(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}`),
);
@parampageStatus
withPaginatorRangeFormat#CngxPaginatorConfigFeature
withPaginatorRangeFormat(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}`),
);
@paramrange
withPaginatorTemplates#CngxPaginatorConfigFeature
withPaginatorTemplates(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 }),
);
@parampayloadPartial

forms/input/input-mask.directive.ts

applyMask#literal type
applyMask(raw: string, tokens, placeholder: string, guide: boolean)
@paramrawstring
@paramtokens
@paramplaceholderstring
@paramguideboolean
firstEmptySlot#number
firstEmptySlot(tokens, masked: string, placeholder: string)
@paramtokens
@parammaskedstring
@paramplaceholderstring
localeToRegion#string
localeToRegion(locale: string)
@paramlocalestring
nextSlotIndex#number
nextSlotIndex(tokens, from: number)
@paramtokens
@paramfromnumber
parseMask#MaskToken[]
parseMask(pattern: string, customTokens?: MaskTokenMap)
@parampatternstring
@paramcustomTokens?MaskTokenMap
prevSlotIndex#number
prevSlotIndex(tokens, from: number)
@paramtokens
@paramfromnumber
resolveZip#literal type
resolveZip(region: string, zips: Record)
@paramregionstring
@paramzipsRecord
selectPattern#string
selectPattern(patterns, rawLength: number, customTokens?: MaskTokenMap)
@parampatterns
@paramrawLengthnumber
@paramcustomTokens?MaskTokenMap
slotCount#number
slotCount(pattern: string, customTokens?: MaskTokenMap)
@parampatternstring
@paramcustomTokens?MaskTokenMap

dialog/dialog/sr-only.ts

applySrOnly#void
applySrOnly(el: HTMLElement)

Apply the visually-hidden (screen-reader-only) recipe to a dynamically created dialog node.

Inline styles instead of relying on the cngx-sr-only class alone so the live region and the drag instruction stay hidden even when no cngx stylesheet is loaded. The node remains perceivable to AT - describedby targets and live regions must never be display: none.

Internal helper - intentionally not exported from public-api.ts.

@paramelHTMLElement

projects/utils/equality.ts

arrayEqual#boolean
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.

@parama
@paramb
setEqual#boolean
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.

@paramaReadonlySet
@parambReadonlySet

select/shared/provide-cngx-select.ts

provideCngxSelect Provider
provideCngxSelectAt Provider
bucket(features)
@paramfeatures
provideCngxSelect#EnvironmentProviders[]
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),
    ),
  ],
});
@paramfeatures
provideCngxSelectAt#Provider[]
provideCngxSelectAt(...features: undefined)

Component-scoped twin of provideCngxSelect. Returns Provider[] because viewProviders rejects EnvironmentProviders.

@paramfeatures

core/utils/build-async-state-view.ts

buildAsyncStateView v0.1.0
buildAsyncStateView#CngxAsyncState
buildAsyncStateView(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

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).

@paramrouterRouter
@paramdataKeystring
@paramiconKeystring
crumbsEqual#boolean
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.

@parama
@paramb

chart/path/curve.ts

buildCurvePath#string
buildCurvePath(points, curve: CngxCurve)

Build the SVG d attribute for a sequence of points.

@parampoints

Pixel-coordinate points to connect.

@paramcurveCngxCurve

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#DotModel
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.

@paramcurrentnumber
@paramtotalnumber
dotsEqual#boolean
dotsEqual(a: DotModel, b: DotModel)
@paramaDotModel
@parambDotModel

ui/breadcrumb/breadcrumb-siblings-router-sync.directive.ts

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.

@paramrouterRouter
@paramdepthnumber
@paramdataKeystring
siblingsEqual#boolean
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.

@parama
@paramb

interactive/guard/can-deactivate.ts

canDeactivateWhenClean#boolean
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())]
}
@paramisDirty
  • Callback that returns true when there are unsaved changes.
@parammessagestring= 'You have unsaved changes. Leave anyway?'
  • Confirmation message. Default: 'You have unsaved changes. Leave anyway?'

A functional guard compatible with Angular's canDeactivate.

forms/input/input-filter.directive.ts

charMatcher#boolean
charMatcher(pattern: InputFilterPattern)
@parampatternInputFilterPattern

projects/utils/clamp.ts

clamp#number
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. A NaN bound behaves like an open bound (every comparison against NaN is false, so that side never clamps) - pass-through, not an error. When the bounds invert (min > max after coercion), min wins and the returned value is min.

@paramvaluenumber
@parammin
@parammax

interactive/slider/slider-core.ts

createSliderCore Factoryv0.1.0
clamp#number
clamp(value: number, lo: number, hi: number)
@paramvaluenumber
@paramlonumber
@paramhinumber
createSliderCore#CngxSliderCore
createSliderCore(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).

snapToStep#number
snapToStep(raw: number, origin: number, step: number)
@paramrawnumber
@paramoriginnumber
@paramstepnumber

ui/accordion/accordion-group.component.ts

clampHeadingLevel#number
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.

@paramvalue
@paramfallbacknumber= 3

ui/data-grid-accordion/data-grid-accordion.component.ts

clampHeadingLevel#number
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.

@paramvalue
@paramfallbacknumber= 3
trackFor#string
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).

@paramtrack
@paramisPrimaryboolean

audio/engine/audio-engine.ts

clampVolume#number
clampVolume(v: number)
@paramvnumber
isSuspendedLike#boolean
isSuspendedLike(state: string)

A context that a resume() can lift - suspended, or Safari's interrupted.

@paramstatestring
mapAudioStatus#AudioStatus
mapAudioStatus(state: string)

Map a raw AudioContext.state onto AudioStatus. Safari reports a non-standard 'interrupted' (phone call, Siri, audio-route change) that the spec union does not carry; it resumes on the next gesture exactly like a suspended context, so it folds to 'suspended' rather than being cast into the union. Any unknown value degrades to 'suspended', the safe resting state a subsequent resume() can lift.

@paramstatestring
scaleGain#number
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.

@paramgain
@paramscalenumber
scaleSteps#ToneStep[]
scaleSteps(steps, scale)
@paramsteps
@paramscale

projects/utils/array.ts

coerceArray#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.

@paramvalue

core/utils/coerce.util.ts

coerceBooleanProperty v0.1.0
coerceNumberProperty v0.1.0
coerceBooleanProperty#boolean
coerceBooleanProperty(value)

Coerces a value to a boolean.

Strings are truthy unless they equal 'false'. All other falsy values return false.

@paramvalue
coerceNumberProperty#number
coerceNumberProperty(value, fallback: number)

Coerces a value to a number.

Returns fallback when the value is null, undefined, NaN, or non-numeric.

Strings parse leniently via Number.parseFloat: a leading numeric prefix wins even when a unit trails it ('12px' coerces to 12, '1.5rem' to 1.5). A string with no leading number ('px12', 'true') returns fallback. Booleans and objects are never numeric and return fallback.

@paramvalue
@paramfallbacknumber= 0

ui/layout/grid.component.ts

coerceColumns#number | string
coerceColumns(value)
@paramvalue

projects/utils/tree.ts

collectDescendantValues#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.

@paramnodeCngxTreeNode
filterTree#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.

@paramnodes
@parampredicate
flattenTree#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.

@paramnodes

Root-level tree nodes.

@paramidFnIdFn= defaultIdFn as IdFn

Derives 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. Ids must be unique across the whole forest: a non-injective idFn silently corrupts the parentIds chains (and with them visibility resolution), so dev mode warns on the first duplicate it sees.

@paramlabelFnLabelFn= defaultLabelFn as LabelFn

Derives visible label. Defaults to String(value).

isNodeVisible#boolean
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.

@paramnodeFlatTreeNode
@paramexpandedIdsReadonlySet
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.

The treetable keeps its own numeric-aware localeCompare collation - this comparator (< / > on the extractor output) is not a drop-in replacement for it.

@paramnodes
@paramby
@paramdirection= 'asc'
walkTree#void
walkTree(nodes, visit)

DFS visitor. visit is called once per node with the current depth.

Returning the literal false from the visitor stops the walk immediately - no further node is visited at any depth. Any other return value continues the traversal, so existing visitors (void or value-returning arrows alike) keep their behavior. The return type is unknown rather than boolean | void on purpose: a concise arrow like (n) => seen.push(n) must stay assignable.

@paramnodes
@paramvisit

forms/input/phone-metadata.ts

createPrefixPhoneMetadata Factoryv0.2.0
providePhoneMetadata Provider
compileMatcher#number
compileMatcher(matcher: PhonePrefixMatcher)
@parammatcherPhonePrefixMatcher
createPrefixPhoneMetadata#CngxPhoneMetadata
createPrefixPhoneMetadata(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'] },
  }),
);
@paramprefixesPhonePrefixMap
providePhoneMetadata#Provider
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)]
@paramadapterCngxPhoneMetadata

data/recycler/range-computer.ts

computeFixedRange#RangeResult
computeFixedRange(scrollTop: number, clientHeight: number, totalCount: number, itemSize: number, overscanBefore: number, overscanAfter: number, columns: number)
@paramscrollTopnumber
@paramclientHeightnumber
@paramtotalCountnumber
@paramitemSizenumber
@paramoverscanBeforenumber
@paramoverscanAfternumber
@paramcolumnsnumber
computeVariableRange#RangeResult
computeVariableRange(scrollTop: number, clientHeight: number, totalCount: number, estimateSize, overscanBefore: number, overscanAfter: number)
@paramscrollTopnumber
@paramclientHeightnumber
@paramtotalCountnumber
@paramestimateSize
@paramoverscanBeforenumber
@paramoverscanAfternumber
estimateTotalSize#number
estimateTotalSize(totalCount: number, estimateSize, columns: number)
@paramtotalCountnumber
@paramestimateSize
@paramcolumnsnumber

data/paginate/paginate-emit.ts

connectPaginateEmit v0.1.0
connectPaginateEmit#void
connectPaginateEmit(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's pageIndex() stays pinned to the input until the consumer feeds it back;
  • an effect() reads the effective pageIndex() / pageSize() and covers a total-shrink clamp the nav-only output misses. The index half of the clamp path is suppressed while the brain is busy or total is 0, so a transient total drop during a refetch never persists a page-0 clamp into a controlled consumer; a clamp that survives the settle emits then.

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.

@parampaginateCngxPaginate

data/paginate/paginate-reset.directive.ts

connectPaginateResetOn v0.1.0
connectPaginateResetOn#void
connectPaginateResetOn(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).

@parampaginateCngxPaginate
@paramkey

data/recycler/connect-recycler-active-descendant.ts

connectRecyclerToActiveDescendant#void
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.

@paramrecyclerCngxRecycler

data/recycler/connect-recycler-roving.ts

connectRecyclerToRoving#void
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.

@paramrecyclerCngxRecycler
@paramrovingCngxRovingTabindex

interactive/menu/menu-focus-stack.ts

connectSubmenuHoverToFocusStack v0.1.0
createMenuFocusStack Factoryv0.1.0
connectSubmenuHoverToFocusStack#void
connectSubmenuHoverToFocusStack(deps: CngxSubmenuHoverRoutingDeps)

Route submenu hover intent through the focus stack. Watches the CngxMenuSubmenuLike.hoverIntent signal of every submenu companion registered anywhere in the menu tree and routes its edges through the same primitives keyboard and click use: a settled hover opens via CngxMenuFocusStack.openSubmenuFor (stack-tracked, first item highlighted, keyboard-visible), a settled un-hover closes via CngxMenuFocusStack.closeSubmenuFor (stack popped innermost-first). The submenu companion itself owns NO open/close mechanics for hover - it only derives intent.

The walk covers the WHOLE registered tree, not just the open chain: a submenu whose menu drops out of the chain (popped by a chain-correcting open, cleared by reset()) still decays its intent afterwards, and that false edge must be observed or the bookkeeping would go stale and fire a spurious close when the menu re-enters the chain. Items in a hidden menu cannot produce real pointer events, so the extra tracking never opens anything on its own.

Edge-driven, not state-reconciling: a keyboard-opened submenu whose intent never settled true is left alone, so pointer-less operation is unchanged.

Must run in an injection context (constructor / field initializer) - it installs an effect. Every focus-stack owner wires it once: CngxMenuTrigger, CngxContextMenuTrigger, and CngxContextMenuFor.

createMenuFocusStack#CngxMenuFocusStack
createMenuFocusStack(deps: CngxMenuFocusStackDeps)

Default focus-stack implementation. Behaviour matches the W3C APG Menu Button pattern that CngxMenuTrigger shipped inline before extraction.

interactive/accordion/accordion-keyboard-nav.ts

createAccordionKeyboardNav Factoryv0.1.0
createAccordionKeyboardNav#CngxAccordionKeyboardNav
createAccordionKeyboardNav(opts: CngxAccordionKeyboardNavOptions)

Level-2 factory implementing the WAI-ARIA APG accordion keyboard model over a registration model rather than contentChildren roving. Registration is immune to component-view boundaries, so any accordion skin - the @cngx/ui/accordion organism, or a future one that renders each header in its own view - gets working Arrow/Home/End nav.

Pillar 1 (Ableitung statt Verwaltung): the tab stop is derived from the coordinator's rovingActiveId, not managed as a second index. Focus movement is a DOM concern the factory owns - the coordinator only records which header should be the stop; it never touches the DOM.

select/shared/action-host-bridge.ts

createActionHostBridge Factory
createActionHostBridge#ActionHostBridge
createActionHostBridge(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 Factory
createADActivationDispatcher#void
createADActivationDispatcher(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.

core/utils/aggregate-async-state.ts

createAggregateAsyncState Factoryv0.1.0
createAggregateAsyncState#CngxAsyncState<(T | undefined)[]>
createAggregateAsyncState(sources: Signal)

Aggregate N independent CngxAsyncStates into one derived CngxAsyncState.

Pure, keyless, and injection-context-free - it reuses buildAsyncStateView so every derived flag (isLoading, isSettled, hasData, ...) stays single-source-consistent with every other producer. The result is a CngxAsyncState, so it flows through cngx-async-container, the transition bridges, and every other consumer unchanged.

Combined status follows a fixed priority rule (first match wins):

  1. any source error -> error
  2. else any source loading/pending -> loading
  3. else any source refreshing -> refreshing
  4. else all sources success -> success
  5. else -> idle (empty list, or any remaining idle)

data is the per-source data() values in input order; each element is T | undefined because a source carries no data until it reaches success. error is the first error in input order (raw, for the single-error bridge path). Emptiness is the aggregate rule - empty only when it has at least one source and every source is itself empty - which the data shape cannot infer (an N-element array is never length 0), so it is supplied explicitly.

Two view fields are constant by construction: rule 2 collapses pending into loading, so the combined status is never 'pending' and isPending is always false; and progress is always undefined because per-source progress values have no comparable scale to aggregate over (a determinate upload percentage and an indeterminate fetch cannot be averaged honestly).

@paramsourcesSignal

core/utils/announcement-phrase.ts

createAnnouncementPhrase Factoryv0.1.0
createAnnouncementPhrase#Signal
createAnnouncementPhrase(options: AnnouncementPhraseOptions)

Declarative live-region phrase with transition arming and expiry - the shared shape behind "voice the change, not the standing state". A real transition (arm) sets the phrase; an expiry transition (spend) clears an already-voiced phrase so a later content change cannot re-announce it; everything else keeps the previous phrase.

Fully derived (linkedSignal over source snapshots) - no effect, no imperative previous-value tracking.

Eager-read caveat. linkedSignal only observes source snapshots it is actually read under. A consumer that renders this phrase behind a short-circuiting arm (busy() ? busyPhrase : phrase()) MUST read the phrase eagerly before branching, otherwise the phrase never observes the busy-start snapshot and the spend transition is skipped.

private readonly selectionPhrase = createAnnouncementPhrase({
  source: () => ({ selected: this.selected(), loading: this.loading() }),
  arm: (curr, prev) =>
    curr.selected !== prev.selected ? (curr.selected ? 'Selected' : 'Deselected') : null,
  spend: (curr, prev) => curr.loading && !prev.loading,
});

select/shared/array-commit-handler.ts

createArrayCommitHandler Factory
createArrayCommitHandler#ArrayCommitHandler
createArrayCommitHandler(opts: ArrayCommitHandlerOptions)

Array-shape commit flow shared by CngxMultiSelect and CngxCombobox. Owns commit-controller lifecycle, reconciliation via sameArrayContents, togglingOption.set(null) on success and at beginClear entry, optimistic rollback on error, live-region "removed" announce on a successful clear and the announceError hook on failure. 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 Factory
createAsyncState#MutableAsyncState
createAsyncState(options?: CreateAsyncStateOptions)

Create 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.0
createAutoplayGate#CngxAutoplayGate
createAutoplayGate(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.

@paramdeps

chart/scales/band.ts

createBandScale Factory
createBandScale#BandScale
createBandScale(domain, range, padding: number)

Construct a BandScale from a categorical domain and a numeric range. See the type-level documentation above for parameter semantics.

@paramdomain
@paramrange
@parampaddingnumber= 0

interactive/breadcrumb/breadcrumb-collapse.ts

createBreadcrumbCollapse Factoryv0.1.0
createBreadcrumbCollapse#CngxBreadcrumbCollapseStrategy

The 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.

chart/renderer/canvas-renderer.ts

createCanvasRenderer Factoryv0.1.0
createCanvasRenderer#CngxChartRenderer
createCanvasRenderer(deps: ChartRendererDeps)

Canvas rendering backend. Mounts a <canvas> absolutely positioned over the chart's SVG frame and paints each LayerGeometry onto its 2D context: Path2D stroke/fill for line/area, per-mark loops for bar/scatter, moveTo/lineTo for threshold, fillRect for band.

Colors resolve through a closure-local cache: the first paint() reads each CSS custom property once via getComputedStyle(host); subsequent paints hit the cache, so the paint hot path performs zero synchronous DOM reads. CngxChartRenderer.invalidateColorCache clears it - the renderer controller calls it on a dimensions() change (a proxy for layout / theming reflow). It never writes the chart context's renderSvg gate; the shell owns that.

The plot inset needs no handling here. Every geometry this backend paints was derived from ctx.xScale() / ctx.yScale(), whose ranges are already the inset plot area, so canvas marks land exactly where SVG marks would without the renderer knowing an inset exists. Axis decoration never reaches this file at all: there is no axis branch in KIND_VARS and no text call anywhere in the backend, because CngxAxis renders its own SVG ungated by renderSvg in both modes. The two paths therefore cannot disagree on where the plot area is, and only one of them ever draws an axis.

They do differ on what happens past the box edge: a canvas clips natively at the element bounds, and since the SVG root no longer sets overflow: visible it now clips there too. Out-of-domain data is cut off on both paths.

chart/renderer/chart-renderer-controller.ts

createChartRendererController Factoryv0.1.0
createChartRendererController#CngxChartRendererController
createChartRendererController(deps: ChartRendererControllerDeps)

Owns the renderer mount / destroy / paint reactive lifecycle so the chart shell holds no rendering brain. Installs two effects:

  • mount - tracks mode(); on change, destroys the previous backend, builds the new one via factory, mounts it, and seeds the first paint.
  • paint - tracks geometries() and the (structurally-deduped) dimensions; repaints on every geometry emission and invalidates the backend's color cache when the dimensions actually change.

Both effects wrap their imperative renderer calls in untracked() so the renderer's own signal reads never feed back into the effect graph. The dimensions are tracked through a linkedSignal guarded by dimensionsEqual, so a resize observer re-emitting the same width/height literal does not trigger a redundant cache invalidation.

Must run in an injection context (the chart shell calls it from a field initialiser / constructor). Ships as a plain create* factory the shell calls directly - no DI token, since the only second consumer today is a test double, which injects a fake factory through the deps.

select/shared/chip-removal-handler.ts

createChipRemovalHandler Factory
createChipRemovalHandler#CngxChipRemovalHandler
createChipRemovalHandler(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.0
createChipStripRoving#CngxChipStripRovingController
createChipStripRoving(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.0
createCngxMatTabOverflowDomAdapter#CngxTabOverflowDomAdapter

Material 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 Factory
createCngxTabOverflowDefaultDomAdapter#CngxTabOverflowDomAdapter

Default 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 Factory
createCommitController#CngxCommitController

Factory 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 Factory
createCommitController#CngxCommitController

Direct (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 Factory
createCommitErrorAnnouncer#void
createCommitErrorAnnouncer(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.

layout/observers/container-size.ts

createContainerSize Factoryv0.1.0
injectContainerSize Injectv0.1.0
createContainerSize#CngxContainerSize
createContainerSize(element: Element, destroyRef: DestroyRef, win)

Creates a CngxContainerSize over element.

Observes the border box, so the reported size matches what an inline-size container query measures. The property() reads are plain computed()s over the same resize signal - the browser has already re-evaluated every container query by the time a ResizeObserver callback runs, so the read returns the current value without a second scheduling hop.

@paramelementElement

The container host.

@paramdestroyRefDestroyRef

Scope whose destruction disconnects the observer.

@paramwin

The window-like object providing ResizeObserver and getComputedStyle.

injectContainerSize#CngxContainerSize
injectContainerSize(target?)

Reads the nearest query container declared by a [cngxContainer] ancestor, or observes target (default: the host element) when there is none.

Use it where a component must branch structurally on the width it was given

  • a different template, a focus move, an aria-modal flip. Purely visual adaptation needs no TypeScript at all: write a @container rule.
private readonly container = inject(CNGX_CONTAINER_SIZE, { optional: true });
private readonly wide = this.container?.property('--cngx-thing-wide', this.host) ?? signal('').asReadonly();
readonly mode = computed(() => (this.wide() === '1' ? 'side' : 'over'));

A component that declares the container in its own stylesheet and reads it itself passes its host explicitly and reads the property off ::after - there is no descendant to carry the value:

private readonly host = inject(ElementRef).nativeElement as Element;
private readonly container = injectContainerSize(this.host);
private readonly narrow = this.container.property('--cngx-thing-narrow', this.host, '::after');
@paramtarget?

Element to observe when no ancestor container exists.

ui/context-menu/context-menu-item-submenu-facade.ts

createContextMenuItemSubmenuFacade Factoryv0.1.0
createContextMenuItemSubmenuFacade#CngxMenuSubmenuPopoverRef
createContextMenuItemSubmenuFacade(target, open)

Build the popover-facade adapter the submenu brain (CngxMenuItemSubmenu) drives instead of a [cngxMenuItemSubmenu] popover input. Every member delegates to the target panel's own CngxPopover, reading target() lazily so the facade stays valid across [submenu] changes and inert (no visible, empty id) while the target is unbound. show() delegates to the supplied open routing rather than popover.show(), so opening goes through the trigger's focus stack (Pillar 1) instead of bypassing it.

Internal decompose glue, not a consumer contract - NOT exported from public-api.ts. Extracting it keeps CngxContextMenuItem's class body a thin shell (reference_atomic_decompose rule 1).

@paramtarget
@paramopen

interactive/menu/context-menu-trigger-core.ts

createContextMenuTriggerCore Factoryv0.1.0
createContextMenuTriggerCore#CngxContextMenuTriggerCore
createContextMenuTriggerCore(deps: CngxContextMenuTriggerCoreDeps)

Build a CngxContextMenuTriggerCore from its dependencies. Pure factory - no Angular DI; the caller resolves collaborators and passes them in.

core/utils/controlled-source.ts

createControlledSource Factoryv0.1.0
createControlledSource#Signal
createControlledSource(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()).

null falls through too: the ?? treats a deliberate null on the priority source exactly like absence and reads the next source. A priority source that needs to express "deliberately empty" must yield a real value ([], '') instead of null - there is no way to make null win here.

@parampriority

Optional higher-precedence source. undefined when no source is present; a signal yielding undefined when the source is bound but empty.

@paramfallbackSignal

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 Factory
createCreateCommitHandler#CreateCommitHandler
createCreateCommitHandler(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.0
createDebouncer#CngxDebouncer
createDebouncer(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.

@paramoptions?

common/command/match.ts

createDefaultCommandMatcher Factoryv0.1.0
createDefaultCommandMatcher#CngxCommandMatcher

Builds the default matcher: a pure label/keyword ranker. An empty query returns every (scoped) command at score 0 in registration order; a non-empty query keeps only commands that match label or keyword, ranked label-exact > label-prefix > label-substring > keyword. The optional scope filters to commands whose group equals it.

select/shared/flat-nav-strategy.ts

createDefaultFlatNavStrategy Factory
createDefaultFlatNavStrategy#CngxFlatNavStrategy
createDefaultFlatNavStrategy(options)

Canonical W3C listbox flat-nav: PageUp/Down step 10 with disabled back-probe; typeahead delegates to the variant's TypeaheadController.

@paramoptions= {}

tabs/registry/directive-by-id-map.ts

createDirectiveByIdMap Factory
createDirectiveByIdMap#Signal>
createDirectiveByIdMap(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 Factory
createDismissHandler#DismissHandler
createDismissHandler(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 Factory
createDisplayBinding#DisplayBinding
createDisplayBinding(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 Factory
createDomAnchorRetry#CngxDomAnchorRetryHandle
createDomAnchorRetry(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 Factory
createStripDensity Factory
createElementWidthSignal#Signal
createElementWidthSignal(element: HTMLElement, destroyRef: DestroyRef)

Tracks an element's content-box width as a reactive signal, over the shared resize kernel (createResizeSignal) rather than a hand-rolled ResizeObserver. Not the CngxResizeObserver directive - that can only attach through hostDirectives, and this factory is called from a field initialiser. In SSR / non-DOM environments the signal stays 0 and no observer is wired.

@paramelementHTMLElement
@paramdestroyRefDestroyRef
createStripDensity#Signal
createStripDensity(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 - both derive a rung from the space the stepper was given and return a single Signal; this one measures, the other reads what CSS resolved.

'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

createEmptyFilterRoot Factory
createFilterExpression Factory
createFilterGroup Factory
createEmptyFilterRoot#FilterGroup

Frozen 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.

createFilterExpression#FilterExpression
createFilterExpression(field: string, operator: string, value?: TValue)

Build a fresh FilterExpression with a generated id.

@paramfieldstring
@paramoperatorstring
@paramvalue?TValue
createFilterGroup#FilterGroup
createFilterGroup(logic: FilterLogic, filters, opts: CreateFilterGroupOptions)

Build a fresh FilterGroup with a generated id. Defaults to and logic, no children, not negated.

@paramlogicFilterLogic= 'and'
@paramfilters= []
ensureFilterTreeIds#FilterGroup
ensureFilterTreeIds(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.

@paramtreeFilterGroup
evaluateExpression#boolean
evaluateExpression(expr: FilterExpression, item: TItem, fieldDef, options?: CngxFilterEvaluationOptions)

Evaluate a single FilterExpression against item.

The contract, in resolution order:

  1. Unknown field (fieldDef is undefined) - false. The expression references a field the consumer never declared.
  2. Unfilled value - true. The user picked a field and an operator but supplied no value (null / undefined / ''), so the row is a no-op that must not exclude every item. Operators whose definition is valueless (builtin isEmpty / isNotEmpty) are exempt and evaluate normally; the registry passed via options.operators extends this exemption to consumer-registered valueless operators.
  3. Unknown operator - one console.warn per key in dev mode, then false for every item. An operator that reaches evaluation without a registered definition is a wiring bug, and a loud conservative false beats a silent one.
  4. Otherwise the resolved CngxFilterOperatorDef.evaluate runs with the item value, the expression value, and the evaluation context.

Semantics of the builtin definitions:

  • eq / neq compare with Object.is identity - no coercion, no case folding.
  • contains / startsWith / endsWith require both sides to be strings (anything else is false) and are case-SENSITIVE unless options.caseInsensitive is true, which lowercases both sides.
  • gt / gte / lt / lte order numbers, Date instances, and strings (lexicographic). Nullish operands and mixed/unsupported type pairs compare as NaN, so every ordering test on them is false.
@paramitemTItem
@paramfieldDef
toFilterPredicate#unknown | null
toFilterPredicate(tree, fields, options?: CngxFilterEvaluationOptions)

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).

Evaluation contract per expression - see evaluateExpression: unknown field keys evaluate false, unfilled values short-circuit true, unknown operators warn once in dev mode and evaluate false. Pass options to evaluate against a consumer-extended operator registry or with case-insensitive substring matching.

@paramtree
@paramfields

forms/field/field-control-aria.ts

createFieldControlAria Factoryv0.1.0
createFieldControlAria#FieldControlAria
createFieldControlAria(presenter, options: FieldControlAriaOptions)

Field-control ARIA scaffolding shared by every control that provides CNGX_FORM_FIELD_CONTROL against a surrounding cngx-form-field: id/disabled/errorState/focused plus the ARIA projection (describedby/labelledby/invalid/required/busy/errormessage/readonly/ disabled) and the focus handlers. Sibling of createFieldSync; unlike the sync it needs no injection context - the caller passes its (optional) presenter.

The host still owns its bindings: each directive binds only the signals its host template declares, and control-specific surfaces (empty, aria-label fallbacks) stay in the control.

@parampresenter
@paramoptionsFieldControlAriaOptions= {}

forms/field/field-sync.ts

createFieldSync Factoryv0.1.0
createFieldSync#void
createFieldSync(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),
});
@paramoptionsFieldSyncOptions

select/shared/field-sync.ts

createFieldSync Factory
createFieldSync#void
createFieldSync(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.

@paramoptionsFieldSyncOptions

forms/filter-builder/filter-builder-announcer.ts

createFilterBuilderAnnouncer Factory
injectFilterBuilderAnnouncerFactory Inject
createFilterBuilderAnnouncer#CngxFilterBuilderAnnouncer
createFilterBuilderAnnouncer(sources: CngxFilterBuilderAnnouncerSources)

Default announcer - derives the live-region string from lastMutation through the i18n formatter bundle.

injectFilterBuilderAnnouncerFactory#CngxFilterBuilderAnnouncerFactory

Inject-context helper that resolves CNGX_FILTER_BUILDER_ANNOUNCER_FACTORY.

forms/filter-builder/filter-builder-state.ts

createFilterBuilderState Factory
createFilterBuilderState#CngxFilterBuilderState
createFilterBuilderState(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 / isEmpty signals
  • path-keyed mutators (addExpression, setLogic, removeNode, clear, ...)
  • the lastMutation event 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

createFilterBuilderTemplateRegistry Factory
injectFilterBuilderTemplateRegistry Inject
createFilterBuilderTemplateRegistry#CngxFilterBuilderTemplateRegistry
createFilterBuilderTemplateRegistry(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.

injectFilterBuilderTemplateRegistry#CngxFilterBuilderTemplateRegistry
injectFilterBuilderTemplateRegistry(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.

forms/filter-builder/filter-builder-row-controller.ts

createFilterRowController Factoryv0.1.0
createFilterRowController#CngxFilterRowController
createFilterRowController(deps: CngxFilterRowControllerDeps)

Build the shared row brain. Pure composition over the deps - no injection context required, no state ownership: reads come from the dep signals, writes go through the sink. Both shipped row components consume this factory through CNGX_FILTER_ROW_CONTROLLER_FACTORY so a consumer can swap the row policy (e.g. a different field-change carry-over) without forking either skin.

data/async-state/forwarded-async-state.ts

createForwardedAsyncState Factoryv0.1.0
createForwardedAsyncState#CngxAsyncState
createForwardedAsyncState(source)

Wraps a changing source of async state in a stable CngxAsyncState façade.

A component that receives its state through an Input cannot publish that object through CNGX_STATEFUL directly: the token is resolved once when a bridge injects it, while the input can be rebound or absent. Capturing the object would hand the bridge a stale state - or undefined before the first binding.

This returns one object whose every member is a computed() that reads the source on each access, so bridges observe transitions of whatever is bound right now. With nothing bound it reports a quiet idle state rather than throwing, which is what lets a bridge sit inside a timeline that has not been given a state yet.

Contrast the select family, which provides CNGX_STATEFUL with useFactory over a concrete commitState field - no forwarding needed there, because the state is created by the component rather than passed in. The split is not about the component, it is about where the state is born: created locally, capture it; arriving through an Input, forward it.

readonly asyncState = createForwardedAsyncState(this.state);

// providers:
{ provide: CNGX_STATEFUL, useFactory: () => ({ state: inject(Host).asyncState }) }
@paramsource

select/shared/panel-renderer.ts

createIdentityPanelRenderer Factory
createIdentityPanelRenderer#PanelRenderer
createIdentityPanelRenderer(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.

core/utils/registry.ts

createKeyedRegistry Factory
createSlotRegistry Factory
createKeyedRegistry#KeyedRegistry
createKeyedRegistry(options?: RegistryOptions)

Signal-based keyed registry with a destroy-safe unregister path.
Encodes the registration lifecycle contract every cngx registry must honour, so the classic teardown race is unwritable: when Angular recreates a keyed child, the successor's register can run BEFORE the predecessor's DestroyRef teardown - an unguarded unregister(key) would then evict the successor's live registration. Here unregister demands the holder instance and only removes on identity match.

Collision semantics mirror CngxErrorRegistry: registering a different value under a live key is silently absorbed (swap-is-noop) with a dev-mode warning, never an in-place swap - downstream readers keep observing the instance they resolved.

The registry is plain signals - create it as a field on the owning directive / service, no injection context required.

@paramoptions?RegistryOptions
createSlotRegistry#SlotRegistry

Signal-based single-slot registry with a destroy-safe release path.
The one-handle sibling of createKeyedRegistry for slots like a dialog's title / description handle: register is last-write-wins (a recreated child simply claims the slot), while unregister only clears the slot when the releasing handle still holds it - the teardown of a replaced predecessor cannot blank a successor's claim. Mirrors the guarded pattern CngxDialog uses for its aria handles.

core/utils/latency-probe.ts

createLatencyProbe Factoryv0.1.0
createLatencyProbe#CngxLatencyProbe
createLatencyProbe(busy, now?)

Measures how long the previous busy window lasted, from a boolean-busy source.

The duration is a wall-clock sample taken at the busy edge - not derivable from signals alone - so the probe writes lastDuration imperatively from an effect. This is a measurement side effect: a distinct write-in-effect pattern, not the same mechanism as createVisibilityGate (which defers its visible write via setTimeout) nor the CngxAsyncContainer exception. It is loop-safe by construction: the effect tracks only busy(), reads the clock in untracked(), and never reads lastDuration back.

Aggregate semantics: the probe measures the busy-envelope. On a false->true edge it stamps startedAt; on the matching true->false edge it records lastDuration = clock() - startedAt. Across concurrent operations behind one boolean source (e.g. CngxAsyncRegistry.isAnythingLoading) that is the whole in-flight window, not any single operation.

Observation invariant: the clock is stamped at the busy transition at effect-flush granularity, independent of whether or when lastDuration is read. A busy window that opens and closes within a single synchronous tick, before change detection flushes the effect, is not measured - real registry transitions cross async boundaries (macrotasks), so the effect flushes between them.

Must be called in an injection context: the internal effect auto-cleans on destroy, mirroring createVisibilityGate.

@parambusy

A boolean-busy source (e.g. state.isBusy or () => registry?.isAnythingLoading() ?? false).

@paramnow?

Injectable clock, default performance.now() (monotonic; correct for durations, unaffected by wall-clock jumps). Inject a plain counter in specs for deterministic edge timestamps.

{ lastDuration, isBusy }.

chart/scales/linear.ts

createLinearScale Factory
createLinearScale#number
createLinearScale(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.

@paramdomain

[start, end] data range. Equal endpoints collapse the scale to a constant function returning range[0].

@paramrange

[start, end] output range (typically pixel coordinates).

(v: number) => number mapping domain values to range values.

select/shared/local-items-buffer.ts

createLocalItemsBuffer Factory
createLocalItemsBuffer#LocalItemsBuffer
createLocalItemsBuffer(compareWith: Signal)

Builds a LocalItemsBuffer. Reads compareWith lazily so mid-flight comparator swaps are honoured.

@paramcompareWithSignal

data/async-state/create-manual-state.ts

createManualState Factory
createManualState#ManualAsyncState

Create 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 Factory
createMaterialBidirectionalSync#CngxMaterialBidirectionalSyncHandle
createMaterialBidirectionalSync(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:

  1. A presenter→Material effect() that tracks presenterIndex and writes through writeSelectedIndex, equality-guarded against readSelectedIndex() to suppress redundant writes. The Material read+write pair runs inside untracked() per reference_signal_architecture rule 2.
  2. A Material→presenter subscription that forwards each selectionChange$ emission to onMaterialSelection (equality-guarded against presenterIndex() 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-stepper/material-bridge/handle.ts

createMatStepHandle Factory
createMatStepHandle#CngxMatStepHandleSetup
createMatStepHandle(matStep: MatStep, idSeed)

Translates a Material MatStep into a cngx CngxStepRegistration.

  • id - always a fresh idSeed() 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-label composition, telemetry) never read empty when Material consumers project a <ng-template matStepLabel>:
    1. MatStep.label when it is a plain string - the canonical shape and the only one that emits a runtime change Material itself observes.
    2. MatStep.ariaLabel when the consumer set the input - designed exactly as the substitute for template labels.
    3. Static-text read from MatStep.stepLabel.template via a throwaway detached EmbeddedViewRef (readMatStepLabelTemplateText). Captures literal matStepLabel markup; dynamic interpolation bails through to (4).
    4. Step <id> - deterministic, derived from the cngx handle id. Always non-empty. Documented limitation: runtime label changes do not propagate. CDK's CdkStep does not expose a _stateChanges Subject analogous to MatTab._stateChanges, so cngx cannot re-trigger the snapshot when Material flips the input later. Surface the same Material-internal coupling family typed in MaterialPrivateSurfaces.CompletedOverrideSource.
  • disabled - fixed false. Material owns step gating via linear + editable + completed; surfacing a cngx-side disabled would duplicate Material's own click-time enforcement and is ignored by <mat-stepper> itself.
  • state - computed() over MatStep.hasError / MatStep.completed. CdkStep.completed's getter reads _completedOverride() - a WritableSignal<boolean | null> typed in MaterialPrivateSurfaces.CompletedOverrideSource. The cngx computed transitively tracks that signal through the getter and re-fires whenever Material flips completion. hasError is a plain property setter on CdkStep, NOT a Signal - a hasError write that is not paired with a completed change does not re-trigger this computed. In practice Material wizards write the two together (step.hasError = true; step.completed = false in 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.
@parammatStepMatStep
@paramidSeed

mat-tabs/material-bridge/handle.ts

createMatTabHandle Factory
createMatTabHandle#CngxMatTabHandleSetup
createMatTabHandle(matTab: MatTab, idSeed, injector: Injector)

Translates a Material MatTab into a cngx CngxTabHandle plus a writable errorAggregator slot the [cngxMatTabError] directive binds.

  • id - fresh idSeed() value; a label-keyed id would collide when two tabs share a label.
  • label / disabled - computed signals retriggered by toSignal(matTab._stateChanges). Bridge lifetime is tied to the supplied injector (typically a per-tab child EnvironmentInjector). _stateChanges is a Material-internal surface.
  • errorAggregator - writable seeded at undefined; [cngxMatTabError] writes its bound aggregator in and resets on teardown. The handle exposes .asReadonly() to preserve the CngxTabHandle contract.
  • directError - writable seeded at false; [cngxMatTabErrorFlag] writes its string | boolean value in and resets on teardown. Folds into hasError / errorMessage.
@parammatTabMatTab
@paramidSeed
@paraminjectorInjector

core/utils/media-query-signal.ts

createMediaQuerySignal Factoryv0.3.0
observeMediaQuery v0.3.0
createMediaQuerySignal#Signal
createMediaQuerySignal(query: string, destroyRef: DestroyRef, host)

Creates a reactive Signal<boolean> that reflects whether host currently matches a CSS media query.

Seeds from MediaQueryList.matches, updates on the change event, and removes the listener when destroyRef is destroyed. On a host without matchMedia (SSR, jsdom) the signal stays false and no listener is wired, so it never throws off the browser.

This is the shared kernel behind injectMediaQuery, CngxMediaQuery, CngxSkeleton, CngxReducedMotion, and the stepper's mobile-viewport signal; consumers that hand-roll a matchMedia listener should route through it instead.

const compact = createMediaQuerySignal(
  '(max-width: 640px)',
  inject(DestroyRef),
  inject(DOCUMENT).defaultView,
);
@paramquerystring

A CSS media query string, e.g. (max-width: 640px).

@paramdestroyRefDestroyRef

Scope whose destruction removes the change listener.

@paramhost

The window-like object to read matchMedia from.

A readonly Signal<boolean> tracking the query's match state.

observeMediaQuery#void
observeMediaQuery(host, query: string, apply)

Subscribes apply to a media query's match state on host.

Seeds synchronously from MediaQueryList.matches, re-applies on every change event, and returns the teardown that removes the listener. On a host without matchMedia (SSR, jsdom) nothing is wired, apply never fires, and the returned teardown is a no-op.

Reach for this low-level form when the query itself is reactive and the subscription must follow it (re-wire per effect run via onCleanup); for a static query, createMediaQuerySignal owns the teardown via DestroyRef.

@paramhost

The window-like object to read matchMedia from.

@paramquerystring

A CSS media query string, e.g. (max-width: 640px).

@paramapply

Receives the current match state, synchronously on subscribe and on every change.

Teardown that removes the change listener.

interactive/menu/menu-announcer.ts

createMenuAnnouncer Factory
injectMenuAnnouncer Inject
createMenuAnnouncer#CngxMenuAnnouncerLike

Default 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.

injectMenuAnnouncer#CngxMenuAnnouncerLike

Resolve 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.0
createMenuTriggerDismissBinding Factoryv0.1.0
createMenuDismissHandler#CngxMenuDismissHandler
createMenuDismissHandler(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.

createMenuTriggerDismissBinding#CngxMenuTriggerDismissBinding
createMenuTriggerDismissBinding(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()).

field/testing/mock-field.ts

createMockField Factory
createMockField#literal type
createMockField(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 interaction
@paramoptsMockFieldOptions= {}
mockValidationError#ValidationError.WithFieldTree
mockValidationError(kind: string, message?: string, extra?: Record)

Creates a mock ValidationError.WithFieldTree for testing.

@paramkindstring
@parammessage?string
@paramextra?Record

interactive/optimistic/optimistic.ts

createOptimistic Factory
createOptimistic#unknown
createOptimistic(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] = createOptimistic(
  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" />
@paramcurrentWritableSignal
  • The writable signal to update optimistically.
@paramaction
  • Async action that confirms the value. Should return the confirmed value.

Tuple of [applyFn, state].

chart/scales/ordinal.ts

createOrdinalScale Factory
createOrdinalScale#string
createOrdinalScale(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.

@paramdomain

Ordered list of categorical values. Values are matched by reference equality on lookup.

@paramcolors

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.0
createOrganismScrollSync#void
createOrganismScrollSync(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 honour prefers-reduced-motion or 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

createOverflowPopoverHighlightSync Factory
createTabOverflowTemplateBindings Factory
createOverflowPopoverHighlightSync#void
createOverflowPopoverHighlightSync(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-activedescendant for 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-activedescendant while real focus stays on the trigger (APG menu-button); the rows are never tab stops.
@parampopoverSignal
@paramadSignal
createTabOverflowTemplateBindings#CngxTabOverflowTemplateBindings
createTabOverflowTemplateBindings(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#string
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).

@paramtabCngxTabHandle

ui/paginator/paginator-announcer.ts

createPaginatorAnnouncer Factoryv0.1.0
createPaginatorAnnouncer#CngxPaginatorAnnouncer

Builds 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 Factory
createPaginatorNavCore#NavCore
createPaginatorNavCore(options: NavCoreOptions)

Shared nav-button glue. Composition, not an abstract base class: each of the four segments calls this factory as a field initialiser and binds the same template, differing only in selector, glyph, and the three options here.

@paramoptionsNavCoreOptions

command-palette/palette/palette-keybinding.ts

createPaletteKeybinding Factoryv0.1.0
createPaletteKeybinding#CngxPaletteKeybinding
createPaletteKeybinding(combo: KeyCombo, onOpen, isMac: boolean)

Default palette keybinding: installs a document keydown listener that calls onOpen whenever the combo is pressed (matched via matchesKeyCombo). Side-effectful by design - it owns the global listener - so its teardown must be called on destroy.

isMac defaults to platform detection; pass it explicitly for deterministic tests.

@paramcomboKeyCombo
@paramonOpen
@paramisMacboolean= detectMac()

select/shared/panel-lifecycle-emitter.ts

createPanelLifecycleEmitter Factory
createPanelLifecycleEmitter#void
createPanelLifecycleEmitter(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 Factory
createPasswordStrength#CngxPasswordStrengthFactory

Builds 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 Factory
createPathBuilder#PathBuilder
createPathBuilder(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 Factory
createProjectedOptionModel#ProjectedOptionModel
createProjectedOptionModel(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 Factory
createRecyclerPanelRendererFactory#CngxPanelRendererFactory
createRecyclerPanelRendererFactory(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.

@paramrecyclerCngxRecycler

select/shared/reorder-commit-handler.ts

createReorderCommitHandler Factory
createReorderCommitHandler#ReorderCommitHandler
createReorderCommitHandler(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.

layout/observers/resize-signal.ts

createResizeSignal Factoryv0.1.0
observeResize v0.1.0
createResizeSignal#Signal
createResizeSignal(element: Element, box: ResizeObserverBoxOptions, destroyRef: DestroyRef, host)

Creates a Signal<ResizeObserverEntry | null> that carries the most recent observation of element, null until the first one arrives.

Disconnects when destroyRef is destroyed. On a host without ResizeObserver (SSR, jsdom) the signal stays null and no observer is created, so it never throws off the browser.

This is the shared kernel behind CngxResizeObserver and createContainerSize; consumers that hand-roll a ResizeObserver should route through it instead.

const entry = createResizeSignal(
  host,
  'border-box',
  inject(DestroyRef),
  inject(DOCUMENT).defaultView,
);
const width = computed(() => entry()?.borderBoxSize[0]?.inlineSize ?? 0);
@paramelementElement

The element to observe.

@paramboxResizeObserverBoxOptions

Which box model to report.

@paramdestroyRefDestroyRef

Scope whose destruction disconnects the observer.

@paramhost

The window-like object to read ResizeObserver from.

A readonly signal carrying the latest entry.

observeResize#void
observeResize(host, element: Element, box: ResizeObserverBoxOptions, apply)

Subscribes apply to size changes of element on host.

Re-applies on every observation and returns the teardown that disconnects the observer. On a host without ResizeObserver (SSR, jsdom) nothing is wired, apply never fires, and the returned teardown is a no-op. Unlike the media-query twin there is no synchronous seed: the first size is whatever the observer reports after the next layout.

Reach for this low-level form when the observed element or the box model is reactive and the subscription must follow it (re-wire per effect run via onCleanup); for a static target, createResizeSignal owns the teardown via DestroyRef.

@paramhost

The window-like object to read ResizeObserver from.

@paramelementElement

The element to observe.

@paramboxResizeObserverBoxOptions

Which box model to report.

@paramapply

Receives the first entry of every observation batch.

Teardown that disconnects the observer.

interactive/retry/with-retry.ts

createRetry Factory
createRetry#unknown
createRetry(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] = createRetry(
  () => 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" />
@paramactionAsyncAction
@paramconfig?RetryConfig

chart/buffer/ring-buffer.ts

createRingBuffer Factory
createRingBuffer#RingBuffer
createRingBuffer(capacity: number)

Construct a RingBuffer with a fixed capacity. The backing array is allocated once; no growth, no per-push allocation.

@paramcapacitynumber

positive integer maximum element count.

select/shared/scalar-commit-handler.ts

createScalarCommitHandler Factory
createScalarCommitHandler#ScalarCommitHandler
createScalarCommitHandler(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.

layout/scroll/scroll-lock-core.ts

createScrollLock Factoryv0.1.0
createScrollLock#void
createScrollLock(html: HTMLElement)

Acquire a ref-counted scroll lock on a document root and get back the matching release function.

The first active lock saves the current overflow / scrollbar-gutter inline styles and sets overflow: hidden + scrollbar-gutter: stable (no layout shift when the scrollbar disappears). Releasing the last active lock restores the saved values. The returned release function is idempotent - calling it twice cannot over-decrement the count.

Shared engine behind CngxScrollLock and CngxDialog's modal scroll lock; consumers composing their own overlay primitives can use it directly.

const release = createScrollLock(document.documentElement);
// ... overlay open ...
release();
@paramhtmlHTMLElement
  • The document root (document.documentElement) to lock.

Release function for this acquisition. Idempotent.

select/shared/search-effects.ts

createSearchEffects Factory
createSearchEffects#void
createSearchEffects(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 Factory
createSelectionController#SelectionController
createSelectionController(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); // stable

chart/chart/significant-change.ts

createSignificantChangeTracker Factoryv0.1.0
createSignificantChangeTracker#Signal
createSignificantChangeTracker(summary: Signal)

Derive the most recent significant transition from a chart-summary signal, or null when the latest update carried no significance.

Mirrors createTransitionTracker's current/previous discipline: an internal linkedSignal holds the current summary and the one before it (structural-equality guarded), and the public signal is a computed with its own structural equal - so a summary that re-emits an equivalent shape yields the same null reference and never re-fires a downstream effect. Threshold crossings take precedence over trend flips (they are operationally more significant).

The chart stays pure-derivation: effects that react to the event live in the companion announcer, not here.

@paramsummarySignal

the chart's CngxChartSummary signal.

interactive/slider/slider-disabled-reason.ts

createSliderDisabledReason Factory
createSliderDisabledReason#CngxSliderDisabledReason
createSliderDisabledReason(opts)

Internal disabled-"why" mechanism shared by CngxSliderTrack and CngxRangeSliderTrack. Appends an sr-only description span to the host via Renderer2 (the tracks are headless directives on consumer markup, so there is no template to project into - same constraint and same shape as CngxChipInteraction). Always-in-DOM per Pillar 2; both the span's aria-hidden and the emitted id reference are gated on disabled() && reason() (Toggle/Radio convergence): accname 1.2 §2A traverses a directly referenced hidden node, so the id must only be emitted while the description applies.

Must run in an injection context (field initializer / constructor). Not exported from public-api.ts - decompose glue, not a consumer contract.

@paramopts

interactive/slider/slider-interaction.ts

createSliderInteraction Factoryv0.1.0
pointerFraction v0.1.0
createSliderInteraction#CngxSliderInteraction
createSliderInteraction(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#number
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.

@paramelHTMLElement
@paramorientation
@paramclientXnumber
@paramclientYnumber

interactive/slider/slider-ticks.ts

createSliderTicks Factoryv0.1.0
createSliderTicks#CngxSliderTicksView
createSliderTicks(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.

@paramoptions
sliderTickValues#number[]
sliderTickValues(min: number, max: number, step: number, maxCount: number)
@paramminnumber
@parammaxnumber
@paramstepnumber
@parammaxCountnumber= 21

data/sort/sort-header-state.ts

createSortHeaderState Factoryv0.1.0
createSortHeaderState#SortHeaderState
createSortHeaderState(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.

@paramsort

Getter for the owning CngxSort engine (() => sortRef, () => grid.sort).

@paramfield

Getter for the column's field key.

The derived { entry, isActive, isAsc, isDesc, priority, toggle } bundle.

common/stepper/commit-handler.ts

createStepperCommitHandler Factory
createStepperCommitHandler#CngxStepperCommitHandler
createStepperCommitHandler(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/display-mode.ts

createStepperDisplayMode Factory
injectStepperCollapse Inject
createStepperDisplayMode#Signal<"classic" | "text" | "dots">
createStepperDisplayMode(collapsed: Signal, mobileCollapse)

Resolves the active stepper display mode by combining the collapse signal

  • whether the stepper's own container is narrow - with the configured mobileCollapse policy. 'classic' keeps the full strip; 'text' / 'dots' swap to the matching compact variant, 'off' stays classic at any width.

The factory takes no DOM API and holds no threshold: where the collapse happens is a @container rule in the stepper stylesheet, what it collapses into is this policy. See core-concepts/responsive-by-default.md.

@paramcollapsedSignal

true while the container sits below the collapse rung.

@parammobileCollapse

The configured collapse policy, read lazily.

injectStepperCollapse#Signal
injectStepperCollapse(host: Element)

Reads the stepper's collapse flag off its own container.

The threshold is a @container rule in stepper-base.css writing --cngx-stepper-collapse onto .cngx-stepper::after. It lands on the pseudo-element because a container query cannot style its own container, and the query container for a pseudo-element is selected from its originating element's inclusive ancestors. Nothing here parses a width, so a consumer re-aims the collapse with a plain CSS rule.

Requires an injection context - it observes through injectContainerSize, so the observer is scoped to the caller's DestroyRef. A field initialiser on the stepper component is one; a bare call from a lifecycle hook is not.

@paramhostElement

The <cngx-stepper> host element, which is its own container.

common/stepper/group-navigation.ts

createStepperGroupNavigation Factory
createStepperGroupNavigation#CngxStepperGroupNavigationView
createStepperGroupNavigation(options: CngxStepperGroupNavigationOptions)

Builds the collapsed-group pointer-navigation view. Level-2 helper so the organism stays a thin renderer.

Indices resolve against flatSteps (DFS order, live flatIndex) rather than group.children - the flattener spreads group nodes but leaves their children as the tree projection where every step still carries the seeded -1. Each candidate is gated by canNavigateTo + busy (mirroring the step-header reachability), so a linear-blocked leading step is skipped to the first reachable one.

common/stepper/group-summary.ts

createStepperGroupSummary Factory
createStepperGroupSummary#CngxStepperGroupSummaryView
createStepperGroupSummary(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. SR phrases resolve through the CngxStepperI18n bundle when passed; English defaults otherwise.

subtreeStats#literal type
subtreeStats(node: CngxStepNode)

Terminal-step totals for a group's subtree; reads each step state().

@paramnodeCngxStepNode

common/stepper/create-stepper-host-proxy.ts

createStepperHostProxy Factoryv0.1.0
createStepperHostProxy#CngxStepperHost
createStepperHostProxy(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.

@paramsupplier

common/stepper/stepper-state-view.ts

createStepperStateView Factoryv0.1.0
resolveStepperErrorSummary v0.1.0
createStepperStateView#CngxStepperStateView
createStepperStateView(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.

@paraminputs
resolveStepperErrorSummary#string
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().

@paramviewPick
@paramstepsOnlySignal
@parami18nCngxStepperI18n
@parammessageOf?

stepper/slots/stepper-template-cascade.ts

createStepperTemplateBindings Factory
createStepperTemplateBindings#CngxStepperTemplateBindings
createStepperTemplateBindings(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.

chart/renderer/svg-renderer.ts

createSvgRenderer Factoryv0.1.0
createSvgRenderer#CngxChartRenderer
createSvgRenderer(_deps: ChartRendererDeps)

SVG passthrough renderer. Layer atoms render their own <svg:path> / <svg:rect> / <svg:circle>, gated by the chart shell's renderSvg computed, so this backend has nothing to do: every method is a no-op. It never writes back to the chart context - renderSvg ownership stays in the chart shell as the single source of truth.

unused; kept for factory signature symmetry with createCanvasRenderer.

tabs/dismissals/tab-dismissals.ts

createTabDismissals Factoryv0.1.0
createTabDismissals#CngxTabDismissals
createTabDismissals(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.requestAdd

tabs/announcements/tab-group-announcements.ts

createTabGroupAnnouncements Factoryv0.1.0
createTabGroupAnnouncements#CngxTabGroupAnnouncements
createTabGroupAnnouncements(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: liveAnnouncement is 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-roledescription is separate from the region aria-label, so AT never reads the same word twice.
  • aria-label and aria-labelledby stay mutually exclusive (resolvedAriaLabel returns null when labelledby is bound).
  • The per-tab descriptor is ARIA-by-value: the cngx-sr-only span is always in the DOM; statusPhrase only fills its content, keeping the aria-describedby reference stable.

tabs/slots/tab-group-template-cascade.ts

createTabGroupTemplateBindings Factoryv0.1.0
createTabGroupTemplateBindings#CngxTabGroupTemplateBindings
createTabGroupTemplateBindings(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 icon slot 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.0
createTabKeyboardNav#CngxTabKeyboardNav
createTabKeyboardNav(opts: CngxTabKeyboardNavOptions)

Level-2 helper implementing the WAI-ARIA APG tabs keyboard model for <cngx-tab-group> with automatic activation: an arrow press moves focus AND activates the target tab in one step, so focus and selection never diverge. Keeps the keyboard logic off the organism class (LOC guard) and out of a competing roving-tabindex state machine.

Pillar 1 (Ableitung statt Verwaltung): the tab stop is derived from the presenter's activeId, not managed as a second index. Pillar 2 (Kommunikation): activation routes through host.select(), which owns disabled-skip, loop, commit gating, and the live-region announcement - the keyboard layer adds only focus movement (a DOM concern the presenter, living in @cngx/common, must not own).

Navigation always originates from the active tab: under automatic activation the focused tab is always the active one, so the clamped active index is the correct origin.

tabs/announcements/tab-nav-announcement.ts

createTabNavAnnouncement Factoryv0.1.0
createTabNavAnnouncement#CngxTabNavAnnouncement
createTabNavAnnouncement(options: CngxTabNavAnnouncementOptions)

Live-region content for the link-driven nav flavour: the active link's accessible label, announced only when the active id actually changes.

Why this is not createTabGroupAnnouncements. That bundle's phrasing is built around the commit lifecycle (pending -> success, rollback, direction prefix) and it gates on the commit transition, so it stays silent at rest for free. The nav path is commit-free by design: links navigate through their own routerLink and nothing calls presenter.select(). Its only meaningful announcement is the landing label, and it needs a different silence gate.

The mount window. announcing stays false through the first render and the microtask after it. On a deep link the active link is the page's initial state, not a change, and the browser already announces the document; pushing the landing section into a polite region there is noise. queueMicrotask rather than the afterNextRender body: every afterNextRender callback for one render runs in a single synchronous batch, so deferring past it makes the gate independent of whether a [cngxTabsRouteSync] seed registered before or after this one.

The prior-id tracker is seeded when that window closes, not at construction, so whatever the seed resolved counts as the starting point instead of as the first announcement. Returning to a section that was active at mount still announces.

common/tabs/router-commit.ts

createTabRouterCommit Factoryv0.1.0
createTabRouterCommit#CngxTabsCommitAction
createTabRouterCommit(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 this navigation's own navigate() promise:

  • resolved true (NavigationEnd) → true (commit the switch)
  • resolved false (NavigationCancel - a CanDeactivate guard blocked, or a newer navigation superseded this one) → false
  • rejected (NavigationError - 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. Correlating on the promise (not router.events) means a concurrent unrelated navigation's outcome can never resolve this commit; the commit controller's cancel() closes the subscriber on supersede, and a late promise resolution lands on the closed subscriber and is dropped by RxJS itself.

common/tabs/commit-handler.ts

createTabsCommitHandler Factory
createTabsCommitHandler#CngxTabsCommitHandler
createTabsCommitHandler(opts: CngxTabsCommitHandlerOptions)

Wraps a CngxCommitController with the action-shape adapter that collapses Observable<boolean> / Promise<boolean> / boolean returns into a single accept: boolean outcome.

runTabsAction#CngxCommitHandle
runTabsAction(action: CngxTabsCommitAction, fromIndex: number, toIndex: number, handlers)
@paramfromIndexnumber
@paramtoIndexnumber
@paramhandlers

common/tabs/url-match.ts

createTabUrlMatch Factory
createTabUrlMatch#CngxTabUrlMatch

The two URL-matching policies [cngxTabsRouteSync] ships, dispatched on CngxTabUrlMatchContext.mode. Same exact-versus-prefix distinction routerLinkActive draws.

Pure: no injection context, no signal reads. The directive resolves the mode cascade and the tab registry, then hands both in.

primaryPathSegments#string[]
primaryPathSegments(router: Router)

Primary-outlet path segments of the current URL, decoded. Going through router.parseUrl (not a hand-rolled split) keeps matrix params (;theme=dark) out of the compared paths, decodes percent-encoded segments so they compare against raw routeFor commands, and confines the walk to the primary outlet so an aux outlet ((aside:x)) can neither pollute the tail nor match.

@paramrouterRouter
resolveByPrefix#string | null
resolveByPrefix(ctx: CngxTabUrlMatchContext)

Find the tab that owns the current URL's subtree: the one whose resolved UrlTree is a subset of the active tree, ignoring query params, fragment, and matrix params.

Routes resolve { relativeTo: ActivatedRoute } rather than against the URL root. A root-anchored segment compare breaks a nav mounted under a base path - at /app/rounds a ['rounds'] route would compare 'app' === 'rounds', resolve null, and hand the first link a wrong aria-current, which is the exact defect prefix mode exists to fix. Relative resolution is safe on the read side because the nav path is commit-free: links navigate through their own routerLink, so nothing here can diverge from a commit navigation.

Longest route wins, so a /rounds tab cannot shadow /rounds/explorer in the same set.

resolveBySuffix#string | null
resolveBySuffix(ctx: CngxTabUrlMatchContext)

Find the tab whose route is the trailing segment(s) of the current URL path. Anchors on position, not a loose "appears anywhere" scan - a tab id that happens to equal an unrelated parent segment cannot win. The path is the decoded primary-outlet segment list (query, fragment, matrix params, and aux outlets excluded). If two tabs produce the same trailing segment(s) (only possible with a colliding custom routeFor), the first registered tab wins; the default id-based mapping never collides.

urlTreeSegmentCount#number
urlTreeSegmentCount(tree: UrlTree)

Path-segment count of a resolved UrlTree, used to rank prefix matches: /rounds/explorer outranks /rounds on a URL both match.

@paramtreeUrlTree

select/shared/template-registry.ts

createTemplateRegistry Factory
createTemplateRegistry#CngxSelectTemplateRegistry
createTemplateRegistry(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 Factory
createThumbValue#WritableSignal
createThumbValue(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.

@parampositionSignal

ui/timeline/timeline-labels.ts

createTimelineFallbackCopy Factoryv0.1.0
createTimelineFallbackCopy#CngxTimelineFallbackCopy
createTimelineFallbackCopy(config: CngxTimelineConfig)

Flattens the config's optional label bag into the exact set the template reads, so the template names what it wants rather than indexing a keyed accessor, and the optional-chaining lives in one place instead of at every use site.

groupLabel keeps the band's key as its own last resort: a consumer who clears the formatter should still get a legible header rather than an empty one that leaves the group unnamed.

@paramconfigCngxTimelineConfig

common/timeline/grouping.ts

createTimelineGrouping Factoryv0.1.0
createTimelineGrouping#TimelineGrouping
createTimelineGrouping(options: TimelineGroupingOptions)

Derives grouped, sorted timeline bands from a flat item list.

Pure derivation: bands come out of one linkedSignal over the items accessor. Nothing is synced, nothing is written back, and the presenter never mutates its input array. The previous bands are read through the computation callback rather than a closure cache, so the result depends on the inputs and the prior value alone - never on how many times it ran.

Three properties make it usable as the organism's only data path:

  • Defensive sort. Consumer data does not have to arrive sorted. The presenter sorts by the accessor date on every run using a stable sort, so items sharing a timestamp keep their input order in both directions.
  • Local-calendar bucketing. day / week / month read local date fields rather than dividing the epoch, which is what keeps 23-hour and 25-hour DST days intact. Consumers who want UTC (or any other rule) pass a TimelineGroupingFn.
  • Append-stability. Groups whose items are the same objects hand back the same band reference, and the signal carries a structural equal. Reuse is deliberately keyed on reference identity rather than on an id: a refetch returns new objects at the same ids, and reusing there would pin the band to a stale payload. Appending therefore leaves every other band's @for block untouched instead of re-rendering the whole timeline.

Reach for the DI token CNGX_TIMELINE_GROUPING_FACTORY rather than this function directly when the caller is a component - that is what makes the bucketing swappable per app or per component.

const grouping = createTimelineGrouping({
  items: this.events,
  dateAccessor: (event) => event.occurredAt,
  groupBy: this.groupBy,
});
groupByDay(date: Date)
@paramdateDate
groupByMonth#TimelineGroupKey
groupByMonth(date: Date)
@paramdateDate
groupByNone(date: Date)
@paramdateDate
groupByWeek(date: Date)
@paramdateDate
groupsEqual#boolean
groupsEqual(a, b)
@parama
@paramb
pad2(value: number)
@paramvaluenumber
resolveGrouper#TimelineGroupingFn
resolveGrouper(mode: TimelineGroupBy)
@parammodeTimelineGroupBy
sameItems#boolean
sameItems(previous, next)
@paramprevious
@paramnext
toDate#Date
toDate(value)
@paramvalue

ui/timeline/slot-cascade.ts

createTimelineSlotBinding Factoryv0.1.0
createTimelineSlots Factoryv0.1.0
createTimelineSlotBinding#Signal
createTimelineSlotBinding(instance: Signal, configured)

The family-standard three-stage template cascade, as one named concept:

instance contentChild  ->  CNGX_*_CONFIG.templates.<key>  ->  null

The contentChild() query itself has to stay a direct field initialiser on the component (AOT rejects it from a helper, NG8110), but resolving the three tiers does not - and writing that resolution out once per slot turns a single rule into eight places it can drift.

configured is a thunk rather than a value so the config tier is read at resolution time, inside the computed().

Exported because an ejected skin resolves the same three tiers and would otherwise have to restate the rule.

@paraminstanceSignal
@paramconfigured
createTimelineSlots#CngxTimelineSlots
createTimelineSlots(queries: CngxTimelineSlotQueries, templates)

Runs createTimelineSlotBinding over the whole slot set at once, so the organism holds one field instead of eight near-identical ones and each region has exactly one call site.

The casts live here rather than at eight use sites: CngxTimelineTemplates is typed against unknown items because the config is app-wide while the slots are generic. TemplateRef is method-bivariant, so a consumer's concrete template still assigns in.

@paramtemplates

ui/timeline/timeline-view.ts

createTimelineView Factoryv0.1.0
createTimelineView#CngxTimelineView
createTimelineView(state, isEmpty, labels: CngxTimelineFallbackCopy)

Derives the whole body switch from one bound state. No second state machine, no boolean fallback inputs - every branch is a computed() over resolveAsyncView, the same lookup table the rest of cngx switches on.

Two cases the lookup table does not cover on its own:

  • No state bound. The timeline is then a plain list over [items], but an empty one still has to reach the empty surface; otherwise *cngxTimelineEmpty would be unreachable on the synchronous path.
  • The announcement. Only loading and refreshing have anything to say, and the string is empty otherwise so the live region stays in the DOM while staying silent.
  • Busy with an empty screen. The lookup table resolves a non-first-load loading or pending to content; with no rows that renders nothing at all, so it is treated as a load instead. refreshing keeps its tail.
  • First-load busy over seed rows. The lookup table resolves a first load to skeleton unconditionally, but [items] documents seed rows as a legitimate companion to a bound state - rows that exist are painted, never hidden behind placeholders. aria-busy still marks the list.

Swap the whole mapping through CNGX_TIMELINE_VIEW_FACTORY rather than forking the organism - treating pending as content, or holding the skeleton through a refresh, is a per-app decision.

@paramstate
@paramisEmpty

chart/scales/time.ts

createTimeScale Factory
createTimeScale#number
createTimeScale(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.

@paramdomain

[start, end] time range. Date and number are interchangeable on either endpoint.

@paramrange

[start, end] output range (typically pixel coordinates).

(v: Date | number) => number mapping time values to range values.

core/utils/transition-tracker.ts

createTransitionTracker Factoryv0.1.0
createTransitionTracker#ValueTransition
createTransitionTracker(source, options?: TransitionTrackerOptions)

Creates a reactive transition tracker for any value source. Defaults to AsyncStatus - the original specialisation - but tracks booleans, enums, or object snapshots (pass options.equal) just the same.

Uses linkedSignal internally - when source() changes, previous holds the prior value and current holds the new one. Both are memoized signals.

At mount, previous equals the source's current value (no phantom idle -> X transition for a source that mounts mid-flight); pass options.seed to seed previous explicitly instead. The mount value is captured lazily at the tracker's first read - a source change before anything observes the tracker folds into the mount value instead of fabricating a transition nobody watched happen.

@paramsource

Reactive function that reads the current AsyncStatus.

Optional TransitionTrackerOptions.

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.0
createTreeAdItems#Signal
createTreeAdItems(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.0
createTreeController#CngxTreeController
createTreeController(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 Factory
createTriggerFocusState#CngxTriggerFocusState

Builds 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 Factory
createTypeaheadController#TypeaheadController
createTypeaheadController(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.

resolvePageJumpTarget#number | null
resolvePageJumpTarget(opts, currentIndex: number, direction, isDisabled, step: number)

PageUp/PageDown helper. Disabled-aware, clamped ±step jump with back-probe fallback. Returns target index or null.

@paramopts
@paramcurrentIndexnumber
@paramdirection
@paramisDisabled
@paramstepnumber= 10

core/utils/visibility-gate.ts

createVisibilityGate Factoryv0.1.0
createVisibilityGate#Signal
createVisibilityGate(isActive: Signal, delay: Signal, minDwell: Signal)

Creates a debounced visibility signal from an active-state signal.

Implements two timing rules:

  • delay: suppresses visibility for fast operations (no flash)
  • minDwell: keeps visible for at least this long once shown (no jarring disappearance)

Must be called in an injection context (uses inject(DestroyRef) for cleanup).

@paramisActiveSignal
@paramdelaySignal
@paramminDwellSignal

A readonly signal that is true when the indicator should be visible.

interactive/menu/menu-nav-strategy.ts

createW3CMenuStrategy Factory
createW3CMenuStrategy#CngxMenuNavStrategy

Default 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).

onArrowRight / onArrowLeft are the inline-forward / inline-back intents, not physical keys. The dispatch site resolves the physical arrow to its logical intent by writing direction before calling the strategy (resolveInlineArrowKey), so under rtl physical ArrowLeft routes to onArrowRight. Custom strategies therefore stay direction-naive and inherit correct RTL behaviour for free.

interactive/hierarchical-nav/hierarchical-nav-strategy.ts

createW3CTreeStrategy Factory
createW3CTreeStrategy#CngxHierarchicalNavStrategy

Default 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.

onArrowRight / onArrowLeft are the inline-forward / inline-back intents, not physical keys. CngxHierarchicalNav resolves the physical arrow to its logical intent by writing direction (resolveInlineArrowKey) before calling the strategy, so under rtl physical ArrowLeft routes to onArrowRight. Custom strategies stay direction-naive and inherit correct RTL behaviour for free.

core/utils/intl-format.util.ts

dateTimeFormatterFor v0.1.0
dateTimeFormatterFor#Intl.DateTimeFormat
dateTimeFormatterFor(locale: string, options)

Bounded Intl.DateTimeFormat cache keyed on locale + options. Constructing an Intl formatter is the expensive half of formatting; this makes repeated formatting allocation-free. Consumers bind static option literals, so the key space stays tiny; the FIFO cap guards a consumer generating per-row options (e.g. varying timeZone) from growing the cache for the app's lifetime.

Keys serialize via JSON.stringify, so two option objects with different property order occupy two (bounded) cache slots - they still format identically.

@paramlocalestring
@paramoptions

projects/utils/decimal-places.ts

decimalPlaces v0.1.0
decimalPlaces#number
decimalPlaces(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.

@paramnnumber

select/tree-select/tree-select.component.ts

dedup#T[]
dedup(arr, eq)
@paramarr
@parameq

mat-tabs/decorations/decoration-projectors.ts

defaultRetryCeilingWarn#void
defaultRetryCeilingWarn(max: number, decoration)
@parammaxnumber
@paramdecoration

data/data-source/smart-data-source.ts

injectSmartDataSource Inject
defaultSearchFn#boolean
defaultSearchFn(item: T, term: string)
@paramitemT
@paramtermstring
defaultSortFn#number
defaultSortFn(a: T, b: T, field: string, dir)
@paramaT
@parambT
@paramfieldstring
@paramdir
injectSmartDataSource#CngxSmartDataSource
injectSmartDataSource(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);
@paramsource
isAsyncState#CngxAsyncState
isAsyncState(source)
@paramsource

display/shared/delta-format.ts

deltaDirection#DeltaDirection
deltaDirection(value: number)

> 0 → up, < 0 → down, 0 → flat.

@paramvaluenumber
deltaSentiment#DeltaSentiment
deltaSentiment(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).

@paramdirectionDeltaDirection
@parampolarityDeltaPolarity
directionGlyph#string
directionGlyph(direction: DeltaDirection)

Arrow glyph for the direction: / / .

@paramdirectionDeltaDirection
formatDelta#string
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.

@paramvaluenumber
@parammodeDeltaMode
@paramlocalestring
@paramformat?

chart/chart/equal-helpers.ts

dimensionsEqual#boolean
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.

@parama
@paramb
insetEqual#boolean
insetEqual(a: CngxChartInset, b: CngxChartInset)

Field-wise equality on the chart's four-sided axis inset. The inset computed rebuilds its literal whenever the projected axis set re-emits, so without this guard an unchanged axis set would cascade into the plot area and from there into both scale ranges and every axis geometry on each pass.

plotAreaEqual#boolean
plotAreaEqual(a: CngxChartPlotArea, b: CngxChartPlotArea)

Field-wise equality on the published plot rectangle. Only the four corners are compared - width/height are derived from them, so an equal pair of corners implies equal extents.

sameItemsArr#boolean
sameItemsArr(a, b)

Length + Object.is per-index equality on any readonly array. The generic sibling of sameNumberArr: injectChartBuffer stores the pushed rows by reference, so a flush that reselects the same rows in the same order yields an element-wise-identical projection even though snapshot() allocates a fresh wrapper each call. Reference equality would never fire here (the wrapper is always new); element-wise is the only guard that dedups the buffer's points cascade.

@parama
@paramb
sameNumberArr#boolean
sameNumberArr(a, b)

Length + Object.is per-index equality on a readonly numeric array. Used by summaryValues and summary.thresholds. Reference-equal arrays short-circuit immediately.

@parama
@paramb
slotContextEqual#boolean
slotContextEqual(a: CngxChartSlotContext, b: CngxChartSlotContext)

Field-wise equality on the slot context handed to every fallback template. small is derived from width, so comparing it would be redundant.

The context is a fresh literal on every read, so without this a resize that lands on the same pixel still re-renders every projected loading / empty / error template.

chart/buffer/lttb.ts

downsampleLTTB#T[]
downsampleLTTB(data, targetSize: number, xAccessor, yAccessor)

Largest-Triangle-Three-Buckets downsampling. Reduces a dense series to targetSize points while preserving the perceptual shape of the line - peaks, troughs, and inflections survive where naive uniform (every-Nth) sampling would drop them. This is the realtime-charting literature's default downsampler (Steinarsson 2013).

Pure TS: no Angular, no signal, no RxJS dependency. Tree-shakeable, so static-chart consumers that never buffer pay nothing. The <cngx-chart> buffer (injectChartBuffer) is the single downsampling boundary at v2; this is its algorithm.

The first and last points are always kept. The middle targetSize - 2 points are chosen one per bucket by the largest-triangle-area heuristic: for each bucket, the point forming the largest triangle with the previous selected point and the next bucket's average is retained.

@paramdata

source series, read-only.

@paramtargetSizenumber

desired output length. When >= data.length (or <= 0), the input is returned unchanged by reference - no allocation, no copy.

@paramxAccessor

projects a row to its numeric X (typically a timestamp or positional index).

@paramyAccessor

projects a row to its numeric Y (the value the triangle area is computed against).

a fresh array of at most targetSize rows, or the input by reference when no reduction is needed.

utils/rxjs-interop/rxjs-interop.ts

ensureObservable#Observable
ensureObservable(value)

Ensures the value is an observable.

@paramvalue

data/sort/sort.directive.ts

entriesEqual#boolean
entriesEqual(a, b)
@parama
@paramb
entryEqual#boolean
entryEqual(a, b)
@parama
@paramb

ui/mat-tabs/mat-tabs-config.ts

injectMatTabsConfig Inject
provideMatTabsConfig Provider
provideMatTabsConfigAt Provider
withAnchorRetryAttempts Feature
withHalfWiredSlotSink Feature
withMatTabRejectionTemplate Feature
feature(config: Partial)
@paramconfigPartial
injectMatTabsConfig#Required

Resolve 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:

  1. CNGX_MAT_TABS_CONFIG value (set via provideMatTabsConfig / provideMatTabsConfigAt).
  2. CNGX_MAT_TABS_CONFIG_DEFAULTS (library default).
mergeFeatures#Partial
mergeFeatures(features)
@paramfeatures
provideMatTabsConfig#EnvironmentProviders
provideMatTabsConfig(...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}`)),
    ),
  ],
});
@paramfeatures
provideMatTabsConfigAt#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).

Merges over the outer config (via skipSelf), so a component-scope call refines the root provideMatTabsConfig instead of reverting its untouched keys to library defaults - only the keys the local features set are overridden for the subtree.

@paramfeatures
withAnchorRetryAttempts#CngxMatTabsConfigFeature
withAnchorRetryAttempts(n: number)

Cap on the [cngxMatTabs] overflow-anchor retry loop. See CngxMatTabsConfig.anchorMaxAttempts for default + rationale.

@paramnnumber
withHalfWiredSlotSink#CngxMatTabsConfigFeature
withHalfWiredSlotSink(sink: CngxMatTabHalfWiredSlotSink)

Override the half-wired-slot diagnostic sink. See CngxMatTabsConfig.halfWiredSlotSink.

withMatTabRejectionTemplate#CngxMatTabsConfigFeature
withMatTabRejectionTemplate(template: TemplateRef)

App-wide override for the rejection SR-descriptor content. Middle tier; per-instance *cngxMatTabRejectionContent still wins. Sibling of withTabRejectionIconTemplate in the cngx-native family.

@paramtemplateTemplateRef

select/shared/action-select-config.ts

provideActionSelectConfig Provider
provideActionSelectConfigAt Provider
withActionAriaLabel Feature
withActionPopoverPlacement Feature
withActionPosition Feature
withCloseOnCreate Feature
withFocusTrapBehavior Feature
withLiveInputFallback Feature
feature(config: Partial)
@paramconfigPartial
provideActionSelectConfig#EnvironmentProviders
provideActionSelectConfig(...features: undefined)

App-wide action-select config. provideActionSelectConfigAt wins.

bootstrapApplication(App, {
  providers: [
    provideActionSelectConfig(
      withFocusTrapBehavior('always'),
      withActionAriaLabel('Quick action'),
      withCloseOnCreate(true),
      withActionPosition('top'),
      withLiveInputFallback(false),
    ),
  ],
});
@paramfeatures
provideActionSelectConfigAt#Provider[]
provideActionSelectConfigAt(...features: undefined)

Component-scoped action-select config. Returns Provider[] because viewProviders rejects EnvironmentProviders.

@paramfeatures
withActionAriaLabel(label: string)

Sets the action-slot ARIA label.

@paramlabelstring
withActionPopoverPlacement#CngxActionSelectConfigFeature
withActionPopoverPlacement(placement: PopoverPlacement)

Sets the popover placement for the action organisms.

@paramplacementPopoverPlacement
withActionPosition(position: CngxActionPosition)

Sets the default *cngxSelectAction slot position.

@parampositionCngxActionPosition
withCloseOnCreate(closeOnCreate)

Forces closeOnCreate across both action organisms. Pass null to restore the variant baselines.

@paramcloseOnCreate
withFocusTrapBehavior#CngxActionSelectConfigFeature
withFocusTrapBehavior(behavior: CngxActionFocusTrapBehavior)
withLiveInputFallback#CngxActionSelectConfigFeature
withLiveInputFallback(enabled: boolean)

Sets the live-input fallback policy. Disable when consumer owns debouncing.

@paramenabledboolean

select/shared/config.ts

provideSelectConfig Provider
provideSelectConfigAt Provider
withAnnouncer Feature
withAriaLabels Feature
withCaret Feature
withChipOverflow Feature
withCommitErrorAnnouncePolicy Feature
withCommitErrorDisplay Feature
withDismissOn Feature
withEnterKeyHint Feature
withFallbackLabels Feature
withInputMode Feature
withLoadingVariant Feature
withMaxVisibleChips Feature
withOpenOn Feature
withPanelClass Feature
withPanelWidth Feature
withPopoverPlacement Feature
withRefreshingVariant Feature
withRestoreFocus Feature
withSelectionIndicator Feature
withSelectionIndicatorPosition Feature
withSelectionIndicatorVariant Feature
withSkeletonRowCount Feature
withTemplates Feature
withTypeaheadDebounce Feature
withTypeaheadWhileClosed Feature
withVirtualization Feature
feature(config: Partial)
@paramconfigPartial
makeSelectConfig#CngxSelectConfig
makeSelectConfig(...features: undefined)

Pure merge of with* features into a CngxSelectConfig value - the exact merge provideSelectConfig / provideSelectConfigAt apply. Reach for it when the config must be built lazily in a useFactory, e.g. to carry TemplateRef payloads that only exist once a holder view is live (see withTemplates). For static values, prefer the two provide functions.

@paramfeatures
provideSelectConfig#EnvironmentProviders
provideSelectConfig(...features: undefined)

App-wide defaults for the Select family. provideSelectConfigAt and per-instance inputs win.

@paramfeatures
provideSelectConfigAt#Provider[]
provideSelectConfigAt(...features: undefined)

Component-scoped config. Returns Provider[] because viewProviders rejects EnvironmentProviders.

@paramfeatures
withAnnouncer(config: CngxSelectAnnouncerConfig)

Configure the live-region announcer used for selection changes.

withAriaLabels(labels: CngxSelectAriaLabels)

Sets ARIA-label overrides. Partial. Per-instance inputs win.

bootstrapApplication(App, {
  providers: [
    provideSelectConfig(
      withAriaLabels({
        clearButton: 'Clear selection',
        chipRemove: 'Remove',
      }),
    ),
  ],
});
withCaret(enabled: boolean)

Whether the trigger's dropdown caret glyph is rendered. Default true.

@paramenabledboolean
withChipOverflow#CngxSelectConfigFeature
withChipOverflow(mode: NonNullable)

Sets the chip-strip overflow mode. Default 'wrap'.

@parammodeNonNullable
withCommitErrorAnnouncePolicy#CngxSelectConfigFeature
withCommitErrorAnnouncePolicy(policy)

Forces the scalar-commit error-announce policy. null restores each variant's baseline.

@parampolicy
withCommitErrorDisplay#CngxSelectConfigFeature
withCommitErrorDisplay(display: CngxSelectCommitErrorDisplay)

Default [commitAction] error surface. Default 'banner'.

withDismissOn(mode)

Dismiss strategy for the panel. Default 'both'.

@parammode
withEnterKeyHint#CngxSelectConfigFeature
withEnterKeyHint(hint)

Forces enterkeyhint across input-trigger variants. null restores each variant's baseline.

@paramhint
withFallbackLabels#CngxSelectConfigFeature
withFallbackLabels(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',
      }),
    ),
  ],
});
withInputMode(mode: NonNullable)

Sets inputmode for input-trigger variants. Default 'search'.

@parammodeNonNullable
withLoadingVariant#CngxSelectConfigFeature
withLoadingVariant(variant: CngxSelectLoadingVariant)

First-load indicator variant. Default 'spinner'.

withMaxVisibleChips#CngxSelectConfigFeature
withMaxVisibleChips(count: number)

Sets maxVisibleChips for truncate mode. Coerces ≤ 0 to 1. Default 3.

@paramcountnumber
withOpenOn(mode)

Open strategy for the trigger. Default 'click'.

@parammode
withPanelClass(panelClass)

Class list applied to every select panel.

@parampanelClass
withPanelWidth(width)

Panel width: 'trigger' (match), fixed px number, or null (natural).

@paramwidth
withPopoverPlacement#CngxSelectConfigFeature
withPopoverPlacement(placement: PopoverPlacement)

Default popover placement for flat variants. Default 'bottom'. Action organisms use withActionPopoverPlacement.

@paramplacementPopoverPlacement
withRefreshingVariant#CngxSelectConfigFeature
withRefreshingVariant(variant: CngxSelectRefreshingVariant)

Sets the refreshing indicator. Default 'bar'. 'none' suppresses.

withRestoreFocus#CngxSelectConfigFeature
withRestoreFocus(enabled: boolean)

Whether the trigger is re-focused after the panel closes. Default true.

@paramenabledboolean
withSelectionIndicator#CngxSelectConfigFeature
withSelectionIndicator(enabled: boolean)

Whether the selected-option checkmark is rendered at all. Default true.

@paramenabledboolean
withSelectionIndicatorPosition#CngxSelectConfigFeature
withSelectionIndicatorPosition(position: CngxSelectSelectionIndicatorPosition)

Sets the selection-indicator position. Default 'before'.

withSelectionIndicatorVariant#CngxSelectConfigFeature
withSelectionIndicatorVariant(variant: CngxSelectSelectionIndicatorVariant)

Sets the selection-indicator glyph. Default 'auto'.

withSkeletonRowCount#CngxSelectConfigFeature
withSkeletonRowCount(count: number)

Sets skeletonRowCount. Default 3.

@paramcountnumber
withTemplates(templates: NonNullable)

Sets default slot templates applied when no per-instance slot is projected. Partial - unset slots keep the library default. Multiple withTemplates calls in the same provider merge per slot in feature-list order.

Resolution per slot: projected *cngxSelect... template -> the nearest provided config's templates.<slot> -> built-in default. Like every CNGX_SELECT_CONFIG key, the token is nearest-wins across injector levels: an At-scope config replaces a root config wholesale for its subtree, it does not inherit root slots.

TemplateRefs need a live view, so they cannot sit in a static provider array evaluated at bootstrap. Declare the templates in a holder component rendered before the selects (an app shell above routed content) and build the config lazily via makeSelectConfig in a useFactory:

@paramtemplatesNonNullable
withTypeaheadDebounce#CngxSelectConfigFeature
withTypeaheadDebounce(ms: number)

Typeahead buffer debounce in ms. Default 300.

@parammsnumber
withTypeaheadWhileClosed#CngxSelectConfigFeature
withTypeaheadWhileClosed(enabled: boolean)

Typeahead-while-closed (native <select> parity). Default true.

@paramenabledboolean
withVirtualization#CngxSelectConfigFeature
withVirtualization(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 }),
)
@paramconfig= true

select/shared/reorderable-select-config.ts

provideReorderableSelectConfig Provider
provideReorderableSelectConfigAt Provider
withDefaultDragHandle Feature
withReorderAriaLabel Feature
withReorderKeyboardModifier Feature
withReorderStripFreeze Feature
feature(config: Partial)
@paramconfigPartial
provideReorderableSelectConfig#EnvironmentProviders
provideReorderableSelectConfig(...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),
    ),
  ],
});
@paramfeatures
provideReorderableSelectConfigAt#Provider[]
provideReorderableSelectConfigAt(...features: undefined)

Component-scoped reorderable-select config. Returns Provider[] because viewProviders rejects EnvironmentProviders.

@paramfeatures
withDefaultDragHandle(template)

Sets the default drag-handle glyph app-wide via TemplateRef<void>.

@paramtemplate
withReorderAriaLabel(label: string)

Sets the chip-strip ARIA label.

@paramlabelstring
withReorderKeyboardModifier#CngxReorderableSelectConfigFeature
withReorderKeyboardModifier(modifier: CngxReorderModifier)

Sets the keyboard modifier gating reorder moves.

@parammodifierCngxReorderModifier
withReorderStripFreeze(freeze: boolean)

Sets strip-freeze-on-commit. false lets reorders supersede in-flight commits via the commit-controller's supersede semantics.

@paramfreezeboolean

forms/input/file-drop.directive.ts

fileKey#string
fileKey(file: File)

Identity key for dedup across drops.

@paramfileFile

select/shared/option.model.ts

filterSelectOptions#CngxSelectOptionsInput
filterSelectOptions(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.

@paramtermstring
@parammatch
flattenSelectOptions#CngxSelectOptionDef[]
flattenSelectOptions(input: CngxSelectOptionsInput)

Flattens grouped + flat input. Used by keyboard nav and compare lookups.

isCngxSelectOptionGroupDef#CngxSelectOptionGroupDef
isCngxSelectOptionGroupDef(item)

Group / flat-option type guard.

@paramitem
isOptionDisabled#boolean
isOptionDisabled(option)

Disabled-check across both option shapes:

  • CngxSelectOptionDef.disabled - plain boolean (data-driven)
  • CngxOption.disabled - InputSignal<boolean> (element-driven, callable)

Direct .disabled access would treat every signal as truthy (TS2774).

@paramoption
mergeLocalItems(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).

@paramlocalItems
@paramcompareWith

data-display/treetable/tree.utils.ts

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.

Delegates to the @cngx/utils tree kernel. The kernel normalizes a matched parent whose children all fail the predicate to children: undefined (never an empty array) - both shapes mean "leaf".

@paramnodes
@parampredicate
flattenTree(input, nodeId?)

Flattens a tree (or forest) into a depth-first ordered array of CngxTreetableFlatNodes.

Thin wrapper over the @cngx/utils tree kernel: normalizes the single-root-or-forest input, preserves the treetable's public - id joiner, and fixes the label derivation to '' (the treetable never consumes labels; the kernel default String(value) would leak [object Object]).

@paraminput
  • A single root node or an array of root nodes.
@paramnodeId?
  • 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, sibling position (posinset / setsize), and a flag indicating whether it has children.

nodeMatchesSearch#boolean
nodeMatchesSearch(value: T, term: string)

Simple full-text search across all primitive fields of a node value. Consumer-side predicate: pair it with filterTree (or your own walk) to build a searchable tree source. Nothing in the library calls it for you.

@paramvalueT
@paramtermstring
sortTree(nodes, field: string, direction)

Sorts each level of the tree independently by a field key. Children remain grouped under their parent; only sibling order changes.

@paramnodes
@paramfieldstring
@paramdirection

forms/field/focus-first-error.ts

focusFirstError#boolean
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.

@paramtreeFieldTree

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

fromHttpResource#CngxAsyncState
fromHttpResource(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 bar

interop/query/from-query.ts

fromQuery(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 + fetching -> loading (no retained data) / refreshing (data retained) - a retry out of error reports busy again
  • error + idle/paused -> error
  • success + fetching -> refreshing (background refetch, data visible)
  • success + idle/paused -> success
  • pending + fetching -> loading (first load, no data yet)
  • pending + idle/paused -> idle (disabled or paused query)

With placeholderData, TanStack reports success + fetching while the first real load runs - that maps to refreshing over the placeholder, deliberately suppressing the skeleton (the intended TanStack UX).

isFirstLoad is !hadSuccess, the kernel-recommended query semantics: a failed or retrying first load stays isFirstLoad === true until data actually arrived once. Bind dataUpdatedAt for the exact latch; without it the bridge falls back to status === 'success' or retained data. Two deliberate divergences from fromResource: a settled first-load error stays first-load here (fromResource flips false on error), and the latch is derived, so a query reset / key swap (dataUpdatedAt back to 0) returns to first-load instead of staying latched forever.

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 binding
@paramqueryCngxQueryLike

data/async-state/from-resource.ts

fromResource#CngxAsyncState
fromResource(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 binding
@paramrefResource

core/utils/transition.util.ts

hasTransition v0.1.0
onTransitionDone v0.1.0
hasTransition#boolean
hasTransition(el: HTMLElement)

Checks whether an element has any CSS transition applied.

@paramelHTMLElement

true if at least one transition-duration value is greater than 0.

onTransitionDone#TransitionDoneHandle
onTransitionDone(el: HTMLElement, onDone)

Listens for the longest CSS transition on an element, then invokes onDone.

The longest transition is the property with the greatest transition-duration + transition-delay total, so a themed delay extends the wait instead of cutting it short. Automatically falls back to a timeout if transitionend never fires.

@paramelHTMLElement
@paramonDone

A {@link TransitionDoneHandle} - flush() completes immediately, cancel() tears down without invoking onDone.

interactive/accordion/accordion.directive.ts

headersEqual#boolean
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).

@parama
@paramb

core/theming/a11y-preferences.ts

injectA11yPreferences Injectv0.1.0
provideA11yPreferences Providerv0.1.0
providePersistence Provider
withContrast Featurev0.1.0
withDensity Featurev0.1.0
withMotion Featurev0.1.0
withPersistence Featurev0.1.0
withTextScale Featurev0.1.0
injectA11yPreferences#literal type

Read the four accessibility-axis signals as one bundle in an injection context. Each is the same WritableSignal the axis token holds, so the accessibility panel both reads current state and writes user choices through it (injectA11yPreferences().motion.set('reduced')).

installAxisPersistence#void
installAxisPersistence(storage: Storage, storageKey: string, axisKey: string, sig: WritableSignal, values)
@paramstorageStorage
@paramstorageKeystring
@paramaxisKeystring
@paramvalues
provideA11yPreferences#EnvironmentProviders
provideA11yPreferences(...features: undefined)

Install all four accessibility axes (density, text-scale, motion, contrast) behind one call. Each axis is set from its matching with* feature, or left at its own library default when the feature is omitted. Duplicate axis features are last-wins.

This is composition over configuration (Pillar 3): the aggregator only forwards the resolved initials to the existing provideDensity / provideTextScale / provideMotion / provideContrast, so no reflector logic is duplicated. The accessibility panel then binds to the four writable signals via injectA11yPreferences.

bootstrapApplication(AppComponent, {
  providers: [
    provideA11yPreferences(withTextScale('lg'), withMotion('reduced')),
  ],
});
@paramfeatures
providePersistence#EnvironmentProviders
providePersistence(storageKey: string)
@paramstorageKeystring
readStored#Record
readStored(storage: Storage, storageKey: string)
@paramstorageStorage
@paramstorageKeystring
withContrast(value: CngxContrastPreference)

Set the initial contrast preference the aggregator installs. Omitting this feature leaves contrast at its auto default, which follows the OS prefers-contrast query.

withDensity(value: CngxDensityValue)

Set the initial density rung the aggregator installs. Omitting this feature leaves density at its comfortable default.

@paramvalueCngxDensityValue
withMotion(value: CngxMotionPreference)

Set the initial motion preference the aggregator installs. Omitting this feature leaves motion at its auto default, which follows the OS prefers-reduced-motion query.

withPersistence#CngxA11yPrefFeature
withPersistence(storageKey: string)

Persist explicit accessibility choices via CNGX_A11Y_STORAGE (browser localStorage by default) under storageKey and rehydrate them on the next load. On startup a stored value overrides an axis only when it is a known-valid member of that axis' union; an unknown, invalid, or missing value leaves the axis at its own default, so a motion/contrast auto stays OS-driven and is never clobbered. The write-back skips the initial flush, so an untouched default is never written and a rehydrated value is not re-written (it is already in storage); only a later change (a user pick) is persisted. The whole feature is browser-guarded via DOCUMENT.defaultView and is a no-op on the server.

bootstrapApplication(AppComponent, {
  providers: [provideA11yPreferences(withPersistence())],
});
@paramstorageKeystring= 'cngx-a11y'
withTextScale(value: CngxTextScaleValue)

Set the initial text-scale rung the aggregator installs. Omitting this feature leaves text-scale at its md (identity) default.

accordion/config/inject-accordion-config.ts

injectAccordionConfig Injectv0.1.0
injectAccordionConfig#CngxAccordionConfig

Convenience 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);
}

select/shared/inject-helpers.ts

injectActionSelectConfig Inject
injectReorderableSelectConfig Inject
injectSelectAnnouncer Inject
injectSelectConfig Inject
injectActionSelectConfig#ReturnType

Effective action-select config for the current injector, merged with library defaults. Always fully populated - never null. Injection context required. Sibling of injectSelectConfig for the CngxActionSelect / CngxActionMultiSelect composites.

injectReorderableSelectConfig#ReturnType

Effective reorderable-select config for the current injector, merged with library defaults. Always fully populated - never null. Injection context required. Sibling of injectSelectConfig for the CngxReorderableMultiSelect composite.

injectSelectAnnouncer#CngxSelectAnnouncer

Root-scoped CngxSelectAnnouncer for custom composites that want to share the family live-region.

const announcer = injectSelectAnnouncer();
announcer.announce('Filter applied: Red', 'polite');
injectSelectConfig#ReturnType

Effective 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;
}

data/async-registry/provide-async-registry.ts

injectAsyncRegistry Inject
provideAsyncRegistry Provider
injectAsyncRegistry#CngxAsyncRegistry | null

Returns the ambient CngxAsyncRegistry, or null when the consumer did not opt in via provideAsyncRegistry. Must run in an injection context.

provideAsyncRegistry#EnvironmentProviders

Provides 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 Inject
injectAsyncState#ReactiveAsyncState
injectAsyncState(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.
@paramfn

audio/config/audio-config.ts

injectAudioConfig Injectv0.1.0
provideCngxAudio Providerv0.1.0
withDebounceMs Featurev0.1.0
withEarcons Featurev0.1.0
withMuted Featurev0.1.0
withRespectReducedMotion Featurev0.1.0
withVolume Featurev0.1.0
injectAudioConfig#CngxAudioConfig

Resolve the effective audio config: library defaults merged with whatever provideCngxAudio(...) contributed. Runs in an injection context.

provideCngxAudio#EnvironmentProviders
provideCngxAudio(...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.

@paramfeatures
withDebounceMs#CngxAudioFeature
withDebounceMs(ms: number)

Set the same-name suppression window in milliseconds.

@parammsnumber
withEarcons(earcons: Record)

Register or override earcons globally. Merged over the six built-ins and over any earlier withEarcons in the same provideCngxAudio call.

@paramearconsRecord
withMuted(muted: unknown)

Start muted (or explicitly unmuted with withMuted(false)).

@parammutedunknown= true
withRespectReducedMotion#CngxAudioFeature
withRespectReducedMotion(respect: boolean)

Toggle the prefers-reduced-motion mute gate. true by default in the library; pass false to keep audio playing under reduced-motion.

@paramrespectboolean
withVolume(volume: number)

Set the master volume in [0, 1].

@paramvolumenumber

breadcrumb/config/inject-breadcrumb-config.ts

injectBreadcrumbConfig Injectv0.1.0
injectBreadcrumbConfig#CngxBreadcrumbConfig

Convenience 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');
}

card/i18n/card-i18n.ts

injectCardI18n Inject
provideCardI18n Provider
withCardI18nLabels Feature
injectCardI18n#CngxCardI18n

Inject the resolved card i18n bundle.

provideCardI18n#Provider
provideCardI18n(...features: undefined)

Provider for the card i18n bundle.

bootstrapApplication(AppComponent, {
  providers: [
    provideCardI18n(withCardI18nLabels({ selected: 'Ausgewählt', deselected: 'Abgewählt' })),
  ],
});
@paramfeatures
withCardI18nLabels#CngxCardI18nFeature
withCardI18nLabels(overrides: Partial)

Override i18n labels via a partial bundle - unset keys keep the English default.

@paramoverridesPartial

chart/buffer/inject-chart-buffer.ts

injectChartBuffer Inject
injectChartBuffer#CngxChartBuffer
injectChartBuffer(opts: ChartBufferOptions)

Bounded, rAF-coalesced realtime feed for <cngx-chart>. Wraps a fixed-capacity ring (createRingBuffer) with an optional LTTB downsample (downsampleLTTB) and exposes the window as a Signal.

Multiple push / pushBatch / clear calls within one animation frame collapse into a single points emission: each mutation writes the ring imperatively and schedules one requestAnimationFrame(flush); the flush bumps a revision signal that points derives from. The flush is a plain callback writing a signal - not an effect() body - so no reactive exception class is involved.

Runs in an injection context; the rAF handle is cancelled via DestroyRef.

export class Telemetry {
  private readonly buffer = injectChartBuffer<Sample>({
    capacity: 1000,
    downsampleTo: 600,
    xAccessor: (s) => s.t,
    yAccessor: (s) => s.value,
  });
  protected readonly points = this.buffer.points; // -> <cngx-chart [data]>
  onSample(s: Sample): void {
    this.buffer.push(s);
  }
}

chart/chart/chart-context.ts

injectChartContext Injectv0.1.0
injectChartContext#CngxChartContext
injectChartContext(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.

Use it from custom layer directives mounted on <svg:g> hosts to read plot, the scales and renderSvg the same way the built-in layer atoms do.

@paramconsumerNamestring

chart-panel/config/inject-chart-panel-config.ts

injectChartPanelConfig Injectv0.1.0
injectChartPanelConfig#CngxChartPanelConfig

Convenience accessor for the chart-panel configuration cascade. Runs in injection context; resolves through the priority chain (per-instance Input -> provideChartPanelConfigAt -> provideChartPanelConfig -> library defaults). Equivalent to inject(CNGX_CHART_PANEL_CONFIG) - the helper exists so consumers don't import the token directly.

common/audio/inject-audio.ts

injectCngxAudio Injectv0.1.0
provideCngxAudioAt Providerv0.1.0
injectCngxAudio#CngxAudioHandle

Resolve 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());
  }
}
provideCngxAudioAt#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(...).

@paramfeatures

command-palette/config/command-palette-config.ts

injectCommandPaletteConfig Injectv0.1.0
provideCommandPaletteConfig Providerv0.1.0
provideCommandPaletteConfigAt Providerv0.1.0
withCommandPaletteLabels Featurev0.1.0
withCommandPaletteTemplates Featurev0.1.0
withKeyboardLegend Featurev0.1.0
withPaletteShortcut Featurev0.1.0
withResultCountFormatter Featurev0.1.0
injectCommandPaletteConfig#CngxCommandPaletteConfig

Resolves the CngxCommandPaletteConfig from the current injection scope. Must run in an injection context.

provideCommandPaletteConfig#EnvironmentProviders
provideCommandPaletteConfig(...features: undefined)

Provide a palette configuration at app root.

provideCommandPaletteConfig(
  withCommandPaletteLabels({ emptyLabel: 'Keine Treffer.' }),
  withKeyboardLegend([{ keys: 'enter', label: 'Ausführen' }]),
)
@paramfeatures
provideCommandPaletteConfigAt#Provider[]
provideCommandPaletteConfigAt(...features: undefined)

Component-scoped palette configuration override. Features merge on top of the enclosing scope (root or a parent viewProviders).

@paramfeatures
withCommandPaletteLabels#CngxCommandPaletteConfigFeature
withCommandPaletteLabels(labels: Partial)

Override any subset of the palette's text labels. Unset labels keep the English defaults.

@paramlabelsPartial
withCommandPaletteTemplates#CngxCommandPaletteConfigFeature
withCommandPaletteTemplates(templates: CngxCommandPaletteTemplates)

Register global default slot templates. Merged over any already set; a per-instance *cngxCommand* slot still wins over these.

withKeyboardLegend(entries)

Replace the footer keyboard legend.

@paramentries
withPaletteShortcut(combo: string)

Set the combo that opens the palette (parsed via parseKeyCombo, e.g. 'mod+k', 'mod+shift+p'). Applies app-wide via provideCommandPaletteConfig or per-scope via provideCommandPaletteConfigAt. A per-instance [openShortcut] input still wins over this.

@paramcombostring
withResultCountFormatter#CngxCommandPaletteConfigFeature
withResultCountFormatter(formatter)

Replace the aria-live result-count formatter (e.g. for pluralisation in another locale).

@paramformatter

common/command/provide-commands.ts

injectCommands Injectv0.1.0
provideCommands Providerv0.1.0
injectCommands#Signal

Merges every registered CNGX_COMMAND_SOURCE into one reactive Signal<readonly CngxCommand[]>. The merge carries an explicit equal (length + per-command identity) so re-emitting an identical set - the same command references in the same order - does not produce a fresh array and cascade the downstream match computed().

Must run in an injection context.

provideCommands#Provider[]
provideCommands(...sources: undefined)

Registers one or more command sources. Each source becomes a multi entry on CNGX_COMMAND_SOURCE; call provideCommands from several app parts and injectCommands merges all of them.

Returns Provider[] (not EnvironmentProviders), so it registers at any injector scope - app root, a lazy route, or a component's providers / viewProviders - letting a route or component contribute a scoped command set without reaching for the raw CNGX_COMMAND_SOURCE multi-provider.

// app root
bootstrapApplication(AppComponent, { providers: [provideCommands(fileCommands)] });
// or component scope
@paramsources

core/theming/contrast.ts

injectContrast Injectv0.1.0
provideContrast Providerv0.1.0
injectContrast#WritableSignal

Read the app-wide contrast signal in an injection context. The returned signal is writable, so injectContrast().set('more') strengthens borders and muted text across the whole document reactively; set('auto') hands control back to the OS preference.

provideContrast#EnvironmentProviders
provideContrast(initial: CngxContrastPreference)

Install the contrast preference at app root and reflect it onto <html data-contrast>, driving the higher-contrast token overrides in contrast-tokens.css. Like the motion reflector, this one removes the attribute for 'auto' so the OS prefers-contrast: more media query stays in charge, and sets it for 'more' / 'normal'. The reflector is an effect (not afterNextRender) so it re-runs on every runtime injectContrast().set(...); the DOM write is wrapped in untracked() per the signal-architecture rules.

bootstrapApplication(AppComponent, {
  providers: [provideContrast('more')],
});
@paraminitialCngxContrastPreference= 'auto'

data-grid-accordion/config/inject-data-grid-accordion-config.ts

injectDataGridAccordionConfig Injectv0.1.0
injectDataGridAccordionConfig#CngxDataGridAccordionConfig

Convenience 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 Inject
injectDataSource#CngxDataSource
injectDataSource(data: Signal)

Factory function for CngxDataSource.

Must be called within an injection context (constructor or field initializer).

readonly dataSource = injectDataSource(this.items);
@paramdataSignal

core/theming/density.ts

injectDensity Injectv0.1.0
provideDensity Providerv0.1.0
injectDensity#WritableSignal

Read the app-wide density signal in an injection context. The returned signal is writable, so injectDensity().set('spacious') re-densifies the whole document reactively.

provideDensity#EnvironmentProviders
provideDensity(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')],
});
@paraminitialCngxDensityValue= 'comfortable'

dialog/config/dialog-config.ts

injectDialogConfig Injectv0.1.0
provideDialogConfig Providerv0.1.0
provideDialogConfigAt Providerv0.1.0
withDialogLabels Featurev0.1.0
injectDialogConfig#CngxDialogDefaults

Inject the currently-resolved dialog defaults. Safe in any injection context; falls back to the English bundle when nothing is provided.

provideDialogConfig#Provider[]
provideDialogConfig(...features: undefined)

Register app-wide dialog defaults composed from with* features.

@paramfeatures
provideDialogConfigAt#Provider[]
provideDialogConfigAt(...features: undefined)

Sub-tree variant - use in viewProviders so the defaults only apply to dialogs opened from descendants of this component.

@paramfeatures
withDialogLabels#CngxDialogConfigFeature
withDialogLabels(overrides: Partial)

Override dialog interaction strings. Unset keys keep the English default, and two calls merge rather than replace.

bootstrapApplication(AppComponent, {
  providers: [
    provideDialogConfig(
      withDialogLabels({ close: 'Dialog schließen', dragHandle: 'Dialog verschieben' }),
    ),
  ],
});
@paramoverridesPartial

core/bidi/direction.ts

injectDirection Injectv0.1.0
provideDirection Providerv0.1.0
provideDirectionAt Providerv0.1.0
injectDirection#Signal

Read the document writing-direction signal in an injection context. Returns 'rtl' under <html dir="rtl"> and re-signals on a runtime root flip. The signal is read-only: cngx reports the direction the DOM owns, it never sets it.

provideDirection#EnvironmentProviders
provideDirection(value: CngxDirection)

Override the direction the CNGX_DIRECTION signal reports with a fixed value, without touching documentElement.dir. This is the environment-scoped entry - bootstrap providers or route providers. Use it in tests or in SSR with a known locale. It installs no observer and never writes the DOM - dir stays the app's to set. For a per-subtree override in a component's viewProviders, use provideDirectionAt (element injectors reject the EnvironmentProviders this returns).

bootstrapApplication(AppComponent, {
  providers: [provideDirection('rtl')],
});

This overload takes a fixed value. To drive the reported direction from a reactive source (a router-derived locale signal, a settings store), override CNGX_DIRECTION directly with a useFactory returning your own Signal<CngxDirection>: { provide: CNGX_DIRECTION, useFactory: () => myLocaleDirection }.

@paramvalueCngxDirection
provideDirectionAt#Provider[]
provideDirectionAt(value: CngxDirection)

Element-injector twin of provideDirection. Returns Provider[] so it can go in a component's viewProviders (or providers) array, forcing the direction injectDirection reports for that DI subtree without touching documentElement.dir. Reach for it when a composite's keyboard-nav logic must honour a forced subtree direction; the environment-scoped provideDirection returns EnvironmentProviders, which an element injector rejects.

@paramvalueCngxDirection

interactive/error-registry/inject-error-aggregator.ts

injectErrorAggregator Injectv0.1.0
injectErrorAggregator#CngxErrorAggregatorContract
injectErrorAggregator(name?: string, sources?: Record, scope?: CngxErrorScopeContract, labels?: Record)

Creates a programmatic CngxErrorAggregatorContract, optionally registered under name in the ambient CngxErrorRegistry.

Positional arguments - NOT a config-options-bag - per Pillar 3 (Komposition statt Konfiguration). When name is set and a registry is provided, auto-registers and auto-deregisters on the surrounding DestroyRef.

@paramname?string

Optional registry name (skips registration when omitted).

@paramsources?Record

Initial source map (key → reactive boolean Signal). Each entry's signal is the live condition the aggregator reads on every recompute.

Optional scope override; when omitted, shouldShow collapses to hasError.

@paramlabels?Record

Optional human-readable labels by source key, used in errorLabels and announcement.

Must be called in an injection context (constructor, factory provider, runInInjectionContext).

Computed-graph derivation is delegated to createErrorAggregatorContract so the directive () and the function form share a single source of truth.

interactive/error-registry/inject-error-scope.ts

injectErrorScope Injectv0.1.0
injectErrorScope#CngxErrorScopeContract
injectErrorScope(name?: string)

Creates a programmatic CngxErrorScopeContract, optionally registered under name in the ambient CngxErrorRegistry.

Use when an error scope must exist without a DOM host - e.g. inside a route guard, an HTTP interceptor, or a service that drives error visibility programmatically. When name is set and a registry is provided in the host environment, the scope auto-registers and auto-deregisters on the surrounding DestroyRef.

Must be called in an injection context (constructor, factory provider, runInInjectionContext).

@paramname?string

feedback/config/feedback-i18n.ts

injectFeedbackI18n Inject
provideFeedbackI18n Providerv0.1.0
withFeedbackI18nLabels Feature
injectFeedbackI18n#CngxFeedbackI18n

Inject the resolved feedback i18n bundle.

provideFeedbackI18n#Provider
provideFeedbackI18n(overrides: CngxFeedbackI18nOverrides)

Provider for the feedback i18n bundle on its own, without the rest of provideFeedback(). This is the entry point a consumer-composed language file uses - provideFeedback() replaces the whole CNGX_FEEDBACK_CONFIG value, so routing a translation through it would reset unrelated feedback defaults the app set elsewhere.

withFeedbackI18nLabels#FeedbackFeature
withFeedbackI18nLabels(overrides: CngxFeedbackI18nOverrides)

Override the feedback region names from inside provideFeedback(). Unset keys keep the English default.

provideFeedback(
  withToasts(),
  withAlerts(),
  withFeedbackI18nLabels({ alertsRegionLabel: 'Hinweise' }),
)

Contributes the token through the feature's _providers, so - like withCloseIcon - a second call in the same provideFeedback() wins over the first rather than merging with it. Pass every key in one call.

forms/filter-builder/filter-builder.config.ts

injectFilterBuilderConfig Inject
provideFilterBuilderConfig Provider
provideFilterBuilderConfigAt Provider
withCaseInsensitiveStrings Feature
withDefaultOperators Feature
withFilterBuilderI18n Feature
withLogicOptions Feature
withMaxNestingDepth Feature
withNegation Feature
withOperators Feature
withTemplates Feature
injectFilterBuilderConfig#CngxFilterBuilderConfig

Inject-context helper that resolves CNGX_FILTER_BUILDER_CONFIG.

isNativeEditor(value: CngxFilterEditor)

Narrowing helper for CngxFilterEditor.

@paramvalueCngxFilterEditor
provideFilterBuilderConfig#EnvironmentProviders
provideFilterBuilderConfig(...features: undefined)

Root / environment-level config. Compose with withFilterBuilderI18n(...), withNegation(true), etc.

@paramfeatures
provideFilterBuilderConfigAt#Provider[]
provideFilterBuilderConfigAt(...features: undefined)

Component/route-level config - same shape as provideFilterBuilderConfig but for non-environment injectors.

@paramfeatures
withCaseInsensitiveStrings#CngxFilterBuilderConfigFeature
withCaseInsensitiveStrings(enabled: boolean)

Fold both sides of the builtin substring trio (contains / startsWith / endsWith) via toLowerCase() at evaluation time. Off by default - the trio is case-SENSITIVE unless enabled. eq / neq keep Object.is identity semantics regardless; custom operator definitions receive the flag through their evaluation context and decide themselves.

@paramenabledboolean
withDefaultOperators(operators: Readonly)

Extend or override the default operator lists keyed by editor type.

@paramoperatorsReadonly
withFilterBuilderI18n#CngxFilterBuilderConfigFeature
withFilterBuilderI18n(partial: Partial)

Override any subset of the i18n bundle. operators is shallow-merged.

@parampartialPartial
withLogicOptions(logics)

Restrict which logic operators (and / or / xor) appear in the group toggle.

@paramlogics
withMaxNestingDepth(depth: number)

Cap the nesting depth of the builder tree. Default 8.

@paramdepthnumber
withNegation(enabled: boolean)

Reveal the per-group negation toggle. Off by default.

@paramenabledboolean
withOperators(defs: Readonly)

Register operator definitions - the one surface where an operator's evaluation, its default picker label, and its valueless flag land together. Merges over the builtin map (and over earlier withOperators calls), so builtins stay evaluable and individual keys can be overridden. Label resolution per row is i18n.operators[key] ?? def.label ?? key.

Registration alone adds no picker entry: expose the key per field via FilterFieldDef.operators or per editor type via withDefaultOperators once an editor can serve it.

@paramdefsReadonly
withTemplates(templates: CngxFilterBuilderTemplates)

Register global template overrides - keyed fallback below per-instance content-child slots.

forms/filter-builder/filter-builder.tokens.ts

injectFilterEditors Inject
injectFilterEditors#ReadonlyMap

Inject 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.

forms/field/form-field.token.ts

injectFormFieldConfig Inject
provideErrorMessages Provider
provideFormField Provider
provideFormFieldAt Provider
withAutocompleteMappings Feature
withConstraintHints Feature
withErrorMessages Feature
withErrorStrategy Feature
withFieldSkin Feature
withNoSpellcheck Feature
withRequiredMarker Feature
injectFormFieldConfig#FormFieldConfig

Reads the resolved form-field config in an injection context.

Resolves nearest-first: a provideFormFieldAt on an ancestor component wins over the app-wide provideFormField, which wins over the library default (an empty config).

provideErrorMessages#EnvironmentProviders
provideErrorMessages(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.' })]
@parammessagesErrorMessageMap
provideFormField#EnvironmentProviders
provideFormField(...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. For a component sub-tree use provideFormFieldAt. 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(),
    ),
  ],
});
@paramfeatures
provideFormFieldAt#Provider[]
provideFormFieldAt(...features: undefined)

Component-scope twin of provideFormField. Returns plain Provider[] for a component's providers / viewProviders, so a sub-tree can carry its own form-field defaults without an environment injector.

The case this exists for is withFieldSkin: a table region that wants every filter control bare, or a dialog that wants fill, while the rest of the app keeps the root default.

Nearest-wins and replace, not merge - same semantics as the environment tier. A component-level call shadows the app-wide config for that sub-tree rather than deep-merging into it, so re-state any feature the sub-tree still needs.

@paramfeatures
withAutocompleteMappings#FormFieldFeature
withAutocompleteMappings(mappings: Record)

Override or extend the autocomplete mappings for CngxInput.

@parammappingsRecord

Additional 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',
}))
withConstraintHints#FormFieldFeature
withConstraintHints(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}`,
}))

rendering - the presenter computes the formatted strings; interpolate them from any child of <cngx-form-field> that injects CngxFormFieldPresenter ```ts

@paramformatters?Partial
withErrorMessages#FormFieldFeature
withErrorMessages(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.`,
}))
@parammessagesErrorMessageMap
withErrorStrategy#FormFieldFeature
withErrorStrategy(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),
))
@paramstrategy
withFieldSkin#FormFieldFeature
withFieldSkin(skin: CngxFieldSkin)

Set the app-wide default appearance for form fields and the controls inside them.

Reaches every host that composes CngxFieldSkinHost: input cngxInput, CngxAffixRow, and the nine select-family triggers. A [skin] on a surrounding cngx-form-field or a [cngxFieldSkin] on a single control still wins locally.

It does NOT reach a control that only opts in by attribute (CngxNumericInput, CngxInputMask, CngxInputFormat, CngxOtpSlot, input[cngxListboxSearch], input[cngxSearch], input[cngxDgaFilter]) - those read the config only once cngxFieldSkin is present on them.

'outline' is already the default and emits no attribute; pass it only to be explicit, or to opt back out of a fill default in a sub-tree.

provideFormField(withFieldSkin('fill'))
@paramskinCngxFieldSkin

The default appearance.

withNoSpellcheck#FormFieldFeature
withNoSpellcheck(fields)

Override or extend the set of field names where spellcheck="false" is auto-applied.

@paramfields

Additional field names to disable spellcheck for.

provideFormField(withNoSpellcheck(['iban', 'accountnumber', 'serialnumber']))
withRequiredMarker#FormFieldFeature
withRequiredMarker(marker: string)

Auto-render a required marker (e.g. *) inside every CngxLabel for required fields. Individual labels can opt out via [showRequired]="false".

@parammarkerstring= '*'

The marker text. Defaults to '*'.

provideFormField(withRequiredMarker())       // shows '*'
provideFormField(withRequiredMarker('(required)'))

forms/input/input-config.ts

injectInputConfig Inject
provideInputConfig Provider
provideInputConfigAt Provider
withCopyResetDelay Feature
withCustomTokens Feature
withDateFormats Feature
withFileMaxFiles Feature
withFileMaxSize Feature
withIbanPatterns Feature
withInputAriaLabels Feature
withMaskGuide Feature
withMaskPlaceholder Feature
withNumericDefaults Feature
withPhoneDefaultRegion Feature
withPhonePatterns Feature
withZipPatterns Feature
injectInputConfig#InputConfig

Inject the resolved input config in an injection context. Resolves without a provider (the token carries a root default factory), so every key left unset keeps its per-directive built-in default.

provideInputConfig#Provider
provideInputConfig(...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:

@paramfeatures
provideInputConfigAt#Provider[]
provideInputConfigAt(...features: undefined)

Component-scoped variant - use in viewProviders so the input config only applies to descendants of this component. Same feature merge and nearest-wins resolution as provideInputConfig.

@paramfeatures
withCopyResetDelay#InputConfigFeature
withCopyResetDelay(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 copyResetDelay config key.
provideInputConfig(withCopyResetDelay(3000));
@parammsnumber
withCustomTokens#InputConfigFeature
withCustomTokens(tokens: MaskTokenMap)

Registers mask tokens globally, beyond the built-in 0 9 A a *.

  • Each token maps one mask character to a pattern regex, an optional flag, and an optional per-char transform.
  • 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" />
@paramtokensMaskTokenMap
withDateFormats#InputConfigFeature
withDateFormats(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 date and datetime presets.
  • 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, then en-US.
  • Masks capture separators and group count only, NOT field order: en-US (MM/DD) and en-GB (DD/MM) share 00/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" />
@paramformatsRecord
withFileMaxFiles#InputConfigFeature
withFileMaxFiles(count: number)

Set the default maxFiles for CngxFileDrop.

@paramcountnumber
withFileMaxSize#InputConfigFeature
withFileMaxSize(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 fileMaxSize config key.
provideInputConfig(withFileMaxSize(5 * 1024 * 1024)); // 5 MiB
@parambytesnumber
withIbanPatterns#InputConfigFeature
withIbanPatterns(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 to AA00 0000 0000 0000 0000 00.
  • Consumer entries merge per key and win on collision.
  • Region comes from iban:<REGION> or LOCALE_ID.
provideInputConfig(withIbanPatterns({ BE: 'AA00 0000 0000 0000' }));
// <input cngxInputMask="iban:BE" />
@parampatternsRecord
withInputAriaLabels#InputConfigFeature
withInputAriaLabels(labels: Partial)

Overrides the built-in ARIA label strings the input directives announce.

  • Unset keys fall back to DEFAULT_INPUT_ARIA_LABELS per 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 -> CngxInputClear button label.
  • copySuccess / copyError -> CngxCopyValue live-region announcements.
  • otpGroup / otpSlot(index, length) / otpComplete -> CngxOtpInput group, per-slot labels, and completion announcement.
  • capsLockOn -> CngxCapsLock assertive warning.
  • passwordStrength(label) -> CngxPasswordStrength polite announcement.
  • inputRejected -> CngxInputFilter assertive rejection.
  • sensitiveReveal / sensitiveHide -> CngxSensitiveValue reveal/hide announcements.
  • ratingValue(value, max) -> CngxRating polite committed-value announcement.
  • ratingItem(step, max) -> CngxRating per-star radio aria-label.
  • phoneCountry -> CngxPhoneInput country-picker aria-label.
provideInputConfig(
  withInputAriaLabels({ clear: 'Leeren', copySuccess: 'Kopiert' }),
);
@paramlabelsPartial
withMaskGuide#InputConfigFeature
withMaskGuide(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, then config.maskGuide, then true.
provideInputConfig(withMaskGuide(false));
@paramguideboolean
withMaskPlaceholder#InputConfigFeature
withMaskPlaceholder(char: string)

Sets the global placeholder character CngxInputMask shows for unfilled slots in guide mode. Library default is '_'.

  • Resolution order: [placeholder] input, then config.maskPlaceholder, then '_'.
  • Sets the app-wide baseline for every masked input that does not bind its own [placeholder].
  • Also fills the aria-placeholder the mask exposes.
provideInputConfig(withMaskPlaceholder('·')); // middle dot
@paramcharstring
withNumericDefaults#InputConfigFeature
withNumericDefaults(defaults)

Sets app-wide defaults for CngxNumericInput.

  • locale overrides LOCALE_ID for numeric inputs only.
  • decimals and step set formatting (step default 1).
  • 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 / numericStep keys.
provideInputConfig(withNumericDefaults({ decimals: 2, locale: 'de-CH' }));
@paramdefaults
withPhoneDefaultRegion#InputConfigFeature
withPhoneDefaultRegion(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 CngxPhoneInput country region; an unknown region is ignored (the first country stays selected).
  • A per-instance [country] binding overrides it.
provideInputConfig(withPhoneDefaultRegion('AT'));
@paramregionstring
withPhonePatterns#InputConfigFeature
withPhonePatterns(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 (UK kept as a back-compat alias of GB).
  • 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 for libphonenumber-js when 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> or LOCALE_ID.
  • Pattern tokens: 0 = required digit, literals pass through.
provideInputConfig(withPhonePatterns({ LI: '+000 000 00 00' }));
// <input cngxInputMask="phone:LI" />
@parampatternsRecord
withZipPatterns#InputConfigFeature
withZipPatterns(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, with UK kept 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 to 00000.
  • Consumer entries merge per key and win on collision.
  • Region comes from zip:<REGION> or LOCALE_ID.
provideInputConfig(withZipPatterns({ NL: '0000 AA' }));
// <input cngxInputMask="zip:NL" />
@parampatternsRecord

interactive/group-host/group-host.ts

injectInteractiveGroupHost Injectv0.1.0
injectInteractiveGroupHost#CngxInteractiveGroupHost
injectInteractiveGroupHost(options: CngxInteractiveGroupHostOptions)

Shared scaffolding for the interactive form-control hosts (toggle, checkbox, the standalone interactive chip, and the value groups): stable uid, focus tracking, the field-host/aggregator error cascade, the aria-invalid gate, and [state]-wins-over-CNGX_STATEFUL async-state resolution for aria-busy.

Call from a field initializer (injection context required - the factory injects CNGX_FORM_FIELD_HOST, CNGX_ERROR_AGGREGATOR, and CNGX_STATEFUL, each optional). Declare the invalid model and the state input above the call site so the option signals exist when the initializer runs.

The factory is wiring, not a swap point: it deliberately has no DI factory token. Consumers authoring their own form-control atoms get the same field-host, aggregator, and stateful integration by calling it directly.

All host communication stays reactive (Pillar 2): errorState, ariaInvalid, and ariaBusy are computed() members of the returned bundle. resolvedState forwards a stable reference (the bound input value or the provider's state field, never a fresh literal), so Object.is dedupes without an equal fn.

private readonly groupHost = injectInteractiveGroupHost({
  uidPrefix: 'cngx-checkbox-group-',
  invalid: this.invalid,
  state: this.state,
});
readonly id = this.groupHost.id;
readonly focused = this.groupHost.focused;
readonly errorState = this.groupHost.errorState;
protected readonly ariaBusy = this.groupHost.ariaBusy;

data/async-registry/inject-latency-probe.ts

injectLatencyProbe Injectv0.1.0
injectLatencyProbe#CngxLatencyProbe

Bridges CngxAsyncRegistry into a CngxLatencyProbe measuring the registry's observed busy-envelope: how long the app was "anything loading" during the last in-flight window.

An app-shell indicator compares lastDuration() against CNGX_LOADING_CONFIG.spinnerVsSkeletonCutoff to pick a spinner (fast last aggregate) vs a skeleton (slow last aggregate) before the next load renders.

When the registry is absent (provideAsyncRegistry never called), injectAsyncRegistry() returns null, so the probe's source is permanently false: it is never busy and never throws.

Must run in an injection context.

A probe over CngxAsyncRegistry.isAnythingLoading.

core/utils/loading-config.ts

injectLoadingConfig Inject
provideLoadingConfig Provider
provideLoadingConfigAt Provider
resolveLoadingTreatment v0.1.0
withMinDwell Feature
withShowDelay Feature
withSpinnerVsSkeletonCutoff Feature
injectLoadingConfig#CngxLoadingConfig

Read the resolved CngxLoadingConfig. Runs in an injection context.

provideLoadingConfig#EnvironmentProviders
provideLoadingConfig(...features: undefined)

Register app-wide loading timing defaults.

bootstrapApplication(AppComponent, {
  providers: [provideLoadingConfig(withShowDelay(200), withMinDwell(600))],
});

Features resolve against CNGX_LOADING_DEFAULTS, not against an ancestor config: every field a with* feature does not touch carries the library default. The same rule applies to provideLoadingConfigAt, so a nested scope does not inherit app-wide overrides - restate them if the subtree should keep them.

@paramfeatures
provideLoadingConfigAt#Provider[]
provideLoadingConfigAt(...features: undefined)

Component-scope twin of provideLoadingConfig for viewProviders.

@paramfeatures
resolveLoadingConfig#CngxLoadingConfig
resolveLoadingConfig(features)
@paramfeatures
resolveLoadingTreatment#Exclude
resolveLoadingTreatment(requested: CngxLoadingTreatment, lastDuration, cutoff: number)

Pick a spinner or a skeleton from an observed busy-envelope duration.

Completes the latency-aware set: createLatencyProbe measures, CngxLoadingConfig.spinnerVsSkeletonCutoff sets the threshold, and this turns the pair into a treatment. Pure, so a surface can call it inside a computed() without a side effect.

Before any window has closed lastDuration is undefined and the answer is 'skeleton': a first load is the one with nothing cached and no measurement to argue otherwise, and a skeleton that resolves quickly reads better than a spinner that outstays a layout shift.

@paramrequestedCngxLoadingTreatment

The surface's loadingTreatment; anything but 'auto' wins verbatim.

@paramlastDuration

createLatencyProbe().lastDuration() - ms of the last completed window.

@paramcutoffnumber

injectLoadingConfig().spinnerVsSkeletonCutoff.

withMinDwell(ms: number)

Override the minimum dwell time for gated loading surfaces.

@parammsnumber
withShowDelay(ms: number)

Override the show delay for gated loading surfaces.

@parammsnumber
withSpinnerVsSkeletonCutoff#CngxLoadingConfigFeature
withSpinnerVsSkeletonCutoff(ms: number)

Override the spinner-vs-skeleton cutoff (ms) an app-shell indicator reads to pick a skeleton for waits longer than this and a spinner for shorter ones.

@parammsnumber

layout/observers/inject-media-query.ts

injectMediaQuery Injectv0.1.0
injectMediaQuery#Signal
injectMediaQuery(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.

@paramquerystring

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

injectMenuConfig Inject
provideMenuConfig Provider
provideMenuConfigAt Provider
injectMenuConfig#CngxMenuConfig

Resolves the CngxMenuConfig from the current injection scope. Must run inside an injection context.

provideMenuConfig#EnvironmentProviders
provideMenuConfig(...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),
    ),
  ],
});
@paramfeatures
provideMenuConfigAt#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).

@paramfeatures

interactive/menu/menu-item-core.ts

injectMenuItemCore Injectv0.1.0
injectMenuItemCore#CngxMenuItemCore
injectMenuItemCore(options: CngxMenuItemCoreOptions)

Shared interaction core of the three menu item variants (CngxMenuItem, CngxMenuItemCheckbox, CngxMenuItemRadio) - previously a tripled block. Owns the AD registration surface (id, isHighlighted, label), the disabled-aware click handler with the announcer's itemDisabled phrase, and the pointer-highlight handler; only the activation step stays per-variant via onActivate.

Call from a field initializer (injection context required - the core injects ElementRef, the optional surrounding CngxActiveDescendant, the announcer factory, and the menu config). Interaction contract, preserved verbatim from the variants: a disabled click announces and stops; without a surrounding active-descendant a click is a complete no-op (no onActivate either); on the AD path the order is highlightByValue -> onActivate -> activateCurrent.

core/theming/motion.ts

injectMotion Injectv0.1.0
provideMotion Providerv0.1.0
injectMotion#WritableSignal

Read the app-wide motion signal in an injection context. The returned signal is writable, so injectMotion().set('reduced') collapses motion across the whole document reactively; set('auto') hands control back to the OS preference.

provideMotion(initial: CngxMotionPreference)

Install the motion preference at app root and reflect it onto <html data-motion>, driving the reduced-motion safety net in motion-tokens.css. Unlike the density reflector, this one removes the attribute for 'auto' so the OS prefers-reduced-motion media query stays in charge, and sets it for 'reduced' / 'full'. The reflector is an effect (not afterNextRender) so it re-runs on every runtime injectMotion().set(...); the DOM write is wrapped in untracked() per the signal-architecture rules.

bootstrapApplication(AppComponent, {
  providers: [provideMotion('reduced')],
});
@paraminitialCngxMotionPreference= 'auto'

interactive/nav/nav-config.ts

injectNavConfig Inject
provideNavConfig Provider
provideNavConfigAt Provider
withNavAnimation Feature
withNavIndent Feature
withSingleAccordion Feature
injectNavConfig#Readonly>

Injects the resolved nav config, merging provided values with defaults. Must be called in an injection context.

provideNavConfig#Provider[]
provideNavConfig(...features: undefined)

Provides nav system configuration. Accepts withXxx() feature functions for composable setup.

Automatically includes CngxNavGroupRegistry when singleAccordion is enabled (either directly or via withSingleAccordion()).

providers: [provideNavConfig(withSingleAccordion(), withNavIndent(16))]
@paramfeatures
provideNavConfigAt#Provider[]
provideNavConfigAt(...features: undefined)

Component-scoped variant - use in viewProviders so the nav config only applies to descendants of this component. Same feature merge as provideNavConfig.

@paramfeatures
withNavAnimation#NavConfigFeature
withNavAnimation(ms: number)

Sets the animation duration for nav group expand/collapse in ms.

@parammsnumber
withNavIndent#NavConfigFeature
withNavIndent(px: number)

Sets the indentation per depth level in px.

@parampxnumber
withSingleAccordion#NavConfigFeature

Enables single-accordion mode - only one nav group can be open at a time.

layout/router/inject-query-param-sync.ts

injectQueryParamSync Injectv0.1.0
injectQueryParamSync#void
injectQueryParamSync(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 Inject
provideRecyclerI18n Provider
injectRecycler#CngxRecycler
injectRecycler(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,
});
@paramconfigRecyclerConfig
provideRecyclerI18n#
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.',
})]
@parami18nRecyclerI18n

sidenav/config/inject-sidenav-config.ts

injectSidenavConfig Injectv0.1.0
injectSidenavConfig#CngxSidenavConfig

Convenience accessor for the sidenav configuration cascade. Runs in injection context; resolves through the priority chain (per-instance Input -> provideSidenavConfigAt -> provideSidenavConfig -> library defaults). Equivalent to inject(CNGX_SIDENAV_CONFIG) - the helper exists so consumers don't import the token directly. Mirrors injectBreadcrumbConfig in @cngx/ui/breadcrumb.

export class CngxSidenav {
  private readonly cfg = injectSidenavConfig();
  readonly width = model<string>(this.cfg.dimensions?.width ?? '280px');
}

stat-card/config/inject-stat-card-config.ts

injectStatCardConfig Injectv0.1.0
injectStatCardConfig#CngxStatCardConfig

Convenience accessor for the stat-card configuration cascade. Runs in injection context; resolves through the priority chain (per-instance Input -> provideStatCardConfigAt -> provideStatCardConfig -> library defaults). Equivalent to inject(CNGX_STAT_CARD_CONFIG) - the helper exists so consumers don't import the token directly.

export class CngxStatCard {
  private readonly cfg = injectStatCardConfig();
  readonly errorText = input<string>(this.cfg.ariaLabels?.errorFallback ?? 'Could not load');
}

common/stepper/stepper-config.ts

injectStepperConfig Inject
provideStepperConfig Provider
provideStepperConfigAt Provider
withDotStepperDotTemplate Feature
withStepBadgeTemplate Feature
withStepBusySpinnerTemplate Feature
withStepErrorTemplate Feature
withStepGroupHeaderTemplate Feature
withStepIndicatorTemplate Feature
withStepperAriaLabels Feature
withStepperCommitMode Feature
withStepperConnectors Feature
withStepperDefaultOrientation Feature
withStepperDensity Feature
withStepperEmptyTemplate Feature
withStepperFallbackLabels Feature
withStepperGroupCollapse Feature
withStepperGroupCollapseSummary Feature
withStepperHeaderNavigation Feature
withStepperLinear Feature
withStepperMobileCollapse Feature
withStepperMobileIndicatorPosition Feature
withStepperMobileSwipe Feature
withStepperRouterSync Feature
withStepperSkin Feature
withStepRejectionTemplate Feature
injectStepperConfig#CngxStepperConfig

Inject the resolved stepper config in an injection context.

provideStepperConfig#EnvironmentProviders
provideStepperConfig(...features: undefined)

Root-level provider for the stepper config. Apply once in the application providers array. Sibling of provideTabsConfig / provideSelectConfig.

@paramfeatures
provideStepperConfigAt#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.

@paramfeatures
withDotStepperDotTemplate#CngxStepperConfigFeature
withDotStepperDotTemplate(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).

@paramtemplateTemplateRef
withStepBadgeTemplate#CngxStepperConfigFeature
withStepBadgeTemplate(template: TemplateRef)

Override the default *cngxStepBadge template app-wide.

@paramtemplateTemplateRef
withStepBusySpinnerTemplate#CngxStepperConfigFeature
withStepBusySpinnerTemplate(template: TemplateRef)

Override the default *cngxStepBusySpinner template app-wide.

@paramtemplateTemplateRef
withStepErrorTemplate#CngxStepperConfigFeature
withStepErrorTemplate(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.

@paramtemplateTemplateRef
withStepGroupHeaderTemplate#CngxStepperConfigFeature
withStepGroupHeaderTemplate(template: TemplateRef)

Override the default *cngxStepGroupHeader template app-wide.

@paramtemplateTemplateRef
withStepIndicatorTemplate#CngxStepperConfigFeature
withStepIndicatorTemplate(template: TemplateRef)

Override the default *cngxStepIndicator template app-wide. Per-instance directive still wins; this only moves the cascade middle tier.

@paramtemplateTemplateRef
withStepperAriaLabels#CngxStepperConfigFeature
withStepperAriaLabels(labels: CngxStepperAriaLabels)

Merge ARIA labels into the cascade. Keys not provided keep their library defaults; per-instance overrides on the stepper still win.

withStepperCommitMode#CngxStepperConfigFeature
withStepperCommitMode(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.

@parammode
withStepperConnectors#CngxStepperConfigFeature
withStepperConnectors(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.

@paramonboolean
withStepperDefaultOrientation#CngxStepperConfigFeature
withStepperDefaultOrientation(orientation)

Override the default orientation. Per-instance [orientation] Input still wins; this only moves the cascade default.

@paramorientation
withStepperDensity#CngxStepperConfigFeature
withStepperDensity(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.

withStepperEmptyTemplate#CngxStepperConfigFeature
withStepperEmptyTemplate(template: TemplateRef)

Override the default *cngxStepperEmpty template app-wide.

@paramtemplateTemplateRef
withStepperFallbackLabels#CngxStepperConfigFeature
withStepperFallbackLabels(labels: CngxStepperFallbackLabels)

Merge fallback role descriptions (groupRoleDescription / stepRoleDescription) into the cascade. These feed the strip's aria-roledescription attributes when the consumer supplies none.

withStepperGroupCollapse#CngxStepperConfigFeature
withStepperGroupCollapse(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.

withStepperGroupCollapseSummary#CngxStepperConfigFeature
withStepperGroupCollapseSummary(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'.

withStepperHeaderNavigation#CngxStepperConfigFeature
withStepperHeaderNavigation(mode: CngxStepperHeaderNavigation)

Set the app-wide default header-navigation policy for <cngx-stepper>. 'none' renders inert label headers (footer-only navigation); 'visited' (library default) keeps the headers as focusable buttons whose reachability folds into the linear axis. Per-instance [headerNavigation] Input still wins.

withStepperLinear#CngxStepperConfigFeature
withStepperLinear(linear: boolean)

Override the default linear-progression setting. Per-instance [linear] Input still wins.

@paramlinearboolean
withStepperMobileCollapse#CngxStepperConfigFeature
withStepperMobileCollapse(mode: CngxStepperMobileCollapse)

Configure the mobile auto-collapse target for <cngx-stepper>. Below 30rem of the stepper's own container width, 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.

withStepperMobileIndicatorPosition#CngxStepperConfigFeature
withStepperMobileIndicatorPosition(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.

withStepperMobileSwipe#CngxStepperConfigFeature
withStepperMobileSwipe(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.

@paramenabledboolean
withStepperRouterSync#CngxStepperConfigFeature
withStepperRouterSync(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').

@parammode
@paramparamstring= 'step'
withStepperSkin(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.

@paramskinCngxStepperSkin
withStepRejectionTemplate#CngxStepperConfigFeature
withStepRejectionTemplate(template: TemplateRef)

Override the default *cngxStepRejection template app-wide. Symmetric with the upcoming tabs cngxTabRejectionIcon (Phase 4).

@paramtemplateTemplateRef

stepper/i18n/stepper-i18n.ts

injectStepperI18n Inject
provideStepperI18n Provider
withStepperI18nLabels Feature
injectStepperI18n#CngxStepperI18n

Inject the resolved stepper i18n bundle in an injection context.

provideStepperI18n#Provider
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' }),
    ),
  ],
});
@paramfeatures
withStepperI18nLabels#CngxStepperI18nFeature
withStepperI18nLabels(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

injectTabsConfig Inject
provideTabsConfig Provider
provideTabsConfigAt Provider
withTabAddIconTemplate Feature
withTabBusySpinnerTemplate Feature
withTabCloseIconTemplate Feature
withTabErrorBadgeTemplate Feature
withTabIconTemplate Feature
withTabOverflowItemTemplate Feature
withTabOverflowMaxDeferMs Feature
withTabOverflowStabilizeMs Feature
withTabOverflowTriggerTemplate Feature
withTabRejectionIconTemplate Feature
withTabsAddable Feature
withTabsAlign Feature
withTabsAriaLabels Feature
withTabsClosable Feature
withTabsCommitMode Feature
withTabsDefaultOrientation Feature
withTabsFallbackLabels Feature
withTabsFitted Feature
withTabsFragmentSync Feature
withTabsIconLayout Feature
withTabsLinkAriaCurrent Feature
withTabsPanelMode Feature
withTabsRouteMatch Feature
withTabsRovingLoop Feature
withTabsSkin Feature
injectTabsConfig#CngxTabsConfig

Inject the resolved tabs config in an injection context.

provideTabsConfig#EnvironmentProviders
provideTabsConfig(...features: undefined)

Root-level provider. Apply once in bootstrapApplication / appConfig.providers. Returns EnvironmentProviders per the canonical cngx config-cascade signature.

@paramfeatures
provideTabsConfigAt#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.

@paramfeatures
resolveFeatures#CngxTabsConfig
resolveFeatures(features)
@paramfeatures
withTabAddIconTemplate#CngxTabsConfigFeature
withTabAddIconTemplate(template: TemplateRef)

App-wide override for the add-tab button glyph. Middle tier; per-instance *cngxTabAddIcon still wins.

@paramtemplateTemplateRef
withTabBusySpinnerTemplate#CngxTabsConfigFeature
withTabBusySpinnerTemplate(template: TemplateRef)

App-wide override for the commit-pending busy-spinner overlay. Middle tier; per-instance *cngxTabBusySpinner still wins. Sibling of withStepBusySpinnerTemplate.

@paramtemplateTemplateRef
withTabCloseIconTemplate#CngxTabsConfigFeature
withTabCloseIconTemplate(template: TemplateRef)

App-wide override for a tab's close-button glyph. Middle tier; per-instance *cngxTabCloseIcon still wins.

@paramtemplateTemplateRef
withTabErrorBadgeTemplate#CngxTabsConfigFeature
withTabErrorBadgeTemplate(template: TemplateRef)

App-wide override for the error-badge decoration. Middle tier; per-instance *cngxTabErrorBadge still wins. Sibling of the stepper family's withStepBadgeTemplate.

@paramtemplateTemplateRef
withTabIconTemplate#CngxTabsConfigFeature
withTabIconTemplate(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.

@paramtemplateTemplateRef
withTabOverflowItemTemplate#CngxTabsConfigFeature
withTabOverflowItemTemplate(template: TemplateRef)

App-wide override for each row inside the overflow popover.
Middle tier; per-instance *cngxTabOverflowItem still wins.

@paramtemplateTemplateRef
withTabOverflowMaxDeferMs#CngxTabsConfigFeature
withTabOverflowMaxDeferMs(ms: number)

Override the worst-case staleness ceiling on the visibility-map commit. See CngxTabsConfig.overflowMaxDeferMs.

@parammsnumber
withTabOverflowStabilizeMs#CngxTabsConfigFeature
withTabOverflowStabilizeMs(ms: number)

Override the IO-debounce window (ms). See CngxTabsConfig.overflowStabilizeMs.

@parammsnumber
withTabOverflowTriggerTemplate#CngxTabsConfigFeature
withTabOverflowTriggerTemplate(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()))]
@paramtemplateTemplateRef
withTabRejectionIconTemplate#CngxTabsConfigFeature
withTabRejectionIconTemplate(template: TemplateRef)

App-wide override for the rejection-icon decoration. Middle tier;
per-instance *cngxTabRejectionIcon still wins. Sibling of withStepRejectionTemplate.

@paramtemplateTemplateRef
withTabsAddable#CngxTabsConfigFeature
withTabsAddable(addable: boolean)

Set the app-wide default for whether the group renders an add-tab button.
Default false. Per-instance [addable] Input wins.

@paramaddableboolean
withTabsAlign(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.

@paramalignCngxTabAlign
withTabsAriaLabels#CngxTabsConfigFeature
withTabsAriaLabels(labels: CngxTabsAriaLabels)

Merge ARIA labels into the cascade. Keys not provided keep their library defaults; per-instance overrides on the tab group still win.

@paramlabelsCngxTabsAriaLabels
withTabsClosable#CngxTabsConfigFeature
withTabsClosable(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.

@paramclosableboolean
withTabsCommitMode#CngxTabsConfigFeature
withTabsCommitMode(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.

@parammode
withTabsDefaultOrientation#CngxTabsConfigFeature
withTabsDefaultOrientation(orientation)

Override the default orientation. Per-instance [orientation] still wins; this changes the cascade default only.

@paramorientation
withTabsFallbackLabels#CngxTabsConfigFeature
withTabsFallbackLabels(labels: CngxTabsFallbackLabels)

Merge text fallback labels (overflow trigger, busy / rejection text) into the cascade. Used when a slot directive is not present.

withTabsFitted(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".

@paramfittedboolean
withTabsFragmentSync#CngxTabsConfigFeature
withTabsFragmentSync(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.

@parammode
@paramparamstring= 'tab'
withTabsIconLayout#CngxTabsConfigFeature
withTabsIconLayout(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.

@paramlayoutCngxTabIconLayout
withTabsLinkAriaCurrent#CngxTabsConfigFeature
withTabsLinkAriaCurrent(token)

Set the aria-current token [cngxTabLink] publishes while active.
Default 'page'.

Use 'true' for a section nav whose links stay active across a whole subtree: the active link is then the current section, and page would tell AT it is the page being viewed. Scope it to one nav with provideTabsConfigAt in that component's viewProviders rather than setting it app-wide, unless every nav in the app is section-shaped. Per-link [ariaCurrent] still wins.

@paramtoken
withTabsPanelMode#CngxTabsConfigFeature
withTabsPanelMode(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).

Under 'lazy', a deep link mounts only the panel it names: both sync directives seed the active tab before the first panel render.

withTabsRouteMatch#CngxTabsConfigFeature
withTabsRouteMatch(mode)

Force one URL-matching mode app-wide for [cngxTabsRouteSync].
'suffix' treats every tab as a leaf route, 'prefix' as a section that stays active for URLs beneath it.

Reach for this only when the host-flavour default is wrong for your app. Unset, a <cngx-tab-nav> matches by prefix and a <cngx-tab-group> by suffix, which is what each markup implies; this feature overrides both. Per-instance [match] still wins.

Changes which policy runs, not the policies themselves - swap those via CNGX_TAB_URL_MATCH_STRATEGY.

@parammode
withTabsRovingLoop#CngxTabsConfigFeature
withTabsRovingLoop(loop: boolean)

Override whether roving-tabindex navigation loops from last back to first (and vice versa). Per-instance [loop] Input still wins.

@paramloopboolean
withTabsSkin(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.

@paramskinCngxTabsSkin

tabs/i18n/tabs-i18n.ts

injectTabsI18n Inject
provideTabsI18n Provider
withTabsI18nLabels Feature
injectTabsI18n#CngxTabsI18n

Inject the resolved tabs i18n bundle.

provideTabsI18n#Provider
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' }),
    ),
  ],
});
@paramfeatures
resolveI18nFeatures#CngxTabsI18n
resolveI18nFeatures(features)
@paramfeatures
withTabsI18nLabels#CngxTabsI18nFeature
withTabsI18nLabels(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.

@paramoverridesPartial

tag/config/inject-tag-config.ts

injectTagConfig Inject
injectTagConfig#CngxTagConfig

Convenience accessor for the tag-family configuration cascade. Runs in injection context; resolves through the priority chain (per-instance Input → provideTagConfigAtprovideTagConfig → 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',
  );
}

core/theming/text-scale.ts

injectTextScale Injectv0.1.0
provideTextScale Providerv0.1.0
injectTextScale#WritableSignal

Read the app-wide text-scale signal in an injection context. The returned signal is writable, so injectTextScale().set('lg') re-scales all root-relative text reactively.

provideTextScale#EnvironmentProviders
provideTextScale(initial: CngxTextScaleValue)

Install the text-scale preference at app root and reflect it onto <html data-text-size>, mirroring how density is applied by attribute. The reflector is an effect (not afterNextRender) so it re-runs on every runtime injectTextScale().set(...); the DOM write is wrapped in untracked() per the signal-architecture rules.

The default md rung is the identity multiplier, so provideTextScale() with no argument (or an md value) leaves every font-size pixel-identical to today.

bootstrapApplication(AppComponent, {
  providers: [provideTextScale('lg')],
});
@paraminitialCngxTextScaleValue= 'md'

toc/config/inject-toc-config.ts

injectTocConfig Injectv0.1.0
injectTocConfig#CngxTocConfig

Convenience accessor for the toc configuration cascade. Runs in injection context; resolves through the priority chain (per-instance Input -> provideTocConfigAt -> provideTocConfig -> library defaults). Equivalent to inject(CNGX_TOC_CONFIG) - the helper exists so consumers don't import the token directly. Mirrors injectBreadcrumbConfig.

export class CngxToc {
  private readonly cfg = injectTocConfig();
  protected readonly navLabel = computed(() => this.cfg.ariaLabels?.nav ?? 'On this page');
}

core/theming/touch-target.ts

injectTouchTargets Injectv0.1.0
provideTouchTargets Providerv0.1.0
injectTouchTargets#WritableSignal

Read the app-wide touch-target signal in an injection context. The returned signal is writable, so injectTouchTargets().set('on') pins the 44px floor across the whole document reactively.

provideTouchTargets#EnvironmentProviders
provideTouchTargets(initial: CngxTouchTargetValue)

Install the touch-target mode at app root and reflect it onto <html data-touch>. auto removes the attribute so the (any-pointer: coarse) media query derives the floor; on / off pin it. The reflector is an effect (not afterNextRender) so it re-runs on every runtime injectTouchTargets().set(...); the DOM write is wrapped in untracked() per the signal-architecture rules.

bootstrapApplication(AppComponent, {
  providers: [provideTouchTargets('on')],
});
@paraminitialCngxTouchTargetValue= 'auto'

interactive/tree-controller/tree-config.ts

injectTreeConfig Injectv0.1.0
provideTreeConfig Providerv0.1.0
provideTreeConfigAt Providerv0.1.0
withDefaultInitiallyExpanded Featurev0.1.0
withDefaultKeyFn Featurev0.1.0
withDefaultLabelFn Featurev0.1.0
withDefaultNodeIdFn Featurev0.1.0
withTreeCacheLimit Featurev0.1.0
injectTreeConfig#CngxTreeConfig

Inject the currently-resolved config. Safe to call in any injection context; returns {} if no provideTreeConfig is registered.

provideTreeConfig#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),
    ),
  ],
});
@paramfeatures
provideTreeConfigAt#Provider[]
provideTreeConfigAt(...features: undefined)

Sub-tree / component-scoped variant - use in viewProviders so the tree config only applies to descendants of this component.

@paramfeatures
withDefaultInitiallyExpanded#CngxTreeConfigFeature
withDefaultInitiallyExpanded(mode)

App-wide default initiallyExpanded seed.

@parammode
withDefaultKeyFn#CngxTreeConfigFeature
withDefaultKeyFn(fn)

App-wide default keyFn. Library default is identity.

@paramfn
withDefaultLabelFn#CngxTreeConfigFeature
withDefaultLabelFn(fn)

App-wide default labelFn. Library default is String(value).

@paramfn
withDefaultNodeIdFn#CngxTreeConfigFeature
withDefaultNodeIdFn(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.

@paramfn
withTreeCacheLimit#CngxTreeConfigFeature
withTreeCacheLimit(limit: number)

Bound the isExpanded(id) signal cache. See CngxTreeConfig.cacheLimit for the trade-off.

@paramlimitnumber

core/tokens/window.token.ts

injectWindow Inject
provideWindow Provider
injectWindow#Window | null

Injects the WINDOW token. Returns null in SSR contexts.

provideWindow#Provider
provideWindow(win)

Provides a custom Window reference (useful for testing or SSR).

@paramwin

forms/filter-builder/filter-builder-internal.ts

isExpressionIncomplete#boolean
isExpressionIncomplete(expression: FilterExpression, operators?: ReadonlyMap)

An expression is incomplete while the user has not finished it: missing field, missing operator, or an unfilled value (see isExpressionValueEmpty).

@paramexpressionFilterExpression
@paramoperators?ReadonlyMap
isExpressionValueEmpty#boolean
isExpressionValueEmpty(expression: FilterExpression, operators?: ReadonlyMap)

The one canonical emptiness test for an expression's value: null / undefined / '' count as unfilled, except for operators whose resolved definition is valueless (builtin isEmpty / isNotEmpty; consumer-registered valueless operators when the config's registry is passed). Shared by evaluateExpression's no-op guard, the presenter's errorState count, and the row's dashed-outline CSS state so the three surfaces can never drift apart again.

@paramexpressionFilterExpression
@paramoperators?ReadonlyMap

ui/toc/toc.component.ts

itemsEqual#boolean
itemsEqual(a, b)

Structural compare of two discovered outlines (id + label + nesting). Guards the discovered signal so a re-scan of unchanged headings keeps the same reference and never re-stamps the outline outlet.

@parama
@paramb

input/mask-presets/registry.ts

provideEagerMaskPresets Providerv0.1.0
loadTable#Promise>
loadTable(key: MaskPresetKey)
@paramkeyMaskPresetKey
provideEagerMaskPresets#EnvironmentProviders
provideEagerMaskPresets(...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 these
@paramkeys

projects/utils/version.ts

makeVersion#Version
makeVersion(full: string)

Creates a parsed Version from a semver string.

The pre-release tag and build metadata are split off before the dot parsing, so '0.2.0-rc.2' yields patch '0' and prerelease 'rc.2' instead of a mangled patch segment. Display-only - see Version.

@paramfullstring

core/utils/keyboard.util.ts

matchesKeyCombo v0.1.0
parseKeyCombo v0.1.0
matchesKeyCombo#boolean
matchesKeyCombo(event: KeyboardEvent, combo: KeyCombo, isMac: boolean)

Tests whether a KeyboardEvent matches a parsed KeyCombo.

A combo matches only a press carrying exactly the modifiers it names: a bare 'b' matches an unmodified b and rejects Cmd+B, Ctrl+B, Shift+B, Alt+B. mod resolves to the platform's primary modifier (Meta on macOS, Ctrl elsewhere) and does not constrain the other one, so 'mod+b' matches Cmd+B on macOS and Ctrl+B elsewhere. A single-character punctuation key is shift-agnostic unless the combo names shift, because the produced character already encodes the shift state (? requires Shift on most layouts): '?' matches the Shift+/ press that yields event.key === '?', while 'shift+?' still requires it and ctrl/meta/alt stay strict for every key.

Events fired during IME composition (event.isComposing) never match - keystrokes that build a composed character are text input, not shortcuts.

@parameventKeyboardEvent

The keyboard event to test.

@paramcomboKeyCombo

The parsed combo to match against.

@paramisMacboolean

Whether the current platform is macOS (affects mod resolution).

parseKeyCombo#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.

A trailing + names the literal plus key: 'mod++' parses as mod + '+' (the split's trailing empty segment is the key, not a dangling separator).

const combo = parseKeyCombo('mod+b');
// { key: 'b', ctrl: false, meta: false, mod: true, shift: false, alt: false }
@paramcombostring

core/utils/typeahead.util.ts

matchesTypeahead v0.1.0
matchesTypeahead#boolean
matchesTypeahead(label: string, term: string)

Case-insensitive prefix match between an item label and a typeahead query term - THE matching semantic of every cngx typeahead surface.

CngxActiveDescendant applies it to the rendered window on every buffered keystroke; CngxTreeSelect's expand-to-reveal miss search applies it to the full flat tree. Both MUST agree on what "matches" means - a revealed node that the rendered-window walk would then skip (or vice versa) breaks the type-to-find contract. Change the semantic here (e.g. locale folding, includes fallback) and every consumer moves together.

Both sides are lowercased, so pre-lowercased buffer terms and raw user input behave identically. An empty term matches every label (startsWith('') is true) - callers gate on a non-empty buffer.

matchesTypeahead('Postgres', 'p');   // true
matchesTypeahead('Postgres', 'PO');  // true
matchesTypeahead('Postgres', 'g');   // false - prefix, not substring
@paramlabelstring
@paramtermstring

core/utils/memo.util.ts

memoize v0.1.0
memoize#V
memoize(fn, options?: MemoizeOptions)

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

The default cache is unbounded and holds strong references to every key and value for the lifetime of the memoized closure. That is only safe when the key space is provably finite (locales, enum keys, a fixed config set). Any call site that derives keys from unbounded input - user text, entity ids, dates - must set MemoizeOptions.cacheLimit, otherwise the cache is a leak by design.

@paramfn
@paramoptions?MemoizeOptions

core/utils/uid.util.ts

nextUid v0.1.0
nextUid#string
nextUid(prefix: string)

Generates a unique ID string with the given prefix.

Each call returns a monotonically increasing ID: prefix-0, prefix-1, etc. Used internally for ARIA id attributes on dialogs, popovers, and tooltips.

The counter is module-level and per JS realm, so IDs are not stable across an SSR render and its client hydration - the server and the browser each count from 0 in their own creation order, and the ids can diverge. The impact is bounded because every cngx consumer re-binds the generated id through signals after hydration, but do not persist a nextUid value or use it as a cross-request key.

@paramprefixstring

paginator/segments/page-model.ts

pageWindow#PageWindow
pageWindow(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.

@paramcurrentnumber
@paramtotalnumber
@paramsiblingCountnumber= 1
@paramboundaryCountnumber= 1
pageWindowEqual#boolean
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.

@paramaPageWindow
@parambPageWindow
range#number[]
range(start: number, end: number)
@paramstartnumber
@paramendnumber

audio/event-mode/parse-bindings.ts

parseEventBindings#ParsedEventBindings
parseEventBindings(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].

@paramspecstring

ui/sidenav/sidenav.ts

parsePx#number | null
parsePx(value: string)

Numeric value of a px-unit (or unitless) CSS length, null for any other unit. rem/ch widths cannot be resolved without layout, and a guessed number would surface as a lying aria-valuenow.

@paramvaluestring

audio/status-mode/parse-status-bindings.ts

parseStatusBindings#ParsedStatusBindings
parseStatusBindings(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.

@paramspecstring

common/tabs/provide-cngx-tabs.ts

provideCngxTabs Providerv0.1.0
provideCngxTabsAt Providerv0.1.0
partitionFeatures#PartitionedFeatures
partitionFeatures(features)
@paramfeatures
provideCngxTabs#EnvironmentProviders
provideCngxTabs(...features: undefined)

Unified aggregator for tabs configuration.
Routes features by _target to provideTabsConfig and provideTabsI18n.
Sibling to

  • provideCngxMenu and
  • provideCngxSelect; apply once in the app providers array.
    Returns EnvironmentProviders; for
  • viewProviders use provideCngxTabsAt - opaque
  • EnvironmentProviders cannot live there.
bootstrapApplication(AppComponent, {
  providers: [
    provideCngxTabs(
      withTabsDefaultOrientation('vertical'),
      withTabsAriaLabels({ tabsRegion: 'Bereiche' }),
      withTabsI18nLabels({ tabsLabel: 'Bereiche', moreTabsLabel: (n) => `${n} mehr` }),
      withTabOverflowStabilizeMs(150),
    ),
  ],
});
@paramfeatures
provideCngxTabsAt#Provider[]
provideCngxTabsAt(...features: undefined)

Component-scoped twin of provideCngxTabs.
Returns Provider[] for viewProviders/providers; same dispatch semantics, different scope.

@paramfeatures

forms/validators/pattern-match.validator.ts

patternMatch#ValidatorFn
patternMatch(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.

Near-duplicate of Angular's Validators.pattern by design: the patternMatch error kind and { pattern, actual } payload line up with the withErrorMessages registry keys, where the built-in emits pattern: { requiredPattern, actualValue }.

@parampatternRegExp

accordion/config/provide-accordion-config.ts

provideAccordionConfig Providerv0.1.0
provideAccordionConfigAt Providerv0.1.0
provideAccordionConfig#EnvironmentProviders
provideAccordionConfig(...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):

  1. Per-instance Input binding.
  2. provideAccordionConfigAt(...) in a parent component's viewProviders.
  3. provideAccordionConfig(...) at the application root.
  4. 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'),
    ),
  ],
});
@paramfeatures
provideAccordionConfigAt#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.

@paramfeatures

data/async-registry/http-interceptor.ts

provideAsyncHttpObservability Provider
provideAsyncHttpObservability#EnvironmentProviders

Sets 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.0
provideBreadcrumbConfigAt Providerv0.1.0
provideBreadcrumbConfig#EnvironmentProviders
provideBreadcrumbConfig(...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):

  1. Per-instance Input binding.
  2. provideBreadcrumbConfigAt(...) in a parent component's viewProviders.
  3. provideBreadcrumbConfig(...) at the application root.
  4. 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'),
    ),
  ],
});
@paramfeatures
provideBreadcrumbConfigAt#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.

@paramfeatures

chart/i18n/chart-i18n.ts

provideChartI18n Provider
provideChartI18n#literal type
provideChartI18n(i18n: Partial)

Provider helper for custom chart i18n strings. Overrides merge over the English defaults, so a consumer localises only the keys they care about and every key added to CngxChartI18n later keeps its default instead of forcing an update. Passing a full object still works - it simply overrides every key.

providers: [provideChartI18n({
  summary: ({ trend, min, max, current }) =>
    `${trend === 'up' ? 'Aufwärtstrend' : 'Abwärtstrend'}. Min ${min}, Max ${max}, aktuell ${current}.`,
  dataTable: () => 'Datentabelle',
})]
@parami18nPartial

chart-panel/config/provide-chart-panel-config.ts

provideChartPanelConfig Providerv0.1.0
provideChartPanelConfigAt Providerv0.1.0
provideChartPanelConfig#EnvironmentProviders
provideChartPanelConfig(...features: undefined)

Application-root configuration cascade for the chart-panel.

bootstrapApplication(AppComponent, {
  providers: [provideChartPanelConfig(withChartPanelLegendPosition('top'))],
});
@paramfeatures
provideChartPanelConfigAt#Provider[]
provideChartPanelConfigAt(...features: undefined)

Component-scoped configuration cascade for the chart-panel. Injects the parent injector's value and deep-merges the supplied features on top.

@paramfeatures

chart/renderer/renderer-factory.ts

provideChartRenderer Providerv0.1.0
withChartRendererFactory Featurev0.1.0
withChartRendererThreshold Featurev0.1.0
provideChartRenderer#Provider[]
provideChartRenderer(...features: undefined)

Aggregate renderer-config features into a provider set. Place in a bootstrap providers array (app-wide) or a component's viewProviders (subtree scope).

provideChartRenderer(withChartRendererThreshold(2000))
@paramfeatures
withChartRendererFactory#CngxChartRendererFeature
withChartRendererFactory(fn: CngxChartRendererFactory)

Replace the renderer factory - e.g. to force one backend or add a third. Consumers wanting a heuristic swap supply a custom factory here (the heuristic-swap axis ships no dedicated token at v2).

withChartRendererThreshold#CngxChartRendererFeature
withChartRendererThreshold(n: number)

Override the SVG->Canvas auto-switch cutoff (default 500).

@paramnnumber

interactive/menu/provide-cngx-menu.ts

provideCngxMenu Provider
provideCngxMenu#EnvironmentProviders
provideCngxMenu(...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),
    ),
  ],
});
@paramfeatures

common/stepper/provide-cngx-stepper.ts

provideCngxStepper Provider
provideCngxStepperAt Provider
provideCngxStepper#EnvironmentProviders
provideCngxStepper(...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' }),
    ),
  ],
});
@paramfeatures
provideCngxStepperAt#Provider[]
provideCngxStepperAt(...features: undefined)

Component-scoped twin of provideCngxStepper. Spread into viewProviders or providers. Same dispatch semantics - only the provider scope differs.

@paramfeatures

data-grid-accordion/config/provide-data-grid-accordion-config.ts

provideDataGridAccordionConfig Providerv0.1.0
provideDataGridAccordionConfigAt Providerv0.1.0
provideDataGridAccordionConfig#EnvironmentProviders
provideDataGridAccordionConfig(...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):

  1. Per-instance [skin] Input binding.
  2. provideDataGridAccordionConfigAt(...) in a parent component's viewProviders.
  3. provideDataGridAccordionConfig(...) at the application root.
  4. Library defaults (CNGX_DATA_GRID_ACCORDION_DEFAULTS).
bootstrapApplication(AppComponent, {
  providers: [provideDataGridAccordionConfig(withDataGridSkin('ledger'))],
});
@paramfeatures
provideDataGridAccordionConfigAt#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.

@paramfeatures

dialog/dialog/dialog.service.ts

provideDialog Provider
provideDialog#Provider[]

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 Provider
provideDialogStack#Provider

Provide 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 Providerv0.1.0
provideEnvironment#Provider
provideEnvironment(env: Environment)

Provides an Environment value for the ENVIRONMENT token.

import { environment } from './environments/environment';

bootstrapApplication(AppComponent, {
  providers: [provideEnvironment(environment)],
});
@paramenvEnvironment

interactive/error-registry/provide-error-registry.ts

provideErrorRegistry Providerv0.1.0
withGlobalRevealOnSubmit Featurev0.1.0
withRevealOnNavigate Featurev0.1.0
provideErrorRegistry#EnvironmentProviders
provideErrorRegistry(...features: undefined)

Provides CngxErrorRegistry and optional global reveal triggers.

Composes feature flags via _apply partial-config functions, mirroring provideFormField's shape. Without features, registers the registry only; pair with withGlobalRevealOnSubmit or withRevealOnNavigate to install ambient reveal behaviour.

bootstrapApplication(AppComponent, {
  providers: [
    provideErrorRegistry(
      withGlobalRevealOnSubmit(),
      withRevealOnNavigate(),
    ),
  ],
});
@paramfeatures
withGlobalRevealOnSubmit#ErrorRegistryFeature

Reveals every registered scope on any DOM submit event.

Coarse-grained - affects every registered scope, every form. The feature installs a capture-phase document listener that calls registry.revealAll() on any submit anywhere in the document, even when the submit fires from a form unrelated to the scope of interest. Appropriate when the application treats every submit as a "show all errors now" trigger.

For finer-grained reveals (per-form, per-flow), skip this feature and call registry.reveal(name) from the consumer's submit handler instead - the scope-name registration path stays available without the global listener.

withRevealOnNavigate#ErrorRegistryFeature

Reveals every registered scope on Router.NavigationStart.

Requires provideRouter() (or equivalent) in the host environment. No-op when no Router is provided so the feature degrades gracefully in test harnesses or non-routed apps.

Fires before route guards. NavigationStart emits before CanActivate / CanDeactivate guards run, so a navigation that gets cancelled by a guard still leaves every scope revealed. Acceptable for the common pattern (reveal-all on attempted navigation) but surface the implication when wiring a guard that cancels based on form errors.

feedback/config/feedback-config.ts

provideFeedback Provider
withAlertIcons Feature
withAlerts Feature
withBanners Feature
withCloseIcon Feature
withLoadingDefaults Feature
withSpinnerTemplate Feature
withToasts Feature
provideFeedback#EnvironmentProviders
provideFeedback(...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 }),
    ),
  ],
});
@paramfeatures
withAlertIcons#FeedbackFeature
withAlertIcons(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,
}))
@paramiconsPartial
withAlerts(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.

@paramopts?

Optional alert defaults.

provideFeedback(
withToasts(),
withAlerts(),
)
withBanners#FeedbackFeature

Enable 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(),
)
withCloseIcon#FeedbackFeature
withCloseIcon(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))
@paramcomponentType
withLoadingDefaults#FeedbackFeature
withLoadingDefaults(opts)

Set default timing for all loading indicators and overlays.

Forwards into the Level-1 CNGX_LOADING_CONFIG cascade so the gated surfaces (indicator, overlay, async-container, skeleton) all read the same defaults. Prefer provideLoadingConfig(withShowDelay(...), withMinDwell(...)) from @cngx/core/utils directly; this feature is kept for back-compat.

Note: this forwarder provides a full CNGX_LOADING_CONFIG value, so under DI last-wins it replaces the whole config - including spinnerVsSkeletonCutoff, which resets to its default. Set the cutoff through provideLoadingConfig(withSpinnerVsSkeletonCutoff(...)) rather than combining it with this back-compat forwarder.

Individual components can still override via their [delay] and [minDwell] inputs.

provideFeedback(withLoadingDefaults({ delay: 300, minDuration: 600 }))
@paramopts
withSpinnerTemplate#FeedbackFeature
withSpinnerTemplate(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))
@paramcomponentType
withToasts(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.

@paramopts?

Optional toast defaults.

provideFeedback(
withToasts(),
withAlertIcons({ error: MyErrorIcon }),
)

common/popover/floating-fallback.ts

provideFloatingFallback Provider
provideFloatingFallback#Provider
provideFloatingFallback(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()]),
]
@paramcomputePositionComputePositionFn
@parammiddleware?

ui/overlay/overlay.service.ts

provideOverlay Provider
provideOverlay#EnvironmentProviders

Provides CngxOverlay as an environment-scoped service.

common/popover/popover-panel.config.ts

providePopoverPanel Provider
withArrow Feature
withArrowTemplate Feature
withAutoDismiss Feature
withCloseButton Feature
withCloseOnSuccess Feature
withDefaultVariant Feature
providePopoverPanel#Provider
providePopoverPanel(...features: undefined)

Provides configuration for CngxPopoverPanel instances.

providers: [
  providePopoverPanel(
    withAutoDismiss({ info: 5000, success: 3000 }),
    withCloseOnSuccess(300),
  ),
]
@paramfeatures
withArrow(show: unknown)

Show the arrow on all panels by default.

@paramshowunknown= true
withArrowTemplate#PopoverPanelFeature
withArrowTemplate(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)),
]
@paramtplTemplateRef
withAutoDismiss#PopoverPanelFeature
withAutoDismiss(timing: Record)

Auto-dismiss the panel after a timeout, per variant.

@paramtimingRecord
  • Map of variant name to dismiss duration in ms.
withAutoDismiss({ info: 5000, success: 3000 })
withCloseButton#PopoverPanelFeature
withCloseButton(show: unknown)

Show the close button on all panels by default.

@paramshowunknown= true
withCloseOnSuccess#PopoverPanelFeature
withCloseOnSuccess(delay: number)

Auto-close the panel after an async action in the footer succeeds.

@paramdelaynumber= 300
  • Delay in ms before closing. Defaults to 300.
withDefaultVariant#PopoverPanelFeature
withDefaultVariant(variant: string)

Set the default variant applied when no variant input is set.

@paramvariantstring

data/recycler/recycler-row.directive.ts

provideRecyclerPlaceholderRow Provider
provideRecyclerPlaceholderRow#Provider
provideRecyclerPlaceholderRow(template: TemplateRef)

Provides an app-wide default placeholder template for every *cngxRecyclerRow that supplies no placeholder: template of its own.

@paramtemplateTemplateRef

sidenav/config/provide-sidenav-config.ts

provideSidenavConfig Providerv0.1.0
provideSidenavConfigAt Providerv0.1.0
provideSidenavConfig#EnvironmentProviders
provideSidenavConfig(...features: undefined)

Application-root configuration cascade for the sidenav family. Pass any combination of withSidenavDimensions, withSidenavShortcut, withSidenavHoverDwell, and withSidenavRouterSync features in bootstrapApplication's providers array.

Resolution priority (high -> low):

  1. Per-instance Input binding.
  2. provideSidenavConfigAt(...) in a parent component's viewProviders.
  3. provideSidenavConfig(...) at the application root.
  4. Library defaults (CNGX_SIDENAV_DEFAULTS).

The provider deep-merges supplied features with the library defaults so consumers only declare keys they want to override.

bootstrapApplication(AppComponent, {
  providers: [
    provideSidenavConfig(
      withSidenavDimensions({ width: '320px' }),
      withSidenavShortcut('mod+b'),
    ),
  ],
});
@paramfeatures
provideSidenavConfigAt#Provider[]
provideSidenavConfigAt(...features: undefined)

Component-scoped configuration cascade for the sidenav family. Pass any combination of feature factories in a parent component's viewProviders array.

Unlike provideSidenavConfig (root-only), provideSidenavConfigAt injects the parent injector's CNGX_SIDENAV_CONFIG value (resolves through the priority chain - root provider, library defaults, or another provideSidenavConfigAt further up) and deep-merges the supplied features on top. Descendant sidenav instances see the merged config; sibling sub-trees keep the inherited value untouched.

@paramfeatures

stat-card/config/provide-stat-card-config.ts

provideStatCardConfig Providerv0.1.0
provideStatCardConfigAt Providerv0.1.0
provideStatCardConfig#EnvironmentProviders
provideStatCardConfig(...features: undefined)

Application-root configuration cascade for the stat-card. Pass any combination of withStatCardAriaLabels and withStatCardLoadingTreatment in bootstrapApplication's providers array.

bootstrapApplication(AppComponent, {
  providers: [
    provideStatCardConfig(
      withStatCardAriaLabels({ errorFallback: 'Kennzahl nicht verfügbar' }),
      withStatCardLoadingTreatment('skeleton'),
    ),
  ],
});
@paramfeatures
provideStatCardConfigAt#Provider[]
provideStatCardConfigAt(...features: undefined)

Component-scoped configuration cascade for the stat-card. Pass any combination of feature factories in a parent component's viewProviders.

Unlike provideStatCardConfig (root-only), this injects the parent injector's CNGX_STAT_CARD_CONFIG value and deep-merges the supplied features on top. Descendant tiles see the merged config; sibling sub-trees keep the inherited value untouched.

@paramfeatures

tag/config/provide-tag-config.ts

provideTagConfig Provider
provideTagConfigAt Provider
provideTagConfig#EnvironmentProviders
provideTagConfig(...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):

  1. Per-instance Input binding.
  2. provideTagConfigAt(...) in a parent component's viewProviders.
  3. provideTagConfig(...) at the application root.
  4. 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',
        },
      }),
    ),
  ],
});
@paramfeatures
provideTagConfigAt#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.

@paramfeatures

feedback/toast/toast.service.ts

provideToasts Provider

Standalone provider for the toast system - use when not using provideFeedback().

bootstrapApplication(AppComponent, {
  providers: [provideToasts()],
});

toc/config/provide-toc-config.ts

provideTocConfig Providerv0.1.0
provideTocConfigAt Providerv0.1.0
provideTocConfig#EnvironmentProviders
provideTocConfig(...features: undefined)

Application-root configuration cascade for the toc organism. Pass any combination of withTocAriaLabels, withTocScrollBehavior, withTocSpy, and withTocTemplates features in bootstrapApplication's providers array.

Resolution priority (high -> low):

  1. Per-instance Input binding.
  2. provideTocConfigAt(...) in a parent component's viewProviders.
  3. provideTocConfig(...) at the application root.
  4. Library defaults (CNGX_TOC_DEFAULTS).

The provider deep-merges supplied features with the library defaults so consumers only declare keys they want to override.

bootstrapApplication(AppComponent, {
  providers: [
    provideTocConfig(
      withTocAriaLabels({ nav: 'On this page' }),
      withTocScrollBehavior('smooth'),
    ),
  ],
});
@paramfeatures
provideTocConfigAt#Provider[]
provideTocConfigAt(...features: undefined)

Component-scoped configuration cascade for the toc organism. Pass any combination of feature factories in a parent component's viewProviders array.

Unlike provideTocConfig (root-only), provideTocConfigAt injects the parent injector's CNGX_TOC_CONFIG value (resolves through the priority chain) and deep-merges the supplied features on top. Descendant toc instances see the merged config; sibling sub-trees keep the inherited value.

@paramfeatures

data-display/treetable/treetable.token.ts

provideTreetable Provider
provideTreetableAt Provider
withCapitaliseHeaders Feature
withHighlightOnHover Feature
withTreetableLabels Feature
withTreetableTemplates Feature
provideTreetable#EnvironmentProviders
provideTreetable(...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,
}];
@paramfeatures
provideTreetableAt#Provider[]
provideTreetableAt(...features: undefined)

Component-scope twin of provideTreetable for viewProviders, so a subtree can carry its own treetable defaults:

@paramfeatures
resolveTreetableConfig#TreetableConfig
resolveTreetableConfig(features)
@paramfeatures
withCapitaliseHeaders#TreetableFeature
withCapitaliseHeaders(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).

@paramenabledunknown= true
  • Capitalise on/off. Default true.
withHighlightOnHover#TreetableFeature
withHighlightOnHover(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).

@paramenabledunknown= true
  • Hover-highlight on/off. Default true.
withTreetableLabels#TreetableFeature
withTreetableLabels(labels: Partial)

Feature: app-wide copy overrides for the treetable's built-in strings (TreetableLabels). Partial - unset keys keep the English library defaults. Later calls merge over earlier ones key-by-key.

provideTreetable(
  withTreetableLabels({
    loading: 'Wird geladen',
    errorFallback: 'Daten konnten nicht geladen werden',
  }),
);
@paramlabelsPartial
  • The keys to override.
withTreetableTemplates#TreetableFeature
withTreetableTemplates(templates: TreetableTemplates)

Feature: app-wide default templates for the async-state surfaces (TreetableTemplates). Partial - unset keys keep the built-in markup. A projected slot on the instance always wins over this tier.

Because the values are TemplateRefs, this feature is typically registered at a component or route scope where a template reference is in reach, not in bootstrapApplication.

@paramtemplatesTreetableTemplates
  • The surface templates to register.

interactive/async-status/async-status.directive.ts

reflectAsyncDisplayStatus#CngxAsyncDisplayStatus
reflectAsyncDisplayStatus(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.

@paramstate

forms/validators/required-true.validator.ts

requiredTrue#ValidatorFn

Validates that the control value is strictly true.

Returns { requiredTrue: { actual } } for any value other than true. Intended for checkbox agreement fields.

Near-duplicate of Angular's Validators.requiredTrue by design: the requiredTrue error kind and { actual } payload line up with the withErrorMessages registry keys.

data/async-state/resolve-view.ts

resolveAsyncView#AsyncView
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
@paramstatusAsyncStatus
@paramfirstLoadboolean
@paramemptyboolean

core/nav/step-resolver.ts

resolveBoundaryStep v0.1.0
resolveStepFrom v0.1.0
resolveBoundaryStep#number | null
resolveBoundaryStep(direction, scan: CngxStepScan)

Resolve the first (direction: 1) or last (direction: -1) enabled index of the range, or null when the range is empty or fully disabled. The Home/End counterpart of resolveStepFrom.

@paramdirection

1 scans from the start, -1 from the end.

@paramscanCngxStepScan

The range description (loop is irrelevant here).

The boundary-nearest enabled index, or null.

resolveStepFrom#number | null
resolveStepFrom(current: number, direction, scan: CngxStepScan)

Resolve the next enabled index from current in direction, skipping disabled indices, wrapping under loop, or null when no enabled index is reachable. The shared kernel of every next/prev-with-disabled-skip iterator (active-descendant, roving tabindex, stepper strip, chip-strip roving) - previously four private copies.

Arithmetic contract (ported exactly from the active-descendant strategy, the strictest of the four sites):

  • A negative current enters from the approach edge: -1 when stepping forward (first probe is index 0), count when stepping backward (first probe is count - 1).
  • current >= count is NOT remapped - every probe is bounds-checked, so the first out-of-range probe resolves to null without loop and wraps via modulo with it.
  • At most count probes run; a fully disabled range resolves to null.

Callers with clamp semantics (stop at the boundary, keep the current index) compose the resolver as resolveStepFrom(...) ?? current.

Like resolveInlineStep, this is a pure function, not a DI chokepoint - each strategy owns its isDisabledAt adapter and calls it directly. The direction axis (RTL arrow flip) stays in resolveInlineStep; this kernel owns only the index axis.

@paramcurrentnumber

The index to step from.

@paramdirection

1 steps forward, -1 backward.

@paramscanCngxStepScan

The range description.

The next enabled index, or null when none is reachable.

data-display/treetable/column-template.utils.ts

resolveCellTpl#TemplateRef | null
resolveCellTpl(col: string, tpls: Signal)

Resolves a custom cell template for the given column key, or returns null when no matching template is projected.

@paramcolstring
@paramtplsSignal
resolveHeaderTpl#TemplateRef | null
resolveHeaderTpl(col: string, tpls: Signal)

Resolves a custom header template for the given column key, or returns null when no matching template is projected.

@paramcolstring
@paramtplsSignal

data/recycler/scroll-observer.ts

resolveElement#HTMLElement | null
resolveElement(ref, doc: Document)
@paramref
@paramdocDocument

core/bidi/inline-nav.ts

resolveInlineArrowKey v0.1.0
resolveInlineStep v0.1.0
resolveInlineArrowKey#string
resolveInlineArrowKey(key: string, direction: CngxDirection)

Resolve a physical arrow key into its logical inline counterpart, honouring the writing direction. Under rtl the physical ArrowLeft and ArrowRight swap, so a dispatch site can route a physical key to the handler that owns its logical intent (inline-forward / inline-back). Any other key - vertical arrows, Home, End, letters - is returned verbatim.

Tier-2 semantic strategies (menu submenu open/close, tree expand/collapse) resolve the key here at the dispatch site, so the strategy contract stays logical and custom strategy overrides get RTL for free.

@paramkeystring

The physical KeyboardEvent.key value.

@paramdirectionCngxDirection

The document writing direction from injectDirection.

The logical key: swapped horizontal arrow under rtl, else key unchanged.

resolveInlineStep#"1" | unknown | null
resolveInlineStep(key: string, direction: CngxDirection)

Resolve a horizontal arrow key into an inline-axis index step, honouring the writing direction. Under ltr, ArrowRight advances (+1) and ArrowLeft retreats (-1); under rtl the two swap, because the first item in reading order sits on the right. Any non-horizontal key (including ArrowUp / ArrowDown / Home / End) returns null - the block axis is the caller's concern and never direction-aware.

This is the mechanical kernel every direction-aware keyboard strategy shares (roving, tabs, stepper strip, slider, reorder); it is a pure function, not a DI chokepoint - each strategy injects its own direction and calls this.

@paramkeystring

The KeyboardEvent.key value.

@paramdirectionCngxDirection

The document writing direction from injectDirection.

1 for inline-forward, -1 for inline-back, null for any other key.

forms/filter-builder/filter-builder-operators.ts

resolveOperatorDef(operator: string, operators?: ReadonlyMap)

Resolve an operator key against a registry, defaulting to the builtin map when none is supplied. Returns undefined for unknown keys - the evaluation path turns that into a one-shot dev warning plus a false result.

@paramoperatorstring
@paramoperators?ReadonlyMap

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

resolveParent#ParentResolution

common/stepper/status-label.ts

resolveStepperStatusLabel#string
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:

  1. success data-state -> statusLabels.done
  2. error data-state -> statusLabels.errored
  3. aria-current="step" -> statusLabels.inProgress
  4. otherwise -> statusLabels.upNext

Group nodes (non-step) return an empty string.

@paramnodeCngxStepNode
@parami18nCngxStepperI18n
@paramisActiveboolean

interactive/menu/menu.directive.ts

sameSubmenuItems#boolean
sameSubmenuItems(a, b)

Shallow identity/length equality for the submenu registry signal.

@parama
@paramb

data/paginate/bucket-paginate.directive.ts

setEquals#boolean
setEquals(a: ReadonlySet, b: ReadonlySet)

Two ReadonlySets are equal when they hold the same members.

@paramaReadonlySet
@parambReadonlySet

data/async-state/operators.ts

tapAsyncProgress#MonoTypeOperatorFunction
tapAsyncProgress(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();
}
@paramstatePick
tapAsyncState#MonoTypeOperatorFunction
tapAsyncState(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, read via the sink's isFirstLoad; an explicit options.status wins). On next: calls setSuccess(value). On error: calls setError(err) and re-throws (does not swallow). On teardown without a value (unsubscribe mid-flight, empty complete): resets the sink to idle so a cancelled request never leaves it busy.

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();
}
@paramstateAsyncStateSink
@paramoptions?
tapHttpAsyncState#OperatorFunction
tapHttpAsyncState(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:

  1. Set loading on subscribe
  2. Report upload/download progress via setProgress()
  3. Extract the response body on HttpEventType.Response
  4. Call setSuccess(body) with the extracted body
  5. 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
}
@paramstateAsyncStateSink
@paramoptions?

display/stat/stat-slots.ts

useStatSlot#string
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

withAccordionLabels Featurev0.1.0
withAccordionSkin Featurev0.1.0
withAccordionTemplates Featurev0.1.0
withDefaultHeadingLevel Featurev0.1.0
withAccordionLabels#CngxAccordionConfigFeature
withAccordionLabels(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.',
  }),
);
@parampayload
withAccordionSkin(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'));
withAccordionTemplates#CngxAccordionConfigFeature
withAccordionTemplates(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 }),
);
@parampayloadNonNullable
withDefaultHeadingLevel#CngxAccordionConfigFeature
withDefaultHeadingLevel(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));
@paramheadingLevelnumber

interactive/menu/menu-config-features.ts

withAriaLabels Feature
withCloseOnSelect Feature
withDismissOnBlur Feature
withDismissOnOutsideClick Feature
withDismissOnScroll Feature
withSubmenuCloseDelay Feature
withSubmenuOpenDelay Feature
withTypeaheadDebounce Feature
withAriaLabels(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.

@parampartialPartial
withCloseOnSelect#CngxMenuConfigFeature
withCloseOnSelect(close: boolean)

Whether activating a leaf item closes the menu. Default: true (menu semantics - distinct from listbox/combobox which stay open).

@paramcloseboolean
withDismissOnBlur#CngxMenuConfigFeature
withDismissOnBlur(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.

@paramdismissboolean
withDismissOnOutsideClick#CngxMenuConfigFeature
withDismissOnOutsideClick(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).

@paramdismissboolean
withDismissOnScroll#CngxMenuConfigFeature
withDismissOnScroll(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.

@paramdismissboolean
withSubmenuCloseDelay#CngxMenuConfigFeature
withSubmenuCloseDelay(ms: number)

Override the hover dwell (milliseconds) before a hover-opened submenu closes after the pointer has left both the parent item and the submenu popover. Feeds the leave delay of the hover intent CngxMenuItemSubmenu composes - the grace window for crossing the gap between parent and popover. Default: 150.

@parammsnumber
withSubmenuOpenDelay#CngxMenuConfigFeature
withSubmenuOpenDelay(ms: number)

Override the hover dwell (milliseconds) before a hovered submenu parent opens its submenu. Feeds the enter delay of the hover intent CngxMenuItemSubmenu composes - a pointer that merely sweeps across the parent never opens. Default: 0 (open on the first settled hover tick). Keyboard and click open paths are unaffected.

@parammsnumber
withTypeaheadDebounce#CngxMenuConfigFeature
withTypeaheadDebounce(ms: number)

Override the typeahead debounce window (milliseconds) for menu navigation. Default: 300.

@parammsnumber

data/async-registry/http-context.ts

withAsyncLabel Feature
withAsyncSkip Feature
withAsyncLabel#HttpContext
withAsyncLabel(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') });
@paramlabelstring
@paramcontextHttpContext= new HttpContext()
withAsyncSkip#HttpContext
withAsyncSkip(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() });
@paramcontextHttpContext= new HttpContext()

breadcrumb/config/features.ts

withBreadcrumbAriaLabels Featurev0.1.0
withBreadcrumbDataKey Featurev0.1.0
withBreadcrumbIconKey Featurev0.1.0
withBreadcrumbSkin Featurev0.1.0
withBreadcrumbAriaLabels#CngxBreadcrumbConfigFeature
withBreadcrumbAriaLabels(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' }),
);
@parampayloadNonNullable
withBreadcrumbDataKey#CngxBreadcrumbConfigFeature
withBreadcrumbDataKey(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'));
@paramdataKeystring
withBreadcrumbIconKey#CngxBreadcrumbConfigFeature
withBreadcrumbIconKey(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'));
@paramiconKeystring
withBreadcrumbSkin(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'));

chart-panel/config/features.ts

withChartPanelAriaLabels Featurev0.1.0
withChartPanelLegendPosition Featurev0.1.0
withChartPanelAriaLabels#CngxChartPanelConfigFeature
withChartPanelAriaLabels(labels: NonNullable)

Override the chart-panel's string fallbacks. Library defaults are English; this is the hook a localised app uses.

@paramlabelsNonNullable
withChartPanelLegendPosition#CngxChartPanelConfigFeature
withChartPanelLegendPosition(position: CngxChartPanelLegendPosition)

Move the cascade default for where a projected cngx-chart-legend sits. Per-instance [legendPosition] still wins.

interop/signals/with-cngx-async-state.ts

withCngxAsyncState Feature
withCngxAsyncState#

@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.

Key collisions are not guarded by cngx: the feature contributes ${key}State / ${key}Sink via withProps and relies on NgRx SignalStore's own dev-mode duplicate-prop warnings to surface a clash. Pick keys that do not shadow existing state or props on the store.

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({ error: () => {} }), // sink owns the failure; keep the global handler quiet
    };
  }),
);
// store.usersState.status(), store.usersState.data() - derived, no flags

forms/input/currency.feature.ts

withCurrency Feature
withCurrency(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>
@paramoptionsCurrencyOptions

data-grid-accordion/config/features.ts

withDataGridSkin Featurev0.1.0
withDataGridSkin(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'));

sidenav/config/features.ts

withSidenavDimensions Featurev0.1.0
withSidenavHoverDwell Featurev0.1.0
withSidenavRouterSync Featurev0.1.0
withSidenavShortcut Featurev0.1.0
withSidenavDimensions#CngxSidenavConfigFeature
withSidenavDimensions(payload: NonNullable)

Override the sidenav family's panel dimensions - width, miniWidth, minWidth, and maxWidth. Per-instance [width] / [miniWidth] / [minWidth] / [maxWidth] bindings still win over the cascade; this only moves the defaults. Partial payloads deep-merge, so pass only the keys you want to change.

provideSidenavConfig(withSidenavDimensions({ width: '320px', miniWidth: '64px' }));
@parampayloadNonNullable
withSidenavHoverDwell#CngxSidenavConfigFeature
withSidenavHoverDwell(payload: NonNullable)

Set the app-wide default mini expand-on-hover dwell - enterDelay (ms before the rail expands) and leaveDelay (ms before it collapses). Flows into the composed CngxHoverIntent via CNGX_HOVER_INTENT_DEFAULTS. Per-instance [enterDelay] / [leaveDelay] still win; the shipped literals (120 / 0) apply when neither is set. Partial payloads deep-merge, so pass only the key you want to change.

provideSidenavConfig(withSidenavHoverDwell({ enterDelay: 200, leaveDelay: 150 }));
@parampayloadNonNullable
withSidenavRouterSync#CngxSidenavConfigFeature
withSidenavRouterSync(payload: NonNullable)

Set the app-wide default query-param key for [cngxSidenavRouterSync] - the param the sidenav's opened state deep-links to. Per-instance [param] still wins; the 'nav' literal applies when neither is set.

provideSidenavConfig(withSidenavRouterSync({ param: 'menu' }));
@parampayloadNonNullable
withSidenavShortcut#CngxSidenavConfigFeature
withSidenavShortcut(combo: string)

Set the app-wide default keyboard shortcut to toggle the sidenav (e.g. 'mod+b', which resolves to ctrl on Windows/Linux and meta on macOS). Per-instance [shortcut] still wins.

provideSidenavConfig(withSidenavShortcut('mod+b'));
@paramcombostring

stat-card/config/features.ts

withStatCardAriaLabels Featurev0.1.0
withStatCardLoadingTreatment Featurev0.1.0
withStatCardAriaLabels#CngxStatCardConfigFeature
withStatCardAriaLabels(labels: NonNullable)

Override the stat-card's string fallbacks - the busy announcement, the first-load error message, and the stale-data note. Per-instance [busyLabel] / [errorText] / [staleText] bindings still win.

Library defaults are English; this is the hook a localised app uses.

provideStatCardConfig(
  withStatCardAriaLabels({ busy: 'Lädt', errorFallback: 'Nicht verfügbar' }),
);
@paramlabelsNonNullable
withStatCardLoadingTreatment#CngxStatCardConfigFeature
withStatCardLoadingTreatment(treatment: CngxLoadingTreatment)

Move the cascade default for what a tile renders while it loads. 'auto' (the library default) picks spinner vs skeleton from the latency the tile observed; 'spinner' and 'skeleton' pin it app-wide. Per-instance [loadingTreatment] still wins.

@paramtreatmentCngxLoadingTreatment

tag/config/features.ts

withTagColors Feature
withTagDefaults Feature
withTagGroupDefaults Feature
withTagSlots Feature
withTagColors(payload: NonNullable)

Register consumer-defined colour entries. When a tag's color resolves to a registered key, the directive emits the entry as element-level --cngx-tag-bg / --cngx-tag-color / --cngx-tag-border values (border wrapped as 1px solid <color> to mirror the predefined cascade). The entry applies uniformly across variants - config colours carry no per-variant surfaces.

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. Unregistered consumer keys emit nothing, so authoring plain [data-color="my-brand"] CSS remains a first-class alternative - the emitter never shadows it with an element style.

provideTagConfig(
  withTagColors({
    'my-brand': {
      bg: '#4f46e5',
      color: '#ffffff',
      border: 'transparent',
    },
  }),
);
@parampayloadNonNullable
withTagDefaults#CngxTagConfigFeature
withTagDefaults(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' }),
);
@parampayloadNonNullable
withTagGroupDefaults#CngxTagConfigFeature
withTagGroupDefaults(payload: NonNullable)

Override the default CngxTagGroup input values (gap, align, semanticList). Per-instance bindings still win.

provideTagConfig(
  withTagGroupDefaults({ gap: 'md', semanticList: true }),
);
@parampayloadNonNullable
withTagSlots(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.

@parampayloadNonNullable

toc/config/features.ts

withTocAriaLabels Featurev0.1.0
withTocScrollBehavior Featurev0.1.0
withTocSpy Featurev0.1.0
withTocTemplates Featurev0.1.0
withTocAriaLabels#CngxTocConfigFeature
withTocAriaLabels(payload: NonNullable)

Override the toc's ARIA-string fallback - the accessible name of the nav landmark that wraps the link list (default 'On this page').

provideTocConfig(withTocAriaLabels({ nav: 'Auf dieser Seite' }));
@parampayloadNonNullable
withTocScrollBehavior#CngxTocConfigFeature
withTocScrollBehavior(scrollBehavior: NonNullable)

Set the app-wide default scroll behaviour used when a link is activated. 'smooth' by default; the organism still swaps to 'auto' at runtime whenever prefers-reduced-motion: reduce matches, so this only chooses the motion-allowed behaviour.

provideTocConfig(withTocScrollBehavior('auto'));
@paramscrollBehaviorNonNullable
withTocSpy(payload: NonNullable)

Move the app-wide CngxScrollSpy defaults - the rootMargin and threshold the rail forwards to its internal spy. Per-instance [rootMargin] / [threshold] bindings still win; this only sets the cascade default (e.g. a fixed-header offset every toc should share).

provideTocConfig(withTocSpy({ rootMargin: '-80px 0px 0px 0px' }));
@parampayloadNonNullable
withTocTemplates#CngxTocConfigFeature
withTocTemplates(payload: NonNullable)

Register a global default item template. Resolves after a per-instance *cngxTocItem slot and before the built-in plain-label rendering, so every un-slotted toc renders the same custom item markup.

@parampayloadNonNullable