@cdevhub/ngx-tw 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, TemplateRef, Directive, input, contentChild, viewChild, ChangeDetectionStrategy, Component, ElementRef, computed, model, output, DestroyRef, contentChildren, viewChildren, linkedSignal, signal, afterNextRender, effect, untracked } from '@angular/core';
2
+ import { inject, TemplateRef, Directive, input, booleanAttribute, computed, contentChild, viewChild, ChangeDetectionStrategy, Component, ElementRef, model, output, DestroyRef, contentChildren, viewChildren, linkedSignal, signal, afterNextRender, effect, untracked } from '@angular/core';
3
3
  import { NgTemplateOutlet } from '@angular/common';
4
4
  import { LiveAnnouncer, FocusKeyManager } from '@angular/cdk/a11y';
5
5
  import { Directionality } from '@angular/cdk/bidi';
@@ -132,9 +132,29 @@ class TabComponent {
132
132
  /** When true, the tab cannot be selected and is skipped by keyboard navigation. Defaults to false. */
133
133
  disabled = input(false, /* @ts-ignore */
134
134
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
135
- /** When true, a dismiss control is rendered in the tab trigger. The control is pointer-only; the keyboard gesture is Delete on the focused tab. Defaults to false. */
136
- closable = input(false, /* @ts-ignore */
137
- ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
135
+ /**
136
+ * When true, a dismiss control is rendered in the tab trigger. The control is pointer-only; the keyboard gesture is Delete on the focused tab. Defaults to false.
137
+ *
138
+ * @deprecated Bind `dismissible` instead. `closable` was the library's only
139
+ * spelling of this concept — `alert`, `badge` and `toast` all call it
140
+ * `dismissible` — and one idea should not have two names. Both work; `closable`
141
+ * is removed in the next major.
142
+ */
143
+ closable = input(false, { ...(ngDevMode ? { debugName: "closable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
144
+ /**
145
+ * When true, a dismiss control is rendered in the tab trigger. The control is pointer-only; the keyboard gesture is Delete on the focused tab. Defaults to false.
146
+ *
147
+ * Canonical spelling, matching `alert`, `badge` and `toast`. Supersedes the
148
+ * deprecated `closable`; binding either one enables the control.
149
+ */
150
+ dismissible = input(false, { ...(ngDevMode ? { debugName: "dismissible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
151
+ /**
152
+ * @internal Whether a dismiss control should render, from either spelling.
153
+ * Both inputs default to `false`, so `||` is the correct reconciliation: it is
154
+ * true exactly when the consumer opted in through one name or the other.
155
+ */
156
+ isDismissible = computed(() => this.dismissible() || this.closable(), /* @ts-ignore */
157
+ ...(ngDevMode ? [{ debugName: "isDismissible" }] : /* istanbul ignore next */ []));
138
158
  /** When true, the tab panel content is only instantiated when the tab becomes active for the first time. Defaults to false. */
139
159
  lazy = input(false, /* @ts-ignore */
140
160
  ...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
@@ -148,7 +168,7 @@ class TabComponent {
148
168
  implicitContentTemplate = viewChild('implicitContent', /* @ts-ignore */
149
169
  ...(ngDevMode ? [{ debugName: "implicitContentTemplate" }] : /* istanbul ignore next */ []));
150
170
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
151
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: TabComponent, isStandalone: true, selector: "tw-tab", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: none" }, queries: [{ propertyName: "triggerTemplate", first: true, predicate: TabTriggerDirective, descendants: true, isSignal: true }, { propertyName: "contentTemplate", first: true, predicate: TabContentDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "implicitContentTemplate", first: true, predicate: ["implicitContent"], descendants: true, isSignal: true }], ngImport: i0, template: `
171
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: TabComponent, isStandalone: true, selector: "tw-tab", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display: none" }, queries: [{ propertyName: "triggerTemplate", first: true, predicate: TabTriggerDirective, descendants: true, isSignal: true }, { propertyName: "contentTemplate", first: true, predicate: TabContentDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "implicitContentTemplate", first: true, predicate: ["implicitContent"], descendants: true, isSignal: true }], ngImport: i0, template: `
152
172
  <ng-template #implicitContent>
153
173
  <ng-content />
154
174
  </ng-template>
@@ -168,7 +188,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
168
188
  'style': 'display: none',
169
189
  },
170
190
  }]
171
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], triggerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TabTriggerDirective), { isSignal: true }] }], contentTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TabContentDirective), { isSignal: true }] }], implicitContentTemplate: [{ type: i0.ViewChild, args: ['implicitContent', { isSignal: true }] }] } });
191
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], triggerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TabTriggerDirective), { isSignal: true }] }], contentTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TabContentDirective), { isSignal: true }] }], implicitContentTemplate: [{ type: i0.ViewChild, args: ['implicitContent', { isSignal: true }] }] } });
172
192
  // ── Tab trigger element wrapper ──
