@m1z23r/ngx-ui 1.1.13 → 1.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, PLATFORM_ID, signal, Injectable, computed, InjectionToken, ApplicationRef, EnvironmentInjector, createComponent, Injector, input, output, ChangeDetectionStrategy, Component, ElementRef, HostListener, effect, Directive, model, Pipe, contentChildren, ViewChild, TemplateRef, contentChild, viewChild, DestroyRef } from '@angular/core';
3
- import { isPlatformBrowser, NgTemplateOutlet, DOCUMENT, DecimalPipe } from '@angular/common';
3
+ import { isPlatformBrowser, NgTemplateOutlet, NgComponentOutlet, DOCUMENT, DecimalPipe } from '@angular/common';
4
4
  import * as i1 from '@angular/forms';
5
5
  import { FormsModule } from '@angular/forms';
6
6
 
@@ -535,6 +535,226 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
535
535
  args: [{ providedIn: 'root' }]
536
536
  }] });
537
537
 
538
+ /**
539
+ * Reference to a dynamically created tab.
540
+ * Provides methods to close the tab and access its result.
541
+ */
542
+ class TabRef {
543
+ closeFn;
544
+ activateFn;
545
+ resolvePromise;
546
+ resultPromise;
547
+ _isActive = signal(false, ...(ngDevMode ? [{ debugName: "_isActive" }] : []));
548
+ /** Unique identifier for this tab */
549
+ id;
550
+ /** Whether this tab is currently active */
551
+ isActive = this._isActive.asReadonly();
552
+ constructor(id, closeFn, activateFn) {
553
+ this.closeFn = closeFn;
554
+ this.activateFn = activateFn;
555
+ this.id = id;
556
+ this.resultPromise = new Promise((resolve) => {
557
+ this.resolvePromise = resolve;
558
+ });
559
+ }
560
+ /**
561
+ * Closes the tab with an optional result value.
562
+ */
563
+ close(result) {
564
+ this.resolvePromise(result);
565
+ this.closeFn(this);
566
+ }
567
+ /**
568
+ * Activates this tab.
569
+ */
570
+ activate() {
571
+ this.activateFn(this);
572
+ }
573
+ /**
574
+ * Returns a promise that resolves when the tab is closed.
575
+ * The promise resolves with the result passed to close(), or undefined if closed without a result.
576
+ */
577
+ afterClosed() {
578
+ return this.resultPromise;
579
+ }
580
+ /** @internal */
581
+ _setActive(active) {
582
+ this._isActive.set(active);
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Injection token for tab data passed to the component.
588
+ */
589
+ const TAB_DATA = new InjectionToken('TAB_DATA');
590
+ /**
591
+ * Injection token for the tab reference.
592
+ */
593
+ const TAB_REF = new InjectionToken('TAB_REF');
594
+ /**
595
+ * Default configuration for dynamic tabs.
596
+ */
597
+ const DEFAULT_TAB_CONFIG = {
598
+ closable: true,
599
+ activate: true,
600
+ };
601
+
602
+ /**
603
+ * Service for managing dynamic tabs.
604
+ *
605
+ * @example
606
+ * ```typescript
607
+ * @Component({ ... })
608
+ * export class MyTabContent {
609
+ * private tabRef = inject(TAB_REF) as TabRef<string>;
610
+ * private data = inject(TAB_DATA) as { message: string };
611
+ *
612
+ * save() {
613
+ * this.tabRef.close('saved');
614
+ * }
615
+ * }
616
+ *
617
+ * // In parent component:
618
+ * const tabRef = this.tabsService.open(MyTabContent, {
619
+ * label: 'New Tab',
620
+ * data: { message: 'Hello' },
621
+ * closable: true
622
+ * });
623
+ *
624
+ * const result = await tabRef.afterClosed();
625
+ * ```
626
+ */
627
+ class TabsService {
628
+ _tabs = signal([], ...(ngDevMode ? [{ debugName: "_tabs" }] : []));
629
+ _activeTabId = signal(null, ...(ngDevMode ? [{ debugName: "_activeTabId" }] : []));
630
+ idCounter = 0;
631
+ /** All currently open tabs */
632
+ tabs = this._tabs.asReadonly();
633
+ /** ID of the currently active tab */
634
+ activeTabId = this._activeTabId.asReadonly();
635
+ /** The currently active tab */
636
+ activeTab = computed(() => {
637
+ const activeId = this._activeTabId();
638
+ return this._tabs().find((t) => t.id === activeId) ?? null;
639
+ }, ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
640
+ /**
641
+ * Opens a new tab with the specified component.
642
+ *
643
+ * @param component - The component to render inside the tab
644
+ * @param config - Configuration options for the tab
645
+ * @returns A TabRef that can be used to close the tab and get results
646
+ */
647
+ open(component, config) {
648
+ const mergedConfig = { ...DEFAULT_TAB_CONFIG, ...config };
649
+ const id = config.id ?? `tab-${++this.idCounter}`;
650
+ const tabRef = new TabRef(id, (ref) => this.close(ref), (ref) => this.activate(ref));
651
+ const tab = {
652
+ id,
653
+ label: config.label,
654
+ icon: config.icon,
655
+ component,
656
+ data: config.data,
657
+ closable: mergedConfig.closable,
658
+ tabRef: tabRef,
659
+ };
660
+ this._tabs.update((tabs) => [...tabs, tab]);
661
+ if (mergedConfig.activate) {
662
+ this.activateById(id);
663
+ }
664
+ return tabRef;
665
+ }
666
+ /**
667
+ * Closes a tab by its TabRef.
668
+ */
669
+ close(tabRef) {
670
+ this.closeById(tabRef.id);
671
+ }
672
+ /**
673
+ * Closes a tab by its ID.
674
+ */
675
+ closeById(id) {
676
+ const tabs = this._tabs();
677
+ const index = tabs.findIndex((t) => t.id === id);
678
+ if (index === -1)
679
+ return;
680
+ // If closing the active tab, activate another one
681
+ if (this._activeTabId() === id) {
682
+ const newActiveTab = tabs[index + 1] ?? tabs[index - 1];
683
+ this._activeTabId.set(newActiveTab?.id ?? null);
684
+ if (newActiveTab) {
685
+ newActiveTab.tabRef._setActive(true);
686
+ }
687
+ }
688
+ // Update the closed tab's active state
689
+ tabs[index].tabRef._setActive(false);
690
+ this._tabs.update((t) => t.filter((tab) => tab.id !== id));
691
+ }
692
+ /**
693
+ * Closes all tabs.
694
+ */
695
+ closeAll() {
696
+ this._tabs().forEach((tab) => {
697
+ tab.tabRef._setActive(false);
698
+ });
699
+ this._tabs.set([]);
700
+ this._activeTabId.set(null);
701
+ }
702
+ /**
703
+ * Activates a tab by its TabRef.
704
+ */
705
+ activate(tabRef) {
706
+ this.activateById(tabRef.id);
707
+ }
708
+ /**
709
+ * Activates a tab by its ID.
710
+ */
711
+ activateById(id) {
712
+ const tabs = this._tabs();
713
+ const tab = tabs.find((t) => t.id === id);
714
+ if (!tab)
715
+ return;
716
+ // Deactivate current tab
717
+ const currentActive = tabs.find((t) => t.id === this._activeTabId());
718
+ if (currentActive) {
719
+ currentActive.tabRef._setActive(false);
720
+ }
721
+ // Activate new tab
722
+ tab.tabRef._setActive(true);
723
+ this._activeTabId.set(id);
724
+ }
725
+ /**
726
+ * Creates an injector with TAB_DATA and TAB_REF for a tab component.
727
+ * @internal
728
+ */
729
+ _createInjector(parentInjector, data, tabRef) {
730
+ return Injector.create({
731
+ providers: [
732
+ { provide: TAB_DATA, useValue: data },
733
+ { provide: TAB_REF, useValue: tabRef },
734
+ ],
735
+ parent: parentInjector,
736
+ });
737
+ }
738
+ /**
739
+ * Gets a tab by its ID.
740
+ */
741
+ getTab(id) {
742
+ return this._tabs().find((t) => t.id === id);
743
+ }
744
+ /**
745
+ * Updates a tab's label.
746
+ */
747
+ updateLabel(id, label) {
748
+ this._tabs.update((tabs) => tabs.map((tab) => (tab.id === id ? { ...tab, label } : tab)));
749
+ }
750
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TabsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
751
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TabsService, providedIn: 'root' });
752
+ }
753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TabsService, decorators: [{
754
+ type: Injectable,
755
+ args: [{ providedIn: 'root' }]
756
+ }] });
757
+
538
758
  /**
539
759
  * Modal wrapper component with backdrop, header, body, and footer slots.
540
760
  * Uses content projection for flexible content.
@@ -2500,6 +2720,273 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
2500
2720
  args: [{ selector: 'ui-tabs', standalone: true, imports: [NgTemplateOutlet, TabActivePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ui-tabs\" [class]=\"tabsClasses()\">\n <div\n #tabList\n class=\"ui-tabs__list\"\n role=\"tablist\"\n [attr.aria-label]=\"ariaLabel()\"\n >\n @for (tab of tabs(); track tab.id() || $index; let i = $index) {\n @let active = tab | tabActive:i:activeTab();\n <button\n #tabButton\n type=\"button\"\n class=\"ui-tabs__tab\"\n role=\"tab\"\n [class.ui-tabs__tab--active]=\"active\"\n [class.ui-tabs__tab--disabled]=\"tab.disabled()\"\n [attr.aria-selected]=\"active\"\n [attr.aria-controls]=\"'panel-' + (tab.id() || i)\"\n [attr.tabindex]=\"active ? 0 : -1\"\n [disabled]=\"tab.disabled()\"\n (click)=\"selectTab(tab, i)\"\n (keydown)=\"handleKeyDown($event, i)\"\n >\n @if (tab.iconTemplate()) {\n <span class=\"ui-tabs__tab-icon\">\n <ng-container *ngTemplateOutlet=\"tab.iconTemplate()!\" />\n </span>\n }\n {{ tab.label() }}\n </button>\n }\n @if (variant() === 'underline') {\n <span\n class=\"ui-tabs__indicator\"\n [style.left.px]=\"indicatorLeft()\"\n [style.width.px]=\"indicatorWidth()\"\n ></span>\n }\n </div>\n <div class=\"ui-tabs__panels\">\n <ng-content />\n </div>\n</div>\n", styles: [":host{display:block}.ui-tabs__list{display:flex;position:relative;gap:var(--ui-spacing-xs)}.ui-tabs__tab{display:inline-flex;align-items:center;gap:var(--ui-spacing-xs);padding:.5rem 1rem;border:none;background:transparent;color:var(--ui-text-muted);font-weight:500;cursor:pointer;transition:all var(--ui-transition-fast);white-space:nowrap}.ui-tabs__tab:hover:not(:disabled){color:var(--ui-text)}.ui-tabs__tab:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px;border-radius:var(--ui-radius-sm)}.ui-tabs__tab--active{color:var(--ui-primary)}.ui-tabs__tab--disabled{opacity:.5;cursor:not-allowed}.ui-tabs__tab-icon{display:flex;align-items:center;justify-content:center}.ui-tabs__panels{margin-top:var(--ui-spacing-md)}.ui-tabs--default .ui-tabs__list{border-bottom:1px solid var(--ui-border)}.ui-tabs--default .ui-tabs__tab{margin-bottom:-1px;border-bottom:2px solid transparent;border-radius:var(--ui-radius-sm) var(--ui-radius-sm) 0 0}.ui-tabs--default .ui-tabs__tab--active{border-bottom-color:var(--ui-primary)}.ui-tabs--pills .ui-tabs__list{background-color:var(--ui-bg-tertiary);padding:3px;border-radius:var(--ui-radius-md);gap:2px}.ui-tabs--pills .ui-tabs__tab{border-radius:var(--ui-radius-sm)}.ui-tabs--pills .ui-tabs__tab--active{background-color:var(--ui-bg);color:var(--ui-text);box-shadow:var(--ui-shadow-sm)}.ui-tabs--underline .ui-tabs__list{border-bottom:1px solid var(--ui-border)}.ui-tabs--underline .ui-tabs__tab{padding-bottom:.75rem}.ui-tabs--underline .ui-tabs__indicator{position:absolute;bottom:0;height:2px;background-color:var(--ui-primary);transition:left var(--ui-transition-normal),width var(--ui-transition-normal)}.ui-tabs--sm .ui-tabs__tab{padding:.375rem .75rem;font-size:var(--ui-font-sm)}.ui-tabs--md .ui-tabs__tab{padding:.5rem 1rem;font-size:var(--ui-font-sm)}.ui-tabs--lg .ui-tabs__tab{padding:.625rem 1.25rem;font-size:var(--ui-font-md)}.ui-tabs--sm.ui-tabs--underline .ui-tabs__tab{padding-bottom:.5rem}.ui-tabs--lg.ui-tabs--underline .ui-tabs__tab{padding-bottom:.875rem}\n"] }]
2501
2721
  }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }, { type: i0.Output, args: ["activeTabChange"] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabComponent), { isSignal: true }] }], tabList: [{ type: i0.ViewChild, args: ['tabList', { isSignal: true }] }] } });
2502
2722
 
2723
+ class DynamicTabsComponent {
2724
+ tabsService = inject(TabsService);
2725
+ injector = inject(Injector);
2726
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
2727
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : []));
2728
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
2729
+ tabList = viewChild('tabList', ...(ngDevMode ? [{ debugName: "tabList" }] : []));
2730
+ indicatorLeft = signal(0, ...(ngDevMode ? [{ debugName: "indicatorLeft" }] : []));
2731
+ indicatorWidth = signal(0, ...(ngDevMode ? [{ debugName: "indicatorWidth" }] : []));
2732
+ tabs = this.tabsService.tabs;
2733
+ activeTabId = this.tabsService.activeTabId;
2734
+ injectorCache = new Map();
2735
+ tabsClasses = computed(() => {
2736
+ return `ui-tabs--${this.variant()} ui-tabs--${this.size()}`;
2737
+ }, ...(ngDevMode ? [{ debugName: "tabsClasses" }] : []));
2738
+ constructor() {
2739
+ effect(() => {
2740
+ const _ = this.activeTabId();
2741
+ this.updateIndicator();
2742
+ });
2743
+ // Clean up injector cache when tabs are removed
2744
+ effect(() => {
2745
+ const currentTabs = this.tabs();
2746
+ const currentIds = new Set(currentTabs.map((t) => t.id));
2747
+ for (const id of this.injectorCache.keys()) {
2748
+ if (!currentIds.has(id)) {
2749
+ this.injectorCache.delete(id);
2750
+ }
2751
+ }
2752
+ });
2753
+ }
2754
+ getInjector(tab) {
2755
+ let injector = this.injectorCache.get(tab.id);
2756
+ if (!injector) {
2757
+ injector = this.tabsService._createInjector(this.injector, tab.data, tab.tabRef);
2758
+ this.injectorCache.set(tab.id, injector);
2759
+ }
2760
+ return injector;
2761
+ }
2762
+ selectTab(tab) {
2763
+ this.tabsService.activateById(tab.id);
2764
+ }
2765
+ closeTab(event, tab) {
2766
+ event.stopPropagation();
2767
+ tab.tabRef.close();
2768
+ }
2769
+ handleKeyDown(event, currentTab) {
2770
+ const tabList = this.tabs();
2771
+ if (tabList.length === 0)
2772
+ return;
2773
+ const currentIndex = tabList.findIndex((t) => t.id === currentTab.id);
2774
+ let nextIndex = -1;
2775
+ switch (event.key) {
2776
+ case 'ArrowLeft':
2777
+ event.preventDefault();
2778
+ nextIndex = (currentIndex - 1 + tabList.length) % tabList.length;
2779
+ break;
2780
+ case 'ArrowRight':
2781
+ event.preventDefault();
2782
+ nextIndex = (currentIndex + 1) % tabList.length;
2783
+ break;
2784
+ case 'Home':
2785
+ event.preventDefault();
2786
+ nextIndex = 0;
2787
+ break;
2788
+ case 'End':
2789
+ event.preventDefault();
2790
+ nextIndex = tabList.length - 1;
2791
+ break;
2792
+ case 'Delete':
2793
+ if (currentTab.closable) {
2794
+ event.preventDefault();
2795
+ currentTab.tabRef.close();
2796
+ }
2797
+ return;
2798
+ default:
2799
+ return;
2800
+ }
2801
+ if (nextIndex >= 0) {
2802
+ const nextTab = tabList[nextIndex];
2803
+ this.tabsService.activateById(nextTab.id);
2804
+ this.focusTab(nextIndex);
2805
+ }
2806
+ }
2807
+ focusTab(index) {
2808
+ const tabListEl = this.tabList()?.nativeElement;
2809
+ if (!tabListEl)
2810
+ return;
2811
+ const buttons = tabListEl.querySelectorAll('.ui-tabs__tab');
2812
+ buttons[index]?.focus();
2813
+ }
2814
+ updateIndicator() {
2815
+ if (this.variant() !== 'underline')
2816
+ return;
2817
+ const tabListEl = this.tabList()?.nativeElement;
2818
+ if (!tabListEl)
2819
+ return;
2820
+ const buttons = tabListEl.querySelectorAll('.ui-tabs__tab');
2821
+ const activeId = this.activeTabId();
2822
+ const tabs = this.tabs();
2823
+ const activeIndex = tabs.findIndex((t) => t.id === activeId);
2824
+ if (activeIndex >= 0 && activeIndex < buttons.length) {
2825
+ const activeButton = buttons[activeIndex];
2826
+ this.indicatorLeft.set(activeButton.offsetLeft);
2827
+ this.indicatorWidth.set(activeButton.offsetWidth);
2828
+ }
2829
+ }
2830
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DynamicTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2831
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: DynamicTabsComponent, isStandalone: true, selector: "ui-dynamic-tabs", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tabList", first: true, predicate: ["tabList"], descendants: true, isSignal: true }], ngImport: i0, template: `
2832
+ <div class="ui-tabs" [class]="tabsClasses()">
2833
+ <div
2834
+ #tabList
2835
+ class="ui-tabs__list"
2836
+ role="tablist"
2837
+ [attr.aria-label]="ariaLabel()"
2838
+ >
2839
+ @for (tab of tabs(); track tab.id) {
2840
+ @let active = tab.id === activeTabId();
2841
+ <button
2842
+ type="button"
2843
+ class="ui-tabs__tab"
2844
+ role="tab"
2845
+ [class.ui-tabs__tab--active]="active"
2846
+ [class.ui-tabs__tab--closable]="tab.closable"
2847
+ [attr.aria-selected]="active"
2848
+ [attr.aria-controls]="'panel-' + tab.id"
2849
+ [attr.tabindex]="active ? 0 : -1"
2850
+ (click)="selectTab(tab)"
2851
+ (keydown)="handleKeyDown($event, tab)"
2852
+ >
2853
+ @if (tab.icon) {
2854
+ <span class="ui-tabs__tab-icon">
2855
+ <ng-container *ngTemplateOutlet="tab.icon" />
2856
+ </span>
2857
+ }
2858
+ <span class="ui-tabs__tab-label">{{ tab.label }}</span>
2859
+ @if (tab.closable) {
2860
+ <button
2861
+ type="button"
2862
+ class="ui-tabs__tab-close"
2863
+ aria-label="Close tab"
2864
+ (click)="closeTab($event, tab)"
2865
+ >
2866
+ <svg
2867
+ width="14"
2868
+ height="14"
2869
+ viewBox="0 0 24 24"
2870
+ fill="none"
2871
+ stroke="currentColor"
2872
+ stroke-width="2"
2873
+ stroke-linecap="round"
2874
+ stroke-linejoin="round"
2875
+ >
2876
+ <line x1="18" y1="6" x2="6" y2="18"></line>
2877
+ <line x1="6" y1="6" x2="18" y2="18"></line>
2878
+ </svg>
2879
+ </button>
2880
+ }
2881
+ </button>
2882
+ }
2883
+ @if (variant() === 'underline') {
2884
+ <span
2885
+ class="ui-tabs__indicator"
2886
+ [style.left.px]="indicatorLeft()"
2887
+ [style.width.px]="indicatorWidth()"
2888
+ ></span>
2889
+ }
2890
+ </div>
2891
+ <div class="ui-tabs__panels">
2892
+ @for (tab of tabs(); track tab.id) {
2893
+ @if (tab.id === activeTabId()) {
2894
+ <div
2895
+ class="ui-tab-panel"
2896
+ role="tabpanel"
2897
+ [attr.id]="'panel-' + tab.id"
2898
+ >
2899
+ <ng-container
2900
+ *ngComponentOutlet="tab.component; injector: getInjector(tab)"
2901
+ />
2902
+ </div>
2903
+ }
2904
+ }
2905
+ </div>
2906
+ </div>
2907
+ `, isInline: true, styles: [":host{display:block}.ui-tabs__list{display:flex;position:relative;gap:var(--ui-spacing-xs)}.ui-tabs__tab{display:inline-flex;align-items:center;gap:var(--ui-spacing-xs);padding:.5rem 1rem;border:none;background:transparent;color:var(--ui-text-muted);font-weight:500;cursor:pointer;transition:all var(--ui-transition-fast);white-space:nowrap}.ui-tabs__tab:hover:not(:disabled){color:var(--ui-text)}.ui-tabs__tab:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px;border-radius:var(--ui-radius-sm)}.ui-tabs__tab--active{color:var(--ui-primary)}.ui-tabs__tab--disabled{opacity:.5;cursor:not-allowed}.ui-tabs__tab-icon{display:flex;align-items:center;justify-content:center}.ui-tabs__panels{margin-top:var(--ui-spacing-md)}.ui-tabs--default .ui-tabs__list{border-bottom:1px solid var(--ui-border)}.ui-tabs--default .ui-tabs__tab{margin-bottom:-1px;border-bottom:2px solid transparent;border-radius:var(--ui-radius-sm) var(--ui-radius-sm) 0 0}.ui-tabs--default .ui-tabs__tab--active{border-bottom-color:var(--ui-primary)}.ui-tabs--pills .ui-tabs__list{background-color:var(--ui-bg-tertiary);padding:3px;border-radius:var(--ui-radius-md);gap:2px}.ui-tabs--pills .ui-tabs__tab{border-radius:var(--ui-radius-sm)}.ui-tabs--pills .ui-tabs__tab--active{background-color:var(--ui-bg);color:var(--ui-text);box-shadow:var(--ui-shadow-sm)}.ui-tabs--underline .ui-tabs__list{border-bottom:1px solid var(--ui-border)}.ui-tabs--underline .ui-tabs__tab{padding-bottom:.75rem}.ui-tabs--underline .ui-tabs__indicator{position:absolute;bottom:0;height:2px;background-color:var(--ui-primary);transition:left var(--ui-transition-normal),width var(--ui-transition-normal)}.ui-tabs--sm .ui-tabs__tab{padding:.375rem .75rem;font-size:var(--ui-font-sm)}.ui-tabs--md .ui-tabs__tab{padding:.5rem 1rem;font-size:var(--ui-font-sm)}.ui-tabs--lg .ui-tabs__tab{padding:.625rem 1.25rem;font-size:var(--ui-font-md)}.ui-tabs--sm.ui-tabs--underline .ui-tabs__tab{padding-bottom:.5rem}.ui-tabs--lg.ui-tabs--underline .ui-tabs__tab{padding-bottom:.875rem}\n", ".ui-tabs__tab--closable{padding-right:var(--ui-spacing-xs)}.ui-tabs__tab-label{flex:1}.ui-tabs__tab-close{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:var(--ui-spacing-xs);padding:0;border:none;border-radius:var(--ui-radius-sm);background:transparent;color:var(--ui-text-muted);cursor:pointer;opacity:0;transition:all var(--ui-transition-fast)}.ui-tabs__tab:hover .ui-tabs__tab-close,.ui-tabs__tab:focus-within .ui-tabs__tab-close{opacity:1}.ui-tabs__tab-close:hover{background:var(--ui-bg-hover);color:var(--ui-danger)}.ui-tabs__tab-close:focus-visible{opacity:1;outline:2px solid var(--ui-primary);outline-offset:1px}.ui-tab-panel{animation:fadeIn var(--ui-transition-fast)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2908
+ }
2909
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: DynamicTabsComponent, decorators: [{
2910
+ type: Component,
2911
+ args: [{ selector: 'ui-dynamic-tabs', standalone: true, imports: [NgComponentOutlet, NgTemplateOutlet], template: `
2912
+ <div class="ui-tabs" [class]="tabsClasses()">
2913
+ <div
2914
+ #tabList
2915
+ class="ui-tabs__list"
2916
+ role="tablist"
2917
+ [attr.aria-label]="ariaLabel()"
2918
+ >
2919
+ @for (tab of tabs(); track tab.id) {
2920
+ @let active = tab.id === activeTabId();
2921
+ <button
2922
+ type="button"
2923
+ class="ui-tabs__tab"
2924
+ role="tab"
2925
+ [class.ui-tabs__tab--active]="active"
2926
+ [class.ui-tabs__tab--closable]="tab.closable"
2927
+ [attr.aria-selected]="active"
2928
+ [attr.aria-controls]="'panel-' + tab.id"
2929
+ [attr.tabindex]="active ? 0 : -1"
2930
+ (click)="selectTab(tab)"
2931
+ (keydown)="handleKeyDown($event, tab)"
2932
+ >
2933
+ @if (tab.icon) {
2934
+ <span class="ui-tabs__tab-icon">
2935
+ <ng-container *ngTemplateOutlet="tab.icon" />
2936
+ </span>
2937
+ }
2938
+ <span class="ui-tabs__tab-label">{{ tab.label }}</span>
2939
+ @if (tab.closable) {
2940
+ <button
2941
+ type="button"
2942
+ class="ui-tabs__tab-close"
2943
+ aria-label="Close tab"
2944
+ (click)="closeTab($event, tab)"
2945
+ >
2946
+ <svg
2947
+ width="14"
2948
+ height="14"
2949
+ viewBox="0 0 24 24"
2950
+ fill="none"
2951
+ stroke="currentColor"
2952
+ stroke-width="2"
2953
+ stroke-linecap="round"
2954
+ stroke-linejoin="round"
2955
+ >
2956
+ <line x1="18" y1="6" x2="6" y2="18"></line>
2957
+ <line x1="6" y1="6" x2="18" y2="18"></line>
2958
+ </svg>
2959
+ </button>
2960
+ }
2961
+ </button>
2962
+ }
2963
+ @if (variant() === 'underline') {
2964
+ <span
2965
+ class="ui-tabs__indicator"
2966
+ [style.left.px]="indicatorLeft()"
2967
+ [style.width.px]="indicatorWidth()"
2968
+ ></span>
2969
+ }
2970
+ </div>
2971
+ <div class="ui-tabs__panels">
2972
+ @for (tab of tabs(); track tab.id) {
2973
+ @if (tab.id === activeTabId()) {
2974
+ <div
2975
+ class="ui-tab-panel"
2976
+ role="tabpanel"
2977
+ [attr.id]="'panel-' + tab.id"
2978
+ >
2979
+ <ng-container
2980
+ *ngComponentOutlet="tab.component; injector: getInjector(tab)"
2981
+ />
2982
+ </div>
2983
+ }
2984
+ }
2985
+ </div>
2986
+ </div>
2987
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.ui-tabs__list{display:flex;position:relative;gap:var(--ui-spacing-xs)}.ui-tabs__tab{display:inline-flex;align-items:center;gap:var(--ui-spacing-xs);padding:.5rem 1rem;border:none;background:transparent;color:var(--ui-text-muted);font-weight:500;cursor:pointer;transition:all var(--ui-transition-fast);white-space:nowrap}.ui-tabs__tab:hover:not(:disabled){color:var(--ui-text)}.ui-tabs__tab:focus-visible{outline:2px solid var(--ui-primary);outline-offset:2px;border-radius:var(--ui-radius-sm)}.ui-tabs__tab--active{color:var(--ui-primary)}.ui-tabs__tab--disabled{opacity:.5;cursor:not-allowed}.ui-tabs__tab-icon{display:flex;align-items:center;justify-content:center}.ui-tabs__panels{margin-top:var(--ui-spacing-md)}.ui-tabs--default .ui-tabs__list{border-bottom:1px solid var(--ui-border)}.ui-tabs--default .ui-tabs__tab{margin-bottom:-1px;border-bottom:2px solid transparent;border-radius:var(--ui-radius-sm) var(--ui-radius-sm) 0 0}.ui-tabs--default .ui-tabs__tab--active{border-bottom-color:var(--ui-primary)}.ui-tabs--pills .ui-tabs__list{background-color:var(--ui-bg-tertiary);padding:3px;border-radius:var(--ui-radius-md);gap:2px}.ui-tabs--pills .ui-tabs__tab{border-radius:var(--ui-radius-sm)}.ui-tabs--pills .ui-tabs__tab--active{background-color:var(--ui-bg);color:var(--ui-text);box-shadow:var(--ui-shadow-sm)}.ui-tabs--underline .ui-tabs__list{border-bottom:1px solid var(--ui-border)}.ui-tabs--underline .ui-tabs__tab{padding-bottom:.75rem}.ui-tabs--underline .ui-tabs__indicator{position:absolute;bottom:0;height:2px;background-color:var(--ui-primary);transition:left var(--ui-transition-normal),width var(--ui-transition-normal)}.ui-tabs--sm .ui-tabs__tab{padding:.375rem .75rem;font-size:var(--ui-font-sm)}.ui-tabs--md .ui-tabs__tab{padding:.5rem 1rem;font-size:var(--ui-font-sm)}.ui-tabs--lg .ui-tabs__tab{padding:.625rem 1.25rem;font-size:var(--ui-font-md)}.ui-tabs--sm.ui-tabs--underline .ui-tabs__tab{padding-bottom:.5rem}.ui-tabs--lg.ui-tabs--underline .ui-tabs__tab{padding-bottom:.875rem}\n", ".ui-tabs__tab--closable{padding-right:var(--ui-spacing-xs)}.ui-tabs__tab-label{flex:1}.ui-tabs__tab-close{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:var(--ui-spacing-xs);padding:0;border:none;border-radius:var(--ui-radius-sm);background:transparent;color:var(--ui-text-muted);cursor:pointer;opacity:0;transition:all var(--ui-transition-fast)}.ui-tabs__tab:hover .ui-tabs__tab-close,.ui-tabs__tab:focus-within .ui-tabs__tab-close{opacity:1}.ui-tabs__tab-close:hover{background:var(--ui-bg-hover);color:var(--ui-danger)}.ui-tabs__tab-close:focus-visible{opacity:1;outline:2px solid var(--ui-primary);outline-offset:1px}.ui-tab-panel{animation:fadeIn var(--ui-transition-fast)}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
2988
+ }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], tabList: [{ type: i0.ViewChild, args: ['tabList', { isSignal: true }] }] } });
2989
+
2503
2990
  class AccordionHeaderDirective {
2504
2991
  templateRef = inject(TemplateRef);
2505
2992
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: AccordionHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
@@ -5137,5 +5624,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
5137
5624
  * Generated bundle index. Do not edit.
5138
5625
  */
