@sonny-ui/core 0.1.0-alpha.13 → 0.1.0-alpha.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.
@@ -3,8 +3,8 @@ import { twMerge } from 'tailwind-merge';
3
3
  import { cva } from 'class-variance-authority';
4
4
  export { cva } from 'class-variance-authority';
5
5
  import * as i0 from '@angular/core';
6
- import { inject, PLATFORM_ID, signal, computed, Injectable, InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, input, Directive, ChangeDetectionStrategy, Component, ElementRef, model, viewChild, forwardRef, HostListener, Injector, afterNextRender, effect, Renderer2, output, contentChildren } from '@angular/core';
7
- import { DOCUMENT, isPlatformBrowser } from '@angular/common';
6
+ import { inject, PLATFORM_ID, signal, computed, Injectable, InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, input, Directive, ChangeDetectionStrategy, Component, ElementRef, model, viewChild, forwardRef, HostListener, TemplateRef, output, contentChildren, contentChild, effect, untracked, Injector, afterNextRender, Renderer2 } from '@angular/core';
7
+ import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
8
8
  import { Dialog, DialogRef } from '@angular/cdk/dialog';
9
9
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
10
10
  import { createGlobalPositionStrategy } from '@angular/cdk/overlay';
@@ -2417,6 +2417,1146 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
2417
2417
  }]
2418
2418
  }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2419
2419
 
