@kanso-protocol/tabs 0.1.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.
@@ -0,0 +1,215 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, inject, ElementRef, Output, Input, ChangeDetectionStrategy, Component, ContentChildren } from '@angular/core';
3
+
4
+ /**
5
+ * Kanso Protocol — Tab (atom)
6
+ *
7
+ * Single tab for use inside `<kp-tabs>`. Projects an optional leading icon
8
+ * and an optional trailing badge. The `selected` / `disabled` inputs drive
9
+ * the visual state; hover is handled in CSS and focus via `:focus-visible`
10
+ * on the underlying button.
11
+ *
12
+ * @example
13
+ * <kp-tab label="Inbox" [selected]="current === 'inbox'" (click)="current='inbox'">
14
+ * <svg kpTabIcon .../>
15
+ * <kp-badge kpTabBadge>12</kp-badge>
16
+ * </kp-tab>
17
+ */
18
+ class KpTabComponent {
19
+ size = 'md';
20
+ label = '';
21
+ selected = false;
22
+ disabled = false;
23
+ /** Set by the parent `<kp-tabs>` when `fullWidth` is enabled on the container */
24
+ fullWidth = false;
25
+ selectedChange = new EventEmitter();
26
+ /** @internal — exposed so the parent `<kp-tabs>` can focus this tab's
27
+ * inner button as part of its arrow-key roving-tabindex navigation. */
28
+ elementRef = inject(ElementRef);
29
+ get hostClasses() {
30
+ const c = ['kp-tab', `kp-tab--${this.size}`];
31
+ if (this.selected)
32
+ c.push('kp-tab--selected');
33
+ if (this.disabled)
34
+ c.push('kp-tab--disabled');
35
+ if (this.fullWidth)
36
+ c.push('kp-tab--full-width');
37
+ return c.join(' ');
38
+ }
39
+ handleClick(event) {
40
+ if (this.disabled) {
41
+ event.preventDefault();
42
+ event.stopPropagation();
43
+ return;
44
+ }
45
+ this.selectedChange.emit(true);
46
+ }
47
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
48
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: KpTabComponent, isStandalone: true, selector: "kp-tab", inputs: { size: "size", label: "label", selected: "selected", disabled: "disabled", fullWidth: "fullWidth" }, outputs: { selectedChange: "selectedChange" }, host: { properties: { "class": "hostClasses", "attr.role": "\"tab\"", "attr.aria-selected": "selected", "attr.aria-disabled": "disabled || null", "attr.tabindex": "disabled ? -1 : (selected ? 0 : -1)" } }, ngImport: i0, template: `
49
+ <button type="button" class="kp-tab__btn" [disabled]="disabled" (click)="handleClick($event)">
50
+ <span class="kp-tab__icon" aria-hidden="true">
51
+ <ng-content select="[kpTabIcon]"/>
52
+ </span>
53
+ <span class="kp-tab__label">{{ label }}</span>
54
+ <span class="kp-tab__badge">
55
+ <ng-content select="[kpTabBadge]"/>
56
+ </span>
57
+ </button>
58
+ `, isInline: true, styles: [":host{display:inline-flex;box-sizing:border-box;height:var(--kp-tab-h);border-bottom:2px solid var(--kp-color-tabs-tab-underline-rest, transparent);font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif);transition:border-color var(--kp-motion-duration-fast) ease,color .12s ease}:host(.kp-tab--full-width){flex:1 1 0}.kp-tab__btn{all:unset;display:inline-flex;align-items:center;justify-content:center;gap:var(--kp-tab-gap);padding:0 var(--kp-tab-pad-x);height:100%;width:100%;color:var(--kp-color-tabs-tab-fg-rest, var(--kp-color-gray-600));cursor:pointer;font-size:var(--kp-tab-font-size);line-height:var(--kp-tab-line-height);font-weight:500}.kp-tab__btn:focus-visible{outline:2px solid var(--kp-color-focus-ring, var(--kp-color-blue-400));outline-offset:-2px}.kp-tab__icon{display:inline-flex;align-items:center;color:var(--kp-color-tabs-tab-icon-rest, var(--kp-color-gray-500))}.kp-tab__icon:empty{display:none}.kp-tab__icon ::ng-deep svg{width:var(--kp-tab-icon-size);height:var(--kp-tab-icon-size)}.kp-tab__badge{display:inline-flex}.kp-tab__badge:empty{display:none}:host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover){border-bottom-color:var(--kp-color-tabs-tab-underline-hover, var(--kp-color-gray-300))}:host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) .kp-tab__btn{color:var(--kp-color-tabs-tab-fg-hover, var(--kp-color-gray-900))}:host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) .kp-tab__icon{color:var(--kp-color-tabs-tab-icon-hover, var(--kp-color-gray-700))}:host(.kp-tab--selected){border-bottom-color:var(--kp-color-tabs-tab-underline-selected, var(--kp-color-blue-600))}:host(.kp-tab--selected) .kp-tab__btn{color:var(--kp-color-tabs-tab-fg-selected, var(--kp-color-blue-600))}:host(.kp-tab--selected) .kp-tab__icon{color:var(--kp-color-tabs-tab-icon-selected, var(--kp-color-blue-600))}:host(.kp-tab--disabled){border-bottom-color:var(--kp-color-tabs-tab-underline-disabled, transparent)}:host(.kp-tab--disabled) .kp-tab__btn{color:var(--kp-color-tabs-tab-fg-disabled, var(--kp-color-gray-300));cursor:not-allowed}:host(.kp-tab--disabled) .kp-tab__icon{color:var(--kp-color-tabs-tab-icon-disabled, var(--kp-color-gray-300))}:host(.kp-tab--sm){--kp-tab-h: 32px;--kp-tab-pad-x: 12px;--kp-tab-gap: 6px;--kp-tab-font-size: 14px;--kp-tab-line-height: 20px;--kp-tab-icon-size: 14px}:host(.kp-tab--md){--kp-tab-h: 40px;--kp-tab-pad-x: 16px;--kp-tab-gap: 8px;--kp-tab-font-size: 14px;--kp-tab-line-height: 20px;--kp-tab-icon-size: 16px}:host(.kp-tab--lg){--kp-tab-h: 48px;--kp-tab-pad-x: 20px;--kp-tab-gap: 8px;--kp-tab-font-size: 16px;--kp-tab-line-height: 24px;--kp-tab-icon-size: 16px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
59
+ }
60
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpTabComponent, decorators: [{
61
+ type: Component,
62
+ args: [{ selector: 'kp-tab', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, host: {
63
+ '[class]': 'hostClasses',
64
+ '[attr.role]': '"tab"',
65
+ '[attr.aria-selected]': 'selected',
66
+ '[attr.aria-disabled]': 'disabled || null',
67
+ '[attr.tabindex]': 'disabled ? -1 : (selected ? 0 : -1)',
68
+ }, template: `
69
+ <button type="button" class="kp-tab__btn" [disabled]="disabled" (click)="handleClick($event)">
70
+ <span class="kp-tab__icon" aria-hidden="true">
71
+ <ng-content select="[kpTabIcon]"/>
72
+ </span>
73
+ <span class="kp-tab__label">{{ label }}</span>
74
+ <span class="kp-tab__badge">
75
+ <ng-content select="[kpTabBadge]"/>
76
+ </span>
77
+ </button>
78
+ `, styles: [":host{display:inline-flex;box-sizing:border-box;height:var(--kp-tab-h);border-bottom:2px solid var(--kp-color-tabs-tab-underline-rest, transparent);font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif);transition:border-color var(--kp-motion-duration-fast) ease,color .12s ease}:host(.kp-tab--full-width){flex:1 1 0}.kp-tab__btn{all:unset;display:inline-flex;align-items:center;justify-content:center;gap:var(--kp-tab-gap);padding:0 var(--kp-tab-pad-x);height:100%;width:100%;color:var(--kp-color-tabs-tab-fg-rest, var(--kp-color-gray-600));cursor:pointer;font-size:var(--kp-tab-font-size);line-height:var(--kp-tab-line-height);font-weight:500}.kp-tab__btn:focus-visible{outline:2px solid var(--kp-color-focus-ring, var(--kp-color-blue-400));outline-offset:-2px}.kp-tab__icon{display:inline-flex;align-items:center;color:var(--kp-color-tabs-tab-icon-rest, var(--kp-color-gray-500))}.kp-tab__icon:empty{display:none}.kp-tab__icon ::ng-deep svg{width:var(--kp-tab-icon-size);height:var(--kp-tab-icon-size)}.kp-tab__badge{display:inline-flex}.kp-tab__badge:empty{display:none}:host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover){border-bottom-color:var(--kp-color-tabs-tab-underline-hover, var(--kp-color-gray-300))}:host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) .kp-tab__btn{color:var(--kp-color-tabs-tab-fg-hover, var(--kp-color-gray-900))}:host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) .kp-tab__icon{color:var(--kp-color-tabs-tab-icon-hover, var(--kp-color-gray-700))}:host(.kp-tab--selected){border-bottom-color:var(--kp-color-tabs-tab-underline-selected, var(--kp-color-blue-600))}:host(.kp-tab--selected) .kp-tab__btn{color:var(--kp-color-tabs-tab-fg-selected, var(--kp-color-blue-600))}:host(.kp-tab--selected) .kp-tab__icon{color:var(--kp-color-tabs-tab-icon-selected, var(--kp-color-blue-600))}:host(.kp-tab--disabled){border-bottom-color:var(--kp-color-tabs-tab-underline-disabled, transparent)}:host(.kp-tab--disabled) .kp-tab__btn{color:var(--kp-color-tabs-tab-fg-disabled, var(--kp-color-gray-300));cursor:not-allowed}:host(.kp-tab--disabled) .kp-tab__icon{color:var(--kp-color-tabs-tab-icon-disabled, var(--kp-color-gray-300))}:host(.kp-tab--sm){--kp-tab-h: 32px;--kp-tab-pad-x: 12px;--kp-tab-gap: 6px;--kp-tab-font-size: 14px;--kp-tab-line-height: 20px;--kp-tab-icon-size: 14px}:host(.kp-tab--md){--kp-tab-h: 40px;--kp-tab-pad-x: 16px;--kp-tab-gap: 8px;--kp-tab-font-size: 14px;--kp-tab-line-height: 20px;--kp-tab-icon-size: 16px}:host(.kp-tab--lg){--kp-tab-h: 48px;--kp-tab-pad-x: 20px;--kp-tab-gap: 8px;--kp-tab-font-size: 16px;--kp-tab-line-height: 24px;--kp-tab-icon-size: 16px}\n"] }]
79
+ }], propDecorators: { size: [{
80
+ type: Input
81
+ }], label: [{
82
+ type: Input
83
+ }], selected: [{
84
+ type: Input
85
+ }], disabled: [{
86
+ type: Input
87
+ }], fullWidth: [{
88
+ type: Input
89
+ }], selectedChange: [{
90
+ type: Output
91
+ }] } });
92
+
93
+ /**
94
+ * Kanso Protocol — Tabs (container)
95
+ *
96
+ * Horizontal strip of `<kp-tab>` children with a shared bottom border.
97
+ * Cascades `size` and `fullWidth` down to every projected `<kp-tab>`
98
+ * automatically, so callers only set those two inputs in one place.
99
+ *
100
+ * A `[kpTabsMore]` slot projects an optional "More ▾" affordance to the
101
+ * right of the strip — useful when tabs overflow and you want a dropdown
102
+ * to hold the rest.
103
+ *
104
+ * @example
105
+ * <kp-tabs size="md" [fullWidth]="false">
106
+ * <kp-tab label="Overview" [selected]="tab === 'overview'" (selectedChange)="tab = 'overview'"/>
107
+ * <kp-tab label="Activity" [selected]="tab === 'activity'" (selectedChange)="tab = 'activity'"/>
108
+ * <kp-tab label="Settings" [selected]="tab === 'settings'" (selectedChange)="tab = 'settings'"/>
109
+ *
110
+ * <button kpTabsMore>More ▾</button>
111
+ * </kp-tabs>
112
+ */
113
+ class KpTabsComponent {
114
+ size = 'md';
115
+ fullWidth = false;
116
+ tabs;
117
+ host = inject(ElementRef);
118
+ ngAfterContentInit() {
119
+ this.applyToTabs();
120
+ this.tabs.changes.subscribe(() => this.applyToTabs());
121
+ }
122
+ applyToTabs() {
123
+ this.tabs?.forEach((t) => {
124
+ t.size = this.size;
125
+ t.fullWidth = this.fullWidth;
126
+ });
127
+ }
128
+ /**
129
+ * WAI-ARIA tablist keyboard pattern:
130
+ * ArrowLeft / ArrowRight — move to previous / next enabled tab (wraps around)
131
+ * Home / End — jump to first / last enabled tab
132
+ * Enter / Space — select the focused tab (native button handles this too)
133
+ * Disabled tabs are skipped. Selection activates on focus change (automatic
134
+ * activation) since our tab bodies are lightweight to render.
135
+ */
136
+ onKeyDown(event) {
137
+ const all = this.tabs?.toArray() ?? [];
138
+ const enabled = all.filter((t) => !t.disabled);
139
+ if (!enabled.length)
140
+ return;
141
+ const active = this.activeTabIndex(enabled);
142
+ let next = null;
143
+ switch (event.key) {
144
+ case 'ArrowRight':
145
+ next = (active + 1) % enabled.length;
146
+ break;
147
+ case 'ArrowLeft':
148
+ next = (active - 1 + enabled.length) % enabled.length;
149
+ break;
150
+ case 'Home':
151
+ next = 0;
152
+ break;
153
+ case 'End':
154
+ next = enabled.length - 1;
155
+ break;
156
+ default: return;
157
+ }
158
+ event.preventDefault();
159
+ const target = enabled[next];
160
+ // "Consumer owns state" — same pattern as click. Emit (selectedChange)
161
+ // on the target tab and let the parent's [selected] binding propagate.
162
+ if (!target.selected)
163
+ target.selectedChange.emit(true);
164
+ this.focusTab(target);
165
+ }
166
+ activeTabIndex(enabled) {
167
+ const doc = this.host.nativeElement.ownerDocument;
168
+ const focused = doc?.activeElement ?? null;
169
+ const byFocus = enabled.findIndex((t) => t.elementRef.nativeElement.contains(focused));
170
+ if (byFocus >= 0)
171
+ return byFocus;
172
+ const bySelection = enabled.findIndex((t) => t.selected);
173
+ return bySelection >= 0 ? bySelection : 0;
174
+ }
175
+ focusTab(tab) {
176
+ const btn = tab.elementRef.nativeElement.querySelector('button.kp-tab__btn');
177
+ btn?.focus();
178
+ }
179
+ get hostClasses() {
180
+ const c = ['kp-tabs'];
181
+ if (this.fullWidth)
182
+ c.push('kp-tabs--full-width');
183
+ return c.join(' ');
184
+ }
185
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
186
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: KpTabsComponent, isStandalone: true, selector: "kp-tabs", inputs: { size: "size", fullWidth: "fullWidth" }, host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class": "hostClasses", "attr.role": "\"tablist\"" } }, queries: [{ propertyName: "tabs", predicate: KpTabComponent }], ngImport: i0, template: `
187
+ <div class="kp-tabs__row"><ng-content/></div>
188
+ <div class="kp-tabs__more"><ng-content select="[kpTabsMore]"/></div>
189
+ `, isInline: true, styles: [":host{position:relative;display:flex;align-items:stretch;border-bottom:1px solid var(--kp-color-tabs-track-border, var(--kp-color-gray-200));font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif)}.kp-tabs__row{display:flex;flex:1 1 auto}:host(.kp-tabs--full-width) .kp-tabs__row>*{flex:1 1 0}.kp-tabs__more{display:inline-flex;flex:0 0 auto}.kp-tabs__more:empty{display:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
190
+ }
191
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KpTabsComponent, decorators: [{
192
+ type: Component,
193
+ args: [{ selector: 'kp-tabs', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, host: {
194
+ '[class]': 'hostClasses',
195
+ '[attr.role]': '"tablist"',
196
+ '(keydown)': 'onKeyDown($event)',
197
+ }, template: `
198
+ <div class="kp-tabs__row"><ng-content/></div>
199
+ <div class="kp-tabs__more"><ng-content select="[kpTabsMore]"/></div>
200
+ `, styles: [":host{position:relative;display:flex;align-items:stretch;border-bottom:1px solid var(--kp-color-tabs-track-border, var(--kp-color-gray-200));font-family:var(--kp-font-family-sans, \"Onest\", system-ui, sans-serif)}.kp-tabs__row{display:flex;flex:1 1 auto}:host(.kp-tabs--full-width) .kp-tabs__row>*{flex:1 1 0}.kp-tabs__more{display:inline-flex;flex:0 0 auto}.kp-tabs__more:empty{display:none}\n"] }]
201
+ }], propDecorators: { size: [{
202
+ type: Input
203
+ }], fullWidth: [{
204
+ type: Input
205
+ }], tabs: [{
206
+ type: ContentChildren,
207
+ args: [KpTabComponent]
208
+ }] } });
209
+
210
+ /**
211
+ * Generated bundle index. Do not edit.
212
+ */
213
+
214
+ export { KpTabComponent, KpTabsComponent };
215
+ //# sourceMappingURL=kanso-protocol-tabs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kanso-protocol-tabs.mjs","sources":["../../../../../packages/components/tabs/src/tab.component.ts","../../../../../packages/components/tabs/src/tabs.component.ts","../../../../../packages/components/tabs/src/kanso-protocol-tabs.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n EventEmitter,\n Input,\n Output,\n inject,\n} from '@angular/core';\n\nexport type KpTabSize = 'sm' | 'md' | 'lg';\n\n/**\n * Kanso Protocol — Tab (atom)\n *\n * Single tab for use inside `<kp-tabs>`. Projects an optional leading icon\n * and an optional trailing badge. The `selected` / `disabled` inputs drive\n * the visual state; hover is handled in CSS and focus via `:focus-visible`\n * on the underlying button.\n *\n * @example\n * <kp-tab label=\"Inbox\" [selected]=\"current === 'inbox'\" (click)=\"current='inbox'\">\n * <svg kpTabIcon .../>\n * <kp-badge kpTabBadge>12</kp-badge>\n * </kp-tab>\n */\n@Component({\n selector: 'kp-tab',\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses',\n '[attr.role]': '\"tab\"',\n '[attr.aria-selected]': 'selected',\n '[attr.aria-disabled]': 'disabled || null',\n '[attr.tabindex]': 'disabled ? -1 : (selected ? 0 : -1)',\n },\n template: `\n <button type=\"button\" class=\"kp-tab__btn\" [disabled]=\"disabled\" (click)=\"handleClick($event)\">\n <span class=\"kp-tab__icon\" aria-hidden=\"true\">\n <ng-content select=\"[kpTabIcon]\"/>\n </span>\n <span class=\"kp-tab__label\">{{ label }}</span>\n <span class=\"kp-tab__badge\">\n <ng-content select=\"[kpTabBadge]\"/>\n </span>\n </button>\n `,\n styles: [`\n :host {\n display: inline-flex;\n box-sizing: border-box;\n height: var(--kp-tab-h);\n border-bottom: 2px solid var(--kp-color-tabs-tab-underline-rest, transparent);\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n transition: border-color var(--kp-motion-duration-fast) ease, color 120ms ease;\n }\n :host(.kp-tab--full-width) { flex: 1 1 0; }\n\n .kp-tab__btn {\n all: unset;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--kp-tab-gap);\n padding: 0 var(--kp-tab-pad-x);\n height: 100%;\n width: 100%;\n color: var(--kp-color-tabs-tab-fg-rest, var(--kp-color-gray-600));\n cursor: pointer;\n font-size: var(--kp-tab-font-size);\n line-height: var(--kp-tab-line-height);\n font-weight: 500;\n }\n .kp-tab__btn:focus-visible {\n outline: 2px solid var(--kp-color-focus-ring, var(--kp-color-blue-400));\n outline-offset: -2px;\n }\n\n .kp-tab__icon {\n display: inline-flex;\n align-items: center;\n color: var(--kp-color-tabs-tab-icon-rest, var(--kp-color-gray-500));\n }\n .kp-tab__icon:empty { display: none; }\n .kp-tab__icon ::ng-deep svg {\n width: var(--kp-tab-icon-size);\n height: var(--kp-tab-icon-size);\n }\n\n .kp-tab__badge { display: inline-flex; }\n .kp-tab__badge:empty { display: none; }\n\n /* Hover (rest only — disabled / selected handled explicitly) */\n :host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) {\n border-bottom-color: var(--kp-color-tabs-tab-underline-hover, var(--kp-color-gray-300));\n }\n :host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) .kp-tab__btn {\n color: var(--kp-color-tabs-tab-fg-hover, var(--kp-color-gray-900));\n }\n :host(:not(.kp-tab--selected):not(.kp-tab--disabled):hover) .kp-tab__icon {\n color: var(--kp-color-tabs-tab-icon-hover, var(--kp-color-gray-700));\n }\n\n /* Selected */\n :host(.kp-tab--selected) {\n border-bottom-color: var(--kp-color-tabs-tab-underline-selected, var(--kp-color-blue-600));\n }\n :host(.kp-tab--selected) .kp-tab__btn {\n color: var(--kp-color-tabs-tab-fg-selected, var(--kp-color-blue-600));\n }\n :host(.kp-tab--selected) .kp-tab__icon {\n color: var(--kp-color-tabs-tab-icon-selected, var(--kp-color-blue-600));\n }\n\n /* Disabled */\n :host(.kp-tab--disabled) {\n border-bottom-color: var(--kp-color-tabs-tab-underline-disabled, transparent);\n }\n :host(.kp-tab--disabled) .kp-tab__btn {\n color: var(--kp-color-tabs-tab-fg-disabled, var(--kp-color-gray-300));\n cursor: not-allowed;\n }\n :host(.kp-tab--disabled) .kp-tab__icon {\n color: var(--kp-color-tabs-tab-icon-disabled, var(--kp-color-gray-300));\n }\n\n /* Sizes */\n :host(.kp-tab--sm) {\n --kp-tab-h: 32px;\n --kp-tab-pad-x: 12px;\n --kp-tab-gap: 6px;\n --kp-tab-font-size: 14px;\n --kp-tab-line-height: 20px;\n --kp-tab-icon-size: 14px;\n }\n :host(.kp-tab--md) {\n --kp-tab-h: 40px;\n --kp-tab-pad-x: 16px;\n --kp-tab-gap: 8px;\n --kp-tab-font-size: 14px;\n --kp-tab-line-height: 20px;\n --kp-tab-icon-size: 16px;\n }\n :host(.kp-tab--lg) {\n --kp-tab-h: 48px;\n --kp-tab-pad-x: 20px;\n --kp-tab-gap: 8px;\n --kp-tab-font-size: 16px;\n --kp-tab-line-height: 24px;\n --kp-tab-icon-size: 16px;\n }\n `],\n})\nexport class KpTabComponent {\n @Input() size: KpTabSize = 'md';\n @Input() label = '';\n @Input() selected = false;\n @Input() disabled = false;\n /** Set by the parent `<kp-tabs>` when `fullWidth` is enabled on the container */\n @Input() fullWidth = false;\n\n @Output() selectedChange = new EventEmitter<boolean>();\n\n /** @internal — exposed so the parent `<kp-tabs>` can focus this tab's\n * inner button as part of its arrow-key roving-tabindex navigation. */\n readonly elementRef = inject(ElementRef) as ElementRef<HTMLElement>;\n\n get hostClasses(): string {\n const c = ['kp-tab', `kp-tab--${this.size}`];\n if (this.selected) c.push('kp-tab--selected');\n if (this.disabled) c.push('kp-tab--disabled');\n if (this.fullWidth) c.push('kp-tab--full-width');\n return c.join(' ');\n }\n\n handleClick(event: MouseEvent): void {\n if (this.disabled) {\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n this.selectedChange.emit(true);\n }\n}\n","import {\n AfterContentInit,\n ChangeDetectionStrategy,\n Component,\n ContentChildren,\n ElementRef,\n Input,\n QueryList,\n inject,\n} from '@angular/core';\n\nimport { KpTabComponent, KpTabSize } from './tab.component';\n\n/**\n * Kanso Protocol — Tabs (container)\n *\n * Horizontal strip of `<kp-tab>` children with a shared bottom border.\n * Cascades `size` and `fullWidth` down to every projected `<kp-tab>`\n * automatically, so callers only set those two inputs in one place.\n *\n * A `[kpTabsMore]` slot projects an optional \"More ▾\" affordance to the\n * right of the strip — useful when tabs overflow and you want a dropdown\n * to hold the rest.\n *\n * @example\n * <kp-tabs size=\"md\" [fullWidth]=\"false\">\n * <kp-tab label=\"Overview\" [selected]=\"tab === 'overview'\" (selectedChange)=\"tab = 'overview'\"/>\n * <kp-tab label=\"Activity\" [selected]=\"tab === 'activity'\" (selectedChange)=\"tab = 'activity'\"/>\n * <kp-tab label=\"Settings\" [selected]=\"tab === 'settings'\" (selectedChange)=\"tab = 'settings'\"/>\n *\n * <button kpTabsMore>More ▾</button>\n * </kp-tabs>\n */\n@Component({\n selector: 'kp-tabs',\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'hostClasses',\n '[attr.role]': '\"tablist\"',\n '(keydown)': 'onKeyDown($event)',\n },\n template: `\n <div class=\"kp-tabs__row\"><ng-content/></div>\n <div class=\"kp-tabs__more\"><ng-content select=\"[kpTabsMore]\"/></div>\n `,\n styles: [`\n :host {\n position: relative;\n display: flex;\n align-items: stretch;\n border-bottom: 1px solid var(--kp-color-tabs-track-border, var(--kp-color-gray-200));\n font-family: var(--kp-font-family-sans, 'Onest', system-ui, sans-serif);\n }\n\n .kp-tabs__row {\n display: flex;\n flex: 1 1 auto;\n /* The Tab atoms already carry their own border-bottom; no extra row chrome needed. */\n }\n :host(.kp-tabs--full-width) .kp-tabs__row > * { flex: 1 1 0; }\n\n .kp-tabs__more { display: inline-flex; flex: 0 0 auto; }\n .kp-tabs__more:empty { display: none; }\n `],\n})\nexport class KpTabsComponent implements AfterContentInit {\n @Input() size: KpTabSize = 'md';\n @Input() fullWidth = false;\n\n @ContentChildren(KpTabComponent) tabs!: QueryList<KpTabComponent>;\n\n private readonly host = inject(ElementRef) as ElementRef<HTMLElement>;\n\n ngAfterContentInit(): void {\n this.applyToTabs();\n this.tabs.changes.subscribe(() => this.applyToTabs());\n }\n\n private applyToTabs(): void {\n this.tabs?.forEach((t) => {\n t.size = this.size;\n t.fullWidth = this.fullWidth;\n });\n }\n\n /**\n * WAI-ARIA tablist keyboard pattern:\n * ArrowLeft / ArrowRight — move to previous / next enabled tab (wraps around)\n * Home / End — jump to first / last enabled tab\n * Enter / Space — select the focused tab (native button handles this too)\n * Disabled tabs are skipped. Selection activates on focus change (automatic\n * activation) since our tab bodies are lightweight to render.\n */\n onKeyDown(event: KeyboardEvent): void {\n const all = this.tabs?.toArray() ?? [];\n const enabled = all.filter((t) => !t.disabled);\n if (!enabled.length) return;\n\n const active = this.activeTabIndex(enabled);\n let next: number | null = null;\n switch (event.key) {\n case 'ArrowRight': next = (active + 1) % enabled.length; break;\n case 'ArrowLeft': next = (active - 1 + enabled.length) % enabled.length; break;\n case 'Home': next = 0; break;\n case 'End': next = enabled.length - 1; break;\n default: return;\n }\n event.preventDefault();\n const target = enabled[next];\n // \"Consumer owns state\" — same pattern as click. Emit (selectedChange)\n // on the target tab and let the parent's [selected] binding propagate.\n if (!target.selected) target.selectedChange.emit(true);\n this.focusTab(target);\n }\n\n private activeTabIndex(enabled: KpTabComponent[]): number {\n const doc = this.host.nativeElement.ownerDocument;\n const focused = doc?.activeElement ?? null;\n const byFocus = enabled.findIndex((t) => t.elementRef.nativeElement.contains(focused));\n if (byFocus >= 0) return byFocus;\n const bySelection = enabled.findIndex((t) => t.selected);\n return bySelection >= 0 ? bySelection : 0;\n }\n\n private focusTab(tab: KpTabComponent): void {\n const btn = tab.elementRef.nativeElement.querySelector<HTMLButtonElement>('button.kp-tab__btn');\n btn?.focus();\n }\n\n get hostClasses(): string {\n const c = ['kp-tabs'];\n if (this.fullWidth) c.push('kp-tabs--full-width');\n return c.join(' ');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@kanso-protocol/tabs",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "peerDependencies": {
6
+ "@angular/core": "^18.0.0",
7
+ "@angular/common": "^18.0.0",
8
+ "@kanso-protocol/core": "^0.0.1"
9
+ },
10
+ "description": "Kanso Protocol — tabs (component).",
11
+ "author": "GregNBlack",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/GregNBlack/kanso-protocol.git",
15
+ "directory": "packages/components/tabs"
16
+ },
17
+ "homepage": "https://gregnblack.github.io/kanso-protocol/?path=/docs/components-tabs--docs",
18
+ "bugs": "https://github.com/GregNBlack/kanso-protocol/issues",
19
+ "keywords": [
20
+ "design-system",
21
+ "angular",
22
+ "kanso",
23
+ "tabs"
24
+ ],
25
+ "sideEffects": false,
26
+ "module": "fesm2022/kanso-protocol-tabs.mjs",
27
+ "typings": "types/kanso-protocol-tabs.d.ts",
28
+ "exports": {
29
+ "./package.json": {
30
+ "default": "./package.json"
31
+ },
32
+ ".": {
33
+ "types": "./types/kanso-protocol-tabs.d.ts",
34
+ "default": "./fesm2022/kanso-protocol-tabs.mjs"
35
+ }
36
+ },
37
+ "type": "module",
38
+ "dependencies": {
39
+ "tslib": "^2.3.0"
40
+ }
41
+ }
@@ -0,0 +1,80 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, ElementRef, AfterContentInit, QueryList } from '@angular/core';
3
+
4
+ type KpTabSize = 'sm' | 'md' | 'lg';
5
+ /**
6
+ * Kanso Protocol — Tab (atom)
7
+ *
8
+ * Single tab for use inside `<kp-tabs>`. Projects an optional leading icon
9
+ * and an optional trailing badge. The `selected` / `disabled` inputs drive
10
+ * the visual state; hover is handled in CSS and focus via `:focus-visible`
11
+ * on the underlying button.
12
+ *
13
+ * @example
14
+ * <kp-tab label="Inbox" [selected]="current === 'inbox'" (click)="current='inbox'">
15
+ * <svg kpTabIcon .../>
16
+ * <kp-badge kpTabBadge>12</kp-badge>
17
+ * </kp-tab>
18
+ */
19
+ declare class KpTabComponent {
20
+ size: KpTabSize;
21
+ label: string;
22
+ selected: boolean;
23
+ disabled: boolean;
24
+ /** Set by the parent `<kp-tabs>` when `fullWidth` is enabled on the container */
25
+ fullWidth: boolean;
26
+ selectedChange: EventEmitter<boolean>;
27
+ /** @internal — exposed so the parent `<kp-tabs>` can focus this tab's
28
+ * inner button as part of its arrow-key roving-tabindex navigation. */
29
+ readonly elementRef: ElementRef<HTMLElement>;
30
+ get hostClasses(): string;
31
+ handleClick(event: MouseEvent): void;
32
+ static ɵfac: i0.ɵɵFactoryDeclaration<KpTabComponent, never>;
33
+ static ɵcmp: i0.ɵɵComponentDeclaration<KpTabComponent, "kp-tab", never, { "size": { "alias": "size"; "required": false; }; "label": { "alias": "label"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "fullWidth": { "alias": "fullWidth"; "required": false; }; }, { "selectedChange": "selectedChange"; }, never, ["[kpTabIcon]", "[kpTabBadge]"], true, never>;
34
+ }
35
+
36
+ /**
37
+ * Kanso Protocol — Tabs (container)
38
+ *
39
+ * Horizontal strip of `<kp-tab>` children with a shared bottom border.
40
+ * Cascades `size` and `fullWidth` down to every projected `<kp-tab>`
41
+ * automatically, so callers only set those two inputs in one place.
42
+ *
43
+ * A `[kpTabsMore]` slot projects an optional "More ▾" affordance to the
44
+ * right of the strip — useful when tabs overflow and you want a dropdown
45
+ * to hold the rest.
46
+ *
47
+ * @example
48
+ * <kp-tabs size="md" [fullWidth]="false">
49
+ * <kp-tab label="Overview" [selected]="tab === 'overview'" (selectedChange)="tab = 'overview'"/>
50
+ * <kp-tab label="Activity" [selected]="tab === 'activity'" (selectedChange)="tab = 'activity'"/>
51
+ * <kp-tab label="Settings" [selected]="tab === 'settings'" (selectedChange)="tab = 'settings'"/>
52
+ *
53
+ * <button kpTabsMore>More ▾</button>
54
+ * </kp-tabs>
55
+ */
56
+ declare class KpTabsComponent implements AfterContentInit {
57
+ size: KpTabSize;
58
+ fullWidth: boolean;
59
+ tabs: QueryList<KpTabComponent>;
60
+ private readonly host;
61
+ ngAfterContentInit(): void;
62
+ private applyToTabs;
63
+ /**
64
+ * WAI-ARIA tablist keyboard pattern:
65
+ * ArrowLeft / ArrowRight — move to previous / next enabled tab (wraps around)
66
+ * Home / End — jump to first / last enabled tab
67
+ * Enter / Space — select the focused tab (native button handles this too)
68
+ * Disabled tabs are skipped. Selection activates on focus change (automatic
69
+ * activation) since our tab bodies are lightweight to render.
70
+ */
71
+ onKeyDown(event: KeyboardEvent): void;
72
+ private activeTabIndex;
73
+ private focusTab;
74
+ get hostClasses(): string;
75
+ static ɵfac: i0.ɵɵFactoryDeclaration<KpTabsComponent, never>;
76
+ static ɵcmp: i0.ɵɵComponentDeclaration<KpTabsComponent, "kp-tabs", never, { "size": { "alias": "size"; "required": false; }; "fullWidth": { "alias": "fullWidth"; "required": false; }; }, {}, ["tabs"], ["*", "[kpTabsMore]"], true, never>;
77
+ }
78
+
79
+ export { KpTabComponent, KpTabsComponent };
80
+ export type { KpTabSize };