@cdevhub/ngx-tw 0.2.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.
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs +108 -0
- package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs +240 -0
- package/fesm2022/cdevhub-ngx-tw-alert.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs +287 -0
- package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs +332 -0
- package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +452 -0
- package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs +227 -0
- package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +417 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +232 -0
- package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs +4482 -0
- package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs +157 -0
- package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs +1454 -0
- package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +593 -0
- package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs +228 -0
- package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +510 -0
- package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs +1721 -0
- package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +721 -0
- package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs +861 -0
- package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +1558 -0
- package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +1833 -0
- package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs +693 -0
- package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +331 -0
- package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +289 -0
- package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs +572 -0
- package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs +36 -0
- package/fesm2022/cdevhub-ngx-tw-icon-lucide.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs +278 -0
- package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs +355 -0
- package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs +241 -0
- package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs +433 -0
- package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs +691 -0
- package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs +678 -0
- package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +335 -0
- package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs +708 -0
- package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +319 -0
- package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs +1722 -0
- package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs +115 -0
- package/fesm2022/cdevhub-ngx-tw-separator.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs +768 -0
- package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs +149 -0
- package/fesm2022/cdevhub-ngx-tw-skeleton.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs +1147 -0
- package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs +346 -0
- package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs +186 -0
- package/fesm2022/cdevhub-ngx-tw-spinner.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs +1082 -0
- package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs +579 -0
- package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs +443 -0
- package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs +391 -0
- package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +315 -0
- package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs +994 -0
- package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs +508 -0
- package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs +147 -0
- package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs +178 -0
- package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +1344 -0
- package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs +1136 -0
- package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs +1311 -0
- package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +533 -0
- package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -0
- package/fesm2022/cdevhub-ngx-tw.mjs +61 -0
- package/fesm2022/cdevhub-ngx-tw.mjs.map +1 -0
- package/package.json +265 -0
- package/theme/_base.css +394 -0
- package/theme/_dark.css +510 -0
- package/theme/_high-contrast.css +290 -0
- package/theme/_semantic.css +317 -0
- package/theme/_typography.css +9 -0
- package/theme/index.css +54 -0
- package/types/cdevhub-ngx-tw-accordion.d.ts +48 -0
- package/types/cdevhub-ngx-tw-alert.d.ts +67 -0
- package/types/cdevhub-ngx-tw-avatar.d.ts +79 -0
- package/types/cdevhub-ngx-tw-badge.d.ts +94 -0
- package/types/cdevhub-ngx-tw-breadcrumbs.d.ts +227 -0
- package/types/cdevhub-ngx-tw-button.d.ts +74 -0
- package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +200 -0
- package/types/cdevhub-ngx-tw-calendar-testing.d.ts +113 -0
- package/types/cdevhub-ngx-tw-calendar.d.ts +1704 -0
- package/types/cdevhub-ngx-tw-card.d.ts +48 -0
- package/types/cdevhub-ngx-tw-carousel.d.ts +406 -0
- package/types/cdevhub-ngx-tw-checkbox.d.ts +136 -0
- package/types/cdevhub-ngx-tw-code-block.d.ts +93 -0
- package/types/cdevhub-ngx-tw-collapsible.d.ts +151 -0
- package/types/cdevhub-ngx-tw-combobox.d.ts +389 -0
- package/types/cdevhub-ngx-tw-command-palette.d.ts +215 -0
- package/types/cdevhub-ngx-tw-core.d.ts +719 -0
- package/types/cdevhub-ngx-tw-date-picker.d.ts +317 -0
- package/types/cdevhub-ngx-tw-date-range-picker.d.ts +313 -0
- package/types/cdevhub-ngx-tw-dialog.d.ts +303 -0
- package/types/cdevhub-ngx-tw-empty-state.d.ts +104 -0
- package/types/cdevhub-ngx-tw-flip-card.d.ts +95 -0
- package/types/cdevhub-ngx-tw-form-field.d.ts +210 -0
- package/types/cdevhub-ngx-tw-icon-lucide.d.ts +29 -0
- package/types/cdevhub-ngx-tw-icon.d.ts +134 -0
- package/types/cdevhub-ngx-tw-input.d.ts +110 -0
- package/types/cdevhub-ngx-tw-item.d.ts +70 -0
- package/types/cdevhub-ngx-tw-menu.d.ts +99 -0
- package/types/cdevhub-ngx-tw-paginator.d.ts +305 -0
- package/types/cdevhub-ngx-tw-popover.d.ts +157 -0
- package/types/cdevhub-ngx-tw-progress-bar.d.ts +100 -0
- package/types/cdevhub-ngx-tw-radio.d.ts +174 -0
- package/types/cdevhub-ngx-tw-segmented-control.d.ts +66 -0
- package/types/cdevhub-ngx-tw-select.d.ts +384 -0
- package/types/cdevhub-ngx-tw-separator.d.ts +28 -0
- package/types/cdevhub-ngx-tw-sheet.d.ts +335 -0
- package/types/cdevhub-ngx-tw-skeleton.d.ts +52 -0
- package/types/cdevhub-ngx-tw-slider.d.ts +258 -0
- package/types/cdevhub-ngx-tw-sort.d.ts +136 -0
- package/types/cdevhub-ngx-tw-spinner.d.ts +50 -0
- package/types/cdevhub-ngx-tw-split.d.ts +265 -0
- package/types/cdevhub-ngx-tw-stat.d.ts +140 -0
- package/types/cdevhub-ngx-tw-stepper.d.ts +127 -0
- package/types/cdevhub-ngx-tw-switch.d.ts +88 -0
- package/types/cdevhub-ngx-tw-tab-nav.d.ts +107 -0
- package/types/cdevhub-ngx-tw-table.d.ts +479 -0
- package/types/cdevhub-ngx-tw-tabs.d.ts +121 -0
- package/types/cdevhub-ngx-tw-textarea.d.ts +62 -0
- package/types/cdevhub-ngx-tw-theme.d.ts +106 -0
- package/types/cdevhub-ngx-tw-time-picker.d.ts +303 -0
- package/types/cdevhub-ngx-tw-timeline.d.ts +278 -0
- package/types/cdevhub-ngx-tw-toast.d.ts +388 -0
- package/types/cdevhub-ngx-tw-tooltip.d.ts +95 -0
- package/types/cdevhub-ngx-tw.d.ts +49 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, signal, ChangeDetectionStrategy, Component, contentChildren, contentChild, computed, inject, effect, untracked, ElementRef, Directive } from '@angular/core';
|
|
3
|
+
import { LiveAnnouncer, FocusKeyManager } from '@angular/cdk/a11y';
|
|
4
|
+
import { tv } from 'tailwind-variants';
|
|
5
|
+
import { twMerge } from 'tailwind-merge';
|
|
6
|
+
import { tabTriggerVariants, getActiveTriggerClasses, getInactiveTriggerClasses } from '@cdevhub/ngx-tw/core';
|
|
7
|
+
|
|
8
|
+
// ── tv() config — nav-shell-only slots (trigger config is shared in core) ──
|
|
9
|
+
const tabNavLayoutVariants = tv({
|
|
10
|
+
slots: {
|
|
11
|
+
nav: 'relative flex shrink-0 items-stretch',
|
|
12
|
+
list: 'flex overflow-x-auto items-stretch',
|
|
13
|
+
},
|
|
14
|
+
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
underline: {
|
|
17
|
+
nav: 'border-b border-border',
|
|
18
|
+
list: 'gap-0',
|
|
19
|
+
},
|
|
20
|
+
enclosed: {
|
|
21
|
+
nav: 'border-b border-border',
|
|
22
|
+
list: 'gap-0',
|
|
23
|
+
},
|
|
24
|
+
pill: {
|
|
25
|
+
nav: 'bg-surface-muted rounded-xl p-1',
|
|
26
|
+
list: 'gap-1',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: 'underline',
|
|
32
|
+
},
|
|
33
|
+
}, { twMerge: true });
|
|
34
|
+
// ── TabNavPanel ──
|
|
35
|
+
let nextPanelId = 0;
|
|
36
|
+
class TabNavPanel {
|
|
37
|
+
/** Unique id for this panel. Referenced by the active link's `aria-controls`. Auto-generated when not provided. */
|
|
38
|
+
id = input(`tw-tab-nav-panel-${nextPanelId++}`, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
39
|
+
/** @internal Id of the active link — set by the associated TabNavComponent. */
|
|
40
|
+
activeTabId = signal(undefined, ...(ngDevMode ? [{ debugName: "activeTabId" }] : /* istanbul ignore next */ []));
|
|
41
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabNavPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
42
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", type: TabNavPanel, isStandalone: true, selector: "tw-tab-nav-panel", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tabpanel", "tabindex": "0" }, properties: { "attr.id": "id()", "attr.aria-labelledby": "activeTabId()" } }, ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
43
|
+
}
|
|
44
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabNavPanel, decorators: [{
|
|
45
|
+
type: Component,
|
|
46
|
+
args: [{
|
|
47
|
+
selector: 'tw-tab-nav-panel',
|
|
48
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
49
|
+
template: '<ng-content />',
|
|
50
|
+
host: {
|
|
51
|
+
role: 'tabpanel',
|
|
52
|
+
tabindex: '0',
|
|
53
|
+
'[attr.id]': 'id()',
|
|
54
|
+
'[attr.aria-labelledby]': 'activeTabId()',
|
|
55
|
+
},
|
|
56
|
+
}]
|
|
57
|
+
}], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }] } });
|
|
58
|
+
// ── TabNavComponent ──
|
|
59
|
+
class TabNavComponent {
|
|
60
|
+
/** Visual style of the tab strip. Defaults to `'underline'`. */
|
|
61
|
+
variant = input('underline', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/** Semantic color used for the active link indicator and text. Defaults to `'primary'`. */
|
|
63
|
+
color = input('primary', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
64
|
+
/** Controls padding and font size of tab links. Defaults to `'md'`. */
|
|
65
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
66
|
+
/** When true, tab links stretch to fill available width equally. Defaults to `false`. */
|
|
67
|
+
fitted = input(false, ...(ngDevMode ? [{ debugName: "fitted" }] : /* istanbul ignore next */ []));
|
|
68
|
+
/** Associated panel element. When provided, the nav follows the ARIA tabs pattern (role=tablist). When omitted, it uses the standard navigation landmark pattern with `aria-current="page"` on the active link. A panel projected as a child of the `<nav>` is auto-discovered. */
|
|
69
|
+
tabPanel = input(undefined, ...(ngDevMode ? [{ debugName: "tabPanel" }] : /* istanbul ignore next */ []));
|
|
70
|
+
/** Additional classes merged onto the `<nav>` host. Useful for layout tweaks or borders that the default tv() config does not cover. */
|
|
71
|
+
navClass = input('', ...(ngDevMode ? [{ debugName: "navClass" }] : /* istanbul ignore next */ []));
|
|
72
|
+
/** Additional classes merged onto every tab link. Applied after base/active/disabled classes so consumer styles always win the cascade. */
|
|
73
|
+
linkClass = input('', ...(ngDevMode ? [{ debugName: "linkClass" }] : /* istanbul ignore next */ []));
|
|
74
|
+
/** Optional label overrides for screen-reader announcements. Only used in the ARIA tabs pattern (i.e. when a panel is associated). */
|
|
75
|
+
labels = input({}, ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
76
|
+
/** @internal Tab links discovered via content projection. */
|
|
77
|
+
links = contentChildren(TabLinkDirective, { ...(ngDevMode ? { debugName: "links" } : /* istanbul ignore next */ {}), descendants: true });
|
|
78
|
+
/** @internal Panel discovered as a content child of the nav. */
|
|
79
|
+
projectedPanel = contentChild(TabNavPanel, ...(ngDevMode ? [{ debugName: "projectedPanel" }] : /* istanbul ignore next */ []));
|
|
80
|
+
/** @internal Resolved panel — explicit `tabPanel` input wins, falls back to a projected `<tw-tab-nav-panel>` inside the nav. */
|
|
81
|
+
resolvedPanel = computed(() => this.tabPanel() ?? this.projectedPanel(), ...(ngDevMode ? [{ debugName: "resolvedPanel" }] : /* istanbul ignore next */ []));
|
|
82
|
+
liveAnnouncer = inject(LiveAnnouncer);
|
|
83
|
+
/** Tracks the previously-announced active link id so we only announce on actual changes. `null` = before the first sync. */
|
|
84
|
+
previousActiveLinkId = signal(null, ...(ngDevMode ? [{ debugName: "previousActiveLinkId" }] : /* istanbul ignore next */ []));
|
|
85
|
+
layoutResult = computed(() => tabNavLayoutVariants({
|
|
86
|
+
variant: this.variant(),
|
|
87
|
+
}), ...(ngDevMode ? [{ debugName: "layoutResult" }] : /* istanbul ignore next */ []));
|
|
88
|
+
triggerResult = computed(() => tabTriggerVariants({
|
|
89
|
+
variant: this.variant(),
|
|
90
|
+
color: this.color(),
|
|
91
|
+
size: this.size(),
|
|
92
|
+
fitted: this.fitted(),
|
|
93
|
+
}), ...(ngDevMode ? [{ debugName: "triggerResult" }] : /* istanbul ignore next */ []));
|
|
94
|
+
/** @internal Combined classes applied to the `<nav>` host. */
|
|
95
|
+
navClasses = computed(() => twMerge(this.layoutResult().nav(), this.navClass()), ...(ngDevMode ? [{ debugName: "navClasses" }] : /* istanbul ignore next */ []));
|
|
96
|
+
/** @internal Base trigger classes (without active/inactive state). `no-underline` is appended because anchors need to override the default browser underline — tabs (which uses `<div role="tab">`) doesn't need it. */
|
|
97
|
+
linkBaseClasses = computed(() => twMerge(this.triggerResult().trigger(), 'no-underline'), ...(ngDevMode ? [{ debugName: "linkBaseClasses" }] : /* istanbul ignore next */ []));
|
|
98
|
+
/** @internal ARIA role — `tablist` when a tabPanel is associated, otherwise inherited from `<nav>`. */
|
|
99
|
+
role = computed(() => (this.resolvedPanel() ? 'tablist' : null), ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|
|
100
|
+
/** @internal ARIA orientation — only set in the tabs pattern. */
|
|
101
|
+
ariaOrientation = computed(() => (this.resolvedPanel() ? 'horizontal' : null), ...(ngDevMode ? [{ debugName: "ariaOrientation" }] : /* istanbul ignore next */ []));
|
|
102
|
+
/** @internal Classes applied to the active state of a link, based on current variant and color. tab-nav is horizontal-only, so the orientation argument is hard-coded. */
|
|
103
|
+
activeLinkClasses = computed(() => getActiveTriggerClasses(this.variant(), this.color(), 'horizontal'), ...(ngDevMode ? [{ debugName: "activeLinkClasses" }] : /* istanbul ignore next */ []));
|
|
104
|
+
/** @internal Classes applied to the inactive state of a link, based on current variant. */
|
|
105
|
+
inactiveLinkClasses = computed(() => getInactiveTriggerClasses(this.variant()), ...(ngDevMode ? [{ debugName: "inactiveLinkClasses" }] : /* istanbul ignore next */ []));
|
|
106
|
+
// Typed as FocusableOption because TabLinkDirective's `disabled` InputSignal
|
|
107
|
+
// is structurally incompatible with FocusableOption.disabled (boolean).
|
|
108
|
+
keyManager = null;
|
|
109
|
+
constructor() {
|
|
110
|
+
// Keep the associated panel's aria-labelledby in sync with the active link
|
|
111
|
+
// and announce route changes to screen readers when in the ARIA tabs pattern.
|
|
112
|
+
effect(() => {
|
|
113
|
+
const panel = this.resolvedPanel();
|
|
114
|
+
if (!panel) {
|
|
115
|
+
// Reset so the next time a panel is associated we don't fire a stale announcement.
|
|
116
|
+
untracked(() => this.previousActiveLinkId.set(null));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const links = this.links();
|
|
120
|
+
const activeLink = links.find((link) => link.active());
|
|
121
|
+
const activeLinkId = activeLink?.linkId() ?? undefined;
|
|
122
|
+
panel.activeTabId.set(activeLinkId);
|
|
123
|
+
const prev = untracked(() => this.previousActiveLinkId());
|
|
124
|
+
untracked(() => this.previousActiveLinkId.set(activeLinkId));
|
|
125
|
+
if (prev === null || prev === activeLinkId || !activeLink) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const idx = links.indexOf(activeLink);
|
|
129
|
+
const label = activeLink.elementRef.nativeElement.textContent?.trim() ?? '';
|
|
130
|
+
const formatter = this.labels().activeTabAnnouncement;
|
|
131
|
+
const message = formatter
|
|
132
|
+
? formatter(label, idx + 1, links.length)
|
|
133
|
+
: `${label}, tab ${idx + 1} of ${links.length}`;
|
|
134
|
+
this.liveAnnouncer.announce(message);
|
|
135
|
+
});
|
|
136
|
+
// Rebuild FocusKeyManager whenever the link set changes. tab-nav is
|
|
137
|
+
// horizontal-only, so we always configure horizontal orientation.
|
|
138
|
+
effect((onCleanup) => {
|
|
139
|
+
const links = this.links();
|
|
140
|
+
if (links.length === 0) {
|
|
141
|
+
this.keyManager = null;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Cast through unknown because `disabled` on TabLinkDirective is an
|
|
145
|
+
// InputSignal — structurally incompatible with FocusableOption's
|
|
146
|
+
// `disabled?: boolean`. FocusKeyManager only calls `focus()` (which we
|
|
147
|
+
// provide) and the disabled check, which we override via skipPredicate.
|
|
148
|
+
const focusable = links;
|
|
149
|
+
const manager = (new FocusKeyManager(focusable)
|
|
150
|
+
.withWrap()
|
|
151
|
+
.withHomeAndEnd()
|
|
152
|
+
.withHorizontalOrientation('ltr'));
|
|
153
|
+
// The `disabled` field on TabLinkDirective is an InputSignal (always
|
|
154
|
+
// truthy as a function), so we cannot rely on FocusKeyManager's default
|
|
155
|
+
// disabled check. Resolve the signal explicitly here.
|
|
156
|
+
manager.skipPredicate((link) => link.disabled());
|
|
157
|
+
this.keyManager = manager;
|
|
158
|
+
onCleanup(() => {
|
|
159
|
+
manager.destroy();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** @internal Handles arrow-key navigation in the ARIA tabs pattern via CDK FocusKeyManager. Manual activation: focus moves, but the link is only activated on Enter/Space (handled by the anchor itself). */
|
|
164
|
+
onKeydown(event) {
|
|
165
|
+
if (!this.resolvedPanel())
|
|
166
|
+
return;
|
|
167
|
+
if (!this.keyManager)
|
|
168
|
+
return;
|
|
169
|
+
const links = this.links();
|
|
170
|
+
if (links.length === 0)
|
|
171
|
+
return;
|
|
172
|
+
// Sync the manager's active item with whichever link the user has DOM-focused
|
|
173
|
+
// (mirrors the accordion pattern; event.target works under shadow DOM).
|
|
174
|
+
const focusedIdx = links.findIndex((link) => link.elementRef.nativeElement === event.target);
|
|
175
|
+
if (focusedIdx >= 0 && focusedIdx !== this.keyManager.activeItemIndex) {
|
|
176
|
+
this.keyManager.setActiveItem(focusedIdx);
|
|
177
|
+
}
|
|
178
|
+
else if (focusedIdx < 0) {
|
|
179
|
+
// No focused link inside the nav — nothing to navigate.
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.keyManager.onKeydown(event);
|
|
183
|
+
}
|
|
184
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
185
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.14", type: TabNavComponent, isStandalone: true, selector: "nav[twTabNav]", 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 }, fitted: { classPropertyName: "fitted", publicName: "fitted", isSignal: true, isRequired: false, transformFunction: null }, tabPanel: { classPropertyName: "tabPanel", publicName: "tabPanel", isSignal: true, isRequired: false, transformFunction: null }, navClass: { classPropertyName: "navClass", publicName: "navClass", isSignal: true, isRequired: false, transformFunction: null }, linkClass: { classPropertyName: "linkClass", publicName: "linkClass", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "class": "navClasses()", "attr.role": "role()", "attr.aria-orientation": "ariaOrientation()" } }, queries: [{ propertyName: "links", predicate: TabLinkDirective, descendants: true, isSignal: true }, { propertyName: "projectedPanel", first: true, predicate: TabNavPanel, descendants: true, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
186
|
+
}
|
|
187
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabNavComponent, decorators: [{
|
|
188
|
+
type: Component,
|
|
189
|
+
args: [{
|
|
190
|
+
selector: 'nav[twTabNav]',
|
|
191
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
192
|
+
template: '<ng-content />',
|
|
193
|
+
host: {
|
|
194
|
+
'[class]': 'navClasses()',
|
|
195
|
+
'[attr.role]': 'role()',
|
|
196
|
+
'[attr.aria-orientation]': 'ariaOrientation()',
|
|
197
|
+
'(keydown)': 'onKeydown($event)',
|
|
198
|
+
},
|
|
199
|
+
}]
|
|
200
|
+
}], 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 }] }], fitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitted", required: false }] }], tabPanel: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabPanel", required: false }] }], navClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "navClass", required: false }] }], linkClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkClass", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], links: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabLinkDirective), { ...{ descendants: true }, isSignal: true }] }], projectedPanel: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TabNavPanel), { isSignal: true }] }] } });
|
|
201
|
+
// ── TabLinkDirective ──
|
|
202
|
+
let nextLinkId = 0;
|
|
203
|
+
/**
|
|
204
|
+
* Note: we intentionally do not declare `implements FocusableOption`. The
|
|
205
|
+
* FocusableOption interface requires a `disabled?: boolean` property, but
|
|
206
|
+
* `disabled` here is an InputSignal — a function whose own boolean-coerced
|
|
207
|
+
* value is always truthy. FocusKeyManager only requires a `focus()` method
|
|
208
|
+
* (structurally satisfied below) and we override the disabled check via
|
|
209
|
+
* `.skipPredicate()` when constructing the manager.
|
|
210
|
+
*/
|
|
211
|
+
class TabLinkDirective {
|
|
212
|
+
/** @internal */
|
|
213
|
+
elementRef = inject(ElementRef);
|
|
214
|
+
parent = inject(TabNavComponent);
|
|
215
|
+
/** Whether the link represents the current active page/tab. Typically bound to router state. Defaults to `false`. */
|
|
216
|
+
active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
217
|
+
/** When true, the link is visually disabled and cannot be activated by click or keyboard. Defaults to `false`. */
|
|
218
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
219
|
+
/** Unique id for this link. Referenced by the panel's `aria-labelledby` when the link is active. Auto-generated when not provided. */
|
|
220
|
+
linkId = input(`tw-tab-link-${nextLinkId++}`, ...(ngDevMode ? [{ debugName: "linkId" }] : /* istanbul ignore next */ []));
|
|
221
|
+
/**
|
|
222
|
+
* @internal ARIA role — `'tab'` when the nav is wired to a panel (APG
|
|
223
|
+
* tablist pattern requires `role="tab"` on every link); `null` otherwise so
|
|
224
|
+
* the anchor's native `role="link"` wins. Do NOT collapse to a single
|
|
225
|
+
* branch — both are meaningful.
|
|
226
|
+
*/
|
|
227
|
+
linkRole = computed(() => (this.parent.resolvedPanel() ? 'tab' : null), ...(ngDevMode ? [{ debugName: "linkRole" }] : /* istanbul ignore next */ []));
|
|
228
|
+
/** @internal `aria-current="page"` on the active link when no tabPanel is associated. */
|
|
229
|
+
ariaCurrent = computed(() => {
|
|
230
|
+
if (this.parent.resolvedPanel())
|
|
231
|
+
return null;
|
|
232
|
+
return this.active() ? 'page' : null;
|
|
233
|
+
}, ...(ngDevMode ? [{ debugName: "ariaCurrent" }] : /* istanbul ignore next */ []));
|
|
234
|
+
/** @internal `aria-selected` applied only in the tabs pattern. */
|
|
235
|
+
ariaSelected = computed(() => {
|
|
236
|
+
if (!this.parent.resolvedPanel())
|
|
237
|
+
return null;
|
|
238
|
+
return this.active() ? 'true' : 'false';
|
|
239
|
+
}, ...(ngDevMode ? [{ debugName: "ariaSelected" }] : /* istanbul ignore next */ []));
|
|
240
|
+
/** @internal `aria-controls` points to the associated panel id. */
|
|
241
|
+
ariaControls = computed(() => this.parent.resolvedPanel()?.id() ?? null, ...(ngDevMode ? [{ debugName: "ariaControls" }] : /* istanbul ignore next */ []));
|
|
242
|
+
/** @internal */
|
|
243
|
+
ariaDisabled = computed(() => (this.disabled() ? 'true' : null), ...(ngDevMode ? [{ debugName: "ariaDisabled" }] : /* istanbul ignore next */ []));
|
|
244
|
+
/** @internal Tabindex — roving in the tabs pattern, default for navigation links, always -1 when disabled. */
|
|
245
|
+
tabIndex = computed(() => {
|
|
246
|
+
if (this.disabled())
|
|
247
|
+
return -1;
|
|
248
|
+
if (!this.parent.resolvedPanel())
|
|
249
|
+
return null;
|
|
250
|
+
return this.active() ? 0 : -1;
|
|
251
|
+
}, ...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
|
|
252
|
+
/** @internal Combined classes: base + active/inactive state + disabled modifiers + consumer overrides. */
|
|
253
|
+
classes = computed(() => {
|
|
254
|
+
const base = this.parent.linkBaseClasses();
|
|
255
|
+
const state = this.active()
|
|
256
|
+
? this.parent.activeLinkClasses()
|
|
257
|
+
: this.parent.inactiveLinkClasses();
|
|
258
|
+
const disabled = this.disabled()
|
|
259
|
+
? 'opacity-50 pointer-events-none cursor-not-allowed'
|
|
260
|
+
: '';
|
|
261
|
+
return twMerge(base, state, disabled, this.parent.linkClass());
|
|
262
|
+
}, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
|
|
263
|
+
/** Programmatically focus this link. */
|
|
264
|
+
focus() {
|
|
265
|
+
this.elementRef.nativeElement.focus();
|
|
266
|
+
}
|
|
267
|
+
/** @internal Intercepts clicks on disabled links to prevent navigation. */
|
|
268
|
+
onClick(event) {
|
|
269
|
+
if (this.disabled()) {
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
event.stopImmediatePropagation();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/** @internal Handles keyboard activation: blocks Enter/Space on disabled links, and activates the link on Space for enabled ones (anchors do not respond to Space by default). */
|
|
275
|
+
onKeydown(event) {
|
|
276
|
+
if (this.disabled()) {
|
|
277
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
278
|
+
event.preventDefault();
|
|
279
|
+
event.stopImmediatePropagation();
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (event.key === ' ') {
|
|
284
|
+
event.preventDefault();
|
|
285
|
+
this.elementRef.nativeElement.click();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabLinkDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
289
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: TabLinkDirective, isStandalone: true, selector: "a[twTabLink]", inputs: { active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, linkId: { classPropertyName: "linkId", publicName: "linkId", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick($event)", "keydown": "onKeydown($event)" }, properties: { "attr.id": "linkId()", "attr.role": "linkRole()", "attr.aria-current": "ariaCurrent()", "attr.aria-selected": "ariaSelected()", "attr.aria-controls": "ariaControls()", "attr.aria-disabled": "ariaDisabled()", "attr.tabindex": "tabIndex()", "class": "classes()" } }, ngImport: i0 });
|
|
290
|
+
}
|
|
291
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TabLinkDirective, decorators: [{
|
|
292
|
+
type: Directive,
|
|
293
|
+
args: [{
|
|
294
|
+
selector: 'a[twTabLink]',
|
|
295
|
+
host: {
|
|
296
|
+
'[attr.id]': 'linkId()',
|
|
297
|
+
'[attr.role]': 'linkRole()',
|
|
298
|
+
'[attr.aria-current]': 'ariaCurrent()',
|
|
299
|
+
'[attr.aria-selected]': 'ariaSelected()',
|
|
300
|
+
'[attr.aria-controls]': 'ariaControls()',
|
|
301
|
+
'[attr.aria-disabled]': 'ariaDisabled()',
|
|
302
|
+
'[attr.tabindex]': 'tabIndex()',
|
|
303
|
+
'[class]': 'classes()',
|
|
304
|
+
'(click)': 'onClick($event)',
|
|
305
|
+
'(keydown)': 'onKeydown($event)',
|
|
306
|
+
},
|
|
307
|
+
}]
|
|
308
|
+
}], propDecorators: { active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], linkId: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkId", required: false }] }] } });
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Generated bundle index. Do not edit.
|
|
312
|
+
*/
|
|
313
|
+
|
|
314
|
+
export { TabLinkDirective, TabNavComponent, TabNavPanel };
|
|
315
|
+
//# sourceMappingURL=cdevhub-ngx-tw-tab-nav.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdevhub-ngx-tw-tab-nav.mjs","sources":["../../../projects/ngx-tw/tab-nav/tab-nav.ts","../../../projects/ngx-tw/tab-nav/cdevhub-ngx-tw-tab-nav.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n ElementRef,\n computed,\n contentChild,\n contentChildren,\n effect,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport { FocusableOption, FocusKeyManager, LiveAnnouncer } from '@angular/cdk/a11y';\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 navigation strip. */\nexport type TabNavVariant = TabTriggerVariant;\n\n/** Optional label overrides for screen-reader announcements emitted by `TabNavComponent`. */\nexport interface TabNavLabels {\n /** Formatter for the LiveAnnouncer message emitted when the active link changes in the ARIA tabs pattern. Receives the link's visible text, its 1-based index, and the total link count. */\n activeTabAnnouncement?: (label: string, index: number, total: number) => string;\n}\n\n// ── tv() config — nav-shell-only slots (trigger config is shared in core) ──\n\nconst tabNavLayoutVariants = tv(\n {\n slots: {\n nav: 'relative flex shrink-0 items-stretch',\n list: 'flex overflow-x-auto items-stretch',\n },\n variants: {\n variant: {\n underline: {\n nav: 'border-b border-border',\n list: 'gap-0',\n },\n enclosed: {\n nav: 'border-b border-border',\n list: 'gap-0',\n },\n pill: {\n nav: 'bg-surface-muted rounded-xl p-1',\n list: 'gap-1',\n },\n },\n },\n defaultVariants: {\n variant: 'underline',\n },\n },\n { twMerge: true },\n);\n\n// ── TabNavPanel ──\n\nlet nextPanelId = 0;\n\n@Component({\n selector: 'tw-tab-nav-panel',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '<ng-content />',\n host: {\n role: 'tabpanel',\n tabindex: '0',\n '[attr.id]': 'id()',\n '[attr.aria-labelledby]': 'activeTabId()',\n },\n})\nexport class TabNavPanel {\n /** Unique id for this panel. Referenced by the active link's `aria-controls`. Auto-generated when not provided. */\n readonly id = input(`tw-tab-nav-panel-${nextPanelId++}`);\n\n /** @internal Id of the active link — set by the associated TabNavComponent. */\n readonly activeTabId = signal<string | undefined>(undefined);\n}\n\n// ── TabNavComponent ──\n\n@Component({\n selector: 'nav[twTabNav]',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: '<ng-content />',\n host: {\n '[class]': 'navClasses()',\n '[attr.role]': 'role()',\n '[attr.aria-orientation]': 'ariaOrientation()',\n '(keydown)': 'onKeydown($event)',\n },\n})\nexport class TabNavComponent {\n /** Visual style of the tab strip. Defaults to `'underline'`. */\n readonly variant = input<TabNavVariant>('underline');\n\n /** Semantic color used for the active link indicator and text. Defaults to `'primary'`. */\n readonly color = input<TwColor>('primary');\n\n /** Controls padding and font size of tab links. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /** When true, tab links stretch to fill available width equally. Defaults to `false`. */\n readonly fitted = input(false);\n\n /** Associated panel element. When provided, the nav follows the ARIA tabs pattern (role=tablist). When omitted, it uses the standard navigation landmark pattern with `aria-current=\"page\"` on the active link. A panel projected as a child of the `<nav>` is auto-discovered. */\n readonly tabPanel = input<TabNavPanel | undefined>(undefined);\n\n /** Additional classes merged onto the `<nav>` host. Useful for layout tweaks or borders that the default tv() config does not cover. */\n readonly navClass = input<string>('');\n\n /** Additional classes merged onto every tab link. Applied after base/active/disabled classes so consumer styles always win the cascade. */\n readonly linkClass = input<string>('');\n\n /** Optional label overrides for screen-reader announcements. Only used in the ARIA tabs pattern (i.e. when a panel is associated). */\n readonly labels = input<TabNavLabels>({});\n\n /** @internal Tab links discovered via content projection. */\n readonly links = contentChildren(TabLinkDirective, { descendants: true });\n\n /** @internal Panel discovered as a content child of the nav. */\n readonly projectedPanel = contentChild(TabNavPanel);\n\n /** @internal Resolved panel — explicit `tabPanel` input wins, falls back to a projected `<tw-tab-nav-panel>` inside the nav. */\n readonly resolvedPanel = computed(() => this.tabPanel() ?? this.projectedPanel());\n\n private readonly liveAnnouncer = inject(LiveAnnouncer);\n\n /** Tracks the previously-announced active link id so we only announce on actual changes. `null` = before the first sync. */\n private readonly previousActiveLinkId = signal<string | null | undefined>(null);\n\n private readonly layoutResult = computed(() =>\n tabNavLayoutVariants({\n variant: this.variant(),\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 /** @internal Combined classes applied to the `<nav>` host. */\n readonly navClasses = computed(() =>\n twMerge(this.layoutResult().nav(), this.navClass()),\n );\n\n /** @internal Base trigger classes (without active/inactive state). `no-underline` is appended because anchors need to override the default browser underline — tabs (which uses `<div role=\"tab\">`) doesn't need it. */\n readonly linkBaseClasses = computed(() =>\n twMerge(this.triggerResult().trigger(), 'no-underline'),\n );\n\n /** @internal ARIA role — `tablist` when a tabPanel is associated, otherwise inherited from `<nav>`. */\n readonly role = computed(() => (this.resolvedPanel() ? 'tablist' : null));\n\n /** @internal ARIA orientation — only set in the tabs pattern. */\n readonly ariaOrientation = computed(() => (this.resolvedPanel() ? 'horizontal' : null));\n\n /** @internal Classes applied to the active state of a link, based on current variant and color. tab-nav is horizontal-only, so the orientation argument is hard-coded. */\n readonly activeLinkClasses = computed(() =>\n getActiveTriggerClasses(this.variant(), this.color(), 'horizontal'),\n );\n\n /** @internal Classes applied to the inactive state of a link, based on current variant. */\n readonly inactiveLinkClasses = computed(() => getInactiveTriggerClasses(this.variant()));\n\n // Typed as FocusableOption because TabLinkDirective's `disabled` InputSignal\n // is structurally incompatible with FocusableOption.disabled (boolean).\n private keyManager: FocusKeyManager<FocusableOption> | null = null;\n\n constructor() {\n // Keep the associated panel's aria-labelledby in sync with the active link\n // and announce route changes to screen readers when in the ARIA tabs pattern.\n effect(() => {\n const panel = this.resolvedPanel();\n if (!panel) {\n // Reset so the next time a panel is associated we don't fire a stale announcement.\n untracked(() => this.previousActiveLinkId.set(null));\n return;\n }\n const links = this.links();\n const activeLink = links.find((link) => link.active());\n const activeLinkId = activeLink?.linkId() ?? undefined;\n panel.activeTabId.set(activeLinkId);\n\n const prev = untracked(() => this.previousActiveLinkId());\n untracked(() => this.previousActiveLinkId.set(activeLinkId));\n if (prev === null || prev === activeLinkId || !activeLink) {\n return;\n }\n\n const idx = links.indexOf(activeLink);\n const label = activeLink.elementRef.nativeElement.textContent?.trim() ?? '';\n const formatter = this.labels().activeTabAnnouncement;\n const message = formatter\n ? formatter(label, idx + 1, links.length)\n : `${label}, tab ${idx + 1} of ${links.length}`;\n this.liveAnnouncer.announce(message);\n });\n\n // Rebuild FocusKeyManager whenever the link set changes. tab-nav is\n // horizontal-only, so we always configure horizontal orientation.\n effect((onCleanup) => {\n const links = this.links();\n if (links.length === 0) {\n this.keyManager = null;\n return;\n }\n // Cast through unknown because `disabled` on TabLinkDirective is an\n // InputSignal — structurally incompatible with FocusableOption's\n // `disabled?: boolean`. FocusKeyManager only calls `focus()` (which we\n // provide) and the disabled check, which we override via skipPredicate.\n const focusable = links as unknown as readonly FocusableOption[];\n const manager = (new FocusKeyManager(focusable)\n .withWrap()\n .withHomeAndEnd()\n .withHorizontalOrientation('ltr')) as FocusKeyManager<FocusableOption>;\n // The `disabled` field on TabLinkDirective is an InputSignal (always\n // truthy as a function), so we cannot rely on FocusKeyManager's default\n // disabled check. Resolve the signal explicitly here.\n manager.skipPredicate((link) => (link as unknown as TabLinkDirective).disabled());\n this.keyManager = manager;\n\n onCleanup(() => {\n manager.destroy();\n });\n });\n }\n\n /** @internal Handles arrow-key navigation in the ARIA tabs pattern via CDK FocusKeyManager. Manual activation: focus moves, but the link is only activated on Enter/Space (handled by the anchor itself). */\n onKeydown(event: KeyboardEvent): void {\n if (!this.resolvedPanel()) return;\n if (!this.keyManager) return;\n const links = this.links();\n if (links.length === 0) return;\n\n // Sync the manager's active item with whichever link the user has DOM-focused\n // (mirrors the accordion pattern; event.target works under shadow DOM).\n const focusedIdx = links.findIndex(\n (link) => link.elementRef.nativeElement === event.target,\n );\n if (focusedIdx >= 0 && focusedIdx !== this.keyManager.activeItemIndex) {\n this.keyManager.setActiveItem(focusedIdx);\n } else if (focusedIdx < 0) {\n // No focused link inside the nav — nothing to navigate.\n return;\n }\n\n this.keyManager.onKeydown(event);\n }\n}\n\n// ── TabLinkDirective ──\n\nlet nextLinkId = 0;\n\n@Directive({\n selector: 'a[twTabLink]',\n host: {\n '[attr.id]': 'linkId()',\n '[attr.role]': 'linkRole()',\n '[attr.aria-current]': 'ariaCurrent()',\n '[attr.aria-selected]': 'ariaSelected()',\n '[attr.aria-controls]': 'ariaControls()',\n '[attr.aria-disabled]': 'ariaDisabled()',\n '[attr.tabindex]': 'tabIndex()',\n '[class]': 'classes()',\n '(click)': 'onClick($event)',\n '(keydown)': 'onKeydown($event)',\n },\n})\n/**\n * Note: we intentionally do not declare `implements FocusableOption`. The\n * FocusableOption interface requires a `disabled?: boolean` property, but\n * `disabled` here is an InputSignal — a function whose own boolean-coerced\n * value is always truthy. FocusKeyManager only requires a `focus()` method\n * (structurally satisfied below) and we override the disabled check via\n * `.skipPredicate()` when constructing the manager.\n */\nexport class TabLinkDirective {\n /** @internal */\n readonly elementRef = inject<ElementRef<HTMLAnchorElement>>(ElementRef);\n\n private readonly parent = inject(TabNavComponent);\n\n /** Whether the link represents the current active page/tab. Typically bound to router state. Defaults to `false`. */\n readonly active = input(false);\n\n /** When true, the link is visually disabled and cannot be activated by click or keyboard. Defaults to `false`. */\n readonly disabled = input(false);\n\n /** Unique id for this link. Referenced by the panel's `aria-labelledby` when the link is active. Auto-generated when not provided. */\n readonly linkId = input(`tw-tab-link-${nextLinkId++}`);\n\n /**\n * @internal ARIA role — `'tab'` when the nav is wired to a panel (APG\n * tablist pattern requires `role=\"tab\"` on every link); `null` otherwise so\n * the anchor's native `role=\"link\"` wins. Do NOT collapse to a single\n * branch — both are meaningful.\n */\n readonly linkRole = computed(() => (this.parent.resolvedPanel() ? 'tab' : null));\n\n /** @internal `aria-current=\"page\"` on the active link when no tabPanel is associated. */\n readonly ariaCurrent = computed(() => {\n if (this.parent.resolvedPanel()) return null;\n return this.active() ? 'page' : null;\n });\n\n /** @internal `aria-selected` applied only in the tabs pattern. */\n readonly ariaSelected = computed(() => {\n if (!this.parent.resolvedPanel()) return null;\n return this.active() ? 'true' : 'false';\n });\n\n /** @internal `aria-controls` points to the associated panel id. */\n readonly ariaControls = computed(() => this.parent.resolvedPanel()?.id() ?? null);\n\n /** @internal */\n readonly ariaDisabled = computed(() => (this.disabled() ? 'true' : null));\n\n /** @internal Tabindex — roving in the tabs pattern, default for navigation links, always -1 when disabled. */\n readonly tabIndex = computed(() => {\n if (this.disabled()) return -1;\n if (!this.parent.resolvedPanel()) return null;\n return this.active() ? 0 : -1;\n });\n\n /** @internal Combined classes: base + active/inactive state + disabled modifiers + consumer overrides. */\n readonly classes = computed(() => {\n const base = this.parent.linkBaseClasses();\n const state = this.active()\n ? this.parent.activeLinkClasses()\n : this.parent.inactiveLinkClasses();\n const disabled = this.disabled()\n ? 'opacity-50 pointer-events-none cursor-not-allowed'\n : '';\n return twMerge(base, state, disabled, this.parent.linkClass());\n });\n\n /** Programmatically focus this link. */\n focus(): void {\n this.elementRef.nativeElement.focus();\n }\n\n /** @internal Intercepts clicks on disabled links to prevent navigation. */\n onClick(event: MouseEvent): void {\n if (this.disabled()) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n }\n\n /** @internal Handles keyboard activation: blocks Enter/Space on disabled links, and activates the link on Space for enabled ones (anchors do not respond to Space by default). */\n onKeydown(event: KeyboardEvent): void {\n if (this.disabled()) {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n return;\n }\n if (event.key === ' ') {\n event.preventDefault();\n this.elementRef.nativeElement.click();\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|