173
193
  class TabTriggerElementDirective {
174
194
  elementRef = inject((ElementRef));
@@ -441,7 +461,7 @@ class TabsComponent {
441
461
  // on the trigger advertises this gesture to assistive tech.
442
462
  if (event.key === 'Delete' && focusedIdx >= 0) {
443
463
  const tab = this.tabs()[focusedIdx];
444
- if (tab.closable() && !tab.disabled()) {
464
+ if (tab.isDismissible() && !tab.disabled()) {
445
465
  event.preventDefault();
446
466
  this.closeTab(tab, event);
447
467
  return;
@@ -595,13 +615,13 @@ class TabsComponent {
595
615
  this.scrollCleanup = () => el.removeEventListener('scroll', onScroll);
596
616
  }
597
617
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
598
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: TabsComponent, isStandalone: true, selector: "tw-tabs", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, fitted: { classPropertyName: "fitted", publicName: "fitted", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", closed: "closed" }, host: { properties: { "class": "rootClasses()" } }, queries: [{ propertyName: "tabs", predicate: TabComponent, isSignal: true }], viewQueries: [{ propertyName: "triggerElements", predicate: TabTriggerElementDirective, descendants: true, isSignal: true }, { propertyName: "tablistInnerRef", first: true, predicate: ["tablistInner"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Tablist container. WAI-ARIA tablist owns roving focus on the tab elements\n inside; the container itself relays keydown but isn't tabbable. -->\n<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n [class]=\"tablistClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Scroll start button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollStart()\"\n (click)=\"scrollStart()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n\n <!-- Scrollable trigger strip -->\n <div #tablistInner [class]=\"tablistInnerClasses()\" style=\"scrollbar-width: none\">\n @for (tab of tabs(); track tab.value()) {\n <!-- Trigger is rendered as <div role=\"tab\">, not <button>: activation is\n wired explicitly via (click) and the parent tablist's keydown\n (Enter / Space), and the roving tab stop comes from\n twTabTriggerElement. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"tab\"\n twTabTriggerElement\n [isFocusStop]=\"isTabFocusStop(tab.value())\"\n [isDisabled]=\"tab.disabled()\"\n [id]=\"getTabId(tab.value())\"\n [attr.aria-selected]=\"isTabActive(tab.value())\"\n [attr.aria-controls]=\"getPanelId(tab.value())\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [attr.aria-keyshortcuts]=\"tab.closable() && !tab.disabled() ? 'Delete' : null\"\n [class]=\"getTriggerClasses(tab.value(), isTabActive(tab.value()))\"\n [class.opacity-50]=\"tab.disabled()\"\n [class.pointer-events-none]=\"tab.disabled()\"\n (click)=\"selectTab(tab)\"\n >\n <!-- Custom trigger template or default label -->\n @if (tab.triggerTemplate(); as triggerDir) {\n <ng-container\n [ngTemplateOutlet]=\"triggerDir.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: { active: isTabActive(tab.value()), disabled: tab.disabled(), value: tab.value() } }\"\n />\n } @else {\n {{ tab.label() }}\n }\n\n <!-- Close affordance \u2014 a pointer-only span, deliberately NOT a button.\n Anything focusable inside an element carrying an interactive role\n fails axe's nested-interactive rule, and hoisting a real button\n out to a sibling only trades that for aria-required-children\n (a tablist may own nothing but tabs \u2014 both measured). The\n keyboard path is the APG-recommended Delete key on the tab\n itself, handled in onKeydown and advertised to assistive tech\n through aria-keyshortcuts on the trigger above. -->\n @if (tab.closable() && !tab.disabled()) {\n <span\n data-tw-tab-close\n aria-hidden=\"true\"\n [class]=\"closeButtonClasses()\"\n (click)=\"closeTab(tab, $event)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path d=\"M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z\"/>\n </svg>\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Scroll end button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollEnd()\"\n (click)=\"scrollEnd()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n</div>\n\n<!-- Tab panels -->\n@for (tab of tabs(); track tab.value()) {\n @if (shouldRenderPanel(tab)) {\n <div\n role=\"tabpanel\"\n [id]=\"getPanelId(tab.value())\"\n [attr.aria-labelledby]=\"getTabId(tab.value())\"\n [class]=\"panelClasses()\"\n [hidden]=\"!isTabActive(tab.value())\"\n tabindex=\"0\"\n >\n @if (tab.contentTemplate(); as contentDir) {\n <ng-container [ngTemplateOutlet]=\"contentDir.templateRef\" />\n } @else if (tab.implicitContentTemplate(); as implicitTpl) {\n <ng-container [ngTemplateOutlet]=\"implicitTpl\" />\n }\n </div>\n }\n}\n\n<!-- Hidden projection slot for tw-tab children -->\n<div style=\"display: none\">\n <ng-content />\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TabTriggerElementDirective, selector: "[twTabTriggerElement]", inputs: ["isFocusStop", "isDisabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
618
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: TabsComponent, isStandalone: true, selector: "tw-tabs", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, fitted: { classPropertyName: "fitted", publicName: "fitted", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", closed: "closed" }, host: { properties: { "class": "rootClasses()" } }, queries: [{ propertyName: "tabs", predicate: TabComponent, isSignal: true }], viewQueries: [{ propertyName: "triggerElements", predicate: TabTriggerElementDirective, descendants: true, isSignal: true }, { propertyName: "tablistInnerRef", first: true, predicate: ["tablistInner"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Tablist container. WAI-ARIA tablist owns roving focus on the tab elements\n inside; the container itself relays keydown but isn't tabbable. -->\n<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n [class]=\"tablistClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Scroll start button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollStart()\"\n (click)=\"scrollStart()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n\n <!-- Scrollable trigger strip -->\n <div #tablistInner [class]=\"tablistInnerClasses()\" style=\"scrollbar-width: none\">\n @for (tab of tabs(); track tab.value()) {\n <!-- Trigger is rendered as <div role=\"tab\">, not <button>: activation is\n wired explicitly via (click) and the parent tablist's keydown\n (Enter / Space), and the roving tab stop comes from\n twTabTriggerElement. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"tab\"\n twTabTriggerElement\n [isFocusStop]=\"isTabFocusStop(tab.value())\"\n [isDisabled]=\"tab.disabled()\"\n [id]=\"getTabId(tab.value())\"\n [attr.aria-selected]=\"isTabActive(tab.value())\"\n [attr.aria-controls]=\"getPanelId(tab.value())\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [attr.aria-keyshortcuts]=\"tab.isDismissible() && !tab.disabled() ? 'Delete' : null\"\n [class]=\"getTriggerClasses(tab.value(), isTabActive(tab.value()))\"\n [class.opacity-50]=\"tab.disabled()\"\n [class.pointer-events-none]=\"tab.disabled()\"\n (click)=\"selectTab(tab)\"\n >\n <!-- Custom trigger template or default label -->\n @if (tab.triggerTemplate(); as triggerDir) {\n <ng-container\n [ngTemplateOutlet]=\"triggerDir.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: { active: isTabActive(tab.value()), disabled: tab.disabled(), value: tab.value() } }\"\n />\n } @else {\n {{ tab.label() }}\n }\n\n <!-- Close affordance \u2014 a pointer-only span, deliberately NOT a button.\n Anything focusable inside an element carrying an interactive role\n fails axe's nested-interactive rule, and hoisting a real button\n out to a sibling only trades that for aria-required-children\n (a tablist may own nothing but tabs \u2014 both measured). The\n keyboard path is the APG-recommended Delete key on the tab\n itself, handled in onKeydown and advertised to assistive tech\n through aria-keyshortcuts on the trigger above. -->\n @if (tab.isDismissible() && !tab.disabled()) {\n <span\n data-tw-tab-close\n aria-hidden=\"true\"\n [class]=\"closeButtonClasses()\"\n (click)=\"closeTab(tab, $event)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path d=\"M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z\"/>\n </svg>\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Scroll end button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollEnd()\"\n (click)=\"scrollEnd()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n</div>\n\n<!-- Tab panels -->\n@for (tab of tabs(); track tab.value()) {\n @if (shouldRenderPanel(tab)) {\n <div\n role=\"tabpanel\"\n [id]=\"getPanelId(tab.value())\"\n [attr.aria-labelledby]=\"getTabId(tab.value())\"\n [class]=\"panelClasses()\"\n [hidden]=\"!isTabActive(tab.value())\"\n tabindex=\"0\"\n >\n @if (tab.contentTemplate(); as contentDir) {\n <ng-container [ngTemplateOutlet]=\"contentDir.templateRef\" />\n } @else if (tab.implicitContentTemplate(); as implicitTpl) {\n <ng-container [ngTemplateOutlet]=\"implicitTpl\" />\n }\n </div>\n }\n}\n\n<!-- Hidden projection slot for tw-tab children -->\n<div style=\"display: none\">\n <ng-content />\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TabTriggerElementDirective, selector: "[twTabTriggerElement]", inputs: ["isFocusStop", "isDisabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
599
619
  }
600
620
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: TabsComponent, decorators: [{
601
621
  type: Component,
602
622
  args: [{ selector: 'tw-tabs', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet, TabTriggerElementDirective], host: {
603
623
  '[class]': 'rootClasses()',
604
- }, template: "<!-- Tablist container. WAI-ARIA tablist owns roving focus on the tab elements\n inside; the container itself relays keydown but isn't tabbable. -->\n<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n [class]=\"tablistClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Scroll start button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollStart()\"\n (click)=\"scrollStart()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n\n <!-- Scrollable trigger strip -->\n <div #tablistInner [class]=\"tablistInnerClasses()\" style=\"scrollbar-width: none\">\n @for (tab of tabs(); track tab.value()) {\n <!-- Trigger is rendered as <div role=\"tab\">, not <button>: activation is\n wired explicitly via (click) and the parent tablist's keydown\n (Enter / Space), and the roving tab stop comes from\n twTabTriggerElement. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"tab\"\n twTabTriggerElement\n [isFocusStop]=\"isTabFocusStop(tab.value())\"\n [isDisabled]=\"tab.disabled()\"\n [id]=\"getTabId(tab.value())\"\n [attr.aria-selected]=\"isTabActive(tab.value())\"\n [attr.aria-controls]=\"getPanelId(tab.value())\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [attr.aria-keyshortcuts]=\"tab.closable() && !tab.disabled() ? 'Delete' : null\"\n [class]=\"getTriggerClasses(tab.value(), isTabActive(tab.value()))\"\n [class.opacity-50]=\"tab.disabled()\"\n [class.pointer-events-none]=\"tab.disabled()\"\n (click)=\"selectTab(tab)\"\n >\n <!-- Custom trigger template or default label -->\n @if (tab.triggerTemplate(); as triggerDir) {\n <ng-container\n [ngTemplateOutlet]=\"triggerDir.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: { active: isTabActive(tab.value()), disabled: tab.disabled(), value: tab.value() } }\"\n />\n } @else {\n {{ tab.label() }}\n }\n\n <!-- Close affordance \u2014 a pointer-only span, deliberately NOT a button.\n Anything focusable inside an element carrying an interactive role\n fails axe's nested-interactive rule, and hoisting a real button\n out to a sibling only trades that for aria-required-children\n (a tablist may own nothing but tabs \u2014 both measured). The\n keyboard path is the APG-recommended Delete key on the tab\n itself, handled in onKeydown and advertised to assistive tech\n through aria-keyshortcuts on the trigger above. -->\n @if (tab.closable() && !tab.disabled()) {\n <span\n data-tw-tab-close\n aria-hidden=\"true\"\n [class]=\"closeButtonClasses()\"\n (click)=\"closeTab(tab, $event)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path d=\"M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z\"/>\n </svg>\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Scroll end button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollEnd()\"\n (click)=\"scrollEnd()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n</div>\n\n<!-- Tab panels -->\n@for (tab of tabs(); track tab.value()) {\n @if (shouldRenderPanel(tab)) {\n <div\n role=\"tabpanel\"\n [id]=\"getPanelId(tab.value())\"\n [attr.aria-labelledby]=\"getTabId(tab.value())\"\n [class]=\"panelClasses()\"\n [hidden]=\"!isTabActive(tab.value())\"\n tabindex=\"0\"\n >\n @if (tab.contentTemplate(); as contentDir) {\n <ng-container [ngTemplateOutlet]=\"contentDir.templateRef\" />\n } @else if (tab.implicitContentTemplate(); as implicitTpl) {\n <ng-container [ngTemplateOutlet]=\"implicitTpl\" />\n }\n </div>\n }\n}\n\n<!-- Hidden projection slot for tw-tab children -->\n<div style=\"display: none\">\n <ng-content />\n</div>\n" }]
624
+ }, template: "<!-- Tablist container. WAI-ARIA tablist owns roving focus on the tab elements\n inside; the container itself relays keydown but isn't tabbable. -->\n<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n [class]=\"tablistClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Scroll start button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollStart()\"\n (click)=\"scrollStart()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n\n <!-- Scrollable trigger strip -->\n <div #tablistInner [class]=\"tablistInnerClasses()\" style=\"scrollbar-width: none\">\n @for (tab of tabs(); track tab.value()) {\n <!-- Trigger is rendered as <div role=\"tab\">, not <button>: activation is\n wired explicitly via (click) and the parent tablist's keydown\n (Enter / Space), and the roving tab stop comes from\n twTabTriggerElement. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"tab\"\n twTabTriggerElement\n [isFocusStop]=\"isTabFocusStop(tab.value())\"\n [isDisabled]=\"tab.disabled()\"\n [id]=\"getTabId(tab.value())\"\n [attr.aria-selected]=\"isTabActive(tab.value())\"\n [attr.aria-controls]=\"getPanelId(tab.value())\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [attr.aria-keyshortcuts]=\"tab.isDismissible() && !tab.disabled() ? 'Delete' : null\"\n [class]=\"getTriggerClasses(tab.value(), isTabActive(tab.value()))\"\n [class.opacity-50]=\"tab.disabled()\"\n [class.pointer-events-none]=\"tab.disabled()\"\n (click)=\"selectTab(tab)\"\n >\n <!-- Custom trigger template or default label -->\n @if (tab.triggerTemplate(); as triggerDir) {\n <ng-container\n [ngTemplateOutlet]=\"triggerDir.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: { active: isTabActive(tab.value()), disabled: tab.disabled(), value: tab.value() } }\"\n />\n } @else {\n {{ tab.label() }}\n }\n\n <!-- Close affordance \u2014 a pointer-only span, deliberately NOT a button.\n Anything focusable inside an element carrying an interactive role\n fails axe's nested-interactive rule, and hoisting a real button\n out to a sibling only trades that for aria-required-children\n (a tablist may own nothing but tabs \u2014 both measured). The\n keyboard path is the APG-recommended Delete key on the tab\n itself, handled in onKeydown and advertised to assistive tech\n through aria-keyshortcuts on the trigger above. -->\n @if (tab.isDismissible() && !tab.disabled()) {\n <span\n data-tw-tab-close\n aria-hidden=\"true\"\n [class]=\"closeButtonClasses()\"\n (click)=\"closeTab(tab, $event)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path d=\"M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z\"/>\n </svg>\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Scroll end button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollEnd()\"\n (click)=\"scrollEnd()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n</div>\n\n<!-- Tab panels -->\n@for (tab of tabs(); track tab.value()) {\n @if (shouldRenderPanel(tab)) {\n <div\n role=\"tabpanel\"\n [id]=\"getPanelId(tab.value())\"\n [attr.aria-labelledby]=\"getTabId(tab.value())\"\n [class]=\"panelClasses()\"\n [hidden]=\"!isTabActive(tab.value())\"\n tabindex=\"0\"\n >\n @if (tab.contentTemplate(); as contentDir) {\n <ng-container [ngTemplateOutlet]=\"contentDir.templateRef\" />\n } @else if (tab.implicitContentTemplate(); as implicitTpl) {\n <ng-container [ngTemplateOutlet]=\"implicitTpl\" />\n }\n </div>\n }\n}\n\n<!-- Hidden projection slot for tw-tab children -->\n<div style=\"display: none\">\n <ng-content />\n</div>\n" }]
605
625
  }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], fitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitted", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], closed: [{ type: i0.Output, args: ["closed"] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabComponent), { isSignal: true }] }], triggerElements: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => TabTriggerElementDirective), { isSignal: true }] }], tablistInnerRef: [{ type: i0.ViewChild, args: ['tablistInner', { isSignal: true }] }] } });
606
626
 
607
627
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"cdevhub-ngx-tw-tabs.mjs","sources":["../../../projects/ngx-tw/tabs/tabs.ts","../../../projects/ngx-tw/tabs/tabs.html","../../../projects/ngx-tw/tabs/cdevhub-ngx-tw-tabs.ts"],"sourcesContent":["import {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n contentChildren,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n linkedSignal,\n model,\n output,\n signal,\n TemplateRef,\n untracked,\n viewChild,\n viewChildren,\n type AfterViewInit,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { type FocusableOption, FocusKeyManager, LiveAnnouncer } from '@angular/cdk/a11y';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { tv } from 'tailwind-variants';\nimport { twMerge } from 'tailwind-merge';\nimport {\n getActiveTriggerClasses,\n getInactiveTriggerClasses,\n tabTriggerVariants,\n type TabTriggerVariant,\n type TwColor,\n type TwSize,\n} from '@cdevhub/ngx-tw/core';\n\n/** Visual style of the tab strip. */\nexport type TabsVariant = TabTriggerVariant;\n\n// ── tv() config (component-local slots only — trigger config is shared) ──\n\nconst tabsLayoutVariants = tv(\n {\n slots: {\n root: 'flex',\n tablist: 'relative flex shrink-0',\n tablistInner: 'flex overflow-x-auto',\n panel: 'min-w-0 flex-1',\n scrollButton:\n 'inline-flex items-center justify-center shrink-0 cursor-pointer transition-colors duration-normal motion-reduce:transition-none disabled:opacity-30 disabled:cursor-default hover:bg-surface-muted',\n // No focus-visible ring: the close control is pointer-only by design\n // (see the ARIA note in tabs.html), so a focus style would be dead code.\n closeButton:\n 'inline-flex items-center justify-center rounded-md cursor-pointer transition-colors duration-normal motion-reduce:transition-none hover:bg-surface-muted',\n },\n variants: {\n variant: {\n underline: {\n tablist: 'border-b border-border',\n tablistInner: 'gap-0',\n panel: 'pt-4',\n },\n enclosed: {\n tablist: 'border-b border-border',\n tablistInner: 'gap-0',\n panel: 'pt-4',\n },\n pill: {\n tablist: 'bg-surface-muted rounded-xl p-1',\n tablistInner: 'gap-1',\n panel: 'pt-4',\n },\n },\n // Square interactive-target scale: see CLAUDE.md \"Icon Sizing\".\n size: {\n xs: { scrollButton: 'size-5', closeButton: 'size-6' },\n sm: { scrollButton: 'size-5', closeButton: 'size-7' },\n md: { scrollButton: 'size-6', closeButton: 'size-8' },\n lg: { scrollButton: 'size-7', closeButton: 'size-9' },\n xl: { scrollButton: 'size-8', closeButton: 'size-9' },\n },\n orientation: {\n horizontal: {\n root: 'flex-col',\n tablist: '',\n tablistInner: 'flex-row',\n },\n vertical: {\n root: 'flex-row',\n tablistInner: 'flex-col overflow-y-auto overflow-x-hidden',\n },\n },\n },\n compoundVariants: [\n {\n variant: 'underline',\n orientation: 'vertical',\n class: {\n tablist: 'border-b-0 border-r border-border',\n panel: 'pt-0 pl-4',\n },\n },\n {\n variant: 'enclosed',\n orientation: 'vertical',\n class: {\n tablist: 'border-b-0 border-r border-border',\n panel: 'pt-0 pl-4',\n },\n },\n {\n variant: 'pill',\n orientation: 'vertical',\n class: {\n panel: 'pt-0 pl-4',\n },\n },\n ],\n defaultVariants: {\n variant: 'underline',\n size: 'md',\n orientation: 'horizontal',\n },\n },\n { twMerge: true },\n);\n\n// ── Per-variant/orientation trigger additions (vertical underline/enclosed\n// re-route the active border axis, hence these compound overrides). ──\n\nfunction getTriggerOrientationExtras(\n variant: TabsVariant,\n orientation: 'horizontal' | 'vertical',\n): string {\n if (orientation !== 'vertical') return '';\n switch (variant) {\n case 'underline':\n return 'border-b-0 -mb-0 border-r-2 border-transparent -mr-px';\n case 'enclosed':\n return '-mb-0 -mr-px';\n case 'pill':\n return '';\n }\n}\n\n// ── Directives ──\n\n@Directive({ selector: 'ng-template[twTabTrigger]' })\nexport class TabTriggerDirective {\n readonly templateRef = inject(TemplateRef);\n}\n\n@Directive({ selector: 'ng-template[twTabContent]' })\nexport class TabContentDirective {\n readonly templateRef = inject(TemplateRef);\n}\n\n// ── TabComponent ──\n\n@Component({\n selector: 'tw-tab',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ng-template #implicitContent>\n <ng-content />\n </ng-template>\n `,\n host: {\n 'style': 'display: none',\n },\n})\nexport class TabComponent {\n /** Unique identifier for this tab. Used to match the active tab value. */\n readonly value = input.required<string>();\n\n /** Plain text label shown in the trigger. Ignored when a custom trigger template is provided. Defaults to an empty string. */\n readonly label = input('');\n\n /** When true, the tab cannot be selected and is skipped by keyboard navigation. Defaults to false. */\n readonly disabled = input(false);\n\n /** When true, a dismiss control is rendered in the tab trigger. The control is pointer-only; the keyboard gesture is Delete on the focused tab. Defaults to false. */\n readonly closable = input(false);\n\n /** When true, the tab panel content is only instantiated when the tab becomes active for the first time. Defaults to false. */\n readonly lazy = input(false);\n\n /** @internal Custom trigger template, if provided. */\n readonly triggerTemplate = contentChild(TabTriggerDirective);\n\n /** @internal Custom content template, if provided via twTabContent. */\n readonly contentTemplate = contentChild(TabContentDirective);\n\n /** @internal Implicit content template wrapping ng-content. */\n readonly implicitContentTemplate = viewChild<TemplateRef<unknown>>('implicitContent');\n}\n\n// ── Tab trigger element wrapper ──\n\n@Directive({\n selector: '[twTabTriggerElement]',\n host: {\n '[attr.tabindex]': 'tabIndex()',\n },\n})\nexport class TabTriggerElementDirective implements FocusableOption {\n readonly elementRef = inject(ElementRef<HTMLElement>);\n\n /**\n * Whether this trigger owns the tablist's single tab stop. Drives\n * `tabindex`. Normally the active tab, but the parent falls back to the\n * first enabled tab when `value` matches nothing, so the tablist is never\n * left with zero tab stops. Defaults to `false`.\n */\n readonly isFocusStop = input(false);\n\n /** Whether this trigger is disabled. Read by `FocusKeyManager` so disabled triggers are skipped during arrow navigation. Defaults to `false`. */\n readonly isDisabled = input(false);\n\n readonly tabIndex = computed(() => this.isFocusStop() ? 0 : -1);\n\n /** Moves DOM focus to the trigger element. Called by `FocusKeyManager` during arrow-key navigation. */\n focus(): void {\n this.elementRef.nativeElement.focus();\n }\n\n /**\n * FocusableOption getter. NOTE: we cannot expose the `isDisabled` input\n * signal directly — the signal function itself is always truthy, which would\n * make FocusKeyManager treat every trigger as disabled. We resolve the\n * signal here so the manager skips the right items.\n */\n get disabled(): boolean {\n return this.isDisabled();\n }\n}\n\n// ── TabsComponent ──\n\nlet nextId = 0;\n\n@Component({\n selector: 'tw-tabs',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgTemplateOutlet, TabTriggerElementDirective],\n templateUrl: './tabs.html',\n host: {\n '[class]': 'rootClasses()',\n },\n})\nexport class TabsComponent implements AfterViewInit {\n /** Controls the visual style of the tab strip. Defaults to 'underline'. */\n readonly variant = input<TabsVariant>('underline');\n\n /** Sets the semantic color for active tab indicators and highlights. Defaults to 'primary'. */\n readonly color = input<TwColor>('primary');\n\n /** Controls padding, font size, and icon size of tab triggers. Defaults to 'md'. */\n readonly size = input<TwSize>('md');\n\n /** Layout direction of the tab strip. Defaults to 'horizontal'. */\n readonly orientation = input<'horizontal' | 'vertical'>('horizontal');\n\n /** When true, tab triggers stretch to fill the available width equally. Defaults to false. */\n readonly fitted = input(false);\n\n /** The value of the currently active tab. Two-way bound. Updates when the user selects a tab. Defaults to an empty string. */\n readonly value = model<string>('');\n\n /** Fires when a closable tab is dismissed, either by clicking its close control or by pressing Delete while the tab has focus. Payload is the tab's value. */\n readonly closed = output<string>();\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly liveAnnouncer = inject(LiveAnnouncer);\n private readonly directionality = inject(Directionality, { optional: true });\n\n /**\n * Live layout direction, read straight from CDK's `Directionality.valueSignal`\n * (CDK 22+) so the key-manager effect re-runs on a runtime `dir` flip with no\n * manual subscription mirror. Without a live direction the tablist hardcodes\n * 'ltr' and ArrowRight moves to the next tab in DOM order but the *previous*\n * tab visually in an RTL locale. CdkStepper and `tw-split` already honour\n * Directionality; `tw-paginator` reads `valueSignal` the same way.\n */\n private readonly layoutDirection = computed<'ltr' | 'rtl'>(\n () => this.directionality?.valueSignal() ?? 'ltr',\n );\n\n /** @internal */\n readonly tabs = contentChildren(TabComponent);\n\n /** @internal */\n readonly triggerElements = viewChildren(TabTriggerElementDirective);\n\n /** @internal */\n readonly tablistInnerRef = viewChild<ElementRef<HTMLElement>>('tablistInner');\n\n private readonly componentId = `tw-tabs-${nextId++}`;\n\n // ── Active tab tracking ──\n\n readonly activeValue = linkedSignal(() => this.value());\n\n // ── Lazy tab tracking — Set of tab values that have been activated at least once ──\n readonly activatedTabs = signal(new Set<string>());\n\n // ── Scroll overflow signals ──\n readonly canScrollStart = signal(false);\n readonly canScrollEnd = signal(false);\n\n private resizeObserver: ResizeObserver | null = null;\n private scrollCleanup: (() => void) | null = null;\n\n // ── Variant classes ──\n\n private readonly layoutResult = computed(() =>\n tabsLayoutVariants({\n variant: this.variant(),\n size: this.size(),\n orientation: this.orientation(),\n }),\n );\n\n private readonly triggerResult = computed(() =>\n tabTriggerVariants({\n variant: this.variant(),\n color: this.color(),\n size: this.size(),\n fitted: this.fitted(),\n }),\n );\n\n readonly rootClasses = computed(() => this.layoutResult().root());\n readonly tablistClasses = computed(() => this.layoutResult().tablist());\n readonly tablistInnerClasses = computed(() => this.layoutResult().tablistInner());\n readonly baseTriggerClasses = computed(() => this.triggerResult().trigger());\n readonly panelClasses = computed(() => this.layoutResult().panel());\n readonly scrollButtonClasses = computed(() => this.layoutResult().scrollButton());\n readonly closeButtonClasses = computed(() => this.layoutResult().closeButton());\n\n // ── Per-trigger active/inactive class computation ──\n\n /** @internal Get combined trigger classes for a given tab. */\n getTriggerClasses(_tabValue: string, isActive: boolean): string {\n const base = this.baseTriggerClasses();\n const orientationExtras = getTriggerOrientationExtras(this.variant(), this.orientation());\n const state = isActive\n ? getActiveTriggerClasses(this.variant(), this.color(), this.orientation())\n : getInactiveTriggerClasses(this.variant());\n return twMerge(base, orientationExtras, state);\n }\n\n // ── ID generation ──\n /** Builds the DOM id for a tab trigger, used by the panel's `aria-labelledby`. */\n getTabId(tabValue: string): string {\n return `${this.componentId}-tab-${tabValue}`;\n }\n\n /** Builds the DOM id for a tab panel, used by the trigger's `aria-controls`. */\n getPanelId(tabValue: string): string {\n return `${this.componentId}-panel-${tabValue}`;\n }\n\n // ── Tab visibility ──\n\n /** Whether the tab with the given value is the currently active one. */\n isTabActive(tabValue: string): boolean {\n return this.activeValue() === tabValue;\n }\n\n /**\n * @internal Value of the tab that owns the tablist's single tab stop.\n *\n * The active tab whenever it exists and is enabled. When `value` matches no\n * tab — a stale id, a value cleared after init, or an active tab that is\n * disabled — the first enabled tab takes the tab stop instead (standard\n * roving-tabindex recovery). Without the fallback every trigger renders\n * `tabindex=\"-1\"` and the whole tablist silently leaves the tab order\n * (WCAG SC 2.1.1).\n */\n readonly focusStopValue = computed<string | null>(() => {\n const tabs = this.tabs();\n const active = this.activeValue();\n const activeTab = tabs.find(t => t.value() === active && !t.disabled());\n if (activeTab) return activeTab.value();\n return tabs.find(t => !t.disabled())?.value() ?? null;\n });\n\n /** Whether the tab with the given value owns the tablist's single tab stop. */\n isTabFocusStop(tabValue: string): boolean {\n return this.focusStopValue() === tabValue;\n }\n\n /** Whether a tab's panel content should be in the DOM. Always true for eager tabs; for lazy tabs, true only once the tab has been activated at least once. */\n shouldRenderPanel(tab: TabComponent): boolean {\n const value = tab.value();\n if (!tab.lazy()) {\n return true;\n }\n // Lazy: render only if it has been activated at least once\n return this.activatedTabs().has(value);\n }\n\n // ── Selection ──\n\n /** Activates a tab, updating the two-way bound `value`, marking it rendered for lazy panels, and announcing the change via `LiveAnnouncer`. Ignored when the tab is disabled. */\n selectTab(tab: TabComponent): void {\n if (tab.disabled()) return;\n const val = tab.value();\n this.activeValue.set(val);\n this.value.set(val);\n\n // Mark as activated for lazy tabs\n this.activatedTabs.update(set => {\n const next = new Set(set);\n next.add(val);\n return next;\n });\n\n // Announce to screen readers\n const tabsArr = this.tabs();\n const idx = tabsArr.findIndex(t => t.value() === val);\n this.liveAnnouncer.announce(\n `${tab.label() || val} tab, ${idx + 1} of ${tabsArr.length}`,\n );\n }\n\n /** Closes a closable tab, emitting `closed` with its value and moving activation to the nearest enabled sibling when the closed tab was active. */\n closeTab(tab: TabComponent, event: Event): void {\n event.stopPropagation();\n if (event instanceof KeyboardEvent) {\n event.preventDefault();\n }\n const val = tab.value();\n this.closed.emit(val);\n\n // If closing the active tab, activate nearest enabled sibling\n if (this.isTabActive(val)) {\n const tabsArr = this.tabs();\n const idx = tabsArr.findIndex(t => t.value() === val);\n const next = this.findNearestEnabledTab(tabsArr, idx);\n if (next) {\n this.selectTab(next);\n }\n }\n }\n\n private findNearestEnabledTab(tabs: readonly TabComponent[], fromIndex: number): TabComponent | null {\n // Search forward first, then backward\n for (let i = fromIndex + 1; i < tabs.length; i++) {\n if (!tabs[i].disabled()) return tabs[i];\n }\n for (let i = fromIndex - 1; i >= 0; i--) {\n if (!tabs[i].disabled()) return tabs[i];\n }\n return null;\n }\n\n // ── Keyboard navigation (CDK FocusKeyManager) ──\n\n private keyManager: FocusKeyManager<TabTriggerElementDirective> | null = null;\n\n /**\n * @internal Handles keyboard navigation. The manager handles Arrow/Home/End;\n * we layer Enter/Space to select the focused tab (the wrapper is a `<div role=\"tab\">`\n * so we lose the native button activation) and Delete to close a closable tab.\n */\n onKeydown(event: KeyboardEvent): void {\n if (!this.keyManager) return;\n const triggers = this.triggerElements();\n if (triggers.length === 0) return;\n\n // Sync active item with the trigger DOM-focused by the user. We accept\n // either `event.target` (which works under shadow DOM) or the currently\n // active tab's index as a safety net (e.g. when the event was dispatched\n // on the tablist container itself, not a trigger).\n const focusedIdx = triggers.findIndex(\n t => t.elementRef.nativeElement === event.target,\n );\n const activeIdx = focusedIdx >= 0\n ? focusedIdx\n : this.tabs().findIndex(t => t.value() === this.activeValue());\n if (activeIdx >= 0 && activeIdx !== this.keyManager.activeItemIndex) {\n this.keyManager.setActiveItem(activeIdx);\n }\n\n // Enter / Space select the focused tab (we no longer get this for free\n // because the trigger is a div, not a button — see DOM restructure below).\n if (event.key === 'Enter' || event.key === ' ') {\n if (focusedIdx >= 0) {\n event.preventDefault();\n this.selectTab(this.tabs()[focusedIdx]);\n }\n return;\n }\n\n // Delete on a closable tab dismisses it (APG-recommended). This is the\n // ONLY keyboard path: the close control cannot be focusable without\n // failing axe's nested-interactive rule, so `aria-keyshortcuts=\"Delete\"`\n // on the trigger advertises this gesture to assistive tech.\n if (event.key === 'Delete' && focusedIdx >= 0) {\n const tab = this.tabs()[focusedIdx];\n if (tab.closable() && !tab.disabled()) {\n event.preventDefault();\n this.closeTab(tab, event);\n return;\n }\n }\n\n // Let the manager handle arrow/home/end navigation.\n const before = this.keyManager.activeItemIndex;\n this.keyManager.onKeydown(event);\n const after = this.keyManager.activeItemIndex;\n if (after !== null && after !== before && after >= 0) {\n // Automatic activation pattern (APG): selection follows focus.\n this.selectTab(this.tabs()[after]);\n }\n }\n\n // ── Scrolling ──\n\n /** Smoothly scrolls the tablist back toward its start by one step, along the current orientation's axis. */\n scrollStart(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n const isVertical = this.orientation() === 'vertical';\n if (isVertical) {\n el.scrollBy({ top: -120, behavior: 'smooth' });\n } else {\n el.scrollBy({ left: -120, behavior: 'smooth' });\n }\n }\n\n /** Smoothly scrolls the tablist forward toward its end by one step, along the current orientation's axis. */\n scrollEnd(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n const isVertical = this.orientation() === 'vertical';\n if (isVertical) {\n el.scrollBy({ top: 120, behavior: 'smooth' });\n } else {\n el.scrollBy({ left: 120, behavior: 'smooth' });\n }\n }\n\n private updateScrollState(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n\n const isVertical = this.orientation() === 'vertical';\n if (isVertical) {\n this.canScrollStart.set(el.scrollTop > 0);\n this.canScrollEnd.set(el.scrollTop + el.clientHeight < el.scrollHeight - 1);\n } else {\n this.canScrollStart.set(el.scrollLeft > 0);\n this.canScrollEnd.set(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);\n }\n }\n\n ngAfterViewInit(): void {\n this.initializeActiveTab();\n }\n\n constructor() {\n afterNextRender(() => {\n this.setupScrollDetection();\n });\n\n // Rebuild FocusKeyManager whenever triggers, orientation or direction\n // change. `layoutDirection()` reads `Directionality.valueSignal` directly,\n // so an app that flips `dir` at runtime rebuilds the manager with the new\n // horizontal orientation without a subscription mirror.\n effect((onCleanup) => {\n const triggers = this.triggerElements();\n const orientation = this.orientation();\n if (triggers.length === 0) {\n this.keyManager = null;\n return;\n }\n const direction = this.layoutDirection();\n const manager = new FocusKeyManager(triggers)\n .withWrap()\n .withHomeAndEnd();\n if (orientation === 'vertical') {\n manager.withVerticalOrientation();\n } else {\n manager.withHorizontalOrientation(direction);\n }\n this.keyManager = manager;\n\n // Initialise the manager's active index to the active tab so the first\n // arrow press moves from the active item, not from -1.\n //\n // `updateActiveItem`, NOT `setActiveItem`: on a FocusKeyManager,\n // `setActiveItem` also moves DOM focus to the item. Calling it here —\n // during construction — makes merely MOUNTING a `<tw-tabs>` steal focus\n // from wherever the user was and scroll the tablist into view. On a page\n // where the tabs sit below the fold that silently jumps the viewport.\n // `updateActiveItem` seeds the index without touching focus, which is\n // all this needs; the keydown handler still uses `setActiveItem`, where\n // moving focus is the point.\n untracked(() => {\n // Seeded from the DOM tab stop, not from `activeValue()`: when `value`\n // matches no tab the tab stop falls back to the first enabled tab, and\n // seeding the manager with -1 there would desynchronise the two.\n const stopValue = this.focusStopValue();\n const tabs = this.tabs();\n const idx = tabs.findIndex(t => t.value() === stopValue);\n if (idx >= 0) {\n manager.updateActiveItem(idx);\n }\n });\n\n onCleanup(() => {\n manager.destroy();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n this.resizeObserver?.disconnect();\n this.scrollCleanup?.();\n });\n }\n\n private initializeActiveTab(): void {\n // If no value was provided, default to the first enabled tab\n if (!this.activeValue()) {\n const first = this.tabs().find(t => !t.disabled());\n if (first) {\n this.activeValue.set(first.value());\n this.value.set(first.value());\n }\n }\n\n // Mark the initial active tab as activated (for lazy tracking)\n const val = this.activeValue();\n if (val) {\n this.activatedTabs.update(set => {\n const next = new Set(set);\n next.add(val);\n return next;\n });\n }\n }\n\n private setupScrollDetection(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n\n // Initial check\n this.updateScrollState();\n\n // ResizeObserver for container size changes\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => {\n this.updateScrollState();\n });\n this.resizeObserver.observe(el);\n }\n\n // Scroll event for scroll position changes\n const onScroll = () => this.updateScrollState();\n el.addEventListener('scroll', onScroll, { passive: true });\n this.scrollCleanup = () => el.removeEventListener('scroll', onScroll);\n }\n}\n","<!-- Tablist container. WAI-ARIA tablist owns roving focus on the tab elements\n inside; the container itself relays keydown but isn't tabbable. -->\n<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n [class]=\"tablistClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Scroll start button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollStart()\"\n (click)=\"scrollStart()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n\n <!-- Scrollable trigger strip -->\n <div #tablistInner [class]=\"tablistInnerClasses()\" style=\"scrollbar-width: none\">\n @for (tab of tabs(); track tab.value()) {\n <!-- Trigger is rendered as <div role=\"tab\">, not <button>: activation is\n wired explicitly via (click) and the parent tablist's keydown\n (Enter / Space), and the roving tab stop comes from\n twTabTriggerElement. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"tab\"\n twTabTriggerElement\n [isFocusStop]=\"isTabFocusStop(tab.value())\"\n [isDisabled]=\"tab.disabled()\"\n [id]=\"getTabId(tab.value())\"\n [attr.aria-selected]=\"isTabActive(tab.value())\"\n [attr.aria-controls]=\"getPanelId(tab.value())\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [attr.aria-keyshortcuts]=\"tab.closable() && !tab.disabled() ? 'Delete' : null\"\n [class]=\"getTriggerClasses(tab.value(), isTabActive(tab.value()))\"\n [class.opacity-50]=\"tab.disabled()\"\n [class.pointer-events-none]=\"tab.disabled()\"\n (click)=\"selectTab(tab)\"\n >\n <!-- Custom trigger template or default label -->\n @if (tab.triggerTemplate(); as triggerDir) {\n <ng-container\n [ngTemplateOutlet]=\"triggerDir.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: { active: isTabActive(tab.value()), disabled: tab.disabled(), value: tab.value() } }\"\n />\n } @else {\n {{ tab.label() }}\n }\n\n <!-- Close affordance — a pointer-only span, deliberately NOT a button.\n Anything focusable inside an element carrying an interactive role\n fails axe's nested-interactive rule, and hoisting a real button\n out to a sibling only trades that for aria-required-children\n (a tablist may own nothing but tabs — both measured). The\n keyboard path is the APG-recommended Delete key on the tab\n itself, handled in onKeydown and advertised to assistive tech\n through aria-keyshortcuts on the trigger above. -->\n @if (tab.closable() && !tab.disabled()) {\n <span\n data-tw-tab-close\n aria-hidden=\"true\"\n [class]=\"closeButtonClasses()\"\n (click)=\"closeTab(tab, $event)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path d=\"M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z\"/>\n </svg>\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Scroll end button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollEnd()\"\n (click)=\"scrollEnd()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n</div>\n\n<!-- Tab panels -->\n@for (tab of tabs(); track tab.value()) {\n @if (shouldRenderPanel(tab)) {\n <div\n role=\"tabpanel\"\n [id]=\"getPanelId(tab.value())\"\n [attr.aria-labelledby]=\"getTabId(tab.value())\"\n [class]=\"panelClasses()\"\n [hidden]=\"!isTabActive(tab.value())\"\n tabindex=\"0\"\n >\n @if (tab.contentTemplate(); as contentDir) {\n <ng-container [ngTemplateOutlet]=\"contentDir.templateRef\" />\n } @else if (tab.implicitContentTemplate(); as implicitTpl) {\n <ng-container [ngTemplateOutlet]=\"implicitTpl\" />\n }\n </div>\n }\n}\n\n<!-- Hidden projection slot for tw-tab children -->\n<div style=\"display: none\">\n <ng-content />\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cdevhub-ngx-tw-tabs.mjs","sources":["../../../projects/ngx-tw/tabs/tabs.ts","../../../projects/ngx-tw/tabs/tabs.html","../../../projects/ngx-tw/tabs/cdevhub-ngx-tw-tabs.ts"],"sourcesContent":["import {\n afterNextRender,\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n contentChildren,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n linkedSignal,\n model,\n output,\n signal,\n TemplateRef,\n untracked,\n viewChild,\n viewChildren,\n type AfterViewInit,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { type FocusableOption, FocusKeyManager, LiveAnnouncer } from '@angular/cdk/a11y';\nimport { Directionality } from '@angular/cdk/bidi';\nimport { tv } from 'tailwind-variants';\nimport { twMerge } from 'tailwind-merge';\nimport {\n getActiveTriggerClasses,\n getInactiveTriggerClasses,\n tabTriggerVariants,\n type TabTriggerVariant,\n type TwColor,\n type TwSize,\n} from '@cdevhub/ngx-tw/core';\n\n/** Visual style of the tab strip. */\nexport type TabsVariant = TabTriggerVariant;\n\n// ── tv() config (component-local slots only — trigger config is shared) ──\n\nconst tabsLayoutVariants = tv(\n {\n slots: {\n root: 'flex',\n tablist: 'relative flex shrink-0',\n tablistInner: 'flex overflow-x-auto',\n panel: 'min-w-0 flex-1',\n scrollButton:\n 'inline-flex items-center justify-center shrink-0 cursor-pointer transition-colors duration-normal motion-reduce:transition-none disabled:opacity-30 disabled:cursor-default hover:bg-surface-muted',\n // No focus-visible ring: the close control is pointer-only by design\n // (see the ARIA note in tabs.html), so a focus style would be dead code.\n closeButton:\n 'inline-flex items-center justify-center rounded-md cursor-pointer transition-colors duration-normal motion-reduce:transition-none hover:bg-surface-muted',\n },\n variants: {\n variant: {\n underline: {\n tablist: 'border-b border-border',\n tablistInner: 'gap-0',\n panel: 'pt-4',\n },\n enclosed: {\n tablist: 'border-b border-border',\n tablistInner: 'gap-0',\n panel: 'pt-4',\n },\n pill: {\n tablist: 'bg-surface-muted rounded-xl p-1',\n tablistInner: 'gap-1',\n panel: 'pt-4',\n },\n },\n // Square interactive-target scale: see CLAUDE.md \"Icon Sizing\".\n size: {\n xs: { scrollButton: 'size-5', closeButton: 'size-6' },\n sm: { scrollButton: 'size-5', closeButton: 'size-7' },\n md: { scrollButton: 'size-6', closeButton: 'size-8' },\n lg: { scrollButton: 'size-7', closeButton: 'size-9' },\n xl: { scrollButton: 'size-8', closeButton: 'size-9' },\n },\n orientation: {\n horizontal: {\n root: 'flex-col',\n tablist: '',\n tablistInner: 'flex-row',\n },\n vertical: {\n root: 'flex-row',\n tablistInner: 'flex-col overflow-y-auto overflow-x-hidden',\n },\n },\n },\n compoundVariants: [\n {\n variant: 'underline',\n orientation: 'vertical',\n class: {\n tablist: 'border-b-0 border-r border-border',\n panel: 'pt-0 pl-4',\n },\n },\n {\n variant: 'enclosed',\n orientation: 'vertical',\n class: {\n tablist: 'border-b-0 border-r border-border',\n panel: 'pt-0 pl-4',\n },\n },\n {\n variant: 'pill',\n orientation: 'vertical',\n class: {\n panel: 'pt-0 pl-4',\n },\n },\n ],\n defaultVariants: {\n variant: 'underline',\n size: 'md',\n orientation: 'horizontal',\n },\n },\n { twMerge: true },\n);\n\n// ── Per-variant/orientation trigger additions (vertical underline/enclosed\n// re-route the active border axis, hence these compound overrides). ──\n\nfunction getTriggerOrientationExtras(\n variant: TabsVariant,\n orientation: 'horizontal' | 'vertical',\n): string {\n if (orientation !== 'vertical') return '';\n switch (variant) {\n case 'underline':\n return 'border-b-0 -mb-0 border-r-2 border-transparent -mr-px';\n case 'enclosed':\n return '-mb-0 -mr-px';\n case 'pill':\n return '';\n }\n}\n\n// ── Directives ──\n\n@Directive({ selector: 'ng-template[twTabTrigger]' })\nexport class TabTriggerDirective {\n readonly templateRef = inject(TemplateRef);\n}\n\n@Directive({ selector: 'ng-template[twTabContent]' })\nexport class TabContentDirective {\n readonly templateRef = inject(TemplateRef);\n}\n\n// ── TabComponent ──\n\n@Component({\n selector: 'tw-tab',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <ng-template #implicitContent>\n <ng-content />\n </ng-template>\n `,\n host: {\n 'style': 'display: none',\n },\n})\nexport class TabComponent {\n /** Unique identifier for this tab. Used to match the active tab value. */\n readonly value = input.required<string>();\n\n /** Plain text label shown in the trigger. Ignored when a custom trigger template is provided. Defaults to an empty string. */\n readonly label = input('');\n\n /** When true, the tab cannot be selected and is skipped by keyboard navigation. Defaults to false. */\n readonly disabled = input(false);\n\n /**\n * When true, a dismiss control is rendered in the tab trigger. The control is pointer-only; the keyboard gesture is Delete on the focused tab. Defaults to false.\n *\n * @deprecated Bind `dismissible` instead. `closable` was the library's only\n * spelling of this concept — `alert`, `badge` and `toast` all call it\n * `dismissible` — and one idea should not have two names. Both work; `closable`\n * is removed in the next major.\n */\n readonly closable = input(false, { transform: booleanAttribute });\n\n /**\n * When true, a dismiss control is rendered in the tab trigger. The control is pointer-only; the keyboard gesture is Delete on the focused tab. Defaults to false.\n *\n * Canonical spelling, matching `alert`, `badge` and `toast`. Supersedes the\n * deprecated `closable`; binding either one enables the control.\n */\n readonly dismissible = input(false, { transform: booleanAttribute });\n\n /**\n * @internal Whether a dismiss control should render, from either spelling.\n * Both inputs default to `false`, so `||` is the correct reconciliation: it is\n * true exactly when the consumer opted in through one name or the other.\n */\n readonly isDismissible = computed(() => this.dismissible() || this.closable());\n\n /** When true, the tab panel content is only instantiated when the tab becomes active for the first time. Defaults to false. */\n readonly lazy = input(false);\n\n /** @internal Custom trigger template, if provided. */\n readonly triggerTemplate = contentChild(TabTriggerDirective);\n\n /** @internal Custom content template, if provided via twTabContent. */\n readonly contentTemplate = contentChild(TabContentDirective);\n\n /** @internal Implicit content template wrapping ng-content. */\n readonly implicitContentTemplate = viewChild<TemplateRef<unknown>>('implicitContent');\n}\n\n// ── Tab trigger element wrapper ──\n\n@Directive({\n selector: '[twTabTriggerElement]',\n host: {\n '[attr.tabindex]': 'tabIndex()',\n },\n})\nexport class TabTriggerElementDirective implements FocusableOption {\n readonly elementRef = inject(ElementRef<HTMLElement>);\n\n /**\n * Whether this trigger owns the tablist's single tab stop. Drives\n * `tabindex`. Normally the active tab, but the parent falls back to the\n * first enabled tab when `value` matches nothing, so the tablist is never\n * left with zero tab stops. Defaults to `false`.\n */\n readonly isFocusStop = input(false);\n\n /** Whether this trigger is disabled. Read by `FocusKeyManager` so disabled triggers are skipped during arrow navigation. Defaults to `false`. */\n readonly isDisabled = input(false);\n\n readonly tabIndex = computed(() => this.isFocusStop() ? 0 : -1);\n\n /** Moves DOM focus to the trigger element. Called by `FocusKeyManager` during arrow-key navigation. */\n focus(): void {\n this.elementRef.nativeElement.focus();\n }\n\n /**\n * FocusableOption getter. NOTE: we cannot expose the `isDisabled` input\n * signal directly — the signal function itself is always truthy, which would\n * make FocusKeyManager treat every trigger as disabled. We resolve the\n * signal here so the manager skips the right items.\n */\n get disabled(): boolean {\n return this.isDisabled();\n }\n}\n\n// ── TabsComponent ──\n\nlet nextId = 0;\n\n@Component({\n selector: 'tw-tabs',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgTemplateOutlet, TabTriggerElementDirective],\n templateUrl: './tabs.html',\n host: {\n '[class]': 'rootClasses()',\n },\n})\nexport class TabsComponent implements AfterViewInit {\n /** Controls the visual style of the tab strip. Defaults to 'underline'. */\n readonly variant = input<TabsVariant>('underline');\n\n /** Sets the semantic color for active tab indicators and highlights. Defaults to 'primary'. */\n readonly color = input<TwColor>('primary');\n\n /** Controls padding, font size, and icon size of tab triggers. Defaults to 'md'. */\n readonly size = input<TwSize>('md');\n\n /** Layout direction of the tab strip. Defaults to 'horizontal'. */\n readonly orientation = input<'horizontal' | 'vertical'>('horizontal');\n\n /** When true, tab triggers stretch to fill the available width equally. Defaults to false. */\n readonly fitted = input(false);\n\n /** The value of the currently active tab. Two-way bound. Updates when the user selects a tab. Defaults to an empty string. */\n readonly value = model<string>('');\n\n /** Fires when a closable tab is dismissed, either by clicking its close control or by pressing Delete while the tab has focus. Payload is the tab's value. */\n readonly closed = output<string>();\n\n private readonly destroyRef = inject(DestroyRef);\n private readonly liveAnnouncer = inject(LiveAnnouncer);\n private readonly directionality = inject(Directionality, { optional: true });\n\n /**\n * Live layout direction, read straight from CDK's `Directionality.valueSignal`\n * (CDK 22+) so the key-manager effect re-runs on a runtime `dir` flip with no\n * manual subscription mirror. Without a live direction the tablist hardcodes\n * 'ltr' and ArrowRight moves to the next tab in DOM order but the *previous*\n * tab visually in an RTL locale. CdkStepper and `tw-split` already honour\n * Directionality; `tw-paginator` reads `valueSignal` the same way.\n */\n private readonly layoutDirection = computed<'ltr' | 'rtl'>(\n () => this.directionality?.valueSignal() ?? 'ltr',\n );\n\n /** @internal */\n readonly tabs = contentChildren(TabComponent);\n\n /** @internal */\n readonly triggerElements = viewChildren(TabTriggerElementDirective);\n\n /** @internal */\n readonly tablistInnerRef = viewChild<ElementRef<HTMLElement>>('tablistInner');\n\n private readonly componentId = `tw-tabs-${nextId++}`;\n\n // ── Active tab tracking ──\n\n readonly activeValue = linkedSignal(() => this.value());\n\n // ── Lazy tab tracking — Set of tab values that have been activated at least once ──\n readonly activatedTabs = signal(new Set<string>());\n\n // ── Scroll overflow signals ──\n readonly canScrollStart = signal(false);\n readonly canScrollEnd = signal(false);\n\n private resizeObserver: ResizeObserver | null = null;\n private scrollCleanup: (() => void) | null = null;\n\n // ── Variant classes ──\n\n private readonly layoutResult = computed(() =>\n tabsLayoutVariants({\n variant: this.variant(),\n size: this.size(),\n orientation: this.orientation(),\n }),\n );\n\n private readonly triggerResult = computed(() =>\n tabTriggerVariants({\n variant: this.variant(),\n color: this.color(),\n size: this.size(),\n fitted: this.fitted(),\n }),\n );\n\n readonly rootClasses = computed(() => this.layoutResult().root());\n readonly tablistClasses = computed(() => this.layoutResult().tablist());\n readonly tablistInnerClasses = computed(() => this.layoutResult().tablistInner());\n readonly baseTriggerClasses = computed(() => this.triggerResult().trigger());\n readonly panelClasses = computed(() => this.layoutResult().panel());\n readonly scrollButtonClasses = computed(() => this.layoutResult().scrollButton());\n readonly closeButtonClasses = computed(() => this.layoutResult().closeButton());\n\n // ── Per-trigger active/inactive class computation ──\n\n /** @internal Get combined trigger classes for a given tab. */\n getTriggerClasses(_tabValue: string, isActive: boolean): string {\n const base = this.baseTriggerClasses();\n const orientationExtras = getTriggerOrientationExtras(this.variant(), this.orientation());\n const state = isActive\n ? getActiveTriggerClasses(this.variant(), this.color(), this.orientation())\n : getInactiveTriggerClasses(this.variant());\n return twMerge(base, orientationExtras, state);\n }\n\n // ── ID generation ──\n /** Builds the DOM id for a tab trigger, used by the panel's `aria-labelledby`. */\n getTabId(tabValue: string): string {\n return `${this.componentId}-tab-${tabValue}`;\n }\n\n /** Builds the DOM id for a tab panel, used by the trigger's `aria-controls`. */\n getPanelId(tabValue: string): string {\n return `${this.componentId}-panel-${tabValue}`;\n }\n\n // ── Tab visibility ──\n\n /** Whether the tab with the given value is the currently active one. */\n isTabActive(tabValue: string): boolean {\n return this.activeValue() === tabValue;\n }\n\n /**\n * @internal Value of the tab that owns the tablist's single tab stop.\n *\n * The active tab whenever it exists and is enabled. When `value` matches no\n * tab — a stale id, a value cleared after init, or an active tab that is\n * disabled — the first enabled tab takes the tab stop instead (standard\n * roving-tabindex recovery). Without the fallback every trigger renders\n * `tabindex=\"-1\"` and the whole tablist silently leaves the tab order\n * (WCAG SC 2.1.1).\n */\n readonly focusStopValue = computed<string | null>(() => {\n const tabs = this.tabs();\n const active = this.activeValue();\n const activeTab = tabs.find(t => t.value() === active && !t.disabled());\n if (activeTab) return activeTab.value();\n return tabs.find(t => !t.disabled())?.value() ?? null;\n });\n\n /** Whether the tab with the given value owns the tablist's single tab stop. */\n isTabFocusStop(tabValue: string): boolean {\n return this.focusStopValue() === tabValue;\n }\n\n /** Whether a tab's panel content should be in the DOM. Always true for eager tabs; for lazy tabs, true only once the tab has been activated at least once. */\n shouldRenderPanel(tab: TabComponent): boolean {\n const value = tab.value();\n if (!tab.lazy()) {\n return true;\n }\n // Lazy: render only if it has been activated at least once\n return this.activatedTabs().has(value);\n }\n\n // ── Selection ──\n\n /** Activates a tab, updating the two-way bound `value`, marking it rendered for lazy panels, and announcing the change via `LiveAnnouncer`. Ignored when the tab is disabled. */\n selectTab(tab: TabComponent): void {\n if (tab.disabled()) return;\n const val = tab.value();\n this.activeValue.set(val);\n this.value.set(val);\n\n // Mark as activated for lazy tabs\n this.activatedTabs.update(set => {\n const next = new Set(set);\n next.add(val);\n return next;\n });\n\n // Announce to screen readers\n const tabsArr = this.tabs();\n const idx = tabsArr.findIndex(t => t.value() === val);\n this.liveAnnouncer.announce(\n `${tab.label() || val} tab, ${idx + 1} of ${tabsArr.length}`,\n );\n }\n\n /** Closes a closable tab, emitting `closed` with its value and moving activation to the nearest enabled sibling when the closed tab was active. */\n closeTab(tab: TabComponent, event: Event): void {\n event.stopPropagation();\n if (event instanceof KeyboardEvent) {\n event.preventDefault();\n }\n const val = tab.value();\n this.closed.emit(val);\n\n // If closing the active tab, activate nearest enabled sibling\n if (this.isTabActive(val)) {\n const tabsArr = this.tabs();\n const idx = tabsArr.findIndex(t => t.value() === val);\n const next = this.findNearestEnabledTab(tabsArr, idx);\n if (next) {\n this.selectTab(next);\n }\n }\n }\n\n private findNearestEnabledTab(tabs: readonly TabComponent[], fromIndex: number): TabComponent | null {\n // Search forward first, then backward\n for (let i = fromIndex + 1; i < tabs.length; i++) {\n if (!tabs[i].disabled()) return tabs[i];\n }\n for (let i = fromIndex - 1; i >= 0; i--) {\n if (!tabs[i].disabled()) return tabs[i];\n }\n return null;\n }\n\n // ── Keyboard navigation (CDK FocusKeyManager) ──\n\n private keyManager: FocusKeyManager<TabTriggerElementDirective> | null = null;\n\n /**\n * @internal Handles keyboard navigation. The manager handles Arrow/Home/End;\n * we layer Enter/Space to select the focused tab (the wrapper is a `<div role=\"tab\">`\n * so we lose the native button activation) and Delete to close a closable tab.\n */\n onKeydown(event: KeyboardEvent): void {\n if (!this.keyManager) return;\n const triggers = this.triggerElements();\n if (triggers.length === 0) return;\n\n // Sync active item with the trigger DOM-focused by the user. We accept\n // either `event.target` (which works under shadow DOM) or the currently\n // active tab's index as a safety net (e.g. when the event was dispatched\n // on the tablist container itself, not a trigger).\n const focusedIdx = triggers.findIndex(\n t => t.elementRef.nativeElement === event.target,\n );\n const activeIdx = focusedIdx >= 0\n ? focusedIdx\n : this.tabs().findIndex(t => t.value() === this.activeValue());\n if (activeIdx >= 0 && activeIdx !== this.keyManager.activeItemIndex) {\n this.keyManager.setActiveItem(activeIdx);\n }\n\n // Enter / Space select the focused tab (we no longer get this for free\n // because the trigger is a div, not a button — see DOM restructure below).\n if (event.key === 'Enter' || event.key === ' ') {\n if (focusedIdx >= 0) {\n event.preventDefault();\n this.selectTab(this.tabs()[focusedIdx]);\n }\n return;\n }\n\n // Delete on a closable tab dismisses it (APG-recommended). This is the\n // ONLY keyboard path: the close control cannot be focusable without\n // failing axe's nested-interactive rule, so `aria-keyshortcuts=\"Delete\"`\n // on the trigger advertises this gesture to assistive tech.\n if (event.key === 'Delete' && focusedIdx >= 0) {\n const tab = this.tabs()[focusedIdx];\n if (tab.isDismissible() && !tab.disabled()) {\n event.preventDefault();\n this.closeTab(tab, event);\n return;\n }\n }\n\n // Let the manager handle arrow/home/end navigation.\n const before = this.keyManager.activeItemIndex;\n this.keyManager.onKeydown(event);\n const after = this.keyManager.activeItemIndex;\n if (after !== null && after !== before && after >= 0) {\n // Automatic activation pattern (APG): selection follows focus.\n this.selectTab(this.tabs()[after]);\n }\n }\n\n // ── Scrolling ──\n\n /** Smoothly scrolls the tablist back toward its start by one step, along the current orientation's axis. */\n scrollStart(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n const isVertical = this.orientation() === 'vertical';\n if (isVertical) {\n el.scrollBy({ top: -120, behavior: 'smooth' });\n } else {\n el.scrollBy({ left: -120, behavior: 'smooth' });\n }\n }\n\n /** Smoothly scrolls the tablist forward toward its end by one step, along the current orientation's axis. */\n scrollEnd(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n const isVertical = this.orientation() === 'vertical';\n if (isVertical) {\n el.scrollBy({ top: 120, behavior: 'smooth' });\n } else {\n el.scrollBy({ left: 120, behavior: 'smooth' });\n }\n }\n\n private updateScrollState(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n\n const isVertical = this.orientation() === 'vertical';\n if (isVertical) {\n this.canScrollStart.set(el.scrollTop > 0);\n this.canScrollEnd.set(el.scrollTop + el.clientHeight < el.scrollHeight - 1);\n } else {\n this.canScrollStart.set(el.scrollLeft > 0);\n this.canScrollEnd.set(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);\n }\n }\n\n ngAfterViewInit(): void {\n this.initializeActiveTab();\n }\n\n constructor() {\n afterNextRender(() => {\n this.setupScrollDetection();\n });\n\n // Rebuild FocusKeyManager whenever triggers, orientation or direction\n // change. `layoutDirection()` reads `Directionality.valueSignal` directly,\n // so an app that flips `dir` at runtime rebuilds the manager with the new\n // horizontal orientation without a subscription mirror.\n effect((onCleanup) => {\n const triggers = this.triggerElements();\n const orientation = this.orientation();\n if (triggers.length === 0) {\n this.keyManager = null;\n return;\n }\n const direction = this.layoutDirection();\n const manager = new FocusKeyManager(triggers)\n .withWrap()\n .withHomeAndEnd();\n if (orientation === 'vertical') {\n manager.withVerticalOrientation();\n } else {\n manager.withHorizontalOrientation(direction);\n }\n this.keyManager = manager;\n\n // Initialise the manager's active index to the active tab so the first\n // arrow press moves from the active item, not from -1.\n //\n // `updateActiveItem`, NOT `setActiveItem`: on a FocusKeyManager,\n // `setActiveItem` also moves DOM focus to the item. Calling it here —\n // during construction — makes merely MOUNTING a `<tw-tabs>` steal focus\n // from wherever the user was and scroll the tablist into view. On a page\n // where the tabs sit below the fold that silently jumps the viewport.\n // `updateActiveItem` seeds the index without touching focus, which is\n // all this needs; the keydown handler still uses `setActiveItem`, where\n // moving focus is the point.\n untracked(() => {\n // Seeded from the DOM tab stop, not from `activeValue()`: when `value`\n // matches no tab the tab stop falls back to the first enabled tab, and\n // seeding the manager with -1 there would desynchronise the two.\n const stopValue = this.focusStopValue();\n const tabs = this.tabs();\n const idx = tabs.findIndex(t => t.value() === stopValue);\n if (idx >= 0) {\n manager.updateActiveItem(idx);\n }\n });\n\n onCleanup(() => {\n manager.destroy();\n });\n });\n\n this.destroyRef.onDestroy(() => {\n this.resizeObserver?.disconnect();\n this.scrollCleanup?.();\n });\n }\n\n private initializeActiveTab(): void {\n // If no value was provided, default to the first enabled tab\n if (!this.activeValue()) {\n const first = this.tabs().find(t => !t.disabled());\n if (first) {\n this.activeValue.set(first.value());\n this.value.set(first.value());\n }\n }\n\n // Mark the initial active tab as activated (for lazy tracking)\n const val = this.activeValue();\n if (val) {\n this.activatedTabs.update(set => {\n const next = new Set(set);\n next.add(val);\n return next;\n });\n }\n }\n\n private setupScrollDetection(): void {\n const el = this.tablistInnerRef()?.nativeElement;\n if (!el) return;\n\n // Initial check\n this.updateScrollState();\n\n // ResizeObserver for container size changes\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(() => {\n this.updateScrollState();\n });\n this.resizeObserver.observe(el);\n }\n\n // Scroll event for scroll position changes\n const onScroll = () => this.updateScrollState();\n el.addEventListener('scroll', onScroll, { passive: true });\n this.scrollCleanup = () => el.removeEventListener('scroll', onScroll);\n }\n}\n","<!-- Tablist container. WAI-ARIA tablist owns roving focus on the tab elements\n inside; the container itself relays keydown but isn't tabbable. -->\n<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n [class]=\"tablistClasses()\"\n role=\"tablist\"\n [attr.aria-orientation]=\"orientation()\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Scroll start button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollStart()\"\n (click)=\"scrollStart()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M9.78 4.22a.75.75 0 0 1 0 1.06L7.06 8l2.72 2.72a.75.75 0 1 1-1.06 1.06L5.47 8.53a.75.75 0 0 1 0-1.06l3.25-3.25a.75.75 0 0 1 1.06 0Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n\n <!-- Scrollable trigger strip -->\n <div #tablistInner [class]=\"tablistInnerClasses()\" style=\"scrollbar-width: none\">\n @for (tab of tabs(); track tab.value()) {\n <!-- Trigger is rendered as <div role=\"tab\">, not <button>: activation is\n wired explicitly via (click) and the parent tablist's keydown\n (Enter / Space), and the roving tab stop comes from\n twTabTriggerElement. -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n role=\"tab\"\n twTabTriggerElement\n [isFocusStop]=\"isTabFocusStop(tab.value())\"\n [isDisabled]=\"tab.disabled()\"\n [id]=\"getTabId(tab.value())\"\n [attr.aria-selected]=\"isTabActive(tab.value())\"\n [attr.aria-controls]=\"getPanelId(tab.value())\"\n [attr.aria-disabled]=\"tab.disabled() || null\"\n [attr.aria-keyshortcuts]=\"tab.isDismissible() && !tab.disabled() ? 'Delete' : null\"\n [class]=\"getTriggerClasses(tab.value(), isTabActive(tab.value()))\"\n [class.opacity-50]=\"tab.disabled()\"\n [class.pointer-events-none]=\"tab.disabled()\"\n (click)=\"selectTab(tab)\"\n >\n <!-- Custom trigger template or default label -->\n @if (tab.triggerTemplate(); as triggerDir) {\n <ng-container\n [ngTemplateOutlet]=\"triggerDir.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: { active: isTabActive(tab.value()), disabled: tab.disabled(), value: tab.value() } }\"\n />\n } @else {\n {{ tab.label() }}\n }\n\n <!-- Close affordance — a pointer-only span, deliberately NOT a button.\n Anything focusable inside an element carrying an interactive role\n fails axe's nested-interactive rule, and hoisting a real button\n out to a sibling only trades that for aria-required-children\n (a tablist may own nothing but tabs — both measured). The\n keyboard path is the APG-recommended Delete key on the tab\n itself, handled in onKeydown and advertised to assistive tech\n through aria-keyshortcuts on the trigger above. -->\n @if (tab.isDismissible() && !tab.disabled()) {\n <span\n data-tw-tab-close\n aria-hidden=\"true\"\n [class]=\"closeButtonClasses()\"\n (click)=\"closeTab(tab, $event)\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path d=\"M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z\"/>\n </svg>\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Scroll end button (horizontal only) -->\n @if (orientation() === 'horizontal' && (canScrollStart() || canScrollEnd())) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [class]=\"scrollButtonClasses()\"\n [disabled]=\"!canScrollEnd()\"\n (click)=\"scrollEnd()\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"currentColor\" class=\"size-4 shrink-0\">\n <path fill-rule=\"evenodd\" d=\"M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8 6.22 5.28a.75.75 0 0 1 0-1.06Z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n }\n</div>\n\n<!-- Tab panels -->\n@for (tab of tabs(); track tab.value()) {\n @if (shouldRenderPanel(tab)) {\n <div\n role=\"tabpanel\"\n [id]=\"getPanelId(tab.value())\"\n [attr.aria-labelledby]=\"getTabId(tab.value())\"\n [class]=\"panelClasses()\"\n [hidden]=\"!isTabActive(tab.value())\"\n tabindex=\"0\"\n >\n @if (tab.contentTemplate(); as contentDir) {\n <ng-container [ngTemplateOutlet]=\"contentDir.templateRef\" />\n } @else if (tab.implicitContentTemplate(); as implicitTpl) {\n <ng-container [ngTemplateOutlet]=\"implicitTpl\" />\n }\n </div>\n }\n}\n\n<!-- Hidden projection slot for tw-tab children -->\n<div style=\"display: none\">\n <ng-content />\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cdevhub-ngx-tw-theme.mjs","sources":["../../../projects/ngx-tw/theme/theme.types.ts","../../../projects/ngx-tw/theme/theme.config.ts","../../../projects/ngx-tw/theme/theme.service.ts","../../../projects/ngx-tw/theme/theme.directive.ts","../../../projects/ngx-tw/theme/theme.bootstrap.ts","../../../projects/ngx-tw/theme/cdevhub-ngx-tw-theme.ts"],"sourcesContent":["/**\n * The user-selectable theme. `'system'` defers to the OS, resolving\n * `prefers-color-scheme` and `prefers-contrast` as two independent axes: dark\n * plus increased contrast lands on `'high-contrast-dark'`, light plus increased\n * contrast on `'high-contrast'`.\n */\nexport type TwTheme = 'light' | 'dark' | 'high-contrast' | 'high-contrast-dark' | 'system';\n\n/**\n * The theme actually applied to the DOM after resolving `'system'` against the\n * OS preference.\n *\n * The two axes are appearance (light / dark) and contrast (normal / increased),\n * so the four values are their product: `'high-contrast'` is the light-based\n * increased-contrast scheme and `'high-contrast-dark'` the dark-based one.\n */\nexport type TwResolvedTheme = 'light' | 'dark' | 'high-contrast' | 'high-contrast-dark';\n\n/** Ordered list of every {@link TwTheme} value, used by `cycleTheme()` and for UI iteration. */\nexport const TW_THEMES = [\n 'light',\n 'dark',\n 'high-contrast',\n 'high-contrast-dark',\n 'system',\n] as const satisfies readonly TwTheme[];\n\n/** Ordered list of every {@link TwResolvedTheme} value (i.e. {@link TW_THEMES} minus `'system'`). */\nexport const TW_RESOLVED_THEMES = [\n 'light',\n 'dark',\n 'high-contrast',\n 'high-contrast-dark',\n] as const satisfies readonly TwResolvedTheme[];\n\n/**\n * Runtime configuration for {@link provideTheme}; controls storage, attribute\n * name, target element, and default.\n *\n * Every member is optional. This interface only ever reaches consumers through\n * `provideTheme(config?: Partial<TwThemeConfig>)`, which fills each unset key\n * from {@link DEFAULT_TW_THEME_CONFIG} — so a consumer holding a config object\n * typed as `TwThemeConfig` must not be forced to restate keys they do not\n * override, and adding a member in a future minor must not break them. The\n * resolved value handed to {@link TW_THEME_CONFIG} is `Required<TwThemeConfig>`.\n */\nexport interface TwThemeConfig {\n /** The default theme when no preference is stored. Defaults to `'system'`. */\n defaultTheme?: TwTheme;\n /**\n * localStorage key for persisting an explicit theme choice. Defaults to\n * `'ngx-tw-theme'`. Only `setTheme()` / `cycleTheme()` write it — providing\n * the service never does.\n */\n storageKey?: string;\n /** The HTML attribute written to the target element. Defaults to `'data-theme'`. */\n attribute?: string;\n /** Which element receives the theme attribute. Defaults to `'documentElement'`. */\n target?: 'documentElement' | 'body';\n}\n\n/**\n * Composite snapshot of `ThemeService` state — selected, resolved, system, and\n * boolean flags.\n *\n * The three flags are **not** mutually exclusive: `'high-contrast-dark'` sets\n * both {@link isDark} and {@link isHighContrast}, because it is dark *and*\n * high contrast. Branch on {@link resolvedTheme} when you need one case.\n */\nexport interface TwThemeState {\n /** The user-selected theme — may be `'system'`. */\n readonly theme: TwTheme;\n /** The theme actually applied to the DOM — never `'system'`. */\n readonly resolvedTheme: TwResolvedTheme;\n /** The OS appearance preference detected via `prefers-color-scheme` and `prefers-contrast`. */\n readonly systemTheme: TwResolvedTheme;\n /** True when {@link resolvedTheme} is a dark scheme — `'dark'` or `'high-contrast-dark'`. */\n readonly isDark: boolean;\n /** True when {@link resolvedTheme} is exactly `'light'`; the light-based `'high-contrast'` does not set it. */\n readonly isLight: boolean;\n /** True when {@link resolvedTheme} is an increased-contrast scheme — `'high-contrast'` or `'high-contrast-dark'`. */\n readonly isHighContrast: boolean;\n}\n\n/**\n * Built-in defaults merged under any user-provided {@link TwThemeConfig} by\n * `provideTheme()`. Typed `Required<TwThemeConfig>` so readers keep a\n * non-optional `string` / `TwTheme` for every field even though the interface\n * itself is all-optional.\n */\nexport const DEFAULT_TW_THEME_CONFIG: Required<TwThemeConfig> = {\n defaultTheme: 'system',\n storageKey: 'ngx-tw-theme',\n attribute: 'data-theme',\n target: 'documentElement',\n};\n","import {\n InjectionToken,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n type EnvironmentProviders,\n} from '@angular/core';\nimport { type TwThemeConfig, DEFAULT_TW_THEME_CONFIG } from './theme.types';\nimport { ThemeService } from './theme.service';\n\n/**\n * Injection token carrying the resolved {@link TwThemeConfig} (storage key,\n * attribute, target element, default theme).\n *\n * The value is `Required<TwThemeConfig>`: `TwThemeConfig`'s members are\n * optional so consumers can pass a partial config, but `provideTheme` fills\n * every key from {@link DEFAULT_TW_THEME_CONFIG} before providing it. Injectors\n * therefore still read a non-optional field for each setting, exactly as\n * before this token's type was tightened.\n */\nexport const TW_THEME_CONFIG = new InjectionToken<Required<TwThemeConfig>>('TW_THEME_CONFIG');\n\n/**\n * @deprecated Renamed to {@link TW_THEME_CONFIG} for consistency with every\n * other ngx-tw injection token. This is the *same token instance*, not a copy —\n * providing under either name and injecting under the other resolves — so the\n * rename is safe to adopt incrementally. Removed in the next major.\n */\nexport const THEME_CONFIG = TW_THEME_CONFIG;\n\n/**\n * Registers {@link ThemeService}, a {@link TW_THEME_CONFIG} value built by merging\n * `config` over {@link DEFAULT_TW_THEME_CONFIG}, and an environment initializer\n * that constructs the service at bootstrap. Call once in the app's environment\n * providers.\n *\n * The initializer is what makes the call self-sufficient. Everything that\n * applies a theme lives in `ThemeService`'s field initialisers and constructor\n * effect, so without it the stored preference was read — and `data-theme`\n * written — only once something happened to `inject(ThemeService)`. An app\n * whose theme toggle sits in a lazily-loaded route therefore rendered every\n * other route with no `data-theme` at all, silently falling back to the\n * `prefers-color-scheme` CSS branch; the failure looked intermittent because\n * it disappeared whenever the stored choice agreed with the OS. Injecting the\n * service yourself is still supported and is idempotent.\n *\n * Keys explicitly set to `undefined` are dropped before merging. Root\n * `tsconfig.json` does not set `exactOptionalPropertyTypes`, so\n * `provideTheme({ storageKey: env.themeKey })` type-checks even when\n * `env.themeKey` is `string | undefined` — and a plain spread would then write\n * `undefined` into a field `TwThemeConfig` types as `string`, making\n * `ThemeService` persist under the literal `localStorage` key `\"undefined\"`\n * (and, for `attribute`, call `setAttribute(\"undefined\", …)`).\n *\n * Providing the service does **not** write to `localStorage`; only an explicit\n * `setTheme()` / `cycleTheme()` does. For a flash-free first paint when the\n * stored choice disagrees with the OS, pair this with\n * {@link TW_THEME_BOOTSTRAP_SCRIPT} in `index.html`.\n */\nexport function provideTheme(config?: Partial<TwThemeConfig>): EnvironmentProviders {\n const overrides = Object.fromEntries(\n Object.entries(config ?? {}).filter(([, value]) => value !== undefined),\n );\n return makeEnvironmentProviders([\n {\n provide: TW_THEME_CONFIG,\n useValue: { ...DEFAULT_TW_THEME_CONFIG, ...overrides } as Required<TwThemeConfig>,\n },\n ThemeService,\n provideEnvironmentInitializer(() => {\n inject(ThemeService);\n }),\n ]);\n}\n","import {\n Injectable,\n NgZone,\n computed,\n effect,\n inject,\n signal,\n PLATFORM_ID,\n type OnDestroy,\n} from '@angular/core';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { TW_THEME_CONFIG } from './theme.config';\nimport { TW_THEMES, type TwTheme, type TwResolvedTheme, type TwThemeState } from './theme.types';\n\n/** OS colour-scheme query backing `'system'` resolution. */\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\n/** OS contrast query backing `'system'` → `'high-contrast'` / `'high-contrast-dark'` resolution. */\nconst CONTRAST_QUERY = '(prefers-contrast: more)';\n\n/**\n * Stateful runtime service that owns the active theme, reacts to OS\n * `prefers-color-scheme` / `prefers-contrast` changes, persists an explicit\n * user selection to `localStorage`, and writes the resolved theme onto the\n * configured DOM target as a `data-theme` attribute.\n *\n * The selected {@link theme} may be `'system'` (defer to the OS); the\n * {@link resolvedTheme} computed from it is always one of `'light'`,\n * `'dark'`, `'high-contrast'`, or `'high-contrast-dark'` — never `'system'`.\n * Register via\n * {@link provideTheme} in the app's environment providers; `provideTheme`\n * also constructs the service at bootstrap, so injecting it is only needed\n * to read or change the theme.\n *\n * Storage is written **only** by {@link setTheme} / {@link cycleTheme}.\n * Merely providing the service never touches `localStorage`, so the\n * configured `defaultTheme` keeps applying until the user actually picks\n * something, and apps under a storage-consent flow can provide the service\n * before consent is granted.\n */\n@Injectable()\nexport class ThemeService implements OnDestroy {\n private readonly config = inject(TW_THEME_CONFIG);\n private readonly document = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly ngZone = inject(NgZone);\n\n private darkQuery: MediaQueryList | null = null;\n private contrastQuery: MediaQueryList | null = null;\n\n // One listener for both queries: the resolution rule reads them together, so\n // a handler that only knew its own query would clobber the other's decision\n // on the next OS tick. The event payload is deliberately ignored — a\n // `MediaQueryList`'s `matches` is already updated when `change` fires, so\n // re-reading both is the single source of truth.\n private readonly mediaListener = () => {\n this.systemTheme.set(this.detectSystemTheme());\n };\n\n /** The user-selected theme (may be `'system'`). */\n readonly theme = signal<TwTheme>(this.loadInitialTheme());\n\n /** The OS appearance preference — `prefers-color-scheme` plus `prefers-contrast`. */\n readonly systemTheme = signal<TwResolvedTheme>(this.detectSystemTheme());\n\n /** The resolved theme actually applied to the DOM (never `'system'`). */\n readonly resolvedTheme = computed<TwResolvedTheme>(() => {\n const t = this.theme();\n return t === 'system' ? this.systemTheme() : t;\n });\n\n /**\n * True when the resolved theme is a **dark** scheme — `'dark'` or\n * `'high-contrast-dark'`.\n *\n * It answers the appearance question, not \"which scheme\": the canonical use\n * is picking a colour that has to sit on the page background (a chart grid,\n * a canvas fill), and dark high contrast needs the dark answer there just as\n * much as plain dark does. It is therefore **not** mutually exclusive with\n * {@link isHighContrast}; branch on {@link resolvedTheme} for one case.\n */\n readonly isDark = computed(() => {\n const t = this.resolvedTheme();\n return t === 'dark' || t === 'high-contrast-dark';\n });\n /**\n * True when the resolved theme is exactly `'light'`.\n *\n * Deliberately narrower than {@link isDark}'s mirror image: the light-based\n * `'high-contrast'` does **not** set it, because widening it would change\n * what a shipped scheme reports to consumers already reading this flag.\n * Use `!isDark()` for the appearance question.\n */\n readonly isLight = computed(() => this.resolvedTheme() === 'light');\n /**\n * True when the resolved theme is an **increased-contrast** scheme —\n * `'high-contrast'` or `'high-contrast-dark'`.\n *\n * It answers the contrast question, so it can be true at the same time as\n * {@link isDark}.\n */\n readonly isHighContrast = computed(() => {\n const t = this.resolvedTheme();\n return t === 'high-contrast' || t === 'high-contrast-dark';\n });\n\n /** Snapshot of the full theme state — selected, resolved, system, and boolean flags. */\n readonly state = computed<TwThemeState>(() => ({\n theme: this.theme(),\n resolvedTheme: this.resolvedTheme(),\n systemTheme: this.systemTheme(),\n isDark: this.isDark(),\n isLight: this.isLight(),\n isHighContrast: this.isHighContrast(),\n }));\n\n constructor() {\n if (this.isBrowser) {\n // Register the media-query listeners outside the Angular zone — they fire\n // on OS preference changes, not user interaction, and we don't want\n // zone.js bookkeeping for every system tick. The listener calls\n // `signal.set()`, which schedules its own change detection.\n // Both `MediaQueryList`s already exist: the `systemTheme` field\n // initialiser above ran `detectSystemTheme()`, which mints them. The\n // `??=` keeps this correct if that ever stops being true.\n this.ngZone.runOutsideAngular(() => {\n this.darkQuery ??= window.matchMedia(DARK_QUERY);\n this.contrastQuery ??= window.matchMedia(CONTRAST_QUERY);\n this.darkQuery.addEventListener('change', this.mediaListener);\n this.contrastQuery.addEventListener('change', this.mediaListener);\n });\n }\n\n effect(() => {\n this.applyToDocument(this.resolvedTheme());\n });\n }\n\n /**\n * Sets the selected theme and persists it. Pass `'system'` to follow the OS\n * preference.\n *\n * This is the only entry point that writes to `localStorage` — see the note\n * on the class.\n */\n setTheme(theme: TwTheme): void {\n this.theme.set(theme);\n this.persistTheme(theme);\n }\n\n /** Advances the selected theme to the next entry in {@link TW_THEMES}, wrapping around, and persists it. */\n cycleTheme(): void {\n const idx = TW_THEMES.indexOf(this.theme());\n this.setTheme(TW_THEMES[(idx + 1) % TW_THEMES.length]);\n }\n\n /**\n * Writes the configured theme attribute onto an arbitrary element, scoping\n * that subtree to the given theme.\n *\n * Each of the four schemes ships an element-agnostic `[data-theme=…]` block\n * (`_light.css`, `_dark.css`, `_high-contrast.css`,\n * `_high-contrast-dark.css`), so the tokens really do re-resolve on the\n * element and cascade into its descendants — including back to `'light'`\n * from inside a dark page.\n *\n * Caveat: those CSS blocks key off the literal `data-theme` attribute. If\n * `provideTheme({ attribute })` renamed it, this method writes the renamed\n * attribute and the shipped stylesheet will not react — a custom attribute\n * only works with matching custom CSS.\n */\n applyToElement(element: HTMLElement, theme: TwResolvedTheme): void {\n element.setAttribute(this.config.attribute, theme);\n }\n\n ngOnDestroy(): void {\n this.darkQuery?.removeEventListener('change', this.mediaListener);\n this.contrastQuery?.removeEventListener('change', this.mediaListener);\n }\n\n private loadInitialTheme(): TwTheme {\n if (!this.isBrowser) return this.config.defaultTheme;\n try {\n const stored = localStorage.getItem(this.config.storageKey);\n if (stored && (TW_THEMES as readonly string[]).includes(stored)) {\n return stored as TwTheme;\n }\n } catch {\n /* localStorage unavailable */\n }\n return this.config.defaultTheme;\n }\n\n /**\n * Resolves `'system'` against the OS.\n *\n * The two preferences are treated as independent axes and composed, not\n * ranked: `prefers-color-scheme` picks the appearance and\n * `prefers-contrast: more` picks the contrast, giving the full 2×2 —\n * `light` / `dark` / `high-contrast` / `high-contrast-dark`.\n *\n * This is only correct because the library ships **both** high-contrast\n * ramps. While `_high-contrast.css` was the only one, contrast had to be\n * suppressed whenever the OS also asked for dark, or a user running dark +\n * increased contrast — a common pairing — would have been moved from a dark\n * surface onto a white one. `_high-contrast-dark.css` removed that\n * constraint, so the ranking is gone; if a future change ever drops one of\n * the two ramps, this method has to go back to ranking them.\n *\n * Both queries are minted here (not in the constructor) because the\n * `systemTheme` field initialiser calls this before the constructor body\n * runs; the constructor then attaches `change` listeners to the same two\n * objects, so the resolution stays live rather than read-once.\n */\n private detectSystemTheme(): TwResolvedTheme {\n if (!this.isBrowser) return 'light';\n const prefersDark = (this.darkQuery ??= window.matchMedia(DARK_QUERY)).matches;\n const prefersMoreContrast = (this.contrastQuery ??= window.matchMedia(CONTRAST_QUERY)).matches;\n if (prefersMoreContrast) return prefersDark ? 'high-contrast-dark' : 'high-contrast';\n return prefersDark ? 'dark' : 'light';\n }\n\n private applyToDocument(theme: TwResolvedTheme): void {\n if (!this.isBrowser) return;\n const target =\n this.config.target === 'documentElement'\n ? this.document.documentElement\n : this.document.body;\n target.setAttribute(this.config.attribute, theme);\n }\n\n private persistTheme(theme: TwTheme): void {\n if (!this.isBrowser) return;\n try {\n localStorage.setItem(this.config.storageKey, theme);\n } catch {\n /* localStorage unavailable */\n }\n }\n}\n","import { Directive, input } from '@angular/core';\nimport type { TwResolvedTheme } from './theme.types';\n\n@Directive({\n selector: '[twTheme]',\n host: {\n '[attr.data-theme]': 'twTheme()',\n },\n})\nexport class ThemeDirective {\n /**\n * Scopes a subtree to a specific resolved theme by writing `data-theme` on\n * the host. Required.\n *\n * Works in every direction: each of the four schemes ships an\n * element-agnostic `[data-theme=\"…\"]` block (`_light.css`, `_dark.css`,\n * `_high-contrast.css`, `_high-contrast-dark.css`), so a `'light'` pane\n * inside a dark page re-resolves the tokens rather than inheriting the\n * ancestor's.\n *\n * The attribute name is the literal `data-theme` that the shipped CSS keys\n * off — it deliberately ignores `provideTheme({ attribute })`, since a\n * renamed attribute matches none of those blocks.\n */\n readonly twTheme = input.required<TwResolvedTheme>();\n}\n","import { DEFAULT_TW_THEME_CONFIG } from './theme.types';\n\n/**\n * Body of an inline `<head>` script that applies a previously persisted theme\n * before the app bundle runs, eliminating the flash of the wrong theme on\n * reload.\n *\n * `ThemeService` cannot cover this case: it is JavaScript in the application\n * bundle, so the browser has already painted by the time it writes\n * `data-theme` — and `provideTheme`'s environment initializer does not change\n * that. The CSS `@media (prefers-color-scheme: dark)` fallback covers users\n * who never chose a theme; the flash is what an *explicit* choice that\n * disagrees with the OS looks like — light chosen on a dark machine, or\n * either high-contrast scheme chosen on a machine not already asking for\n * increased contrast (the CSS has a `prefers-color-scheme` branch but\n * deliberately no `prefers-contrast` one, so neither contrast scheme is\n * reachable without an explicit `data-theme`).\n *\n * The string is built from {@link DEFAULT_TW_THEME_CONFIG}, so the storage key\n * and attribute cannot drift from what `ThemeService` actually uses — that\n * drift is the reason this ships as code rather than as a README snippet\n * alone. Copy the literal script into `index.html`, or interpolate this\n * constant during an SSR / index transform:\n *\n * ```html\n * <head>\n * <script>try{var t=localStorage.getItem('ngx-tw-theme');if(t&&t!=='system')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>\n * </head>\n * ```\n *\n * Notes on the behaviour it deliberately does *not* have:\n * - A stored `'system'` writes nothing, so the CSS `prefers-color-scheme`\n * fallback keeps deciding — which is exactly what `'system'` means.\n * - It assumes the default `target: 'documentElement'`. A `<head>` script runs\n * before `<body>` exists, so `target: 'body'` cannot be bootstrapped this\n * way.\n * - If you overrode `storageKey` or `attribute` via `provideTheme`, adapt the\n * literal snippet to match; this constant only encodes the defaults.\n * - It is dependency-free and wrapped in `try`/`catch`, so a browser with\n * storage blocked falls through to the CSS behaviour instead of throwing\n * before the app loads.\n */\nexport const TW_THEME_BOOTSTRAP_SCRIPT =\n `try{var t=localStorage.getItem(${JSON.stringify(DEFAULT_TW_THEME_CONFIG.storageKey)});` +\n `if(t&&t!=='system')document.documentElement.setAttribute(` +\n `${JSON.stringify(DEFAULT_TW_THEME_CONFIG.attribute)},t)}catch(e){}`;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cdevhub-ngx-tw-theme.mjs","sources":["../../../projects/ngx-tw/theme/theme.types.ts","../../../projects/ngx-tw/theme/theme.config.ts","../../../projects/ngx-tw/theme/theme.service.ts","../../../projects/ngx-tw/theme/theme.directive.ts","../../../projects/ngx-tw/theme/theme.bootstrap.ts","../../../projects/ngx-tw/theme/cdevhub-ngx-tw-theme.ts"],"sourcesContent":["/**\n * The user-selectable theme. `'system'` defers to the OS, resolving\n * `prefers-color-scheme` and `prefers-contrast` as two independent axes: dark\n * plus increased contrast lands on `'high-contrast-dark'`, light plus increased\n * contrast on `'high-contrast'`.\n */\nexport type TwTheme = 'light' | 'dark' | 'high-contrast' | 'high-contrast-dark' | 'system';\n\n/**\n * The theme actually applied to the DOM after resolving `'system'` against the\n * OS preference.\n *\n * The two axes are appearance (light / dark) and contrast (normal / increased),\n * so the four values are their product: `'high-contrast'` is the light-based\n * increased-contrast scheme and `'high-contrast-dark'` the dark-based one.\n */\nexport type TwResolvedTheme = 'light' | 'dark' | 'high-contrast' | 'high-contrast-dark';\n\n/** Ordered list of every {@link TwTheme} value, used by `cycleTheme()` and for UI iteration. */\nexport const TW_THEMES = [\n 'light',\n 'dark',\n 'high-contrast',\n 'high-contrast-dark',\n 'system',\n] as const satisfies readonly TwTheme[];\n\n/** Ordered list of every {@link TwResolvedTheme} value (i.e. {@link TW_THEMES} minus `'system'`). */\nexport const TW_RESOLVED_THEMES = [\n 'light',\n 'dark',\n 'high-contrast',\n 'high-contrast-dark',\n] as const satisfies readonly TwResolvedTheme[];\n\n/**\n * Runtime configuration for {@link provideTheme}; controls storage, attribute\n * name, target element, and default.\n *\n * Every member is optional. This interface only ever reaches consumers through\n * `provideTheme(config?: Partial<TwThemeConfig>)`, which fills each unset key\n * from {@link DEFAULT_TW_THEME_CONFIG} — so a consumer holding a config object\n * typed as `TwThemeConfig` must not be forced to restate keys they do not\n * override, and adding a member in a future minor must not break them. The\n * resolved value handed to {@link TW_THEME_CONFIG} is `Required<TwThemeConfig>`.\n */\nexport interface TwThemeConfig {\n /** The default theme when no preference is stored. Defaults to `'system'`. */\n defaultTheme?: TwTheme;\n /**\n * localStorage key for persisting an explicit theme choice. Defaults to\n * `'ngx-tw-theme'`. Only `setTheme()` / `cycleTheme()` write it — providing\n * the service never does.\n */\n storageKey?: string;\n /**\n * The HTML attribute written to the target element. Defaults to `'data-theme'`.\n *\n * @deprecated Changing this breaks theming, and cannot be made to work. The\n * shipped stylesheets key off the **literal** `data-theme` — `_light.css`,\n * `_dark.css` and both high-contrast files each declare an element-agnostic\n * `[data-theme=\"…\"]` block, and CSS cannot parameterise an attribute name. So\n * a renamed attribute matches none of them: `ThemeService.applyToElement()`\n * writes the new name and nothing reacts, while `ThemeDirective` ignores this\n * option entirely and keeps writing `data-theme` (which is the only reason\n * `[twTheme]` still works when it is set). Two of the three consumers\n * therefore disagree with it by design.\n *\n * It is deprecated rather than removed because removing it is a breaking\n * change; it will go in the next major. To scope a theme to a subtree use the\n * `[twTheme]` directive, and to retheme, override the semantic tokens in your\n * own `@theme` block rather than renaming the attribute they hang off.\n */\n attribute?: string;\n /** Which element receives the theme attribute. Defaults to `'documentElement'`. */\n target?: 'documentElement' | 'body';\n}\n\n/**\n * Composite snapshot of `ThemeService` state — selected, resolved, system, and\n * boolean flags.\n *\n * The three flags are **not** mutually exclusive: `'high-contrast-dark'` sets\n * both {@link isDark} and {@link isHighContrast}, because it is dark *and*\n * high contrast. Branch on {@link resolvedTheme} when you need one case.\n */\nexport interface TwThemeState {\n /** The user-selected theme — may be `'system'`. */\n readonly theme: TwTheme;\n /** The theme actually applied to the DOM — never `'system'`. */\n readonly resolvedTheme: TwResolvedTheme;\n /** The OS appearance preference detected via `prefers-color-scheme` and `prefers-contrast`. */\n readonly systemTheme: TwResolvedTheme;\n /** True when {@link resolvedTheme} is a dark scheme — `'dark'` or `'high-contrast-dark'`. */\n readonly isDark: boolean;\n /** True when {@link resolvedTheme} is exactly `'light'`; the light-based `'high-contrast'` does not set it. */\n readonly isLight: boolean;\n /** True when {@link resolvedTheme} is an increased-contrast scheme — `'high-contrast'` or `'high-contrast-dark'`. */\n readonly isHighContrast: boolean;\n}\n\n/**\n * Built-in defaults merged under any user-provided {@link TwThemeConfig} by\n * `provideTheme()`. Typed `Required<TwThemeConfig>` so readers keep a\n * non-optional `string` / `TwTheme` for every field even though the interface\n * itself is all-optional.\n */\nexport const DEFAULT_TW_THEME_CONFIG: Required<TwThemeConfig> = {\n defaultTheme: 'system',\n storageKey: 'ngx-tw-theme',\n attribute: 'data-theme',\n target: 'documentElement',\n};\n","import {\n InjectionToken,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n type EnvironmentProviders,\n} from '@angular/core';\nimport { type TwThemeConfig, DEFAULT_TW_THEME_CONFIG } from './theme.types';\nimport { ThemeService } from './theme.service';\n\n/**\n * Injection token carrying the resolved {@link TwThemeConfig} (storage key,\n * attribute, target element, default theme).\n *\n * The value is `Required<TwThemeConfig>`: `TwThemeConfig`'s members are\n * optional so consumers can pass a partial config, but `provideTheme` fills\n * every key from {@link DEFAULT_TW_THEME_CONFIG} before providing it. Injectors\n * therefore still read a non-optional field for each setting, exactly as\n * before this token's type was tightened.\n */\nexport const TW_THEME_CONFIG = new InjectionToken<Required<TwThemeConfig>>('TW_THEME_CONFIG');\n\n/**\n * @deprecated Renamed to {@link TW_THEME_CONFIG} for consistency with every\n * other ngx-tw injection token. This is the *same token instance*, not a copy —\n * providing under either name and injecting under the other resolves — so the\n * rename is safe to adopt incrementally. Removed in the next major.\n */\nexport const THEME_CONFIG = TW_THEME_CONFIG;\n\n/**\n * Registers {@link ThemeService}, a {@link TW_THEME_CONFIG} value built by merging\n * `config` over {@link DEFAULT_TW_THEME_CONFIG}, and an environment initializer\n * that constructs the service at bootstrap. Call once in the app's environment\n * providers.\n *\n * The initializer is what makes the call self-sufficient. Everything that\n * applies a theme lives in `ThemeService`'s field initialisers and constructor\n * effect, so without it the stored preference was read — and `data-theme`\n * written — only once something happened to `inject(ThemeService)`. An app\n * whose theme toggle sits in a lazily-loaded route therefore rendered every\n * other route with no `data-theme` at all, silently falling back to the\n * `prefers-color-scheme` CSS branch; the failure looked intermittent because\n * it disappeared whenever the stored choice agreed with the OS. Injecting the\n * service yourself is still supported and is idempotent.\n *\n * Keys explicitly set to `undefined` are dropped before merging. Root\n * `tsconfig.json` does not set `exactOptionalPropertyTypes`, so\n * `provideTheme({ storageKey: env.themeKey })` type-checks even when\n * `env.themeKey` is `string | undefined` — and a plain spread would then write\n * `undefined` into a field `TwThemeConfig` types as `string`, making\n * `ThemeService` persist under the literal `localStorage` key `\"undefined\"`\n * (and, for `attribute`, call `setAttribute(\"undefined\", …)`).\n *\n * Providing the service does **not** write to `localStorage`; only an explicit\n * `setTheme()` / `cycleTheme()` does. For a flash-free first paint when the\n * stored choice disagrees with the OS, pair this with\n * {@link TW_THEME_BOOTSTRAP_SCRIPT} in `index.html`.\n */\nexport function provideTheme(config?: Partial<TwThemeConfig>): EnvironmentProviders {\n const overrides = Object.fromEntries(\n Object.entries(config ?? {}).filter(([, value]) => value !== undefined),\n );\n return makeEnvironmentProviders([\n {\n provide: TW_THEME_CONFIG,\n useValue: { ...DEFAULT_TW_THEME_CONFIG, ...overrides } as Required<TwThemeConfig>,\n },\n ThemeService,\n provideEnvironmentInitializer(() => {\n inject(ThemeService);\n }),\n ]);\n}\n","import {\n Injectable,\n NgZone,\n computed,\n effect,\n inject,\n signal,\n PLATFORM_ID,\n type OnDestroy,\n} from '@angular/core';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport { TW_THEME_CONFIG } from './theme.config';\nimport { TW_THEMES, type TwTheme, type TwResolvedTheme, type TwThemeState } from './theme.types';\n\n/** OS colour-scheme query backing `'system'` resolution. */\nconst DARK_QUERY = '(prefers-color-scheme: dark)';\n\n/** OS contrast query backing `'system'` → `'high-contrast'` / `'high-contrast-dark'` resolution. */\nconst CONTRAST_QUERY = '(prefers-contrast: more)';\n\n/**\n * Stateful runtime service that owns the active theme, reacts to OS\n * `prefers-color-scheme` / `prefers-contrast` changes, persists an explicit\n * user selection to `localStorage`, and writes the resolved theme onto the\n * configured DOM target as a `data-theme` attribute.\n *\n * The selected {@link theme} may be `'system'` (defer to the OS); the\n * {@link resolvedTheme} computed from it is always one of `'light'`,\n * `'dark'`, `'high-contrast'`, or `'high-contrast-dark'` — never `'system'`.\n * Register via\n * {@link provideTheme} in the app's environment providers; `provideTheme`\n * also constructs the service at bootstrap, so injecting it is only needed\n * to read or change the theme.\n *\n * Storage is written **only** by {@link setTheme} / {@link cycleTheme}.\n * Merely providing the service never touches `localStorage`, so the\n * configured `defaultTheme` keeps applying until the user actually picks\n * something, and apps under a storage-consent flow can provide the service\n * before consent is granted.\n */\n@Injectable()\nexport class ThemeService implements OnDestroy {\n private readonly config = inject(TW_THEME_CONFIG);\n private readonly document = inject(DOCUMENT);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly ngZone = inject(NgZone);\n\n private darkQuery: MediaQueryList | null = null;\n private contrastQuery: MediaQueryList | null = null;\n\n // One listener for both queries: the resolution rule reads them together, so\n // a handler that only knew its own query would clobber the other's decision\n // on the next OS tick. The event payload is deliberately ignored — a\n // `MediaQueryList`'s `matches` is already updated when `change` fires, so\n // re-reading both is the single source of truth.\n private readonly mediaListener = () => {\n this.systemTheme.set(this.detectSystemTheme());\n };\n\n /** The user-selected theme (may be `'system'`). */\n readonly theme = signal<TwTheme>(this.loadInitialTheme());\n\n /** The OS appearance preference — `prefers-color-scheme` plus `prefers-contrast`. */\n readonly systemTheme = signal<TwResolvedTheme>(this.detectSystemTheme());\n\n /** The resolved theme actually applied to the DOM (never `'system'`). */\n readonly resolvedTheme = computed<TwResolvedTheme>(() => {\n const t = this.theme();\n return t === 'system' ? this.systemTheme() : t;\n });\n\n /**\n * True when the resolved theme is a **dark** scheme — `'dark'` or\n * `'high-contrast-dark'`.\n *\n * It answers the appearance question, not \"which scheme\": the canonical use\n * is picking a colour that has to sit on the page background (a chart grid,\n * a canvas fill), and dark high contrast needs the dark answer there just as\n * much as plain dark does. It is therefore **not** mutually exclusive with\n * {@link isHighContrast}; branch on {@link resolvedTheme} for one case.\n */\n readonly isDark = computed(() => {\n const t = this.resolvedTheme();\n return t === 'dark' || t === 'high-contrast-dark';\n });\n /**\n * True when the resolved theme is exactly `'light'`.\n *\n * Deliberately narrower than {@link isDark}'s mirror image: the light-based\n * `'high-contrast'` does **not** set it, because widening it would change\n * what a shipped scheme reports to consumers already reading this flag.\n * Use `!isDark()` for the appearance question.\n */\n readonly isLight = computed(() => this.resolvedTheme() === 'light');\n /**\n * True when the resolved theme is an **increased-contrast** scheme —\n * `'high-contrast'` or `'high-contrast-dark'`.\n *\n * It answers the contrast question, so it can be true at the same time as\n * {@link isDark}.\n */\n readonly isHighContrast = computed(() => {\n const t = this.resolvedTheme();\n return t === 'high-contrast' || t === 'high-contrast-dark';\n });\n\n /** Snapshot of the full theme state — selected, resolved, system, and boolean flags. */\n readonly state = computed<TwThemeState>(() => ({\n theme: this.theme(),\n resolvedTheme: this.resolvedTheme(),\n systemTheme: this.systemTheme(),\n isDark: this.isDark(),\n isLight: this.isLight(),\n isHighContrast: this.isHighContrast(),\n }));\n\n constructor() {\n if (this.isBrowser) {\n // Register the media-query listeners outside the Angular zone — they fire\n // on OS preference changes, not user interaction, and we don't want\n // zone.js bookkeeping for every system tick. The listener calls\n // `signal.set()`, which schedules its own change detection.\n // Both `MediaQueryList`s already exist: the `systemTheme` field\n // initialiser above ran `detectSystemTheme()`, which mints them. The\n // `??=` keeps this correct if that ever stops being true.\n this.ngZone.runOutsideAngular(() => {\n this.darkQuery ??= window.matchMedia(DARK_QUERY);\n this.contrastQuery ??= window.matchMedia(CONTRAST_QUERY);\n this.darkQuery.addEventListener('change', this.mediaListener);\n this.contrastQuery.addEventListener('change', this.mediaListener);\n });\n }\n\n effect(() => {\n this.applyToDocument(this.resolvedTheme());\n });\n }\n\n /**\n * Sets the selected theme and persists it. Pass `'system'` to follow the OS\n * preference.\n *\n * This is the only entry point that writes to `localStorage` — see the note\n * on the class.\n */\n setTheme(theme: TwTheme): void {\n this.theme.set(theme);\n this.persistTheme(theme);\n }\n\n /** Advances the selected theme to the next entry in {@link TW_THEMES}, wrapping around, and persists it. */\n cycleTheme(): void {\n const idx = TW_THEMES.indexOf(this.theme());\n this.setTheme(TW_THEMES[(idx + 1) % TW_THEMES.length]);\n }\n\n /**\n * Writes the configured theme attribute onto an arbitrary element, scoping\n * that subtree to the given theme.\n *\n * Each of the four schemes ships an element-agnostic `[data-theme=…]` block\n * (`_light.css`, `_dark.css`, `_high-contrast.css`,\n * `_high-contrast-dark.css`), so the tokens really do re-resolve on the\n * element and cascade into its descendants — including back to `'light'`\n * from inside a dark page.\n *\n * Caveat: those CSS blocks key off the literal `data-theme` attribute. If\n * `provideTheme({ attribute })` renamed it, this method writes the renamed\n * attribute and the shipped stylesheet will not react — a custom attribute\n * only works with matching custom CSS.\n */\n applyToElement(element: HTMLElement, theme: TwResolvedTheme): void {\n element.setAttribute(this.config.attribute, theme);\n }\n\n ngOnDestroy(): void {\n this.darkQuery?.removeEventListener('change', this.mediaListener);\n this.contrastQuery?.removeEventListener('change', this.mediaListener);\n }\n\n private loadInitialTheme(): TwTheme {\n if (!this.isBrowser) return this.config.defaultTheme;\n try {\n const stored = localStorage.getItem(this.config.storageKey);\n if (stored && (TW_THEMES as readonly string[]).includes(stored)) {\n return stored as TwTheme;\n }\n } catch {\n /* localStorage unavailable */\n }\n return this.config.defaultTheme;\n }\n\n /**\n * Resolves `'system'` against the OS.\n *\n * The two preferences are treated as independent axes and composed, not\n * ranked: `prefers-color-scheme` picks the appearance and\n * `prefers-contrast: more` picks the contrast, giving the full 2×2 —\n * `light` / `dark` / `high-contrast` / `high-contrast-dark`.\n *\n * This is only correct because the library ships **both** high-contrast\n * ramps. While `_high-contrast.css` was the only one, contrast had to be\n * suppressed whenever the OS also asked for dark, or a user running dark +\n * increased contrast — a common pairing — would have been moved from a dark\n * surface onto a white one. `_high-contrast-dark.css` removed that\n * constraint, so the ranking is gone; if a future change ever drops one of\n * the two ramps, this method has to go back to ranking them.\n *\n * Both queries are minted here (not in the constructor) because the\n * `systemTheme` field initialiser calls this before the constructor body\n * runs; the constructor then attaches `change` listeners to the same two\n * objects, so the resolution stays live rather than read-once.\n */\n private detectSystemTheme(): TwResolvedTheme {\n if (!this.isBrowser) return 'light';\n const prefersDark = (this.darkQuery ??= window.matchMedia(DARK_QUERY)).matches;\n const prefersMoreContrast = (this.contrastQuery ??= window.matchMedia(CONTRAST_QUERY)).matches;\n if (prefersMoreContrast) return prefersDark ? 'high-contrast-dark' : 'high-contrast';\n return prefersDark ? 'dark' : 'light';\n }\n\n private applyToDocument(theme: TwResolvedTheme): void {\n if (!this.isBrowser) return;\n const target =\n this.config.target === 'documentElement'\n ? this.document.documentElement\n : this.document.body;\n target.setAttribute(this.config.attribute, theme);\n }\n\n private persistTheme(theme: TwTheme): void {\n if (!this.isBrowser) return;\n try {\n localStorage.setItem(this.config.storageKey, theme);\n } catch {\n /* localStorage unavailable */\n }\n }\n}\n","import { Directive, input } from '@angular/core';\nimport type { TwResolvedTheme } from './theme.types';\n\n@Directive({\n selector: '[twTheme]',\n host: {\n '[attr.data-theme]': 'twTheme()',\n },\n})\nexport class ThemeDirective {\n /**\n * Scopes a subtree to a specific resolved theme by writing `data-theme` on\n * the host. Required.\n *\n * Works in every direction: each of the four schemes ships an\n * element-agnostic `[data-theme=\"…\"]` block (`_light.css`, `_dark.css`,\n * `_high-contrast.css`, `_high-contrast-dark.css`), so a `'light'` pane\n * inside a dark page re-resolves the tokens rather than inheriting the\n * ancestor's.\n *\n * The attribute name is the literal `data-theme` that the shipped CSS keys\n * off — it deliberately ignores `provideTheme({ attribute })`, since a\n * renamed attribute matches none of those blocks.\n */\n readonly twTheme = input.required<TwResolvedTheme>();\n}\n","import { DEFAULT_TW_THEME_CONFIG } from './theme.types';\n\n/**\n * Body of an inline `<head>` script that applies a previously persisted theme\n * before the app bundle runs, eliminating the flash of the wrong theme on\n * reload.\n *\n * `ThemeService` cannot cover this case: it is JavaScript in the application\n * bundle, so the browser has already painted by the time it writes\n * `data-theme` — and `provideTheme`'s environment initializer does not change\n * that. The CSS `@media (prefers-color-scheme: dark)` fallback covers users\n * who never chose a theme; the flash is what an *explicit* choice that\n * disagrees with the OS looks like — light chosen on a dark machine, or\n * either high-contrast scheme chosen on a machine not already asking for\n * increased contrast (the CSS has a `prefers-color-scheme` branch but\n * deliberately no `prefers-contrast` one, so neither contrast scheme is\n * reachable without an explicit `data-theme`).\n *\n * The string is built from {@link DEFAULT_TW_THEME_CONFIG}, so the storage key\n * and attribute cannot drift from what `ThemeService` actually uses — that\n * drift is the reason this ships as code rather than as a README snippet\n * alone. Copy the literal script into `index.html`, or interpolate this\n * constant during an SSR / index transform:\n *\n * ```html\n * <head>\n * <script>try{var t=localStorage.getItem('ngx-tw-theme');if(t&&t!=='system')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>\n * </head>\n * ```\n *\n * Notes on the behaviour it deliberately does *not* have:\n * - A stored `'system'` writes nothing, so the CSS `prefers-color-scheme`\n * fallback keeps deciding — which is exactly what `'system'` means.\n * - It assumes the default `target: 'documentElement'`. A `<head>` script runs\n * before `<body>` exists, so `target: 'body'` cannot be bootstrapped this\n * way.\n * - If you overrode `storageKey` or `attribute` via `provideTheme`, adapt the\n * literal snippet to match; this constant only encodes the defaults.\n * - It is dependency-free and wrapped in `try`/`catch`, so a browser with\n * storage blocked falls through to the CSS behaviour instead of throwing\n * before the app loads.\n */\nexport const TW_THEME_BOOTSTRAP_SCRIPT =\n `try{var t=localStorage.getItem(${JSON.stringify(DEFAULT_TW_THEME_CONFIG.storageKey)});` +\n `if(t&&t!=='system')document.documentElement.setAttribute(` +\n `${JSON.stringify(DEFAULT_TW_THEME_CONFIG.attribute)},t)}catch(e){}`;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}