2420
+ const paginationItemVariants = cva('inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', {
2421
+ variants: {
2422
+ variant: {
2423
+ default: 'bg-background hover:bg-accent hover:text-accent-foreground',
2424
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
2425
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
2426
+ },
2427
+ size: {
2428
+ sm: 'h-8 w-8 text-xs',
2429
+ md: 'h-9 w-9',
2430
+ lg: 'h-10 w-10',
2431
+ },
2432
+ active: {
2433
+ true: 'bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground',
2434
+ false: '',
2435
+ },
2436
+ },
2437
+ defaultVariants: {
2438
+ variant: 'default',
2439
+ size: 'md',
2440
+ active: false,
2441
+ },
2442
+ });
2443
+
2444
+ function computePageRange(totalPages, currentPage, siblingCount, boundaryCount) {
2445
+ const range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i);
2446
+ const startPages = range(1, Math.min(boundaryCount, totalPages));
2447
+ const endPages = range(Math.max(totalPages - boundaryCount + 1, boundaryCount + 1), totalPages);
2448
+ const siblingsStart = Math.max(Math.min(currentPage - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1), boundaryCount + 2);
2449
+ const siblingsEnd = Math.min(Math.max(currentPage + siblingCount, boundaryCount + siblingCount * 2 + 2), endPages.length > 0 ? endPages[0] - 2 : totalPages - 1);
2450
+ const result = [...startPages];
2451
+ if (siblingsStart > boundaryCount + 2) {
2452
+ result.push('ellipsis');
2453
+ }
2454
+ else if (boundaryCount + 1 < totalPages - boundaryCount) {
2455
+ result.push(boundaryCount + 1);
2456
+ }
2457
+ result.push(...range(siblingsStart, siblingsEnd));
2458
+ if (siblingsEnd < totalPages - boundaryCount - 1) {
2459
+ result.push('ellipsis');
2460
+ }
2461
+ else if (totalPages - boundaryCount > boundaryCount) {
2462
+ result.push(totalPages - boundaryCount);
2463
+ }
2464
+ result.push(...endPages);
2465
+ return [...new Set(result)].sort((a, b) => {
2466
+ if (a === 'ellipsis')
2467
+ return 0;
2468
+ if (b === 'ellipsis')
2469
+ return 0;
2470
+ return a - b;
2471
+ });
2472
+ }
2473
+ class SnyPaginationComponent {
2474
+ currentPage = model(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
2475
+ totalPages = input.required(...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
2476
+ siblingCount = input(1, ...(ngDevMode ? [{ debugName: "siblingCount" }] : /* istanbul ignore next */ []));
2477
+ boundaryCount = input(1, ...(ngDevMode ? [{ debugName: "boundaryCount" }] : /* istanbul ignore next */ []));
2478
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2479
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
2480
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
2481
+ pages = computed(() => computePageRange(this.totalPages(), this.currentPage(), this.siblingCount(), this.boundaryCount()), ...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
2482
+ hasPrev = computed(() => this.currentPage() > 1, ...(ngDevMode ? [{ debugName: "hasPrev" }] : /* istanbul ignore next */ []));
2483
+ hasNext = computed(() => this.currentPage() < this.totalPages(), ...(ngDevMode ? [{ debugName: "hasNext" }] : /* istanbul ignore next */ []));
2484
+ goToPage(page) {
2485
+ if (page === 'ellipsis')
2486
+ return;
2487
+ this.currentPage.set(page);
2488
+ }
2489
+ prev() {
2490
+ if (this.hasPrev())
2491
+ this.currentPage.update((p) => p - 1);
2492
+ }
2493
+ next() {
2494
+ if (this.hasNext())
2495
+ this.currentPage.update((p) => p + 1);
2496
+ }
2497
+ pageClass(page) {
2498
+ return cn(paginationItemVariants({
2499
+ variant: this.variant(),
2500
+ size: this.size(),
2501
+ active: page === this.currentPage(),
2502
+ }));
2503
+ }
2504
+ navBtnClass() {
2505
+ return cn(paginationItemVariants({ variant: this.variant(), size: this.size(), active: false }));
2506
+ }
2507
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2508
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyPaginationComponent, isStandalone: true, selector: "sny-pagination", inputs: { currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, totalPages: { classPropertyName: "totalPages", publicName: "totalPages", isSignal: true, isRequired: true, transformFunction: null }, siblingCount: { classPropertyName: "siblingCount", publicName: "siblingCount", isSignal: true, isRequired: false, transformFunction: null }, boundaryCount: { classPropertyName: "boundaryCount", publicName: "boundaryCount", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange" }, host: { attributes: { "role": "navigation", "aria-label": "Pagination" } }, ngImport: i0, template: `
2509
+ <div class="flex items-center gap-1">
2510
+ <button
2511
+ [class]="navBtnClass()"
2512
+ [disabled]="!hasPrev()"
2513
+ [attr.aria-label]="'Go to previous page'"
2514
+ (click)="prev()"
2515
+ >
2516
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
2517
+ </button>
2518
+
2519
+ @for (page of pages(); track $index) {
2520
+ @if (page === 'ellipsis') {
2521
+ <span class="flex h-9 w-9 items-center justify-center" aria-hidden="true">...</span>
2522
+ } @else {
2523
+ <button
2524
+ [class]="pageClass(page)"
2525
+ [attr.aria-label]="'Page ' + page"
2526
+ [attr.aria-current]="page === currentPage() ? 'page' : null"
2527
+ (click)="goToPage(page)"
2528
+ >
2529
+ {{ page }}
2530
+ </button>
2531
+ }
2532
+ }
2533
+
2534
+ <button
2535
+ [class]="navBtnClass()"
2536
+ [disabled]="!hasNext()"
2537
+ [attr.aria-label]="'Go to next page'"
2538
+ (click)="next()"
2539
+ >
2540
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
2541
+ </button>
2542
+ </div>
2543
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2544
+ }
2545
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyPaginationComponent, decorators: [{
2546
+ type: Component,
2547
+ args: [{
2548
+ selector: 'sny-pagination',
2549
+ standalone: true,
2550
+ changeDetection: ChangeDetectionStrategy.OnPush,
2551
+ host: {
2552
+ 'role': 'navigation',
2553
+ 'aria-label': 'Pagination',
2554
+ },
2555
+ template: `
2556
+ <div class="flex items-center gap-1">
2557
+ <button
2558
+ [class]="navBtnClass()"
2559
+ [disabled]="!hasPrev()"
2560
+ [attr.aria-label]="'Go to previous page'"
2561
+ (click)="prev()"
2562
+ >
2563
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
2564
+ </button>
2565
+
2566
+ @for (page of pages(); track $index) {
2567
+ @if (page === 'ellipsis') {
2568
+ <span class="flex h-9 w-9 items-center justify-center" aria-hidden="true">...</span>
2569
+ } @else {
2570
+ <button
2571
+ [class]="pageClass(page)"
2572
+ [attr.aria-label]="'Page ' + page"
2573
+ [attr.aria-current]="page === currentPage() ? 'page' : null"
2574
+ (click)="goToPage(page)"
2575
+ >
2576
+ {{ page }}
2577
+ </button>
2578
+ }
2579
+ }
2580
+
2581
+ <button
2582
+ [class]="navBtnClass()"
2583
+ [disabled]="!hasNext()"
2584
+ [attr.aria-label]="'Go to next page'"
2585
+ (click)="next()"
2586
+ >
2587
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
2588
+ </button>
2589
+ </div>
2590
+ `,
2591
+ }]
2592
+ }], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], totalPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalPages", required: true }] }], siblingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "siblingCount", required: false }] }], boundaryCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundaryCount", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2593
+
2594
+ const dropdownContentVariants = cva('z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md', {
2595
+ variants: {},
2596
+ defaultVariants: {},
2597
+ });
2598
+ const dropdownItemVariants = cva('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[active]:bg-accent data-[active]:text-accent-foreground', {
2599
+ variants: {
2600
+ variant: {
2601
+ default: '',
2602
+ destructive: 'text-destructive data-[active]:bg-destructive/10 data-[active]:text-destructive',
2603
+ },
2604
+ },
2605
+ defaultVariants: {
2606
+ variant: 'default',
2607
+ },
2608
+ });
2609
+
2610
+ const SNY_DROPDOWN = new InjectionToken('SnyDropdown');
2611
+ class SnyDropdownDirective {
2612
+ elementRef = inject(ElementRef);
2613
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
2614
+ toggle() { this.isOpen.update((v) => !v); }
2615
+ open() { this.isOpen.set(true); }
2616
+ close() { this.isOpen.set(false); }
2617
+ onDocumentClick(event) {
2618
+ if (!this.elementRef.nativeElement.contains(event.target)) {
2619
+ this.close();
2620
+ }
2621
+ }
2622
+ onEscape() {
2623
+ this.close();
2624
+ }
2625
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2626
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.5", type: SnyDropdownDirective, isStandalone: true, selector: "[snyDropdown]", host: { listeners: { "document:click": "onDocumentClick($event)", "keydown.escape": "onEscape()" }, properties: { "class": "\"relative inline-block\"" } }, providers: [{ provide: SNY_DROPDOWN, useExisting: SnyDropdownDirective }], exportAs: ["snyDropdown"], ngImport: i0 });
2627
+ }
2628
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownDirective, decorators: [{
2629
+ type: Directive,
2630
+ args: [{
2631
+ selector: '[snyDropdown]',
2632
+ standalone: true,
2633
+ exportAs: 'snyDropdown',
2634
+ providers: [{ provide: SNY_DROPDOWN, useExisting: SnyDropdownDirective }],
2635
+ host: {
2636
+ '[class]': '"relative inline-block"',
2637
+ },
2638
+ }]
2639
+ }], propDecorators: { onDocumentClick: [{
2640
+ type: HostListener,
2641
+ args: ['document:click', ['$event']]
2642
+ }], onEscape: [{
2643
+ type: HostListener,
2644
+ args: ['keydown.escape']
2645
+ }] } });
2646
+ class SnyDropdownTriggerDirective {
2647
+ dropdown = inject(SNY_DROPDOWN);
2648
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2649
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.5", type: SnyDropdownTriggerDirective, isStandalone: true, selector: "[snyDropdownTrigger]", host: { listeners: { "click": "dropdown.toggle()" }, properties: { "attr.aria-expanded": "dropdown.isOpen()", "attr.aria-haspopup": "\"menu\"" } }, ngImport: i0 });
2650
+ }
2651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownTriggerDirective, decorators: [{
2652
+ type: Directive,
2653
+ args: [{
2654
+ selector: '[snyDropdownTrigger]',
2655
+ standalone: true,
2656
+ host: {
2657
+ '(click)': 'dropdown.toggle()',
2658
+ '[attr.aria-expanded]': 'dropdown.isOpen()',
2659
+ '[attr.aria-haspopup]': '"menu"',
2660
+ },
2661
+ }]
2662
+ }] });
2663
+ class SnyDropdownContentDirective {
2664
+ dropdown = inject(SNY_DROPDOWN);
2665
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
2666
+ computedClass = computed(() => cn(dropdownContentVariants(), 'absolute mt-1 left-0 animate-in fade-in-0 zoom-in-95', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
2667
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2668
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyDropdownContentDirective, isStandalone: true, selector: "[snyDropdownContent]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "menu" }, properties: { "class": "computedClass()", "style.display": "dropdown.isOpen() ? \"\" : \"none\"" } }, ngImport: i0 });
2669
+ }
2670
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownContentDirective, decorators: [{
2671
+ type: Directive,
2672
+ args: [{
2673
+ selector: '[snyDropdownContent]',
2674
+ standalone: true,
2675
+ host: {
2676
+ 'role': 'menu',
2677
+ '[class]': 'computedClass()',
2678
+ '[style.display]': 'dropdown.isOpen() ? "" : "none"',
2679
+ },
2680
+ }]
2681
+ }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2682
+ class SnyMenuContentDirective {
2683
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
2684
+ computedClass = computed(() => cn(dropdownContentVariants(), this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
2685
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2686
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuContentDirective, isStandalone: true, selector: "[snyMenuContent]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "computedClass()" } }, ngImport: i0 });
2687
+ }
2688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuContentDirective, decorators: [{
2689
+ type: Directive,
2690
+ args: [{
2691
+ selector: '[snyMenuContent]',
2692
+ standalone: true,
2693
+ host: {
2694
+ '[class]': 'computedClass()',
2695
+ },
2696
+ }]
2697
+ }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2698
+ class SnyMenuItemDirective {
2699
+ dropdown = inject(SNY_DROPDOWN, { optional: true });
2700
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
2701
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
2702
+ computedClass = computed(() => cn(dropdownItemVariants({ variant: this.variant() }), 'cursor-pointer hover:bg-accent hover:text-accent-foreground', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
2703
+ onClick() {
2704
+ this.dropdown?.close();
2705
+ }
2706
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2707
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuItemDirective, isStandalone: true, selector: "[snyMenuItem]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "menuitem" }, listeners: { "click": "onClick()" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
2708
+ }
2709
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuItemDirective, decorators: [{
2710
+ type: Directive,
2711
+ args: [{
2712
+ selector: '[snyMenuItem]',
2713
+ standalone: true,
2714
+ host: {
2715
+ 'role': 'menuitem',
2716
+ '[class]': 'computedClass()',
2717
+ '(click)': 'onClick()',
2718
+ },
2719
+ }]
2720
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2721
+ class SnyMenuSeparatorDirective {
2722
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
2723
+ computedClass = computed(() => cn('-mx-1 my-1 h-px bg-muted', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
2724
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuSeparatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2725
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuSeparatorDirective, isStandalone: true, selector: "[snyMenuSeparator]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
2726
+ }
2727
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuSeparatorDirective, decorators: [{
2728
+ type: Directive,
2729
+ args: [{
2730
+ selector: '[snyMenuSeparator]',
2731
+ standalone: true,
2732
+ host: {
2733
+ 'role': 'separator',
2734
+ '[class]': 'computedClass()',
2735
+ },
2736
+ }]
2737
+ }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2738
+ class SnyMenuLabelDirective {
2739
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
2740
+ computedClass = computed(() => cn('px-2 py-1.5 text-sm font-semibold', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
2741
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2742
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuLabelDirective, isStandalone: true, selector: "[snyMenuLabel]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "computedClass()" } }, ngImport: i0 });
2743
+ }
2744
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuLabelDirective, decorators: [{
2745
+ type: Directive,
2746
+ args: [{
2747
+ selector: '[snyMenuLabel]',
2748
+ standalone: true,
2749
+ host: {
2750
+ '[class]': 'computedClass()',
2751
+ },
2752
+ }]
2753
+ }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2754
+
2755
+ class SnyCellDefDirective {
2756
+ snyCell = input.required(...(ngDevMode ? [{ debugName: "snyCell" }] : /* istanbul ignore next */ []));
2757
+ template = inject(TemplateRef);
2758
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCellDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2759
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyCellDefDirective, isStandalone: true, selector: "[snyCell]", inputs: { snyCell: { classPropertyName: "snyCell", publicName: "snyCell", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
2760
+ }
2761
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCellDefDirective, decorators: [{
2762
+ type: Directive,
2763
+ args: [{
2764
+ selector: '[snyCell]',
2765
+ standalone: true,
2766
+ }]
2767
+ }], propDecorators: { snyCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "snyCell", required: true }] }] } });
2768
+ class SnyHeaderCellDefDirective {
2769
+ snyHeaderCell = input.required(...(ngDevMode ? [{ debugName: "snyHeaderCell" }] : /* istanbul ignore next */ []));
2770
+ template = inject(TemplateRef);
2771
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyHeaderCellDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2772
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyHeaderCellDefDirective, isStandalone: true, selector: "[snyHeaderCell]", inputs: { snyHeaderCell: { classPropertyName: "snyHeaderCell", publicName: "snyHeaderCell", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
2773
+ }
2774
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyHeaderCellDefDirective, decorators: [{
2775
+ type: Directive,
2776
+ args: [{
2777
+ selector: '[snyHeaderCell]',
2778
+ standalone: true,
2779
+ }]
2780
+ }], propDecorators: { snyHeaderCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "snyHeaderCell", required: true }] }] } });
2781
+ class SnyBulkActionsDefDirective {
2782
+ template = inject(TemplateRef);
2783
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyBulkActionsDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2784
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.5", type: SnyBulkActionsDefDirective, isStandalone: true, selector: "[snyBulkActions]", ngImport: i0 });
2785
+ }
2786
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyBulkActionsDefDirective, decorators: [{
2787
+ type: Directive,
2788
+ args: [{
2789
+ selector: '[snyBulkActions]',
2790
+ standalone: true,
2791
+ }]
2792
+ }] });
2793
+ class SnyRowExpandDefDirective {
2794
+ template = inject(TemplateRef);
2795
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyRowExpandDefDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2796
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.5", type: SnyRowExpandDefDirective, isStandalone: true, selector: "[snyRowExpand]", ngImport: i0 });
2797
+ }
2798
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyRowExpandDefDirective, decorators: [{
2799
+ type: Directive,
2800
+ args: [{
2801
+ selector: '[snyRowExpand]',
2802
+ standalone: true,
2803
+ }]
2804
+ }] });
2805
+
2806
+ const DEFAULT_PAGINATION = {
2807
+ pageSize: 10,
2808
+ pageSizeOptions: [5, 10, 25, 50],
2809
+ };
2810
+ class SnyDataTableComponent {
2811
+ // Inputs
2812
+ columns = input.required(...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
2813
+ data = input.required(...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
2814
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
2815
+ density = input('normal', ...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
2816
+ hoverable = input(true, ...(ngDevMode ? [{ debugName: "hoverable" }] : /* istanbul ignore next */ []));
2817
+ stickyHeader = input(false, ...(ngDevMode ? [{ debugName: "stickyHeader" }] : /* istanbul ignore next */ []));
2818
+ selectable = input(false, ...(ngDevMode ? [{ debugName: "selectable" }] : /* istanbul ignore next */ []));
2819
+ paginated = input(true, ...(ngDevMode ? [{ debugName: "paginated" }] : /* istanbul ignore next */ []));
2820
+ filterable = input(true, ...(ngDevMode ? [{ debugName: "filterable" }] : /* istanbul ignore next */ []));
2821
+ showExport = input(false, ...(ngDevMode ? [{ debugName: "showExport" }] : /* istanbul ignore next */ []));
2822
+ showColumnToggle = input(false, ...(ngDevMode ? [{ debugName: "showColumnToggle" }] : /* istanbul ignore next */ []));
2823
+ expandable = input(false, ...(ngDevMode ? [{ debugName: "expandable" }] : /* istanbul ignore next */ []));
2824
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
2825
+ loadingRows = input(5, ...(ngDevMode ? [{ debugName: "loadingRows" }] : /* istanbul ignore next */ []));
2826
+ paginationConfig = input(DEFAULT_PAGINATION, ...(ngDevMode ? [{ debugName: "paginationConfig" }] : /* istanbul ignore next */ []));
2827
+ trackBy = input('', ...(ngDevMode ? [{ debugName: "trackBy" }] : /* istanbul ignore next */ []));
2828
+ noDataText = input('No data available', ...(ngDevMode ? [{ debugName: "noDataText" }] : /* istanbul ignore next */ []));
2829
+ // Model
2830
+ selectedRows = model([], ...(ngDevMode ? [{ debugName: "selectedRows" }] : /* istanbul ignore next */ []));
2831
+ // Outputs
2832
+ sortChanged = output();
2833
+ rowClicked = output();
2834
+ dataExported = output();
2835
+ // Content queries
2836
+ cellDefs = contentChildren(SnyCellDefDirective, ...(ngDevMode ? [{ debugName: "cellDefs" }] : /* istanbul ignore next */ []));
2837
+ headerCellDefs = contentChildren(SnyHeaderCellDefDirective, ...(ngDevMode ? [{ debugName: "headerCellDefs" }] : /* istanbul ignore next */ []));
2838
+ bulkActionsDef = contentChild(SnyBulkActionsDefDirective, ...(ngDevMode ? [{ debugName: "bulkActionsDef" }] : /* istanbul ignore next */ []));
2839
+ rowExpandDef = contentChild(SnyRowExpandDefDirective, ...(ngDevMode ? [{ debugName: "rowExpandDef" }] : /* istanbul ignore next */ []));
2840
+ // Internal state
2841
+ sortState = signal({ key: '', direction: null }, ...(ngDevMode ? [{ debugName: "sortState" }] : /* istanbul ignore next */ []));
2842
+ filterText = signal('', ...(ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []));
2843
+ currentPage = signal(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
2844
+ pageSize = signal(10, ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
2845
+ hiddenColumns = signal(new Set(), ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
2846
+ expandedRows = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedRows" }] : /* istanbul ignore next */ []));
2847
+ // Template def maps
2848
+ cellDefMap = computed(() => {
2849
+ const map = new Map();
2850
+ for (const def of this.cellDefs()) {
2851
+ map.set(def.snyCell(), def.template);
2852
+ }
2853
+ return map;
2854
+ }, ...(ngDevMode ? [{ debugName: "cellDefMap" }] : /* istanbul ignore next */ []));
2855
+ headerCellDefMap = computed(() => {
2856
+ const map = new Map();
2857
+ for (const def of this.headerCellDefs()) {
2858
+ map.set(def.snyHeaderCell(), def.template);
2859
+ }
2860
+ return map;
2861
+ }, ...(ngDevMode ? [{ debugName: "headerCellDefMap" }] : /* istanbul ignore next */ []));
2862
+ // Visible columns
2863
+ visibleColumns = computed(() => this.columns().filter((col) => col.visible !== false && !this.hiddenColumns().has(col.key)), ...(ngDevMode ? [{ debugName: "visibleColumns" }] : /* istanbul ignore next */ []));
2864
+ // Page size options
2865
+ pageSizeOptions = computed(() => this.paginationConfig().pageSizeOptions.map((n) => ({
2866
+ value: String(n),
2867
+ label: String(n),
2868
+ })), ...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
2869
+ pageSizeValue = computed(() => String(this.pageSize()), ...(ngDevMode ? [{ debugName: "pageSizeValue" }] : /* istanbul ignore next */ []));
2870
+ // Skeleton rows
2871
+ skeletonRows = computed(() => Array.from({ length: this.loadingRows() }, (_, i) => i), ...(ngDevMode ? [{ debugName: "skeletonRows" }] : /* istanbul ignore next */ []));
2872
+ // Bulk actions visibility
2873
+ showBulkActions = computed(() => this.selectable() &&
2874
+ this.selectedRows().length > 0 &&
2875
+ this.bulkActionsDef() != null, ...(ngDevMode ? [{ debugName: "showBulkActions" }] : /* istanbul ignore next */ []));
2876
+ // Data pipeline (filter uses all columns, not just visible)
2877
+ filteredData = computed(() => {
2878
+ const text = this.filterText().toLowerCase().trim();
2879
+ const rows = this.data();
2880
+ if (!text)
2881
+ return rows;
2882
+ const cols = this.columns().filter((c) => c.filterable !== false);
2883
+ return rows.filter((row) => cols.some((col) => String(row[col.key] ?? '').toLowerCase().includes(text)));
2884
+ }, ...(ngDevMode ? [{ debugName: "filteredData" }] : /* istanbul ignore next */ []));
2885
+ sortedData = computed(() => {
2886
+ const { key, direction } = this.sortState();
2887
+ const rows = this.filteredData();
2888
+ if (!key || !direction)
2889
+ return rows;
2890
+ return [...rows].sort((a, b) => {
2891
+ const aVal = a[key];
2892
+ const bVal = b[key];
2893
+ if (aVal == null && bVal == null)
2894
+ return 0;
2895
+ if (aVal == null)
2896
+ return direction === 'asc' ? -1 : 1;
2897
+ if (bVal == null)
2898
+ return direction === 'asc' ? 1 : -1;
2899
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
2900
+ return direction === 'asc' ? aVal - bVal : bVal - aVal;
2901
+ }
2902
+ const cmp = String(aVal).localeCompare(String(bVal));
2903
+ return direction === 'asc' ? cmp : -cmp;
2904
+ });
2905
+ }, ...(ngDevMode ? [{ debugName: "sortedData" }] : /* istanbul ignore next */ []));
2906
+ totalPages = computed(() => Math.max(1, Math.ceil(this.filteredData().length / this.pageSize())), ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
2907
+ paginatedData = computed(() => {
2908
+ if (!this.paginated())
2909
+ return this.sortedData();
2910
+ const start = (this.currentPage() - 1) * this.pageSize();
2911
+ return this.sortedData().slice(start, start + this.pageSize());
2912
+ }, ...(ngDevMode ? [{ debugName: "paginatedData" }] : /* istanbul ignore next */ []));
2913
+ totalColSpan = computed(() => this.visibleColumns().length +
2914
+ (this.selectable() ? 1 : 0) +
2915
+ (this.expandable() ? 1 : 0), ...(ngDevMode ? [{ debugName: "totalColSpan" }] : /* istanbul ignore next */ []));
2916
+ // Selection computed
2917
+ allSelected = computed(() => {
2918
+ const page = this.paginatedData();
2919
+ if (page.length === 0)
2920
+ return false;
2921
+ const selected = this.selectedRows();
2922
+ return page.every((row) => this.isRowInList(row, selected));
2923
+ }, ...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
2924
+ someSelected = computed(() => {
2925
+ const page = this.paginatedData();
2926
+ const selected = this.selectedRows();
2927
+ return page.some((row) => this.isRowInList(row, selected));
2928
+ }, ...(ngDevMode ? [{ debugName: "someSelected" }] : /* istanbul ignore next */ []));
2929
+ constructor() {
2930
+ effect(() => {
2931
+ const config = this.paginationConfig();
2932
+ untracked(() => this.pageSize.set(config.pageSize));
2933
+ });
2934
+ effect(() => {
2935
+ this.filterText();
2936
+ this.pageSize();
2937
+ this.data();
2938
+ untracked(() => this.currentPage.set(1));
2939
+ });
2940
+ }
2941
+ // Sort
2942
+ toggleSort(key) {
2943
+ const current = this.sortState();
2944
+ let direction;
2945
+ if (current.key !== key) {
2946
+ direction = 'asc';
2947
+ }
2948
+ else if (current.direction === 'asc') {
2949
+ direction = 'desc';
2950
+ }
2951
+ else if (current.direction === 'desc') {
2952
+ direction = null;
2953
+ }
2954
+ else {
2955
+ direction = 'asc';
2956
+ }
2957
+ const next = { key: direction ? key : '', direction };
2958
+ this.sortState.set(next);
2959
+ this.sortChanged.emit(next);
2960
+ }
2961
+ // Filter
2962
+ onFilterInput(event) {
2963
+ this.filterText.set(event.target.value);
2964
+ }
2965
+ // Page size
2966
+ onPageSizeChange(value) {
2967
+ this.pageSize.set(Number(value));
2968
+ }
2969
+ // Selection
2970
+ toggleSelectAll() {
2971
+ if (this.allSelected()) {
2972
+ const page = this.paginatedData();
2973
+ this.selectedRows.update((sel) => sel.filter((r) => !page.some((p) => this.rowsEqual(r, p))));
2974
+ }
2975
+ else {
2976
+ const page = this.paginatedData();
2977
+ this.selectedRows.update((sel) => {
2978
+ const newSel = [...sel];
2979
+ for (const row of page) {
2980
+ if (!this.isRowInList(row, newSel))
2981
+ newSel.push(row);
2982
+ }
2983
+ return newSel;
2984
+ });
2985
+ }
2986
+ }
2987
+ toggleRowSelection(row) {
2988
+ this.selectedRows.update((sel) => this.isRowInList(row, sel)
2989
+ ? sel.filter((r) => !this.rowsEqual(r, row))
2990
+ : [...sel, row]);
2991
+ }
2992
+ // Row click
2993
+ onRowClick(row) {
2994
+ this.rowClicked.emit(row);
2995
+ }
2996
+ // Export
2997
+ onExport() {
2998
+ this.dataExported.emit(this.filteredData());
2999
+ }
3000
+ // Column visibility
3001
+ toggleColumnVisibility(key) {
3002
+ this.hiddenColumns.update((set) => {
3003
+ const next = new Set(set);
3004
+ if (next.has(key))
3005
+ next.delete(key);
3006
+ else
3007
+ next.add(key);
3008
+ return next;
3009
+ });
3010
+ }
3011
+ // Expansion
3012
+ toggleRowExpansion(row) {
3013
+ const key = this.trackBy() ? row[this.trackBy()] : row;
3014
+ this.expandedRows.update((set) => {
3015
+ const next = new Set(set);
3016
+ if (next.has(key))
3017
+ next.delete(key);
3018
+ else
3019
+ next.add(key);
3020
+ return next;
3021
+ });
3022
+ }
3023
+ isExpanded(row) {
3024
+ const key = this.trackBy() ? row[this.trackBy()] : row;
3025
+ return this.expandedRows().has(key);
3026
+ }
3027
+ // Helpers
3028
+ isSelected(row) {
3029
+ return this.isRowInList(row, this.selectedRows());
3030
+ }
3031
+ trackByFn(row, index) {
3032
+ const key = this.trackBy();
3033
+ return key ? row[key] : index;
3034
+ }
3035
+ isRowInList(row, list) {
3036
+ return list.some((r) => this.rowsEqual(r, row));
3037
+ }
3038
+ rowsEqual(a, b) {
3039
+ const key = this.trackBy();
3040
+ if (key)
3041
+ return a[key] === b[key];
3042
+ return a === b;
3043
+ }
3044
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3045
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyDataTableComponent, isStandalone: true, selector: "sny-data-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, hoverable: { classPropertyName: "hoverable", publicName: "hoverable", isSignal: true, isRequired: false, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, paginated: { classPropertyName: "paginated", publicName: "paginated", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, showColumnToggle: { classPropertyName: "showColumnToggle", publicName: "showColumnToggle", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingRows: { classPropertyName: "loadingRows", publicName: "loadingRows", isSignal: true, isRequired: false, transformFunction: null }, paginationConfig: { classPropertyName: "paginationConfig", publicName: "paginationConfig", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, noDataText: { classPropertyName: "noDataText", publicName: "noDataText", isSignal: true, isRequired: false, transformFunction: null }, selectedRows: { classPropertyName: "selectedRows", publicName: "selectedRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedRows: "selectedRowsChange", sortChanged: "sortChanged", rowClicked: "rowClicked", dataExported: "dataExported" }, queries: [{ propertyName: "cellDefs", predicate: SnyCellDefDirective, isSignal: true }, { propertyName: "headerCellDefs", predicate: SnyHeaderCellDefDirective, isSignal: true }, { propertyName: "bulkActionsDef", first: true, predicate: SnyBulkActionsDefDirective, descendants: true, isSignal: true }, { propertyName: "rowExpandDef", first: true, predicate: SnyRowExpandDefDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
3046
+ <!-- Toolbar -->
3047
+ @if (filterable() || showExport() || showColumnToggle()) {
3048
+ <div class="flex items-center justify-between gap-4 mb-4 flex-wrap">
3049
+ @if (filterable()) {
3050
+ <input
3051
+ snyInput
3052
+ [value]="filterText()"
3053
+ (input)="onFilterInput($event)"
3054
+ placeholder="Filter..."
3055
+ class="w-full sm:max-w-sm"
3056
+ />
3057
+ }
3058
+ <div class="flex items-center gap-2">
3059
+ @if (showExport()) {
3060
+ <button snyBtn variant="outline" size="sm" (click)="onExport()">
3061
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="sm:mr-2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="M14 2v6h6"/><path d="M12 18v-6"/><path d="m9 15 3-3 3 3"/></svg>
3062
+ <span class="hidden sm:inline">Export</span>
3063
+ </button>
3064
+ }
3065
+ @if (showColumnToggle()) {
3066
+ <div snyDropdown class="relative">
3067
+ <button snyBtn variant="outline" size="sm" snyDropdownTrigger>
3068
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="sm:mr-2"><path d="M12 3v18"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/></svg>
3069
+ <span class="hidden sm:inline">Columns</span>
3070
+ </button>
3071
+ <div snyDropdownContent class="w-48 right-0 left-auto">
3072
+ @for (col of columns(); track col.key) {
3073
+ <label snyMenuItem class="flex items-center gap-2 cursor-pointer">
3074
+ <input
3075
+ type="checkbox"
3076
+ snyCheckbox
3077
+ [checked]="!hiddenColumns().has(col.key)"
3078
+ (change)="toggleColumnVisibility(col.key)"
3079
+ (click)="$event.stopPropagation()"
3080
+ />
3081
+ {{ col.label }}
3082
+ </label>
3083
+ }
3084
+ </div>
3085
+ </div>
3086
+ }
3087
+ </div>
3088
+ </div>
3089
+ }
3090
+
3091
+ <!-- Bulk Actions Bar -->
3092
+ @if (showBulkActions()) {
3093
+ @let selected = selectedRows();
3094
+ <div class="flex items-center gap-2 mb-4 p-3 bg-muted/50 rounded-sm border border-border flex-wrap">
3095
+ <span class="text-sm font-medium text-muted-foreground mr-2">
3096
+ {{ selected.length }} selected
3097
+ </span>
3098
+ <ng-container
3099
+ [ngTemplateOutlet]="bulkActionsDef()!.template"
3100
+ [ngTemplateOutletContext]="{ $implicit: selected }"
3101
+ />
3102
+ <button
3103
+ snyBtn variant="ghost" size="sm" class="ml-auto"
3104
+ (click)="selectedRows.set([])"
3105
+ >
3106
+ Clear
3107
+ </button>
3108
+ </div>
3109
+ }
3110
+
3111
+ <!-- Table -->
3112
+ <div class="overflow-auto border border-border rounded-sm">
3113
+ <table
3114
+ snyTable
3115
+ [variant]="variant()"
3116
+ [density]="density()"
3117
+ [hoverable]="hoverable()"
3118
+ [stickyHeader]="stickyHeader()"
3119
+ >
3120
+ <thead snyTableHeader>
3121
+ <tr snyTableRow>
3122
+ @if (selectable()) {
3123
+ <th snyTableHead class="w-12">
3124
+ <input
3125
+ type="checkbox"
3126
+ snyCheckbox
3127
+ [checked]="allSelected()"
3128
+ [indeterminate]="someSelected() && !allSelected()"
3129
+ (change)="toggleSelectAll()"
3130
+ />
3131
+ </th>
3132
+ }
3133
+ @if (expandable()) {
3134
+ <th snyTableHead class="w-10"></th>
3135
+ }
3136
+ @let sort = sortState();
3137
+ @let headerDefs = headerCellDefMap();
3138
+ @for (col of visibleColumns(); track col.key) {
3139
+ <th
3140
+ snyTableHead
3141
+ [style.width]="col.width ?? null"
3142
+ [class]="col.sortable ? 'cursor-pointer select-none' : ''"
3143
+ (click)="col.sortable ? toggleSort(col.key) : null"
3144
+ >
3145
+ @if (headerDefs.has(col.key)) {
3146
+ <ng-container
3147
+ [ngTemplateOutlet]="headerDefs.get(col.key)!"
3148
+ [ngTemplateOutletContext]="{ $implicit: col }"
3149
+ />
3150
+ } @else {
3151
+ <div class="flex items-center gap-1">
3152
+ <span>{{ col.label }}</span>
3153
+ @if (col.sortable) {
3154
+ @let isActive = sort.key === col.key;
3155
+ @if (isActive && sort.direction === 'asc') {
3156
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 7-7 7 7"/></svg>
3157
+ } @else if (isActive && sort.direction === 'desc') {
3158
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m19 12-7 7-7-7"/></svg>
3159
+ } @else {
3160
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-30"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
3161
+ }
3162
+ }
3163
+ </div>
3164
+ }
3165
+ </th>
3166
+ }
3167
+ </tr>
3168
+ </thead>
3169
+ <tbody snyTableBody>
3170
+ @if (loading()) {
3171
+ @for (i of skeletonRows(); track i) {
3172
+ <tr snyTableRow>
3173
+ @if (selectable()) {
3174
+ <td snyTableCell class="w-12"><div snySkeleton class="w-4 h-4 rounded"></div></td>
3175
+ }
3176
+ @if (expandable()) {
3177
+ <td snyTableCell class="w-10"><div snySkeleton class="w-4 h-4 rounded"></div></td>
3178
+ }
3179
+ @for (col of visibleColumns(); track col.key) {
3180
+ <td snyTableCell [style.width]="col.width ?? null">
3181
+ <div snySkeleton class="w-full h-4 rounded"></div>
3182
+ </td>
3183
+ }
3184
+ </tr>
3185
+ }
3186
+ } @else if (paginatedData().length === 0) {
3187
+ <tr snyTableRow>
3188
+ <td
3189
+ snyTableCell
3190
+ [attr.colspan]="totalColSpan()"
3191
+ class="text-center text-muted-foreground py-8"
3192
+ >
3193
+ {{ noDataText() }}
3194
+ </td>
3195
+ </tr>
3196
+ } @else {
3197
+ @let cellDefs = cellDefMap();
3198
+ @let cols = visibleColumns();
3199
+ @let expandTpl = rowExpandDef();
3200
+ @for (row of paginatedData(); track trackByFn(row, $index)) {
3201
+ <tr
3202
+ snyTableRow
3203
+ [attr.data-state]="isSelected(row) ? 'selected' : null"
3204
+ (click)="onRowClick(row)"
3205
+ class="cursor-pointer"
3206
+ >
3207
+ @if (selectable()) {
3208
+ <td snyTableCell class="w-12">
3209
+ <input
3210
+ type="checkbox"
3211
+ snyCheckbox
3212
+ [checked]="isSelected(row)"
3213
+ (change)="toggleRowSelection(row)"
3214
+ (click)="$event.stopPropagation()"
3215
+ />
3216
+ </td>
3217
+ }
3218
+ @if (expandable()) {
3219
+ <td snyTableCell class="w-10">
3220
+ <button
3221
+ class="p-0.5 rounded hover:bg-accent transition-transform duration-150"
3222
+ [class.rotate-90]="isExpanded(row)"
3223
+ (click)="toggleRowExpansion(row); $event.stopPropagation()"
3224
+ >
3225
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
3226
+ </button>
3227
+ </td>
3228
+ }
3229
+ @for (col of cols; track col.key) {
3230
+ <td snyTableCell [style.width]="col.width ?? null">
3231
+ @if (cellDefs.has(col.key)) {
3232
+ <ng-container
3233
+ [ngTemplateOutlet]="cellDefs.get(col.key)!"
3234
+ [ngTemplateOutletContext]="{ $implicit: row[col.key], row: row }"
3235
+ />
3236
+ } @else {
3237
+ {{ row[col.key] }}
3238
+ }
3239
+ </td>
3240
+ }
3241
+ </tr>
3242
+ @if (expandable() && isExpanded(row) && expandTpl) {
3243
+ <tr snyTableRow>
3244
+ <td snyTableCell [attr.colspan]="totalColSpan()" class="bg-muted/30">
3245
+ <ng-container
3246
+ [ngTemplateOutlet]="expandTpl.template"
3247
+ [ngTemplateOutletContext]="{ $implicit: row }"
3248
+ />
3249
+ </td>
3250
+ </tr>
3251
+ }
3252
+ }
3253
+ }
3254
+ </tbody>
3255
+ </table>
3256
+ </div>
3257
+
3258
+ <!-- Footer -->
3259
+ @if (paginated()) {
3260
+ <div class="flex flex-col sm:flex-row items-start sm:items-center justify-between mt-4 gap-3 sm:gap-4">
3261
+ <span class="text-sm text-muted-foreground">
3262
+ @if (selectable()) {
3263
+ {{ selectedRows().length }} of {{ filteredData().length }} row(s) selected
3264
+ } @else {
3265
+ {{ filteredData().length }} row(s)
3266
+ }
3267
+ </span>
3268
+ <div class="flex items-center gap-3 sm:gap-4 flex-wrap">
3269
+ <div class="flex items-center gap-2">
3270
+ <span class="hidden sm:inline text-sm text-muted-foreground whitespace-nowrap">Rows per page</span>
3271
+ <sny-select
3272
+ [options]="pageSizeOptions()"
3273
+ [value]="pageSizeValue()"
3274
+ (valueChange)="onPageSizeChange($event)"
3275
+ size="sm"
3276
+ class="w-20"
3277
+ />
3278
+ </div>
3279
+ <sny-pagination
3280
+ [currentPage]="currentPage()"
3281
+ (currentPageChange)="currentPage.set($event)"
3282
+ [totalPages]="totalPages()"
3283
+ />
3284
+ </div>
3285
+ </div>
3286
+ }
3287
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: SnyTableDirective, selector: "table[snyTable]", inputs: ["variant", "density", "hoverable", "stickyHeader", "class"] }, { kind: "directive", type: SnyTableHeaderDirective, selector: "thead[snyTableHeader]", inputs: ["class"] }, { kind: "directive", type: SnyTableBodyDirective, selector: "tbody[snyTableBody]", inputs: ["class"] }, { kind: "directive", type: SnyTableRowDirective, selector: "tr[snyTableRow]", inputs: ["class"] }, { kind: "directive", type: SnyTableHeadDirective, selector: "th[snyTableHead]", inputs: ["class"] }, { kind: "directive", type: SnyTableCellDirective, selector: "td[snyTableCell]", inputs: ["class"] }, { kind: "component", type: SnyPaginationComponent, selector: "sny-pagination", inputs: ["currentPage", "totalPages", "siblingCount", "boundaryCount", "size", "variant", "class"], outputs: ["currentPageChange"] }, { kind: "directive", type: SnyCheckboxDirective, selector: "input[type=\"checkbox\"][snyCheckbox]", inputs: ["size", "class"] }, { kind: "directive", type: SnyInputDirective, selector: "input[snyInput], textarea[snyInput]", inputs: ["variant", "inputSize", "class", "ariaDescribedBy"] }, { kind: "directive", type: SnyButtonDirective, selector: "button[snyBtn], a[snyBtn]", inputs: ["variant", "size", "disabled", "loading", "class"] }, { kind: "component", type: SnySelectComponent, selector: "sny-select", inputs: ["options", "placeholder", "size", "disabled", "class", "value"], outputs: ["valueChange"] }, { kind: "directive", type: SnySkeletonDirective, selector: "[snySkeleton]", inputs: ["variant", "size", "class"] }, { kind: "directive", type: SnyDropdownDirective, selector: "[snyDropdown]", exportAs: ["snyDropdown"] }, { kind: "directive", type: SnyDropdownTriggerDirective, selector: "[snyDropdownTrigger]" }, { kind: "directive", type: SnyDropdownContentDirective, selector: "[snyDropdownContent]", inputs: ["class"] }, { kind: "directive", type: SnyMenuItemDirective, selector: "[snyMenuItem]", inputs: ["variant", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3288
+ }
3289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDataTableComponent, decorators: [{
3290
+ type: Component,
3291
+ args: [{
3292
+ selector: 'sny-data-table',
3293
+ standalone: true,
3294
+ changeDetection: ChangeDetectionStrategy.OnPush,
3295
+ imports: [
3296
+ NgTemplateOutlet,
3297
+ SnyTableDirective,
3298
+ SnyTableHeaderDirective,
3299
+ SnyTableBodyDirective,
3300
+ SnyTableRowDirective,
3301
+ SnyTableHeadDirective,
3302
+ SnyTableCellDirective,
3303
+ SnyPaginationComponent,
3304
+ SnyCheckboxDirective,
3305
+ SnyInputDirective,
3306
+ SnyButtonDirective,
3307
+ SnySelectComponent,
3308
+ SnySkeletonDirective,
3309
+ SnyDropdownDirective,
3310
+ SnyDropdownTriggerDirective,
3311
+ SnyDropdownContentDirective,
3312
+ SnyMenuItemDirective,
3313
+ ],
3314
+ template: `
3315
+ <!-- Toolbar -->
3316
+ @if (filterable() || showExport() || showColumnToggle()) {
3317
+ <div class="flex items-center justify-between gap-4 mb-4 flex-wrap">
3318
+ @if (filterable()) {
3319
+ <input
3320
+ snyInput
3321
+ [value]="filterText()"
3322
+ (input)="onFilterInput($event)"
3323
+ placeholder="Filter..."
3324
+ class="w-full sm:max-w-sm"
3325
+ />
3326
+ }
3327
+ <div class="flex items-center gap-2">
3328
+ @if (showExport()) {
3329
+ <button snyBtn variant="outline" size="sm" (click)="onExport()">
3330
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="sm:mr-2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z"/><path d="M14 2v6h6"/><path d="M12 18v-6"/><path d="m9 15 3-3 3 3"/></svg>
3331
+ <span class="hidden sm:inline">Export</span>
3332
+ </button>
3333
+ }
3334
+ @if (showColumnToggle()) {
3335
+ <div snyDropdown class="relative">
3336
+ <button snyBtn variant="outline" size="sm" snyDropdownTrigger>
3337
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="sm:mr-2"><path d="M12 3v18"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/></svg>
3338
+ <span class="hidden sm:inline">Columns</span>
3339
+ </button>
3340
+ <div snyDropdownContent class="w-48 right-0 left-auto">
3341
+ @for (col of columns(); track col.key) {
3342
+ <label snyMenuItem class="flex items-center gap-2 cursor-pointer">
3343
+ <input
3344
+ type="checkbox"
3345
+ snyCheckbox
3346
+ [checked]="!hiddenColumns().has(col.key)"
3347
+ (change)="toggleColumnVisibility(col.key)"
3348
+ (click)="$event.stopPropagation()"
3349
+ />
3350
+ {{ col.label }}
3351
+ </label>
3352
+ }
3353
+ </div>
3354
+ </div>
3355
+ }
3356
+ </div>
3357
+ </div>
3358
+ }
3359
+
3360
+ <!-- Bulk Actions Bar -->
3361
+ @if (showBulkActions()) {
3362
+ @let selected = selectedRows();
3363
+ <div class="flex items-center gap-2 mb-4 p-3 bg-muted/50 rounded-sm border border-border flex-wrap">
3364
+ <span class="text-sm font-medium text-muted-foreground mr-2">
3365
+ {{ selected.length }} selected
3366
+ </span>
3367
+ <ng-container
3368
+ [ngTemplateOutlet]="bulkActionsDef()!.template"
3369
+ [ngTemplateOutletContext]="{ $implicit: selected }"
3370
+ />
3371
+ <button
3372
+ snyBtn variant="ghost" size="sm" class="ml-auto"
3373
+ (click)="selectedRows.set([])"
3374
+ >
3375
+ Clear
3376
+ </button>
3377
+ </div>
3378
+ }
3379
+
3380
+ <!-- Table -->
3381
+ <div class="overflow-auto border border-border rounded-sm">
3382
+ <table
3383
+ snyTable
3384
+ [variant]="variant()"
3385
+ [density]="density()"
3386
+ [hoverable]="hoverable()"
3387
+ [stickyHeader]="stickyHeader()"
3388
+ >
3389
+ <thead snyTableHeader>
3390
+ <tr snyTableRow>
3391
+ @if (selectable()) {
3392
+ <th snyTableHead class="w-12">
3393
+ <input
3394
+ type="checkbox"
3395
+ snyCheckbox
3396
+ [checked]="allSelected()"
3397
+ [indeterminate]="someSelected() && !allSelected()"
3398
+ (change)="toggleSelectAll()"
3399
+ />
3400
+ </th>
3401
+ }
3402
+ @if (expandable()) {
3403
+ <th snyTableHead class="w-10"></th>
3404
+ }
3405
+ @let sort = sortState();
3406
+ @let headerDefs = headerCellDefMap();
3407
+ @for (col of visibleColumns(); track col.key) {
3408
+ <th
3409
+ snyTableHead
3410
+ [style.width]="col.width ?? null"
3411
+ [class]="col.sortable ? 'cursor-pointer select-none' : ''"
3412
+ (click)="col.sortable ? toggleSort(col.key) : null"
3413
+ >
3414
+ @if (headerDefs.has(col.key)) {
3415
+ <ng-container
3416
+ [ngTemplateOutlet]="headerDefs.get(col.key)!"
3417
+ [ngTemplateOutletContext]="{ $implicit: col }"
3418
+ />
3419
+ } @else {
3420
+ <div class="flex items-center gap-1">
3421
+ <span>{{ col.label }}</span>
3422
+ @if (col.sortable) {
3423
+ @let isActive = sort.key === col.key;
3424
+ @if (isActive && sort.direction === 'asc') {
3425
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12 7-7 7 7"/></svg>
3426
+ } @else if (isActive && sort.direction === 'desc') {
3427
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m19 12-7 7-7-7"/></svg>
3428
+ } @else {
3429
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-30"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>
3430
+ }
3431
+ }
3432
+ </div>
3433
+ }
3434
+ </th>
3435
+ }
3436
+ </tr>
3437
+ </thead>
3438
+ <tbody snyTableBody>
3439
+ @if (loading()) {
3440
+ @for (i of skeletonRows(); track i) {
3441
+ <tr snyTableRow>
3442
+ @if (selectable()) {
3443
+ <td snyTableCell class="w-12"><div snySkeleton class="w-4 h-4 rounded"></div></td>
3444
+ }
3445
+ @if (expandable()) {
3446
+ <td snyTableCell class="w-10"><div snySkeleton class="w-4 h-4 rounded"></div></td>
3447
+ }
3448
+ @for (col of visibleColumns(); track col.key) {
3449
+ <td snyTableCell [style.width]="col.width ?? null">
3450
+ <div snySkeleton class="w-full h-4 rounded"></div>
3451
+ </td>
3452
+ }
3453
+ </tr>
3454
+ }
3455
+ } @else if (paginatedData().length === 0) {
3456
+ <tr snyTableRow>
3457
+ <td
3458
+ snyTableCell
3459
+ [attr.colspan]="totalColSpan()"
3460
+ class="text-center text-muted-foreground py-8"
3461
+ >
3462
+ {{ noDataText() }}
3463
+ </td>
3464
+ </tr>
3465
+ } @else {
3466
+ @let cellDefs = cellDefMap();
3467
+ @let cols = visibleColumns();
3468
+ @let expandTpl = rowExpandDef();
3469
+ @for (row of paginatedData(); track trackByFn(row, $index)) {
3470
+ <tr
3471
+ snyTableRow
3472
+ [attr.data-state]="isSelected(row) ? 'selected' : null"
3473
+ (click)="onRowClick(row)"
3474
+ class="cursor-pointer"
3475
+ >
3476
+ @if (selectable()) {
3477
+ <td snyTableCell class="w-12">
3478
+ <input
3479
+ type="checkbox"
3480
+ snyCheckbox
3481
+ [checked]="isSelected(row)"
3482
+ (change)="toggleRowSelection(row)"
3483
+ (click)="$event.stopPropagation()"
3484
+ />
3485
+ </td>
3486
+ }
3487
+ @if (expandable()) {
3488
+ <td snyTableCell class="w-10">
3489
+ <button
3490
+ class="p-0.5 rounded hover:bg-accent transition-transform duration-150"
3491
+ [class.rotate-90]="isExpanded(row)"
3492
+ (click)="toggleRowExpansion(row); $event.stopPropagation()"
3493
+ >
3494
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
3495
+ </button>
3496
+ </td>
3497
+ }
3498
+ @for (col of cols; track col.key) {
3499
+ <td snyTableCell [style.width]="col.width ?? null">
3500
+ @if (cellDefs.has(col.key)) {
3501
+ <ng-container
3502
+ [ngTemplateOutlet]="cellDefs.get(col.key)!"
3503
+ [ngTemplateOutletContext]="{ $implicit: row[col.key], row: row }"
3504
+ />
3505
+ } @else {
3506
+ {{ row[col.key] }}
3507
+ }
3508
+ </td>
3509
+ }
3510
+ </tr>
3511
+ @if (expandable() && isExpanded(row) && expandTpl) {
3512
+ <tr snyTableRow>
3513
+ <td snyTableCell [attr.colspan]="totalColSpan()" class="bg-muted/30">
3514
+ <ng-container
3515
+ [ngTemplateOutlet]="expandTpl.template"
3516
+ [ngTemplateOutletContext]="{ $implicit: row }"
3517
+ />
3518
+ </td>
3519
+ </tr>
3520
+ }
3521
+ }
3522
+ }
3523
+ </tbody>
3524
+ </table>
3525
+ </div>
3526
+
3527
+ <!-- Footer -->
3528
+ @if (paginated()) {
3529
+ <div class="flex flex-col sm:flex-row items-start sm:items-center justify-between mt-4 gap-3 sm:gap-4">
3530
+ <span class="text-sm text-muted-foreground">
3531
+ @if (selectable()) {
3532
+ {{ selectedRows().length }} of {{ filteredData().length }} row(s) selected
3533
+ } @else {
3534
+ {{ filteredData().length }} row(s)
3535
+ }
3536
+ </span>
3537
+ <div class="flex items-center gap-3 sm:gap-4 flex-wrap">
3538
+ <div class="flex items-center gap-2">
3539
+ <span class="hidden sm:inline text-sm text-muted-foreground whitespace-nowrap">Rows per page</span>
3540
+ <sny-select
3541
+ [options]="pageSizeOptions()"
3542
+ [value]="pageSizeValue()"
3543
+ (valueChange)="onPageSizeChange($event)"
3544
+ size="sm"
3545
+ class="w-20"
3546
+ />
3547
+ </div>
3548
+ <sny-pagination
3549
+ [currentPage]="currentPage()"
3550
+ (currentPageChange)="currentPage.set($event)"
3551
+ [totalPages]="totalPages()"
3552
+ />
3553
+ </div>
3554
+ </div>
3555
+ }
3556
+ `,
3557
+ }]
3558
+ }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], hoverable: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoverable", required: false }] }], stickyHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyHeader", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], paginated: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginated", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], showColumnToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showColumnToggle", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingRows", required: false }] }], paginationConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginationConfig", required: false }] }], trackBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackBy", required: false }] }], noDataText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noDataText", required: false }] }], selectedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedRows", required: false }] }, { type: i0.Output, args: ["selectedRowsChange"] }], sortChanged: [{ type: i0.Output, args: ["sortChanged"] }], rowClicked: [{ type: i0.Output, args: ["rowClicked"] }], dataExported: [{ type: i0.Output, args: ["dataExported"] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SnyCellDefDirective), { isSignal: true }] }], headerCellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => SnyHeaderCellDefDirective), { isSignal: true }] }], bulkActionsDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SnyBulkActionsDefDirective), { isSignal: true }] }], rowExpandDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => SnyRowExpandDefDirective), { isSignal: true }] }] } });
3559
+
2420
3560
  class SnySheetRef {
2421
3561
  cdkRef;
2422
3562
  constructor(cdkRef) {
@@ -2906,216 +4046,55 @@ class SnyTooltipDirective {
2906
4046
  const hostRect = this.el.nativeElement.getBoundingClientRect();
2907
4047
  const tooltipRect = this.tooltipEl.getBoundingClientRect();
2908
4048
  const position = this.tooltipPosition();
2909
- const gap = 8;
2910
- let top = 0;
2911
- let left = 0;
2912
- switch (position) {
2913
- case 'top':
2914
- top = hostRect.top - tooltipRect.height - gap;
2915
- left = hostRect.left + (hostRect.width - tooltipRect.width) / 2;
2916
- break;
2917
- case 'bottom':
2918
- top = hostRect.bottom + gap;
2919
- left = hostRect.left + (hostRect.width - tooltipRect.width) / 2;
2920
- break;
2921
- case 'left':
2922
- top = hostRect.top + (hostRect.height - tooltipRect.height) / 2;
2923
- left = hostRect.left - tooltipRect.width - gap;
2924
- break;
2925
- case 'right':
2926
- top = hostRect.top + (hostRect.height - tooltipRect.height) / 2;
2927
- left = hostRect.right + gap;
2928
- break;
2929
- }
2930
- this.tooltipEl.style.top = `${top}px`;
2931
- this.tooltipEl.style.left = `${left}px`;
2932
- }
2933
- destroyTooltip() {
2934
- if (this.tooltipEl) {
2935
- this.tooltipEl.remove();
2936
- this.tooltipEl = null;
2937
- }
2938
- }
2939
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2940
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyTooltipDirective, isStandalone: true, selector: "[snyTooltip]", inputs: { snyTooltip: { classPropertyName: "snyTooltip", publicName: "snyTooltip", isSignal: true, isRequired: true, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, tooltipDelay: { classPropertyName: "tooltipDelay", publicName: "tooltipDelay", isSignal: true, isRequired: false, transformFunction: null }, tooltipDisabled: { classPropertyName: "tooltipDisabled", publicName: "tooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "show()", "mouseleave": "hide()", "focus": "show()", "blur": "hide()", "keydown.escape": "hide()" }, properties: { "attr.aria-describedby": "isOpen() ? tooltipId : null" } }, exportAs: ["snyTooltip"], ngImport: i0 });
2941
- }
2942
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyTooltipDirective, decorators: [{
2943
- type: Directive,
2944
- args: [{
2945
- selector: '[snyTooltip]',
2946
- standalone: true,
2947
- exportAs: 'snyTooltip',
2948
- host: {
2949
- '(mouseenter)': 'show()',
2950
- '(mouseleave)': 'hide()',
2951
- '(focus)': 'show()',
2952
- '(blur)': 'hide()',
2953
- '(keydown.escape)': 'hide()',
2954
- '[attr.aria-describedby]': 'isOpen() ? tooltipId : null',
2955
- },
2956
- }]
2957
- }], ctorParameters: () => [], propDecorators: { snyTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "snyTooltip", required: true }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], tooltipDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDelay", required: false }] }], tooltipDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDisabled", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
2958
-
2959
- const dropdownContentVariants = cva('z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md', {
2960
- variants: {},
2961
- defaultVariants: {},
2962
- });
2963
- const dropdownItemVariants = cva('relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[active]:bg-accent data-[active]:text-accent-foreground', {
2964
- variants: {
2965
- variant: {
2966
- default: '',
2967
- destructive: 'text-destructive data-[active]:bg-destructive/10 data-[active]:text-destructive',
2968
- },
2969
- },
2970
- defaultVariants: {
2971
- variant: 'default',
2972
- },
2973
- });
2974
-
2975
- const SNY_DROPDOWN = new InjectionToken('SnyDropdown');
2976
- class SnyDropdownDirective {
2977
- elementRef = inject(ElementRef);
2978
- isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
2979
- toggle() { this.isOpen.update((v) => !v); }
2980
- open() { this.isOpen.set(true); }
2981
- close() { this.isOpen.set(false); }
2982
- onDocumentClick(event) {
2983
- if (!this.elementRef.nativeElement.contains(event.target)) {
2984
- this.close();
2985
- }
2986
- }
2987
- onEscape() {
2988
- this.close();
2989
- }
2990
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2991
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.5", type: SnyDropdownDirective, isStandalone: true, selector: "[snyDropdown]", host: { listeners: { "document:click": "onDocumentClick($event)", "keydown.escape": "onEscape()" }, properties: { "class": "\"relative inline-block\"" } }, providers: [{ provide: SNY_DROPDOWN, useExisting: SnyDropdownDirective }], exportAs: ["snyDropdown"], ngImport: i0 });
2992
- }
2993
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownDirective, decorators: [{
2994
- type: Directive,
2995
- args: [{
2996
- selector: '[snyDropdown]',
2997
- standalone: true,
2998
- exportAs: 'snyDropdown',
2999
- providers: [{ provide: SNY_DROPDOWN, useExisting: SnyDropdownDirective }],
3000
- host: {
3001
- '[class]': '"relative inline-block"',
3002
- },
3003
- }]
3004
- }], propDecorators: { onDocumentClick: [{
3005
- type: HostListener,
3006
- args: ['document:click', ['$event']]
3007
- }], onEscape: [{
3008
- type: HostListener,
3009
- args: ['keydown.escape']
3010
- }] } });
3011
- class SnyDropdownTriggerDirective {
3012
- dropdown = inject(SNY_DROPDOWN);
3013
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3014
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.5", type: SnyDropdownTriggerDirective, isStandalone: true, selector: "[snyDropdownTrigger]", host: { listeners: { "click": "dropdown.toggle()" }, properties: { "attr.aria-expanded": "dropdown.isOpen()", "attr.aria-haspopup": "\"menu\"" } }, ngImport: i0 });
3015
- }
3016
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownTriggerDirective, decorators: [{
3017
- type: Directive,
3018
- args: [{
3019
- selector: '[snyDropdownTrigger]',
3020
- standalone: true,
3021
- host: {
3022
- '(click)': 'dropdown.toggle()',
3023
- '[attr.aria-expanded]': 'dropdown.isOpen()',
3024
- '[attr.aria-haspopup]': '"menu"',
3025
- },
3026
- }]
3027
- }] });
3028
- class SnyDropdownContentDirective {
3029
- dropdown = inject(SNY_DROPDOWN);
3030
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
3031
- computedClass = computed(() => cn(dropdownContentVariants(), 'absolute mt-1 left-0 animate-in fade-in-0 zoom-in-95', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
3032
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3033
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyDropdownContentDirective, isStandalone: true, selector: "[snyDropdownContent]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "menu" }, properties: { "class": "computedClass()", "style.display": "dropdown.isOpen() ? \"\" : \"none\"" } }, ngImport: i0 });
3034
- }
3035
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyDropdownContentDirective, decorators: [{
3036
- type: Directive,
3037
- args: [{
3038
- selector: '[snyDropdownContent]',
3039
- standalone: true,
3040
- host: {
3041
- 'role': 'menu',
3042
- '[class]': 'computedClass()',
3043
- '[style.display]': 'dropdown.isOpen() ? "" : "none"',
3044
- },
3045
- }]
3046
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3047
- class SnyMenuContentDirective {
3048
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
3049
- computedClass = computed(() => cn(dropdownContentVariants(), this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
3050
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3051
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuContentDirective, isStandalone: true, selector: "[snyMenuContent]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "computedClass()" } }, ngImport: i0 });
3052
- }
3053
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuContentDirective, decorators: [{
3054
- type: Directive,
3055
- args: [{
3056
- selector: '[snyMenuContent]',
3057
- standalone: true,
3058
- host: {
3059
- '[class]': 'computedClass()',
3060
- },
3061
- }]
3062
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3063
- class SnyMenuItemDirective {
3064
- dropdown = inject(SNY_DROPDOWN, { optional: true });
3065
- variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3066
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
3067
- computedClass = computed(() => cn(dropdownItemVariants({ variant: this.variant() }), 'cursor-pointer hover:bg-accent hover:text-accent-foreground', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
3068
- onClick() {
3069
- this.dropdown?.close();
4049
+ const gap = 8;
4050
+ let top = 0;
4051
+ let left = 0;
4052
+ switch (position) {
4053
+ case 'top':
4054
+ top = hostRect.top - tooltipRect.height - gap;
4055
+ left = hostRect.left + (hostRect.width - tooltipRect.width) / 2;
4056
+ break;
4057
+ case 'bottom':
4058
+ top = hostRect.bottom + gap;
4059
+ left = hostRect.left + (hostRect.width - tooltipRect.width) / 2;
4060
+ break;
4061
+ case 'left':
4062
+ top = hostRect.top + (hostRect.height - tooltipRect.height) / 2;
4063
+ left = hostRect.left - tooltipRect.width - gap;
4064
+ break;
4065
+ case 'right':
4066
+ top = hostRect.top + (hostRect.height - tooltipRect.height) / 2;
4067
+ left = hostRect.right + gap;
4068
+ break;
4069
+ }
4070
+ this.tooltipEl.style.top = `${top}px`;
4071
+ this.tooltipEl.style.left = `${left}px`;
3070
4072
  }
3071
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3072
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuItemDirective, isStandalone: true, selector: "[snyMenuItem]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "menuitem" }, listeners: { "click": "onClick()" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
3073
- }
3074
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuItemDirective, decorators: [{
3075
- type: Directive,
3076
- args: [{
3077
- selector: '[snyMenuItem]',
3078
- standalone: true,
3079
- host: {
3080
- 'role': 'menuitem',
3081
- '[class]': 'computedClass()',
3082
- '(click)': 'onClick()',
3083
- },
3084
- }]
3085
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3086
- class SnyMenuSeparatorDirective {
3087
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
3088
- computedClass = computed(() => cn('-mx-1 my-1 h-px bg-muted', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
3089
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuSeparatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3090
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuSeparatorDirective, isStandalone: true, selector: "[snyMenuSeparator]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "class": "computedClass()" } }, ngImport: i0 });
3091
- }
3092
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuSeparatorDirective, decorators: [{
3093
- type: Directive,
3094
- args: [{
3095
- selector: '[snyMenuSeparator]',
3096
- standalone: true,
3097
- host: {
3098
- 'role': 'separator',
3099
- '[class]': 'computedClass()',
3100
- },
3101
- }]
3102
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3103
- class SnyMenuLabelDirective {
3104
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
3105
- computedClass = computed(() => cn('px-2 py-1.5 text-sm font-semibold', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
3106
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3107
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyMenuLabelDirective, isStandalone: true, selector: "[snyMenuLabel]", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "computedClass()" } }, ngImport: i0 });
4073
+ destroyTooltip() {
4074
+ if (this.tooltipEl) {
4075
+ this.tooltipEl.remove();
4076
+ this.tooltipEl = null;
4077
+ }
4078
+ }
4079
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4080
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyTooltipDirective, isStandalone: true, selector: "[snyTooltip]", inputs: { snyTooltip: { classPropertyName: "snyTooltip", publicName: "snyTooltip", isSignal: true, isRequired: true, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, tooltipDelay: { classPropertyName: "tooltipDelay", publicName: "tooltipDelay", isSignal: true, isRequired: false, transformFunction: null }, tooltipDisabled: { classPropertyName: "tooltipDisabled", publicName: "tooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "show()", "mouseleave": "hide()", "focus": "show()", "blur": "hide()", "keydown.escape": "hide()" }, properties: { "attr.aria-describedby": "isOpen() ? tooltipId : null" } }, exportAs: ["snyTooltip"], ngImport: i0 });
3108
4081
  }
3109
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyMenuLabelDirective, decorators: [{
4082
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyTooltipDirective, decorators: [{
3110
4083
  type: Directive,
3111
4084
  args: [{
3112
- selector: '[snyMenuLabel]',
4085
+ selector: '[snyTooltip]',
3113
4086
  standalone: true,
4087
+ exportAs: 'snyTooltip',
3114
4088
  host: {
3115
- '[class]': 'computedClass()',
4089
+ '(mouseenter)': 'show()',
4090
+ '(mouseleave)': 'hide()',
4091
+ '(focus)': 'show()',
4092
+ '(blur)': 'hide()',
4093
+ '(keydown.escape)': 'hide()',
4094
+ '[attr.aria-describedby]': 'isOpen() ? tooltipId : null',
3116
4095
  },
3117
4096
  }]
3118
- }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
4097
+ }], ctorParameters: () => [], propDecorators: { snyTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "snyTooltip", required: true }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], tooltipDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDelay", required: false }] }], tooltipDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDisabled", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3119
4098
 
3120
4099
  const fileInputVariants = cva('flex w-full cursor-pointer items-center gap-2 rounded-md border bg-background px-3 py-2 text-sm ring-offset-background transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', {
3121
4100
  variants: {
@@ -3684,180 +4663,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
3684
4663
  }]
3685
4664
  }], propDecorators: { side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3686
4665
 
3687
- const paginationItemVariants = cva('inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', {
3688
- variants: {
3689
- variant: {
3690
- default: 'bg-background hover:bg-accent hover:text-accent-foreground',
3691
- outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
3692
- ghost: 'hover:bg-accent hover:text-accent-foreground',
3693
- },
3694
- size: {
3695
- sm: 'h-8 w-8 text-xs',
3696
- md: 'h-9 w-9',
3697
- lg: 'h-10 w-10',
3698
- },
3699
- active: {
3700
- true: 'bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground',
3701
- false: '',
3702
- },
3703
- },
3704
- defaultVariants: {
3705
- variant: 'default',
3706
- size: 'md',
3707
- active: false,
3708
- },
3709
- });
3710
-
3711
- function computePageRange(totalPages, currentPage, siblingCount, boundaryCount) {
3712
- const range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i);
3713
- const startPages = range(1, Math.min(boundaryCount, totalPages));
3714
- const endPages = range(Math.max(totalPages - boundaryCount + 1, boundaryCount + 1), totalPages);
3715
- const siblingsStart = Math.max(Math.min(currentPage - siblingCount, totalPages - boundaryCount - siblingCount * 2 - 1), boundaryCount + 2);
3716
- const siblingsEnd = Math.min(Math.max(currentPage + siblingCount, boundaryCount + siblingCount * 2 + 2), endPages.length > 0 ? endPages[0] - 2 : totalPages - 1);
3717
- const result = [...startPages];
3718
- if (siblingsStart > boundaryCount + 2) {
3719
- result.push('ellipsis');
3720
- }
3721
- else if (boundaryCount + 1 < totalPages - boundaryCount) {
3722
- result.push(boundaryCount + 1);
3723
- }
3724
- result.push(...range(siblingsStart, siblingsEnd));
3725
- if (siblingsEnd < totalPages - boundaryCount - 1) {
3726
- result.push('ellipsis');
3727
- }
3728
- else if (totalPages - boundaryCount > boundaryCount) {
3729
- result.push(totalPages - boundaryCount);
3730
- }
3731
- result.push(...endPages);
3732
- return [...new Set(result)].sort((a, b) => {
3733
- if (a === 'ellipsis')
3734
- return 0;
3735
- if (b === 'ellipsis')
3736
- return 0;
3737
- return a - b;
3738
- });
3739
- }
3740
- class SnyPaginationComponent {
3741
- currentPage = model(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
3742
- totalPages = input.required(...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
3743
- siblingCount = input(1, ...(ngDevMode ? [{ debugName: "siblingCount" }] : /* istanbul ignore next */ []));
3744
- boundaryCount = input(1, ...(ngDevMode ? [{ debugName: "boundaryCount" }] : /* istanbul ignore next */ []));
3745
- size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3746
- variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3747
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
3748
- pages = computed(() => computePageRange(this.totalPages(), this.currentPage(), this.siblingCount(), this.boundaryCount()), ...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
3749
- hasPrev = computed(() => this.currentPage() > 1, ...(ngDevMode ? [{ debugName: "hasPrev" }] : /* istanbul ignore next */ []));
3750
- hasNext = computed(() => this.currentPage() < this.totalPages(), ...(ngDevMode ? [{ debugName: "hasNext" }] : /* istanbul ignore next */ []));
3751
- goToPage(page) {
3752
- if (page === 'ellipsis')
3753
- return;
3754
- this.currentPage.set(page);
3755
- }
3756
- prev() {
3757
- if (this.hasPrev())
3758
- this.currentPage.update((p) => p - 1);
3759
- }
3760
- next() {
3761
- if (this.hasNext())
3762
- this.currentPage.update((p) => p + 1);
3763
- }
3764
- pageClass(page) {
3765
- return cn(paginationItemVariants({
3766
- variant: this.variant(),
3767
- size: this.size(),
3768
- active: page === this.currentPage(),
3769
- }));
3770
- }
3771
- navBtnClass() {
3772
- return cn(paginationItemVariants({ variant: this.variant(), size: this.size(), active: false }));
3773
- }
3774
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3775
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyPaginationComponent, isStandalone: true, selector: "sny-pagination", inputs: { currentPage: { classPropertyName: "currentPage", publicName: "currentPage", isSignal: true, isRequired: false, transformFunction: null }, totalPages: { classPropertyName: "totalPages", publicName: "totalPages", isSignal: true, isRequired: true, transformFunction: null }, siblingCount: { classPropertyName: "siblingCount", publicName: "siblingCount", isSignal: true, isRequired: false, transformFunction: null }, boundaryCount: { classPropertyName: "boundaryCount", publicName: "boundaryCount", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { currentPage: "currentPageChange" }, host: { attributes: { "role": "navigation", "aria-label": "Pagination" } }, ngImport: i0, template: `
3776
- <div class="flex items-center gap-1">
3777
- <button
3778
- [class]="navBtnClass()"
3779
- [disabled]="!hasPrev()"
3780
- [attr.aria-label]="'Go to previous page'"
3781
- (click)="prev()"
3782
- >
3783
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
3784
- </button>
3785
-
3786
- @for (page of pages(); track $index) {
3787
- @if (page === 'ellipsis') {
3788
- <span class="flex h-9 w-9 items-center justify-center" aria-hidden="true">...</span>
3789
- } @else {
3790
- <button
3791
- [class]="pageClass(page)"
3792
- [attr.aria-label]="'Page ' + page"
3793
- [attr.aria-current]="page === currentPage() ? 'page' : null"
3794
- (click)="goToPage(page)"
3795
- >
3796
- {{ page }}
3797
- </button>
3798
- }
3799
- }
3800
-
3801
- <button
3802
- [class]="navBtnClass()"
3803
- [disabled]="!hasNext()"
3804
- [attr.aria-label]="'Go to next page'"
3805
- (click)="next()"
3806
- >
3807
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
3808
- </button>
3809
- </div>
3810
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3811
- }
3812
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyPaginationComponent, decorators: [{
3813
- type: Component,
3814
- args: [{
3815
- selector: 'sny-pagination',
3816
- standalone: true,
3817
- changeDetection: ChangeDetectionStrategy.OnPush,
3818
- host: {
3819
- 'role': 'navigation',
3820
- 'aria-label': 'Pagination',
3821
- },
3822
- template: `
3823
- <div class="flex items-center gap-1">
3824
- <button
3825
- [class]="navBtnClass()"
3826
- [disabled]="!hasPrev()"
3827
- [attr.aria-label]="'Go to previous page'"
3828
- (click)="prev()"
3829
- >
3830
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
3831
- </button>
3832
-
3833
- @for (page of pages(); track $index) {
3834
- @if (page === 'ellipsis') {
3835
- <span class="flex h-9 w-9 items-center justify-center" aria-hidden="true">...</span>
3836
- } @else {
3837
- <button
3838
- [class]="pageClass(page)"
3839
- [attr.aria-label]="'Page ' + page"
3840
- [attr.aria-current]="page === currentPage() ? 'page' : null"
3841
- (click)="goToPage(page)"
3842
- >
3843
- {{ page }}
3844
- </button>
3845
- }
3846
- }
3847
-
3848
- <button
3849
- [class]="navBtnClass()"
3850
- [disabled]="!hasNext()"
3851
- [attr.aria-label]="'Go to next page'"
3852
- (click)="next()"
3853
- >
3854
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
3855
- </button>
3856
- </div>
3857
- `,
3858
- }]
3859
- }], propDecorators: { currentPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPage", required: false }] }, { type: i0.Output, args: ["currentPageChange"] }], totalPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalPages", required: true }] }], siblingCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "siblingCount", required: false }] }], boundaryCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "boundaryCount", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3860
-
3861
4666
  const dividerVariants = cva('shrink-0 bg-border', {
3862
4667
  variants: {
3863
4668
  orientation: {
@@ -5302,5 +6107,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
5302
6107
  * Generated bundle index. Do not edit.
5303
6108
  */
5304
6109
 
5305
- export { SNY_ACCORDION, SNY_ACCORDION_ITEM, SNY_CAROUSEL, SNY_CHAT_BUBBLE, SNY_CONFIG, SNY_DIALOG_DATA, SNY_DRAWER, SNY_DROPDOWN, SNY_FAB, SNY_SHEET_DATA, SNY_STEPS, SNY_TABLE, SNY_TABS, SNY_TIMELINE, SnyAccordionContentDirective, SnyAccordionDirective, SnyAccordionItemDirective, SnyAccordionTriggerDirective, SnyAlertDescriptionDirective, SnyAlertDirective, SnyAlertTitleDirective, SnyAvatarComponent, SnyBadgeDirective, SnyBreadcrumbDirective, SnyBreadcrumbItemDirective, SnyBreadcrumbLinkDirective, SnyBreadcrumbListDirective, SnyBreadcrumbPageDirective, SnyBreadcrumbSeparatorDirective, SnyButtonDirective, SnyButtonGroupDirective, SnyCalendarComponent, SnyCardContentDirective, SnyCardDescriptionDirective, SnyCardDirective, SnyCardFooterDirective, SnyCardHeaderDirective, SnyCardTitleDirective, SnyCarouselContentDirective, SnyCarouselDirective, SnyCarouselItemDirective, SnyCarouselNextDirective, SnyCarouselPrevDirective, SnyChatBubbleAvatarDirective, SnyChatBubbleBodyDirective, SnyChatBubbleContentDirective, SnyChatBubbleDirective, SnyChatBubbleFooterDirective, SnyChatBubbleHeaderDirective, SnyCheckboxDirective, SnyComboboxComponent, SnyDialogCloseDirective, SnyDialogContentDirective, SnyDialogDescriptionDirective, SnyDialogFooterDirective, SnyDialogHeaderDirective, SnyDialogRef, SnyDialogService, SnyDialogTitleDirective, SnyDiffComponent, SnyDividerComponent, SnyDockDirective, SnyDockItemDirective, SnyDrawerContentDirective, SnyDrawerLayoutComponent, SnyDrawerLayoutDirective, SnyDrawerSideDirective, SnyDropdownContentDirective, SnyDropdownDirective, SnyDropdownTriggerDirective, SnyFabActionDirective, SnyFabDirective, SnyFabTriggerDirective, SnyFieldsetContentDirective, SnyFieldsetDirective, SnyFieldsetLegendDirective, SnyFileInputComponent, SnyIndicatorBadgeDirective, SnyIndicatorDirective, SnyInputDirective, SnyKbdDirective, SnyLabelDirective, SnyLinkDirective, SnyListDirective, SnyListItemActionDirective, SnyListItemContentDirective, SnyListItemDirective, SnyListItemIconDirective, SnyLoaderComponent, SnyMenuContentDirective, SnyMenuItemDirective, SnyMenuLabelDirective, SnyMenuSeparatorDirective, SnyNavbarBrandDirective, SnyNavbarContentDirective, SnyNavbarDirective, SnyNavbarEndDirective, SnyPaginationComponent, SnyProgressComponent, SnyRadialProgressComponent, SnyRadioDirective, SnyRatingComponent, SnySelectComponent, SnySheetCloseDirective, SnySheetContentDirective, SnySheetDescriptionDirective, SnySheetHeaderDirective, SnySheetRef, SnySheetService, SnySheetTitleDirective, SnySkeletonDirective, SnySliderComponent, SnyStatDescriptionDirective, SnyStatDirective, SnyStatFigureDirective, SnyStatTitleDirective, SnyStatValueDirective, SnyStatusDirective, SnyStepDirective, SnyStepsDirective, SnySwitchComponent, SnyTableBodyDirective, SnyTableCaptionDirective, SnyTableCellDirective, SnyTableDirective, SnyTableFooterDirective, SnyTableHeadDirective, SnyTableHeaderDirective, SnyTableRowDirective, SnyTabsContentDirective, SnyTabsDirective, SnyTabsListDirective, SnyTabsTriggerDirective, SnyTextareaDirective, SnyTimelineDirective, SnyTimelineEndDirective, SnyTimelineItemDirective, SnyTimelineMiddleDirective, SnyTimelineStartDirective, SnyToastService, SnyToasterComponent, SnyToggleDirective, SnyTooltipDirective, SnyValidatorDirective, SnyValidatorHintDirective, ThemeService, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, cardVariants, checkboxVariants, cn, comboboxTriggerVariants, dividerVariants, dropdownContentVariants, dropdownItemVariants, fieldsetVariants, fileInputVariants, inputVariants, kbdVariants, labelVariants, linkVariants, loaderVariants, paginationItemVariants, progressBarVariants, progressTrackVariants, provideSonnyUI, radioVariants, ratingVariants, selectTriggerVariants, skeletonVariants, sliderTrackVariants, statusVariants, switchTrackVariants, tableCellVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleVariants, tooltipVariants };
6110
+ export { SNY_ACCORDION, SNY_ACCORDION_ITEM, SNY_CAROUSEL, SNY_CHAT_BUBBLE, SNY_CONFIG, SNY_DIALOG_DATA, SNY_DRAWER, SNY_DROPDOWN, SNY_FAB, SNY_SHEET_DATA, SNY_STEPS, SNY_TABLE, SNY_TABS, SNY_TIMELINE, SnyAccordionContentDirective, SnyAccordionDirective, SnyAccordionItemDirective, SnyAccordionTriggerDirective, SnyAlertDescriptionDirective, SnyAlertDirective, SnyAlertTitleDirective, SnyAvatarComponent, SnyBadgeDirective, SnyBreadcrumbDirective, SnyBreadcrumbItemDirective, SnyBreadcrumbLinkDirective, SnyBreadcrumbListDirective, SnyBreadcrumbPageDirective, SnyBreadcrumbSeparatorDirective, SnyBulkActionsDefDirective, SnyButtonDirective, SnyButtonGroupDirective, SnyCalendarComponent, SnyCardContentDirective, SnyCardDescriptionDirective, SnyCardDirective, SnyCardFooterDirective, SnyCardHeaderDirective, SnyCardTitleDirective, SnyCarouselContentDirective, SnyCarouselDirective, SnyCarouselItemDirective, SnyCarouselNextDirective, SnyCarouselPrevDirective, SnyCellDefDirective, SnyChatBubbleAvatarDirective, SnyChatBubbleBodyDirective, SnyChatBubbleContentDirective, SnyChatBubbleDirective, SnyChatBubbleFooterDirective, SnyChatBubbleHeaderDirective, SnyCheckboxDirective, SnyComboboxComponent, SnyDataTableComponent, SnyDialogCloseDirective, SnyDialogContentDirective, SnyDialogDescriptionDirective, SnyDialogFooterDirective, SnyDialogHeaderDirective, SnyDialogRef, SnyDialogService, SnyDialogTitleDirective, SnyDiffComponent, SnyDividerComponent, SnyDockDirective, SnyDockItemDirective, SnyDrawerContentDirective, SnyDrawerLayoutComponent, SnyDrawerLayoutDirective, SnyDrawerSideDirective, SnyDropdownContentDirective, SnyDropdownDirective, SnyDropdownTriggerDirective, SnyFabActionDirective, SnyFabDirective, SnyFabTriggerDirective, SnyFieldsetContentDirective, SnyFieldsetDirective, SnyFieldsetLegendDirective, SnyFileInputComponent, SnyHeaderCellDefDirective, SnyIndicatorBadgeDirective, SnyIndicatorDirective, SnyInputDirective, SnyKbdDirective, SnyLabelDirective, SnyLinkDirective, SnyListDirective, SnyListItemActionDirective, SnyListItemContentDirective, SnyListItemDirective, SnyListItemIconDirective, SnyLoaderComponent, SnyMenuContentDirective, SnyMenuItemDirective, SnyMenuLabelDirective, SnyMenuSeparatorDirective, SnyNavbarBrandDirective, SnyNavbarContentDirective, SnyNavbarDirective, SnyNavbarEndDirective, SnyPaginationComponent, SnyProgressComponent, SnyRadialProgressComponent, SnyRadioDirective, SnyRatingComponent, SnyRowExpandDefDirective, SnySelectComponent, SnySheetCloseDirective, SnySheetContentDirective, SnySheetDescriptionDirective, SnySheetHeaderDirective, SnySheetRef, SnySheetService, SnySheetTitleDirective, SnySkeletonDirective, SnySliderComponent, SnyStatDescriptionDirective, SnyStatDirective, SnyStatFigureDirective, SnyStatTitleDirective, SnyStatValueDirective, SnyStatusDirective, SnyStepDirective, SnyStepsDirective, SnySwitchComponent, SnyTableBodyDirective, SnyTableCaptionDirective, SnyTableCellDirective, SnyTableDirective, SnyTableFooterDirective, SnyTableHeadDirective, SnyTableHeaderDirective, SnyTableRowDirective, SnyTabsContentDirective, SnyTabsDirective, SnyTabsListDirective, SnyTabsTriggerDirective, SnyTextareaDirective, SnyTimelineDirective, SnyTimelineEndDirective, SnyTimelineItemDirective, SnyTimelineMiddleDirective, SnyTimelineStartDirective, SnyToastService, SnyToasterComponent, SnyToggleDirective, SnyTooltipDirective, SnyValidatorDirective, SnyValidatorHintDirective, ThemeService, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, cardVariants, checkboxVariants, cn, comboboxTriggerVariants, dividerVariants, dropdownContentVariants, dropdownItemVariants, fieldsetVariants, fileInputVariants, inputVariants, kbdVariants, labelVariants, linkVariants, loaderVariants, paginationItemVariants, progressBarVariants, progressTrackVariants, provideSonnyUI, radioVariants, ratingVariants, selectTriggerVariants, skeletonVariants, sliderTrackVariants, statusVariants, switchTrackVariants, tableCellVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleVariants, tooltipVariants };
5306
6111
  //# sourceMappingURL=sonny-ui-core.mjs.map