@fuse_ui/tabs 0.0.1
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/README.md +7 -0
- package/fesm2022/fuse_ui-tabs.mjs +100 -0
- package/fesm2022/fuse_ui-tabs.mjs.map +1 -0
- package/package.json +60 -0
- package/types/fuse_ui-tabs.d.ts +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, input, inject, computed, ChangeDetectionStrategy, Component, output, contentChildren, viewChild, viewChildren, signal, Injector, DestroyRef, effect, afterNextRender } from '@angular/core';
|
|
3
|
+
import { FuseBadgeComponent } from '@fuse_ui/badge';
|
|
4
|
+
|
|
5
|
+
const FUSE_TABS = new InjectionToken('FUSE_TABS');
|
|
6
|
+
|
|
7
|
+
class FuseTabComponent {
|
|
8
|
+
// ─── Inputs ──────────────────────────────────────────────────────────────────
|
|
9
|
+
tabId = input.required(...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
|
|
10
|
+
label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
11
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
12
|
+
badge = input(null, ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
|
|
13
|
+
// ─── Parent context ───────────────────────────────────────────────────────────
|
|
14
|
+
tabs = inject(FUSE_TABS);
|
|
15
|
+
// ─── Derived ─────────────────────────────────────────────────────────────────
|
|
16
|
+
isActive = computed(() => this.tabs.activeTabId() === this.tabId(), ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
|
|
17
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuseTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.6", type: FuseTabComponent, isStandalone: true, selector: "fuse-tab", inputs: { tabId: { classPropertyName: "tabId", publicName: "tabId", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "fuse-tab-host" }, ngImport: i0, template: "<div\n role=\"tabpanel\"\n [id]=\"'panel-' + tabId()\"\n [attr.aria-labelledby]=\"tabId()\"\n [hidden]=\"!isActive()\"\n class=\"fuse-tab__panel\">\n <ng-content></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19
|
+
}
|
|
20
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuseTabComponent, decorators: [{
|
|
21
|
+
type: Component,
|
|
22
|
+
args: [{ selector: 'fuse-tab', standalone: true, imports: [FuseBadgeComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'fuse-tab-host' }, template: "<div\n role=\"tabpanel\"\n [id]=\"'panel-' + tabId()\"\n [attr.aria-labelledby]=\"tabId()\"\n [hidden]=\"!isActive()\"\n class=\"fuse-tab__panel\">\n <ng-content></ng-content>\n</div>\n" }]
|
|
23
|
+
}], propDecorators: { tabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabId", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }] } });
|
|
24
|
+
|
|
25
|
+
class FuseTabsComponent {
|
|
26
|
+
// ─── Inputs ──────────────────────────────────────────────────────────────────
|
|
27
|
+
activeTab = input('', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
28
|
+
variant = input('line', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
29
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
30
|
+
// ─── Outputs ─────────────────────────────────────────────────────────────────
|
|
31
|
+
tabChange = output();
|
|
32
|
+
// ─── Queries ─────────────────────────────────────────────────────────────────
|
|
33
|
+
tabs = contentChildren(FuseTabComponent, ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
34
|
+
tabList = viewChild.required('tabList');
|
|
35
|
+
indicator = viewChild('indicator', ...(ngDevMode ? [{ debugName: "indicator" }] : /* istanbul ignore next */ []));
|
|
36
|
+
tabButtons = viewChildren('tabBtn', ...(ngDevMode ? [{ debugName: "tabButtons" }] : /* istanbul ignore next */ []));
|
|
37
|
+
// ─── State ───────────────────────────────────────────────────────────────────
|
|
38
|
+
activeTabId = signal('', ...(ngDevMode ? [{ debugName: "activeTabId" }] : /* istanbul ignore next */ []));
|
|
39
|
+
// ─── Services ────────────────────────────────────────────────────────────────
|
|
40
|
+
injector = inject(Injector);
|
|
41
|
+
destroyRef = inject(DestroyRef);
|
|
42
|
+
// ─── Computed ────────────────────────────────────────────────────────────────
|
|
43
|
+
tabListClass = computed(() => [
|
|
44
|
+
'fuse-tabs__list',
|
|
45
|
+
`fuse-tabs__list--${this.variant()}`,
|
|
46
|
+
`fuse-tabs__list--${this.size()}`,
|
|
47
|
+
].join(' '), ...(ngDevMode ? [{ debugName: "tabListClass" }] : /* istanbul ignore next */ []));
|
|
48
|
+
tabBtnClass = (tab) => computed(() => [
|
|
49
|
+
'fuse-tab-btn',
|
|
50
|
+
this.activeTabId() === tab.tabId() ? 'fuse-tab-btn--active' : '',
|
|
51
|
+
tab.disabled() ? 'fuse-tab-btn--disabled' : '',
|
|
52
|
+
].filter(Boolean).join(' '));
|
|
53
|
+
// ─── Sync activeTab input → activeTabId signal ───────────────────────────────
|
|
54
|
+
_sync = effect(() => {
|
|
55
|
+
const desired = this.activeTab() || this.tabs()[0]?.tabId() || '';
|
|
56
|
+
this.activeTabId.set(desired);
|
|
57
|
+
}, ...(ngDevMode ? [{ debugName: "_sync" }] : /* istanbul ignore next */ []));
|
|
58
|
+
// ─── Lifecycle ───────────────────────────────────────────────────────────────
|
|
59
|
+
constructor() {
|
|
60
|
+
afterNextRender(() => this.updateIndicator());
|
|
61
|
+
}
|
|
62
|
+
ngAfterViewInit() {
|
|
63
|
+
// Re-measure when tab list is ready (handles SSR-safe fallback)
|
|
64
|
+
this.updateIndicator();
|
|
65
|
+
}
|
|
66
|
+
// ─── Public API (TabsRef) ────────────────────────────────────────────────────
|
|
67
|
+
setActive(tabId) {
|
|
68
|
+
this.activeTabId.set(tabId);
|
|
69
|
+
this.tabChange.emit(tabId);
|
|
70
|
+
// Wait for Angular to re-render the active class before measuring
|
|
71
|
+
afterNextRender(() => this.updateIndicator(), { injector: this.injector });
|
|
72
|
+
}
|
|
73
|
+
// ─── Indicator ───────────────────────────────────────────────────────────────
|
|
74
|
+
updateIndicator() {
|
|
75
|
+
const indicatorRef = this.indicator();
|
|
76
|
+
if (!indicatorRef)
|
|
77
|
+
return; // only 'line' variant has indicator
|
|
78
|
+
const buttons = this.tabButtons();
|
|
79
|
+
const idx = this.tabs().findIndex(t => t.tabId() === this.activeTabId());
|
|
80
|
+
if (idx < 0 || !buttons[idx])
|
|
81
|
+
return;
|
|
82
|
+
const btn = buttons[idx].nativeElement;
|
|
83
|
+
const el = indicatorRef.nativeElement;
|
|
84
|
+
el.style.left = `${btn.offsetLeft}px`;
|
|
85
|
+
el.style.width = `${btn.offsetWidth}px`;
|
|
86
|
+
}
|
|
87
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuseTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
88
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuseTabsComponent, isStandalone: true, selector: "fuse-tabs", inputs: { activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabChange: "tabChange" }, host: { classAttribute: "fuse-tabs-host" }, providers: [{ provide: FUSE_TABS, useExisting: FuseTabsComponent }], queries: [{ propertyName: "tabs", predicate: FuseTabComponent, isSignal: true }], viewQueries: [{ propertyName: "tabList", first: true, predicate: ["tabList"], descendants: true, isSignal: true }, { propertyName: "indicator", first: true, predicate: ["indicator"], descendants: true, isSignal: true }, { propertyName: "tabButtons", predicate: ["tabBtn"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #tabList [class]=\"tabListClass()\" role=\"tablist\">\n @for (tab of tabs(); track tab.tabId()) {\n <button\n #tabBtn\n role=\"tab\"\n type=\"button\"\n [id]=\"tab.tabId()\"\n [attr.aria-selected]=\"activeTabId() === tab.tabId()\"\n [attr.aria-controls]=\"'panel-' + tab.tabId()\"\n [disabled]=\"tab.disabled() || null\"\n [class]=\"tabBtnClass(tab)()\"\n (click)=\"setActive(tab.tabId())\">\n {{ tab.label() }}\n @if (tab.badge()) {\n <fuse-badge [content]=\"tab.badge()!\" size=\"sm\"></fuse-badge>\n }\n </button>\n }\n\n @if (variant() === 'line') {\n <div #indicator class=\"fuse-tabs__indicator\" aria-hidden=\"true\"></div>\n }\n</div>\n\n<div class=\"fuse-tabs__panels\">\n <ng-content></ng-content>\n</div>\n", styles: [":host{display:block;font-family:var(--fuse-font-family, system-ui, sans-serif)}:host-context(.ios){--fuse-tabs-radius: var(--fuse-radius-lg, 12px)}:host-context(.md){--fuse-tabs-radius: var(--fuse-radius-md, 8px)}.fuse-tabs__list{position:relative;display:flex;align-items:center;gap:0}.fuse-tabs__list--line{border-bottom:1px solid var(--fuse-color-border-default);gap:0}.fuse-tabs__list--pills{gap:var(--fuse-spacing-1, 4px);padding:var(--fuse-spacing-1, 4px)}.fuse-tabs__list--boxed{gap:0;background:var(--fuse-color-bg-sunken);border:1px solid var(--fuse-color-border-default);border-radius:var(--fuse-tabs-radius, var(--fuse-radius-md, 8px));padding:var(--fuse-spacing-1, 4px)}.fuse-tab-btn{display:inline-flex;align-items:center;gap:var(--fuse-spacing-1, 6px);border:none;background:transparent;color:var(--fuse-color-text-secondary);font-family:inherit;font-weight:500;cursor:pointer;white-space:nowrap;position:relative;transition:color var(--fuse-duration-fast, .15s) var(--fuse-easing-smooth, ease),background var(--fuse-duration-fast, .15s) var(--fuse-easing-smooth, ease)}@media(prefers-reduced-motion:reduce){.fuse-tab-btn{transition:none}}.fuse-tab-btn:focus-visible{outline:2px solid var(--fuse-color-border-focus);outline-offset:2px;border-radius:var(--fuse-radius-sm, 4px)}.fuse-tab-btn:hover:not(.fuse-tab-btn--disabled):not(.fuse-tab-btn--active){color:var(--fuse-color-text-primary)}.fuse-tab-btn.fuse-tab-btn--active{color:var(--fuse-color-primary)}.fuse-tab-btn.fuse-tab-btn--disabled,.fuse-tab-btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.fuse-tabs__list--sm .fuse-tab-btn{padding:var(--fuse-spacing-2, 6px) var(--fuse-spacing-3, 10px);font-size:var(--fuse-fluid-sm, .875rem)}.fuse-tabs__list--md .fuse-tab-btn{padding:var(--fuse-spacing-3, 8px) var(--fuse-spacing-4, 14px);font-size:var(--fuse-fluid-md, 1rem)}.fuse-tabs__list--pills .fuse-tab-btn{border-radius:var(--fuse-radius-full, 9999px)}.fuse-tabs__list--pills .fuse-tab-btn.fuse-tab-btn--active{background:var(--fuse-color-primary);color:var(--fuse-color-on-primary)}.fuse-tabs__list--boxed .fuse-tab-btn{border-radius:calc(var(--fuse-tabs-radius, var(--fuse-radius-md, 8px)) - 2px);flex:1;justify-content:center}.fuse-tabs__list--boxed .fuse-tab-btn.fuse-tab-btn--active{background:var(--fuse-color-bg-surface);color:var(--fuse-color-text-primary);box-shadow:var(--fuse-shadow-sm)}.fuse-tabs__indicator{position:absolute;bottom:-1px;height:2px;background:var(--fuse-color-primary);border-radius:var(--fuse-radius-full, 9999px);transition:left var(--fuse-duration-slow, .3s) var(--fuse-easing-spring-enter, cubic-bezier(.34, 1.56, .64, 1)),width var(--fuse-duration-slow, .3s) var(--fuse-easing-spring-enter, cubic-bezier(.34, 1.56, .64, 1))}@media(prefers-reduced-motion:reduce){.fuse-tabs__indicator{transition:none}}.fuse-tabs__panels{padding-top:var(--fuse-spacing-4, 16px)}fuse-tab{display:block}\n"], dependencies: [{ kind: "component", type: FuseBadgeComponent, selector: "fuse-badge", inputs: ["variant", "color", "size", "dot", "content"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
89
|
+
}
|
|
90
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuseTabsComponent, decorators: [{
|
|
91
|
+
type: Component,
|
|
92
|
+
args: [{ selector: 'fuse-tabs', standalone: true, imports: [FuseTabComponent, FuseBadgeComponent], changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: FUSE_TABS, useExisting: FuseTabsComponent }], host: { class: 'fuse-tabs-host' }, template: "<div #tabList [class]=\"tabListClass()\" role=\"tablist\">\n @for (tab of tabs(); track tab.tabId()) {\n <button\n #tabBtn\n role=\"tab\"\n type=\"button\"\n [id]=\"tab.tabId()\"\n [attr.aria-selected]=\"activeTabId() === tab.tabId()\"\n [attr.aria-controls]=\"'panel-' + tab.tabId()\"\n [disabled]=\"tab.disabled() || null\"\n [class]=\"tabBtnClass(tab)()\"\n (click)=\"setActive(tab.tabId())\">\n {{ tab.label() }}\n @if (tab.badge()) {\n <fuse-badge [content]=\"tab.badge()!\" size=\"sm\"></fuse-badge>\n }\n </button>\n }\n\n @if (variant() === 'line') {\n <div #indicator class=\"fuse-tabs__indicator\" aria-hidden=\"true\"></div>\n }\n</div>\n\n<div class=\"fuse-tabs__panels\">\n <ng-content></ng-content>\n</div>\n", styles: [":host{display:block;font-family:var(--fuse-font-family, system-ui, sans-serif)}:host-context(.ios){--fuse-tabs-radius: var(--fuse-radius-lg, 12px)}:host-context(.md){--fuse-tabs-radius: var(--fuse-radius-md, 8px)}.fuse-tabs__list{position:relative;display:flex;align-items:center;gap:0}.fuse-tabs__list--line{border-bottom:1px solid var(--fuse-color-border-default);gap:0}.fuse-tabs__list--pills{gap:var(--fuse-spacing-1, 4px);padding:var(--fuse-spacing-1, 4px)}.fuse-tabs__list--boxed{gap:0;background:var(--fuse-color-bg-sunken);border:1px solid var(--fuse-color-border-default);border-radius:var(--fuse-tabs-radius, var(--fuse-radius-md, 8px));padding:var(--fuse-spacing-1, 4px)}.fuse-tab-btn{display:inline-flex;align-items:center;gap:var(--fuse-spacing-1, 6px);border:none;background:transparent;color:var(--fuse-color-text-secondary);font-family:inherit;font-weight:500;cursor:pointer;white-space:nowrap;position:relative;transition:color var(--fuse-duration-fast, .15s) var(--fuse-easing-smooth, ease),background var(--fuse-duration-fast, .15s) var(--fuse-easing-smooth, ease)}@media(prefers-reduced-motion:reduce){.fuse-tab-btn{transition:none}}.fuse-tab-btn:focus-visible{outline:2px solid var(--fuse-color-border-focus);outline-offset:2px;border-radius:var(--fuse-radius-sm, 4px)}.fuse-tab-btn:hover:not(.fuse-tab-btn--disabled):not(.fuse-tab-btn--active){color:var(--fuse-color-text-primary)}.fuse-tab-btn.fuse-tab-btn--active{color:var(--fuse-color-primary)}.fuse-tab-btn.fuse-tab-btn--disabled,.fuse-tab-btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.fuse-tabs__list--sm .fuse-tab-btn{padding:var(--fuse-spacing-2, 6px) var(--fuse-spacing-3, 10px);font-size:var(--fuse-fluid-sm, .875rem)}.fuse-tabs__list--md .fuse-tab-btn{padding:var(--fuse-spacing-3, 8px) var(--fuse-spacing-4, 14px);font-size:var(--fuse-fluid-md, 1rem)}.fuse-tabs__list--pills .fuse-tab-btn{border-radius:var(--fuse-radius-full, 9999px)}.fuse-tabs__list--pills .fuse-tab-btn.fuse-tab-btn--active{background:var(--fuse-color-primary);color:var(--fuse-color-on-primary)}.fuse-tabs__list--boxed .fuse-tab-btn{border-radius:calc(var(--fuse-tabs-radius, var(--fuse-radius-md, 8px)) - 2px);flex:1;justify-content:center}.fuse-tabs__list--boxed .fuse-tab-btn.fuse-tab-btn--active{background:var(--fuse-color-bg-surface);color:var(--fuse-color-text-primary);box-shadow:var(--fuse-shadow-sm)}.fuse-tabs__indicator{position:absolute;bottom:-1px;height:2px;background:var(--fuse-color-primary);border-radius:var(--fuse-radius-full, 9999px);transition:left var(--fuse-duration-slow, .3s) var(--fuse-easing-spring-enter, cubic-bezier(.34, 1.56, .64, 1)),width var(--fuse-duration-slow, .3s) var(--fuse-easing-spring-enter, cubic-bezier(.34, 1.56, .64, 1))}@media(prefers-reduced-motion:reduce){.fuse-tabs__indicator{transition:none}}.fuse-tabs__panels{padding-top:var(--fuse-spacing-4, 16px)}fuse-tab{display:block}\n"] }]
|
|
93
|
+
}], ctorParameters: () => [], propDecorators: { activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FuseTabComponent), { isSignal: true }] }], tabList: [{ type: i0.ViewChild, args: ['tabList', { isSignal: true }] }], indicator: [{ type: i0.ViewChild, args: ['indicator', { isSignal: true }] }], tabButtons: [{ type: i0.ViewChildren, args: ['tabBtn', { isSignal: true }] }] } });
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generated bundle index. Do not edit.
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
export { FUSE_TABS, FuseTabComponent, FuseTabsComponent };
|
|
100
|
+
//# sourceMappingURL=fuse_ui-tabs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuse_ui-tabs.mjs","sources":["../../../../packages/tabs/src/lib/tabs/fuse-tabs.token.ts","../../../../packages/tabs/src/lib/tabs/fuse-tab.component.ts","../../../../packages/tabs/src/lib/tabs/fuse-tab.component.html","../../../../packages/tabs/src/lib/tabs/fuse-tabs.component.ts","../../../../packages/tabs/src/lib/tabs/fuse-tabs.component.html","../../../../packages/tabs/src/fuse_ui-tabs.ts"],"sourcesContent":["import { InjectionToken, Signal } from '@angular/core';\n\nexport interface TabsRef {\n activeTabId: Signal<string>;\n setActive(tabId: string): void;\n}\n\nexport const FUSE_TABS = new InjectionToken<TabsRef>('FUSE_TABS');\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n inject,\n input,\n} from '@angular/core';\nimport { FuseBadgeComponent } from '@fuse_ui/badge';\nimport { FUSE_TABS } from './fuse-tabs.token';\n\n@Component({\n selector: 'fuse-tab',\n standalone: true,\n imports: [FuseBadgeComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './fuse-tab.component.html',\n host: { class: 'fuse-tab-host' },\n})\nexport class FuseTabComponent {\n // ─── Inputs ──────────────────────────────────────────────────────────────────\n\n readonly tabId = input.required<string>();\n readonly label = input.required<string>();\n readonly disabled = input(false);\n readonly badge = input<string | null>(null);\n\n // ─── Parent context ───────────────────────────────────────────────────────────\n\n protected readonly tabs = inject(FUSE_TABS);\n\n // ─── Derived ─────────────────────────────────────────────────────────────────\n\n protected readonly isActive = computed(() =>\n this.tabs.activeTabId() === this.tabId()\n );\n}\n","<div\n role=\"tabpanel\"\n [id]=\"'panel-' + tabId()\"\n [attr.aria-labelledby]=\"tabId()\"\n [hidden]=\"!isActive()\"\n class=\"fuse-tab__panel\">\n <ng-content></ng-content>\n</div>\n","import {\n AfterViewInit,\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n Injector,\n afterNextRender,\n computed,\n contentChildren,\n effect,\n inject,\n output,\n input,\n signal,\n viewChild,\n viewChildren,\n} from '@angular/core';\nimport { FuseBadgeComponent } from '@fuse_ui/badge';\nimport { FuseTabComponent } from './fuse-tab.component';\nimport { FUSE_TABS, TabsRef } from './fuse-tabs.token';\n\n@Component({\n selector: 'fuse-tabs',\n standalone: true,\n imports: [FuseTabComponent, FuseBadgeComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [{ provide: FUSE_TABS, useExisting: FuseTabsComponent }],\n templateUrl: './fuse-tabs.component.html',\n styleUrl: './fuse-tabs.component.scss',\n host: { class: 'fuse-tabs-host' },\n})\nexport class FuseTabsComponent implements TabsRef, AfterViewInit {\n // ─── Inputs ──────────────────────────────────────────────────────────────────\n\n readonly activeTab = input<string>('');\n readonly variant = input<'line' | 'pills' | 'boxed'>('line');\n readonly size = input<'sm' | 'md'>('md');\n\n // ─── Outputs ─────────────────────────────────────────────────────────────────\n\n readonly tabChange = output<string>();\n\n // ─── Queries ─────────────────────────────────────────────────────────────────\n\n readonly tabs = contentChildren(FuseTabComponent);\n readonly tabList = viewChild.required<ElementRef<HTMLElement>>('tabList');\n readonly indicator = viewChild<ElementRef<HTMLElement>>('indicator');\n readonly tabButtons = viewChildren<ElementRef<HTMLButtonElement>>('tabBtn');\n\n // ─── State ───────────────────────────────────────────────────────────────────\n\n readonly activeTabId = signal('');\n\n // ─── Services ────────────────────────────────────────────────────────────────\n\n private readonly injector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n\n // ─── Computed ────────────────────────────────────────────────────────────────\n\n protected readonly tabListClass = computed(() =>\n [\n 'fuse-tabs__list',\n `fuse-tabs__list--${this.variant()}`,\n `fuse-tabs__list--${this.size()}`,\n ].join(' ')\n );\n\n protected readonly tabBtnClass = (tab: FuseTabComponent) =>\n computed(() => [\n 'fuse-tab-btn',\n this.activeTabId() === tab.tabId() ? 'fuse-tab-btn--active' : '',\n tab.disabled() ? 'fuse-tab-btn--disabled' : '',\n ].filter(Boolean).join(' '));\n\n // ─── Sync activeTab input → activeTabId signal ───────────────────────────────\n\n private readonly _sync = effect(() => {\n const desired = this.activeTab() || this.tabs()[0]?.tabId() || '';\n this.activeTabId.set(desired);\n });\n\n // ─── Lifecycle ───────────────────────────────────────────────────────────────\n\n constructor() {\n afterNextRender(() => this.updateIndicator());\n }\n\n ngAfterViewInit(): void {\n // Re-measure when tab list is ready (handles SSR-safe fallback)\n this.updateIndicator();\n }\n\n // ─── Public API (TabsRef) ────────────────────────────────────────────────────\n\n setActive(tabId: string): void {\n this.activeTabId.set(tabId);\n this.tabChange.emit(tabId);\n // Wait for Angular to re-render the active class before measuring\n afterNextRender(() => this.updateIndicator(), { injector: this.injector });\n }\n\n // ─── Indicator ───────────────────────────────────────────────────────────────\n\n private updateIndicator(): void {\n const indicatorRef = this.indicator();\n if (!indicatorRef) return; // only 'line' variant has indicator\n\n const buttons = this.tabButtons();\n const idx = this.tabs().findIndex(t => t.tabId() === this.activeTabId());\n if (idx < 0 || !buttons[idx]) return;\n\n const btn = buttons[idx].nativeElement;\n const el = indicatorRef.nativeElement;\n el.style.left = `${btn.offsetLeft}px`;\n el.style.width = `${btn.offsetWidth}px`;\n }\n}\n","<div #tabList [class]=\"tabListClass()\" role=\"tablist\">\n @for (tab of tabs(); track tab.tabId()) {\n <button\n #tabBtn\n role=\"tab\"\n type=\"button\"\n [id]=\"tab.tabId()\"\n [attr.aria-selected]=\"activeTabId() === tab.tabId()\"\n [attr.aria-controls]=\"'panel-' + tab.tabId()\"\n [disabled]=\"tab.disabled() || null\"\n [class]=\"tabBtnClass(tab)()\"\n (click)=\"setActive(tab.tabId())\">\n {{ tab.label() }}\n @if (tab.badge()) {\n <fuse-badge [content]=\"tab.badge()!\" size=\"sm\"></fuse-badge>\n }\n </button>\n }\n\n @if (variant() === 'line') {\n <div #indicator class=\"fuse-tabs__indicator\" aria-hidden=\"true\"></div>\n }\n</div>\n\n<div class=\"fuse-tabs__panels\">\n <ng-content></ng-content>\n</div>\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,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fuse_ui/tabs",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"fuse-ui",
|
|
10
|
+
"angular",
|
|
11
|
+
"ionic",
|
|
12
|
+
"ionic8",
|
|
13
|
+
"angular18",
|
|
14
|
+
"angular19",
|
|
15
|
+
"angular20",
|
|
16
|
+
"angular21",
|
|
17
|
+
"ui-components",
|
|
18
|
+
"design-system",
|
|
19
|
+
"css-variables",
|
|
20
|
+
"signals",
|
|
21
|
+
"standalone",
|
|
22
|
+
"multi-theme",
|
|
23
|
+
"dark-mode",
|
|
24
|
+
"fluid-typography",
|
|
25
|
+
"animated"
|
|
26
|
+
],
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@angular/core": ">=18.0.0",
|
|
29
|
+
"@angular/common": ">=18.0.0",
|
|
30
|
+
"rxjs": ">=7.4.0",
|
|
31
|
+
"@fuse_ui/badge": "0.0.1"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"@ionic/angular": {
|
|
35
|
+
"optional": true
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"sideEffects": [
|
|
39
|
+
"*.css",
|
|
40
|
+
"**/*.scss"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20.0.0"
|
|
44
|
+
},
|
|
45
|
+
"module": "fesm2022/fuse_ui-tabs.mjs",
|
|
46
|
+
"typings": "types/fuse_ui-tabs.d.ts",
|
|
47
|
+
"exports": {
|
|
48
|
+
"./package.json": {
|
|
49
|
+
"default": "./package.json"
|
|
50
|
+
},
|
|
51
|
+
".": {
|
|
52
|
+
"types": "./types/fuse_ui-tabs.d.ts",
|
|
53
|
+
"default": "./fesm2022/fuse_ui-tabs.mjs"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"type": "module",
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"tslib": "^2.3.0"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { InjectionToken, Signal, AfterViewInit, ElementRef } from '@angular/core';
|
|
3
|
+
import * as _fuse_ui_tabs from '@fuse_ui/tabs';
|
|
4
|
+
|
|
5
|
+
interface TabsRef {
|
|
6
|
+
activeTabId: Signal<string>;
|
|
7
|
+
setActive(tabId: string): void;
|
|
8
|
+
}
|
|
9
|
+
declare const FUSE_TABS: InjectionToken<TabsRef>;
|
|
10
|
+
|
|
11
|
+
declare class FuseTabComponent {
|
|
12
|
+
readonly tabId: _angular_core.InputSignal<string>;
|
|
13
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
14
|
+
readonly disabled: _angular_core.InputSignal<boolean>;
|
|
15
|
+
readonly badge: _angular_core.InputSignal<string | null>;
|
|
16
|
+
protected readonly tabs: _fuse_ui_tabs.TabsRef;
|
|
17
|
+
protected readonly isActive: _angular_core.Signal<boolean>;
|
|
18
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FuseTabComponent, never>;
|
|
19
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FuseTabComponent, "fuse-tab", never, { "tabId": { "alias": "tabId"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare class FuseTabsComponent implements TabsRef, AfterViewInit {
|
|
23
|
+
readonly activeTab: _angular_core.InputSignal<string>;
|
|
24
|
+
readonly variant: _angular_core.InputSignal<"line" | "pills" | "boxed">;
|
|
25
|
+
readonly size: _angular_core.InputSignal<"sm" | "md">;
|
|
26
|
+
readonly tabChange: _angular_core.OutputEmitterRef<string>;
|
|
27
|
+
readonly tabs: _angular_core.Signal<readonly FuseTabComponent[]>;
|
|
28
|
+
readonly tabList: _angular_core.Signal<ElementRef<HTMLElement>>;
|
|
29
|
+
readonly indicator: _angular_core.Signal<ElementRef<HTMLElement> | undefined>;
|
|
30
|
+
readonly tabButtons: _angular_core.Signal<readonly ElementRef<HTMLButtonElement>[]>;
|
|
31
|
+
readonly activeTabId: _angular_core.WritableSignal<string>;
|
|
32
|
+
private readonly injector;
|
|
33
|
+
private readonly destroyRef;
|
|
34
|
+
protected readonly tabListClass: _angular_core.Signal<string>;
|
|
35
|
+
protected readonly tabBtnClass: (tab: FuseTabComponent) => _angular_core.Signal<string>;
|
|
36
|
+
private readonly _sync;
|
|
37
|
+
constructor();
|
|
38
|
+
ngAfterViewInit(): void;
|
|
39
|
+
setActive(tabId: string): void;
|
|
40
|
+
private updateIndicator;
|
|
41
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FuseTabsComponent, never>;
|
|
42
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FuseTabsComponent, "fuse-tabs", never, { "activeTab": { "alias": "activeTab"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, { "tabChange": "tabChange"; }, ["tabs"], ["*"], true, never>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { FUSE_TABS, FuseTabComponent, FuseTabsComponent };
|
|
46
|
+
export type { TabsRef };
|