5139
5626
 
5140
- export { AccordionComponent, AccordionHeaderDirective, AccordionItemComponent, AlertComponent, BadgeComponent, ButtonComponent, CardComponent, CellTemplateDirective, CellValuePipe, CheckboxComponent, ChipInputComponent, ChipTemplateDirective, CircularProgressComponent, ContentComponent, ContextMenuDirective, DIALOG_DATA, DIALOG_REF, DatepickerComponent, DatetimepickerComponent, DialogRef, DialogService, DropdownComponent, DropdownDividerComponent, DropdownItemComponent, DropdownTriggerDirective, FileChooserComponent, FilePreviewPipe, FileSizePipe, FooterComponent, InputComponent, LOADABLE, LoadingDirective, LoadingService, ModalComponent, NavbarComponent, OptionComponent, OptionTemplateDirective, PaginationComponent, ProgressComponent, RadioComponent, RadioGroupComponent, SelectComponent, ShellComponent, SidebarComponent, SidebarService, SidebarToggleComponent, SliderComponent, SpinnerComponent, SplitComponent, SplitPaneComponent, SwitchComponent, TabActivePipe, TabComponent, TabIconDirective, TableComponent, TabsComponent, TextareaComponent, TimepickerComponent, ToastRef, ToastService, TooltipDirective, TreeComponent, TreeNodeComponent };
5627
+ export { AccordionComponent, AccordionHeaderDirective, AccordionItemComponent, AlertComponent, BadgeComponent, ButtonComponent, CardComponent, CellTemplateDirective, CellValuePipe, CheckboxComponent, ChipInputComponent, ChipTemplateDirective, CircularProgressComponent, ContentComponent, ContextMenuDirective, DIALOG_DATA, DIALOG_REF, DatepickerComponent, DatetimepickerComponent, DialogRef, DialogService, DropdownComponent, DropdownDividerComponent, DropdownItemComponent, DropdownTriggerDirective, DynamicTabsComponent, FileChooserComponent, FilePreviewPipe, FileSizePipe, FooterComponent, InputComponent, LOADABLE, LoadingDirective, LoadingService, ModalComponent, NavbarComponent, OptionComponent, OptionTemplateDirective, PaginationComponent, ProgressComponent, RadioComponent, RadioGroupComponent, SelectComponent, ShellComponent, SidebarComponent, SidebarService, SidebarToggleComponent, SliderComponent, SpinnerComponent, SplitComponent, SplitPaneComponent, SwitchComponent, TAB_DATA, TAB_REF, TabActivePipe, TabComponent, TabIconDirective, TabRef, TableComponent, TabsComponent, TabsService, TextareaComponent, TimepickerComponent, ToastRef, ToastService, TooltipDirective, TreeComponent, TreeNodeComponent };
5141
5628
  //# sourceMappingURL=m1z23r-ngx-ui.mjs.map