@tolle_/tolle-ui 0.0.27-beta → 0.0.29-beta

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.
@@ -7,9 +7,9 @@ import { CommonModule, isPlatformBrowser, DOCUMENT, NgIf, NgTemplateOutlet } fro
7
7
  import { cva } from 'class-variance-authority';
8
8
  import * as i2 from '@angular/forms';
9
9
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
10
- import { autoUpdate, computePosition, offset, flip, shift } from '@floating-ui/dom';
10
+ import { autoUpdate, computePosition, offset, flip, shift, size } from '@floating-ui/dom';
11
11
  import { Subject, Subscription, BehaviorSubject } from 'rxjs';
12
- import { startOfWeek, startOfMonth, endOfWeek, endOfMonth, eachDayOfInterval, subMonths, subYears, addMonths, addYears, isSameMonth, setMonth, setYear, isSameDay, isToday, isBefore, startOfDay, parse, isValid, format, isWithinInterval } from 'date-fns';
12
+ import { format, startOfWeek, startOfMonth, endOfWeek, endOfMonth, eachDayOfInterval, subMonths, subYears, addMonths, addYears, setMonth, setYear, isSameDay, isToday, isSameMonth, isBefore, startOfDay, parse, isValid, isWithinInterval } from 'date-fns';
13
13
  import * as i1$1 from '@angular/cdk/overlay';
14
14
  import { OverlayConfig } from '@angular/cdk/overlay';
15
15
  import { ComponentPortal } from '@angular/cdk/portal';
@@ -403,7 +403,7 @@ class CardComponent {
403
403
  cn = cn;
404
404
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
405
405
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CardComponent, isStandalone: true, selector: "tolle-card", inputs: { class: "class" }, ngImport: i0, template: `
406
- <div [class]="cn('rounded-md border border-border bg-card text-card-foreground shadow', class)">
406
+ <div [class]="cn('rounded-md border border-border text-card-foreground shadow', class)">
407
407
  <ng-content></ng-content>
408
408
  </div>
409
409
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
@@ -415,7 +415,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
415
415
  standalone: true,
416
416
  imports: [CommonModule],
417
417
  template: `
418
- <div [class]="cn('rounded-md border border-border bg-card text-card-foreground shadow', class)">
418
+ <div [class]="cn('rounded-md border border-border text-card-foreground shadow', class)">
419
419
  <ng-content></ng-content>
420
420
  </div>
421
421
  `,
@@ -512,6 +512,8 @@ class SelectItemComponent {
512
512
  value;
513
513
  class = '';
514
514
  selected = false;
515
+ disabled = false;
516
+ multiSelect = false; // Will be set by parent component
515
517
  hidden = false;
516
518
  constructor(selectService, el) {
517
519
  this.selectService = selectService;
@@ -519,33 +521,85 @@ class SelectItemComponent {
519
521
  }
520
522
  // Helper method for the parent to get the searchable text
521
523
  getLabel() {
522
- return this.el.nativeElement.innerText || '';
524
+ return this.el.nativeElement.textContent?.trim() || '';
525
+ }
526
+ getItemClasses() {
527
+ return cn(
528
+ // Base state
529
+ 'focus:bg-accent focus:text-accent-foreground',
530
+ // Hover states (only if not disabled)
531
+ !this.disabled && [
532
+ 'cursor-pointer',
533
+ 'hover:bg-accent hover:text-accent-foreground'
534
+ ],
535
+ // Selected state
536
+ this.selected && [
537
+ this.multiSelect
538
+ ? 'bg-primary/5 text-foreground'
539
+ : 'bg-accent text-accent-foreground'
540
+ ],
541
+ // Disabled state
542
+ this.disabled && [
543
+ 'opacity-50',
544
+ 'cursor-not-allowed',
545
+ 'hover:bg-transparent hover:text-foreground'
546
+ ]);
523
547
  }
524
548
  onClick(event) {
525
- if (this.hidden)
549
+ if (this.hidden || this.disabled)
526
550
  return;
527
551
  event.stopPropagation();
528
552
  if (this.selectService) {
529
- // Get the text content to show in the trigger button
530
- const label = this.el.nativeElement.innerText.trim();
553
+ const label = this.getLabel();
554
+ if (this.multiSelect) {
555
+ // For multi-select, toggle selection
556
+ this.selected = !this.selected;
557
+ }
558
+ // For both single and multi-select, notify parent
531
559
  this.selectService.registerClick(this.value, label);
532
560
  }
533
561
  }
534
562
  cn = cn;
535
563
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectItemComponent, deps: [{ token: SelectService, optional: true }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
536
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SelectItemComponent, isStandalone: true, selector: "tolle-select-item", inputs: { value: "value", class: "class", selected: "selected" }, host: { listeners: { "click": "onClick($event)" } }, ngImport: i0, template: `
564
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SelectItemComponent, isStandalone: true, selector: "tolle-select-item", inputs: { value: "value", class: "class", selected: "selected", disabled: "disabled", multiSelect: "multiSelect" }, host: { listeners: { "click": "onClick($event)" } }, ngImport: i0, template: `
537
565
  <div
538
566
  *ngIf="!hidden"
539
567
  [class]="cn(
540
- 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground transition-colors',
541
- selected ? 'bg-accent text-accent-foreground' : '',
568
+ 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors',
569
+ getItemClasses(),
542
570
  class
543
571
  )"
572
+ [attr.aria-disabled]="disabled"
573
+ [attr.aria-selected]="selected"
574
+ role="option"
544
575
  >
545
- <span *ngIf="selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
576
+ <!-- Single Select: Checkmark -->
577
+ <span *ngIf="!multiSelect && selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
546
578
  <i class="ri-check-line text-primary"></i>
547
579
  </span>
548
- <ng-content></ng-content>
580
+
581
+ <!-- Multi-Select: Checkbox -->
582
+ <span *ngIf="multiSelect" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
583
+ <div [class]="cn(
584
+ 'flex h-4 w-4 items-center justify-center rounded-sm border transition-all duration-200',
585
+ selected ? 'border-primary bg-primary text-primary-foreground' : 'border-input'
586
+ )">
587
+ <i [class]="cn(
588
+ 'ri-check-line text-xs transition-all duration-200',
589
+ selected ? 'opacity-100 scale-100' : 'opacity-0 scale-75'
590
+ )"></i>
591
+ </div>
592
+ </span>
593
+
594
+ <!-- Content -->
595
+ <span class="flex-1 truncate">
596
+ <ng-content></ng-content>
597
+ </span>
598
+
599
+ <!-- Disabled indicator -->
600
+ <span *ngIf="disabled && !selected" class="ml-2 text-xs text-muted-foreground/50">
601
+ <i class="ri-forbid-line"></i>
602
+ </span>
549
603
  </div>
550
604
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
551
605
  }
@@ -559,15 +613,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
559
613
  <div
560
614
  *ngIf="!hidden"
561
615
  [class]="cn(
562
- 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground transition-colors',
563
- selected ? 'bg-accent text-accent-foreground' : '',
616
+ 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors',
617
+ getItemClasses(),
564
618
  class
565
619
  )"
620
+ [attr.aria-disabled]="disabled"
621
+ [attr.aria-selected]="selected"
622
+ role="option"
566
623
  >
567
- <span *ngIf="selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
624
+ <!-- Single Select: Checkmark -->
625
+ <span *ngIf="!multiSelect && selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
568
626
  <i class="ri-check-line text-primary"></i>
569
627
  </span>
570
- <ng-content></ng-content>
628
+
629
+ <!-- Multi-Select: Checkbox -->
630
+ <span *ngIf="multiSelect" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
631
+ <div [class]="cn(
632
+ 'flex h-4 w-4 items-center justify-center rounded-sm border transition-all duration-200',
633
+ selected ? 'border-primary bg-primary text-primary-foreground' : 'border-input'
634
+ )">
635
+ <i [class]="cn(
636
+ 'ri-check-line text-xs transition-all duration-200',
637
+ selected ? 'opacity-100 scale-100' : 'opacity-0 scale-75'
638
+ )"></i>
639
+ </div>
640
+ </span>
641
+
642
+ <!-- Content -->
643
+ <span class="flex-1 truncate">
644
+ <ng-content></ng-content>
645
+ </span>
646
+
647
+ <!-- Disabled indicator -->
648
+ <span *ngIf="disabled && !selected" class="ml-2 text-xs text-muted-foreground/50">
649
+ <i class="ri-forbid-line"></i>
650
+ </span>
571
651
  </div>
572
652
  `,
573
653
  }]
@@ -579,6 +659,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
579
659
  type: Input
580
660
  }], selected: [{
581
661
  type: Input
662
+ }], disabled: [{
663
+ type: Input
664
+ }], multiSelect: [{
665
+ type: Input
582
666
  }], onClick: [{
583
667
  type: HostListener,
584
668
  args: ['click', ['$event']]
@@ -618,44 +702,25 @@ class SelectComponent {
618
702
  this.close();
619
703
  }));
620
704
  }
621
- // SIMPLIFIED: Zardui-inspired trigger styling
622
705
  get computedTriggerClass() {
623
- return cn(
624
- // Base styles
625
- 'flex w-full items-center justify-between rounded-md border transition-all duration-200', 'bg-background text-foreground',
626
- // Border and shadow
627
- 'border-input shadow-sm',
628
- // Sizing
629
- this.size === 'xs' && 'h-8 px-2 text-xs', this.size === 'sm' && 'h-9 px-3 text-sm', this.size === 'default' && 'h-10 px-3 text-sm', this.size === 'lg' && 'h-11 px-4 text-base',
630
- // Focus state - SIMPLE LIKE ZARDUI
631
- !(this.readonly || this.disabled) && [
706
+ return cn('flex w-full items-center justify-between rounded-md border transition-all duration-200', 'bg-background text-foreground', 'border-input shadow-sm', this.size === 'xs' && 'h-8 px-2 text-xs', this.size === 'sm' && 'h-9 px-3 text-sm', this.size === 'default' && 'h-10 px-3 text-sm', this.size === 'lg' && 'h-11 px-4 text-base', !(this.readonly || this.disabled) && [
632
707
  'focus:outline-none',
633
708
  'focus:ring-4',
634
709
  'focus:ring-ring/30',
635
710
  'focus:ring-offset-0',
636
711
  'focus:shadow-none',
637
- // Border darkens on focus automatically
638
712
  'focus:border-primary/80'
639
- ],
640
- // Hover state
641
- !(this.readonly || this.disabled) && 'hover:border-accent',
642
- // Disabled state
643
- this.disabled && [
713
+ ], !(this.readonly || this.disabled) && 'hover:border-accent', this.disabled && [
644
714
  'cursor-not-allowed opacity-50',
645
715
  'border-opacity-50'
646
- ],
647
- // Readonly state
648
- this.readonly && [
716
+ ], this.readonly && [
649
717
  'cursor-default',
650
718
  'border-dashed',
651
719
  !this.disabled && 'focus:ring-0 focus:border-opacity-100'
652
720
  ], this.class);
653
721
  }
654
- // UPDATED: Dynamic icon sizing relative to the trigger size
655
722
  get iconClass() {
656
- return cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform duration-200', this.isOpen ? 'rotate-180' : '', (this.size === 'xs' || this.size === 'sm') ? 'text-[14px]' : 'text-[18px]',
657
- // Hide or fade icon when interaction is blocked
658
- (this.disabled || this.readonly) && 'opacity-30');
723
+ return cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform duration-200', this.isOpen ? 'rotate-180' : '', (this.size === 'xs' || this.size === 'sm') ? 'text-[14px]' : 'text-[18px]', (this.disabled || this.readonly) && 'opacity-30');
659
724
  }
660
725
  ngAfterContentInit() {
661
726
  this.updateItemSelection();
@@ -675,9 +740,7 @@ class SelectComponent {
675
740
  }
676
741
  open() {
677
742
  this.isOpen = true;
678
- // Trigger focus on the button for focus styling
679
743
  this.trigger.nativeElement.focus();
680
- // Tick to ensure DOM is rendered before positioning
681
744
  setTimeout(() => this.updatePosition());
682
745
  }
683
746
  close() {
@@ -692,10 +755,25 @@ class SelectComponent {
692
755
  return;
693
756
  this.cleanupAutoUpdate = autoUpdate(this.trigger.nativeElement, this.popover.nativeElement, () => {
694
757
  computePosition(this.trigger.nativeElement, this.popover.nativeElement, {
758
+ strategy: 'fixed', // 3. Use fixed strategy
695
759
  placement: 'bottom-start',
696
- middleware: [offset(4), flip(), shift({ padding: 8 })],
697
- }).then(({ x, y }) => {
760
+ middleware: [
761
+ offset(4),
762
+ flip(),
763
+ shift({ padding: 8 }),
764
+ // 4. Use size middleware to sync width and handle constraints
765
+ size({
766
+ apply({ rects, elements, availableHeight }) {
767
+ Object.assign(elements.floating.style, {
768
+ width: `${rects.reference.width}px`,
769
+ maxHeight: `${availableHeight}px`
770
+ });
771
+ },
772
+ }),
773
+ ],
774
+ }).then(({ x, y, strategy }) => {
698
775
  Object.assign(this.popover.nativeElement.style, {
776
+ position: strategy, // 5. Apply strategy to style
699
777
  left: `${x}px`,
700
778
  top: `${y}px`,
701
779
  visibility: 'visible',
@@ -746,7 +824,7 @@ class SelectComponent {
746
824
  multi: true
747
825
  }
748
826
  ], queries: [{ propertyName: "items", predicate: SelectItemComponent, descendants: true }], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: `
749
- <div [class]="cn('w-full', 'size-' + size)" #container>
827
+ <div [class]="cn('relative w-full', 'size-' + size)" #container>
750
828
  <button
751
829
  type="button"
752
830
  #trigger
@@ -763,8 +841,7 @@ class SelectComponent {
763
841
  <div
764
842
  #popover
765
843
  *ngIf="isOpen"
766
- [ngStyle]="{minWidth: container.clientWidth+'px'}"
767
- class="absolute bg-popover z-50 min-w-full max-h-[300px] overflow-auto flex flex-col rounded-md border border-border text-popover-foreground shadow-md"
844
+ class="fixed bg-popover z-[999] overflow-auto flex flex-col rounded-md border border-border text-popover-foreground shadow-md"
768
845
  style="visibility: hidden; top: 0; left: 0;">
769
846
  <div *ngIf="searchable" class="p-2 border-b border-border bg-popover h-auto">
770
847
  <tolle-input
@@ -785,7 +862,7 @@ class SelectComponent {
785
862
  </div>
786
863
  </div>
787
864
  </div>
788
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error", "hideHintOnFocus"] }] });
865
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error", "hideHintOnFocus"] }] });
789
866
  }
790
867
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, decorators: [{
791
868
  type: Component,
@@ -802,7 +879,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
802
879
  }
803
880
  ],
804
881
  template: `
805
- <div [class]="cn('w-full', 'size-' + size)" #container>
882
+ <div [class]="cn('relative w-full', 'size-' + size)" #container>
806
883
  <button
807
884
  type="button"
808
885
  #trigger
@@ -819,8 +896,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
819
896
  <div
820
897
  #popover
821
898
  *ngIf="isOpen"
822
- [ngStyle]="{minWidth: container.clientWidth+'px'}"
823
- class="absolute bg-popover z-50 min-w-full max-h-[300px] overflow-auto flex flex-col rounded-md border border-border text-popover-foreground shadow-md"
899
+ class="fixed bg-popover z-[999] overflow-auto flex flex-col rounded-md border border-border text-popover-foreground shadow-md"
824
900
  style="visibility: hidden; top: 0; left: 0;">
825
901
  <div *ngIf="searchable" class="p-2 border-b border-border bg-popover h-auto">
826
902
  <tolle-input
@@ -1866,6 +1942,9 @@ class MultiSelectComponent {
1866
1942
  searchable = false;
1867
1943
  disabled = false;
1868
1944
  class = '';
1945
+ maxSelections;
1946
+ maxDisplayItems = 3;
1947
+ error = false; // Added to support error styling
1869
1948
  trigger;
1870
1949
  popover;
1871
1950
  items;
@@ -1882,10 +1961,58 @@ class MultiSelectComponent {
1882
1961
  this.toggleValue(val);
1883
1962
  });
1884
1963
  }
1964
+ // NEW: Matches InputComponent styles exactly
1965
+ get computedTriggerClass() {
1966
+ return cn(
1967
+ // Base styles
1968
+ 'flex min-h-10 w-full items-center justify-between rounded-md border transition-all duration-200 h-auto', 'bg-background text-sm',
1969
+ // Border and shadow
1970
+ 'border-input shadow-sm',
1971
+ // Padding based on size (aligned with InputComponent logic)
1972
+ this.size === 'xs' && 'px-2 py-1', this.size === 'sm' && 'px-3 py-1.5', this.size === 'default' && 'px-3 py-2', this.size === 'lg' && 'px-4 py-2',
1973
+ // Focus state - ZARDUI STYLE (Soft ring, no offset)
1974
+ !this.disabled && [
1975
+ 'focus:outline-none',
1976
+ 'focus:ring-4',
1977
+ 'focus:ring-ring/30',
1978
+ 'focus:ring-offset-0',
1979
+ 'focus:shadow-none',
1980
+ 'focus:border-primary/80' // Darkens border on focus
1981
+ ],
1982
+ // Hover state
1983
+ !this.disabled && 'hover:border-accent',
1984
+ // Error state
1985
+ this.error && [
1986
+ 'border-destructive',
1987
+ !this.disabled && [
1988
+ 'focus:border-destructive/80',
1989
+ 'focus:ring-destructive/30'
1990
+ ]
1991
+ ],
1992
+ // Disabled state
1993
+ this.disabled && [
1994
+ 'cursor-not-allowed opacity-50',
1995
+ 'border-opacity-50'
1996
+ ], this.class);
1997
+ }
1885
1998
  ngAfterContentInit() {
1886
1999
  this.syncItems();
1887
2000
  this.items.changes.subscribe(() => this.syncItems());
1888
2001
  }
2002
+ get displayItems() {
2003
+ return this.selectedItems.slice(0, this.maxDisplayItems);
2004
+ }
2005
+ get exceedsDisplayLimit() {
2006
+ return this.value.length > this.maxDisplayItems;
2007
+ }
2008
+ get selectableItems() {
2009
+ return this.items ? this.items.filter(item => !item.disabled) : [];
2010
+ }
2011
+ get availableSelections() {
2012
+ if (!this.maxSelections)
2013
+ return Infinity;
2014
+ return Math.max(0, this.maxSelections - this.value.length);
2015
+ }
1889
2016
  toggle() {
1890
2017
  if (this.disabled)
1891
2018
  return;
@@ -1897,16 +2024,34 @@ class MultiSelectComponent {
1897
2024
  }
1898
2025
  close() {
1899
2026
  this.isOpen = false;
2027
+ this.searchQuery = '';
2028
+ this.onSearchChange('');
1900
2029
  if (this.cleanup)
1901
2030
  this.cleanup();
1902
2031
  }
1903
2032
  updatePosition() {
2033
+ if (!this.trigger || !this.popover)
2034
+ return;
1904
2035
  this.cleanup = autoUpdate(this.trigger.nativeElement, this.popover.nativeElement, () => {
1905
2036
  computePosition(this.trigger.nativeElement, this.popover.nativeElement, {
2037
+ strategy: 'fixed',
1906
2038
  placement: 'bottom-start',
1907
- middleware: [offset(4), flip(), shift({ padding: 8 })],
1908
- }).then(({ x, y }) => {
2039
+ middleware: [
2040
+ offset(4),
2041
+ flip(),
2042
+ shift({ padding: 8 }),
2043
+ size({
2044
+ apply({ rects, elements, availableHeight }) {
2045
+ Object.assign(elements.floating.style, {
2046
+ width: `${rects.reference.width}px`,
2047
+ maxHeight: `${availableHeight}px`
2048
+ });
2049
+ },
2050
+ }),
2051
+ ],
2052
+ }).then(({ x, y, strategy }) => {
1909
2053
  Object.assign(this.popover.nativeElement.style, {
2054
+ position: strategy,
1910
2055
  left: `${x}px`,
1911
2056
  top: `${y}px`,
1912
2057
  visibility: 'visible',
@@ -1916,12 +2061,32 @@ class MultiSelectComponent {
1916
2061
  }
1917
2062
  toggleValue(val) {
1918
2063
  const index = this.value.indexOf(val);
1919
- index > -1 ? this.value.splice(index, 1) : this.value.push(val);
2064
+ if (index > -1) {
2065
+ this.value.splice(index, 1);
2066
+ }
2067
+ else {
2068
+ if (this.maxSelections && this.value.length >= this.maxSelections)
2069
+ return;
2070
+ this.value.push(val);
2071
+ }
1920
2072
  this.syncItems();
1921
- this.onChange([...this.value]); // Return new array reference for Change Detection
2073
+ this.onChange([...this.value]);
1922
2074
  }
1923
2075
  selectAll() {
1924
- this.value = this.items.map(i => i.value);
2076
+ if (!this.items)
2077
+ return;
2078
+ let itemsToSelect = [];
2079
+ if (this.maxSelections) {
2080
+ const availableItems = this.items
2081
+ .filter(item => !item.disabled && !this.value.includes(item.value))
2082
+ .slice(0, this.availableSelections)
2083
+ .map(item => item.value);
2084
+ itemsToSelect = [...this.value, ...availableItems];
2085
+ }
2086
+ else {
2087
+ itemsToSelect = this.items.filter(item => !item.disabled).map(item => item.value);
2088
+ }
2089
+ this.value = itemsToSelect;
1925
2090
  this.syncItems();
1926
2091
  this.onChange([...this.value]);
1927
2092
  }
@@ -1940,8 +2105,15 @@ class MultiSelectComponent {
1940
2105
  this.selectedItems = [];
1941
2106
  this.items.forEach(item => {
1942
2107
  item.selected = this.value.includes(item.value);
1943
- if (item.selected)
2108
+ if (item.selected) {
1944
2109
  this.selectedItems.push({ label: item.getLabel(), value: item.value });
2110
+ }
2111
+ if (this.maxSelections && this.value.length >= this.maxSelections) {
2112
+ item.disabled = !this.value.includes(item.value);
2113
+ }
2114
+ else if (item.disabled) {
2115
+ item.disabled = false;
2116
+ }
1945
2117
  });
1946
2118
  }
1947
2119
  onSearchChange(q) {
@@ -1963,45 +2135,93 @@ class MultiSelectComponent {
1963
2135
  // ControlValueAccessor
1964
2136
  onChange = () => { };
1965
2137
  onTouched = () => { };
1966
- writeValue(v) { this.value = Array.isArray(v) ? v : []; this.syncItems(); }
2138
+ writeValue(v) {
2139
+ this.value = Array.isArray(v) ? v : [];
2140
+ this.syncItems();
2141
+ }
1967
2142
  registerOnChange(fn) { this.onChange = fn; }
1968
2143
  registerOnTouched(fn) { this.onTouched = fn; }
2144
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
1969
2145
  cn = cn;
1970
2146
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MultiSelectComponent, deps: [{ token: SelectService }], target: i0.ɵɵFactoryTarget.Component });
1971
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MultiSelectComponent, isStandalone: true, selector: "tolle-multi-select", inputs: { placeholder: "placeholder", size: "size", searchable: "searchable", disabled: "disabled", class: "class" }, host: { listeners: { "document:mousedown": "onClickOutside($event)" } }, providers: [
2147
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MultiSelectComponent, isStandalone: true, selector: "tolle-multi-select", inputs: { placeholder: "placeholder", size: "size", searchable: "searchable", disabled: "disabled", class: "class", maxSelections: "maxSelections", maxDisplayItems: "maxDisplayItems", error: "error" }, host: { listeners: { "document:mousedown": "onClickOutside($event)" } }, providers: [
1972
2148
  SelectService,
1973
2149
  { provide: NG_VALUE_ACCESSOR, useExisting: MultiSelectComponent, multi: true }
1974
2150
  ], queries: [{ propertyName: "items", predicate: SelectItemComponent, descendants: true }], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true }], ngImport: i0, template: `
1975
2151
  <div [class]="cn('relative w-full', 'size-' + size)" #container>
1976
- <button #trigger type="button" (click)="toggle()" [disabled]="disabled"
1977
- [class]="cn('flex min-h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm focus:ring-2 focus:ring-ring focus:ring-offset-2 transition-all h-auto', class)">
2152
+ <button
2153
+ #trigger
2154
+ type="button"
2155
+ (click)="toggle()"
2156
+ [disabled]="disabled"
2157
+ [class]="computedTriggerClass">
2158
+
1978
2159
  <div class="flex flex-wrap gap-1 items-center max-w-[95%]">
1979
2160
  <ng-container *ngIf="value?.length; else placeholderTpl">
1980
- <tolle-badge *ngFor="let item of selectedItems" size="xs" variant="secondary" [removable]="true" (onRemove)="removeValue($event, item.value)">
2161
+ <tolle-badge *ngFor="let item of displayItems" size="xs" variant="secondary" [removable]="true" (onRemove)="removeValue($event, item.value)">
1981
2162
  {{ item.label }}
1982
2163
  </tolle-badge>
2164
+ <span *ngIf="exceedsDisplayLimit" class="text-xs text-muted-foreground px-1">
2165
+ +{{ value.length - maxDisplayItems }} more
2166
+ </span>
2167
+ <span *ngIf="maxSelections && value.length >= maxSelections" class="text-xs text-muted-foreground px-1">
2168
+ (Max reached)
2169
+ </span>
1983
2170
  </ng-container>
1984
2171
  <ng-template #placeholderTpl><span class="text-muted-foreground">{{ placeholder }}</span></ng-template>
1985
2172
  </div>
1986
- <i [class]="cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform', isOpen ? 'rotate-180' : '')"></i>
2173
+ <i [class]="cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform duration-200', isOpen ? 'rotate-180' : '')"></i>
1987
2174
  </button>
1988
2175
 
1989
- <div #popover *ngIf="isOpen" class="absolute bg-popover z-50 min-w-full rounded-md border border-border shadow-md overflow-hidden" style="visibility: hidden;">
2176
+ <div #popover *ngIf="isOpen"
2177
+ class="fixed bg-popover z-[999] rounded-md border border-border shadow-md overflow-hidden"
2178
+ style="visibility: hidden; top: 0; left: 0;">
1990
2179
 
1991
2180
  <div class="p-2 border-b border-border space-y-2 bg-popover">
2181
+ <div class="flex items-center justify-between px-1 text-xs">
2182
+ <span class="text-muted-foreground">
2183
+ {{ value.length }} selected
2184
+ <span *ngIf="maxSelections">/ {{ maxSelections }} max</span>
2185
+ </span>
2186
+ <span *ngIf="maxSelections && value.length >= maxSelections" class="text-destructive text-xs font-medium">
2187
+ Maximum reached
2188
+ </span>
2189
+ </div>
2190
+
1992
2191
  <tolle-input *ngIf="searchable" size="xs" placeholder="Search..." [(ngModel)]="searchQuery" (ngModelChange)="onSearchChange($event)">
1993
2192
  <i prefix class="ri-search-line"></i>
1994
2193
  </tolle-input>
1995
2194
 
1996
2195
  <div class="flex items-center justify-between px-1">
1997
- <button type="button" (click)="selectAll()" class="text-[10px] font-bold uppercase text-primary hover:underline">Select All</button>
1998
- <button type="button" (click)="clearAll()" class="text-[10px] font-bold uppercase text-muted-foreground hover:underline">Clear</button>
2196
+ <button type="button"
2197
+ (click)="selectAll()"
2198
+ [disabled]="maxSelections && selectableItems.length > maxSelections"
2199
+ [class]="cn(
2200
+ 'text-[10px] font-bold uppercase transition-colors',
2201
+ maxSelections && selectableItems.length > maxSelections
2202
+ ? 'text-muted-foreground cursor-not-allowed'
2203
+ : 'text-primary hover:underline'
2204
+ )">
2205
+ Select All
2206
+ </button>
2207
+ <button type="button" (click)="clearAll()" class="text-[10px] font-bold uppercase text-muted-foreground hover:underline">
2208
+ Clear
2209
+ </button>
1999
2210
  </div>
2000
2211
  </div>
2001
2212
 
2002
2213
  <div class="p-1 max-h-60 overflow-y-auto">
2003
2214
  <ng-content></ng-content>
2004
- <div *ngIf="noResults" class="py-4 text-center text-xs text-muted-foreground">No results found for "{{searchQuery}}"</div>
2215
+ <div *ngIf="noResults" class="py-4 text-center text-xs text-muted-foreground">
2216
+ No results found for "{{searchQuery}}"
2217
+ </div>
2218
+ <div *ngIf="maxSelections && value.length >= maxSelections"
2219
+ class="p-2 text-center border-t border-border bg-muted/20">
2220
+ <span class="text-xs text-destructive">
2221
+ <i class="ri-alert-line mr-1"></i>
2222
+ Maximum selection limit reached ({{maxSelections}})
2223
+ </span>
2224
+ </div>
2005
2225
  </div>
2006
2226
  </div>
2007
2227
  </div>
@@ -2019,35 +2239,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2019
2239
  ],
2020
2240
  template: `
2021
2241
  <div [class]="cn('relative w-full', 'size-' + size)" #container>
2022
- <button #trigger type="button" (click)="toggle()" [disabled]="disabled"
2023
- [class]="cn('flex min-h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm focus:ring-2 focus:ring-ring focus:ring-offset-2 transition-all h-auto', class)">
2242
+ <button
2243
+ #trigger
2244
+ type="button"
2245
+ (click)="toggle()"
2246
+ [disabled]="disabled"
2247
+ [class]="computedTriggerClass">
2248
+
2024
2249
  <div class="flex flex-wrap gap-1 items-center max-w-[95%]">
2025
2250
  <ng-container *ngIf="value?.length; else placeholderTpl">
2026
- <tolle-badge *ngFor="let item of selectedItems" size="xs" variant="secondary" [removable]="true" (onRemove)="removeValue($event, item.value)">
2251
+ <tolle-badge *ngFor="let item of displayItems" size="xs" variant="secondary" [removable]="true" (onRemove)="removeValue($event, item.value)">
2027
2252
  {{ item.label }}
2028
2253
  </tolle-badge>
2254
+ <span *ngIf="exceedsDisplayLimit" class="text-xs text-muted-foreground px-1">
2255
+ +{{ value.length - maxDisplayItems }} more
2256
+ </span>
2257
+ <span *ngIf="maxSelections && value.length >= maxSelections" class="text-xs text-muted-foreground px-1">
2258
+ (Max reached)
2259
+ </span>
2029
2260
  </ng-container>
2030
2261
  <ng-template #placeholderTpl><span class="text-muted-foreground">{{ placeholder }}</span></ng-template>
2031
2262
  </div>
2032
- <i [class]="cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform', isOpen ? 'rotate-180' : '')"></i>
2263
+ <i [class]="cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform duration-200', isOpen ? 'rotate-180' : '')"></i>
2033
2264
  </button>
2034
2265
 
2035
- <div #popover *ngIf="isOpen" class="absolute bg-popover z-50 min-w-full rounded-md border border-border shadow-md overflow-hidden" style="visibility: hidden;">
2266
+ <div #popover *ngIf="isOpen"
2267
+ class="fixed bg-popover z-[999] rounded-md border border-border shadow-md overflow-hidden"
2268
+ style="visibility: hidden; top: 0; left: 0;">
2036
2269
 
2037
2270
  <div class="p-2 border-b border-border space-y-2 bg-popover">
2271
+ <div class="flex items-center justify-between px-1 text-xs">
2272
+ <span class="text-muted-foreground">
2273
+ {{ value.length }} selected
2274
+ <span *ngIf="maxSelections">/ {{ maxSelections }} max</span>
2275
+ </span>
2276
+ <span *ngIf="maxSelections && value.length >= maxSelections" class="text-destructive text-xs font-medium">
2277
+ Maximum reached
2278
+ </span>
2279
+ </div>
2280
+
2038
2281
  <tolle-input *ngIf="searchable" size="xs" placeholder="Search..." [(ngModel)]="searchQuery" (ngModelChange)="onSearchChange($event)">
2039
2282
  <i prefix class="ri-search-line"></i>
2040
2283
  </tolle-input>
2041
2284
 
2042
2285
  <div class="flex items-center justify-between px-1">
2043
- <button type="button" (click)="selectAll()" class="text-[10px] font-bold uppercase text-primary hover:underline">Select All</button>
2044
- <button type="button" (click)="clearAll()" class="text-[10px] font-bold uppercase text-muted-foreground hover:underline">Clear</button>
2286
+ <button type="button"
2287
+ (click)="selectAll()"
2288
+ [disabled]="maxSelections && selectableItems.length > maxSelections"
2289
+ [class]="cn(
2290
+ 'text-[10px] font-bold uppercase transition-colors',
2291
+ maxSelections && selectableItems.length > maxSelections
2292
+ ? 'text-muted-foreground cursor-not-allowed'
2293
+ : 'text-primary hover:underline'
2294
+ )">
2295
+ Select All
2296
+ </button>
2297
+ <button type="button" (click)="clearAll()" class="text-[10px] font-bold uppercase text-muted-foreground hover:underline">
2298
+ Clear
2299
+ </button>
2045
2300
  </div>
2046
2301
  </div>
2047
2302
 
2048
2303
  <div class="p-1 max-h-60 overflow-y-auto">
2049
2304
  <ng-content></ng-content>
2050
- <div *ngIf="noResults" class="py-4 text-center text-xs text-muted-foreground">No results found for "{{searchQuery}}"</div>
2305
+ <div *ngIf="noResults" class="py-4 text-center text-xs text-muted-foreground">
2306
+ No results found for "{{searchQuery}}"
2307
+ </div>
2308
+ <div *ngIf="maxSelections && value.length >= maxSelections"
2309
+ class="p-2 text-center border-t border-border bg-muted/20">
2310
+ <span class="text-xs text-destructive">
2311
+ <i class="ri-alert-line mr-1"></i>
2312
+ Maximum selection limit reached ({{maxSelections}})
2313
+ </span>
2314
+ </div>
2051
2315
  </div>
2052
2316
  </div>
2053
2317
  </div>
@@ -2063,6 +2327,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2063
2327
  type: Input
2064
2328
  }], class: [{
2065
2329
  type: Input
2330
+ }], maxSelections: [{
2331
+ type: Input
2332
+ }], maxDisplayItems: [{
2333
+ type: Input
2334
+ }], error: [{
2335
+ type: Input
2066
2336
  }], trigger: [{
2067
2337
  type: ViewChild,
2068
2338
  args: ['trigger']
@@ -2079,7 +2349,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2079
2349
 
2080
2350
  class CalendarComponent {
2081
2351
  class = '';
2352
+ mode = 'date';
2082
2353
  disablePastDates = false;
2354
+ showQuickActions = true;
2355
+ minDate;
2356
+ maxDate;
2357
+ formatMonthFn;
2358
+ formatYearFn;
2359
+ formatDateFn;
2360
+ dateSelect = new EventEmitter();
2083
2361
  currentView = 'date';
2084
2362
  viewDate = new Date();
2085
2363
  selectedDate = null;
@@ -2087,87 +2365,222 @@ class CalendarComponent {
2087
2365
  daysInMonth = [];
2088
2366
  months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2089
2367
  years = [];
2368
+ yearRangeStart;
2090
2369
  navBtnClass = cn('h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 border border-input rounded-md flex items-center justify-center hover:bg-accent hover:text-accent-foreground transition-all');
2370
+ quickActionBtnClass = cn('px-3 py-1.5 text-sm rounded hover:bg-accent text-muted-foreground hover:text-foreground transition-colors');
2091
2371
  onTouched = () => { };
2092
2372
  onChange = () => { };
2093
2373
  cn = cn;
2374
+ constructor() {
2375
+ this.yearRangeStart = new Date().getFullYear() - 6;
2376
+ }
2094
2377
  ngOnInit() {
2378
+ // Initialize based on mode
2379
+ if (this.mode === 'month') {
2380
+ this.currentView = 'month';
2381
+ }
2382
+ else if (this.mode === 'year') {
2383
+ this.currentView = 'year';
2384
+ }
2095
2385
  this.generateDays();
2096
2386
  this.generateYears();
2097
2387
  }
2388
+ // Format helpers
2389
+ formatMonthYear(date, type) {
2390
+ if (type === 'month' && this.formatMonthFn) {
2391
+ return this.formatMonthFn(date);
2392
+ }
2393
+ if (type === 'year' && this.formatYearFn) {
2394
+ return this.formatYearFn(date);
2395
+ }
2396
+ return type === 'month' ? format(date, 'MMMM') : format(date, 'yyyy');
2397
+ }
2398
+ formatDate(date, type) {
2399
+ if (type === 'day' && this.formatDateFn) {
2400
+ return this.formatDateFn(date);
2401
+ }
2402
+ return format(date, type === 'day' ? 'd' : type === 'month' ? 'MMM' : 'yyyy');
2403
+ }
2098
2404
  generateDays() {
2405
+ if (this.mode !== 'date')
2406
+ return;
2099
2407
  const start = startOfWeek(startOfMonth(this.viewDate));
2100
2408
  const end = endOfWeek(endOfMonth(this.viewDate));
2101
2409
  this.daysInMonth = eachDayOfInterval({ start, end });
2102
2410
  }
2103
2411
  generateYears() {
2104
2412
  const currentYear = this.viewDate.getFullYear();
2105
- // Generates a 16-year window centered roughly on current view
2106
- this.years = Array.from({ length: 16 }, (_, i) => currentYear - 6 + i);
2413
+ if (this.mode === 'year') {
2414
+ // For year picker, show a 12-year grid
2415
+ this.years = Array.from({ length: 12 }, (_, i) => this.yearRangeStart + i);
2416
+ }
2417
+ else {
2418
+ // For date mode year selector, show 16 years centered on current
2419
+ this.years = Array.from({ length: 16 }, (_, i) => currentYear - 6 + i);
2420
+ }
2107
2421
  }
2108
2422
  setView(view) {
2109
2423
  this.currentView = view;
2110
- // If switching to year view, ensure the year grid is centered on current view year
2111
2424
  if (view === 'year') {
2112
2425
  this.generateYears();
2113
2426
  }
2114
2427
  }
2115
2428
  prev() {
2116
- if (this.currentView === 'date') {
2117
- this.viewDate = subMonths(this.viewDate, 1);
2118
- this.generateDays();
2429
+ if (this.mode === 'date') {
2430
+ if (this.currentView === 'date') {
2431
+ this.viewDate = subMonths(this.viewDate, 1);
2432
+ this.generateDays();
2433
+ }
2434
+ else if (this.currentView === 'year') {
2435
+ this.viewDate = subYears(this.viewDate, 16);
2436
+ this.generateYears();
2437
+ }
2438
+ else if (this.currentView === 'month') {
2439
+ this.viewDate = subYears(this.viewDate, 1);
2440
+ }
2119
2441
  }
2120
- else if (this.currentView === 'year') {
2121
- this.viewDate = subYears(this.viewDate, 16);
2442
+ else if (this.mode === 'month') {
2443
+ this.viewDate = subYears(this.viewDate, 1);
2444
+ }
2445
+ else if (this.mode === 'year') {
2446
+ this.yearRangeStart -= 12;
2122
2447
  this.generateYears();
2123
2448
  }
2124
- else if (this.currentView === 'month') {
2125
- this.viewDate = subYears(this.viewDate, 1);
2449
+ else if (this.mode === 'month-year') {
2450
+ if (this.currentView === 'month') {
2451
+ this.viewDate = subYears(this.viewDate, 1);
2452
+ }
2453
+ else {
2454
+ this.yearRangeStart -= 12;
2455
+ this.generateYears();
2456
+ }
2126
2457
  }
2127
2458
  }
2128
2459
  next() {
2129
- if (this.currentView === 'date') {
2130
- this.viewDate = addMonths(this.viewDate, 1);
2131
- this.generateDays();
2460
+ if (this.mode === 'date') {
2461
+ if (this.currentView === 'date') {
2462
+ this.viewDate = addMonths(this.viewDate, 1);
2463
+ this.generateDays();
2464
+ }
2465
+ else if (this.currentView === 'year') {
2466
+ this.viewDate = addYears(this.viewDate, 16);
2467
+ this.generateYears();
2468
+ }
2469
+ else if (this.currentView === 'month') {
2470
+ this.viewDate = addYears(this.viewDate, 1);
2471
+ }
2132
2472
  }
2133
- else if (this.currentView === 'year') {
2134
- this.viewDate = addYears(this.viewDate, 16);
2473
+ else if (this.mode === 'month') {
2474
+ this.viewDate = addYears(this.viewDate, 1);
2475
+ }
2476
+ else if (this.mode === 'year') {
2477
+ this.yearRangeStart += 12;
2135
2478
  this.generateYears();
2136
2479
  }
2137
- else if (this.currentView === 'month') {
2138
- this.viewDate = addYears(this.viewDate, 1);
2480
+ else if (this.mode === 'month-year') {
2481
+ if (this.currentView === 'month') {
2482
+ this.viewDate = addYears(this.viewDate, 1);
2483
+ }
2484
+ else {
2485
+ this.yearRangeStart += 12;
2486
+ this.generateYears();
2487
+ }
2139
2488
  }
2140
2489
  }
2490
+ prevYears() {
2491
+ this.yearRangeStart -= 12;
2492
+ this.generateYears();
2493
+ }
2494
+ nextYears() {
2495
+ this.yearRangeStart += 12;
2496
+ this.generateYears();
2497
+ }
2141
2498
  selectDate(date) {
2142
2499
  if (this.isDateDisabled(date))
2143
2500
  return;
2144
2501
  this.selectedDate = date;
2145
- if (!isSameMonth(date, this.viewDate)) {
2146
- this.viewDate = date;
2147
- this.generateDays();
2148
- }
2149
2502
  this.onChange(date);
2150
2503
  this.onTouched();
2504
+ this.dateSelect.emit(date);
2151
2505
  }
2152
2506
  selectMonth(monthIndex) {
2153
- this.viewDate = setMonth(this.viewDate, monthIndex);
2154
- this.currentView = 'date';
2155
- this.generateDays();
2507
+ if (this.mode === 'date') {
2508
+ this.viewDate = setMonth(this.viewDate, monthIndex);
2509
+ this.currentView = 'date';
2510
+ this.generateDays();
2511
+ }
2512
+ else if (this.mode === 'month') {
2513
+ this.viewDate = setMonth(this.viewDate, monthIndex);
2514
+ this.selectedDate = this.viewDate;
2515
+ this.onChange(this.viewDate);
2516
+ this.onTouched();
2517
+ this.dateSelect.emit(this.viewDate);
2518
+ }
2156
2519
  }
2157
2520
  selectYear(year) {
2158
- this.viewDate = setYear(this.viewDate, year);
2159
- this.currentView = 'date'; // Jump straight back to date view for efficiency
2160
- this.generateDays();
2521
+ if (this.mode === 'date') {
2522
+ this.viewDate = setYear(this.viewDate, year);
2523
+ this.currentView = 'date';
2524
+ this.generateDays();
2525
+ }
2526
+ else if (this.mode === 'year' || this.mode === 'month') {
2527
+ this.viewDate = setYear(this.viewDate, year);
2528
+ this.selectedDate = this.viewDate;
2529
+ this.onChange(this.viewDate);
2530
+ this.onTouched();
2531
+ this.dateSelect.emit(this.viewDate);
2532
+ }
2161
2533
  }
2162
2534
  getDayClass(date) {
2163
2535
  const isSelected = this.selectedDate && isSameDay(date, this.selectedDate);
2164
2536
  const isTodayDate = isToday(date);
2165
2537
  const isOutside = !isSameMonth(date, this.viewDate);
2166
2538
  const isDisabled = this.isDateDisabled(date);
2167
- return cn('h-9 w-9 p-0 font-normal text-sm rounded-md transition-all flex items-center justify-center', !isSelected && !isDisabled && 'hover:bg-accent hover:text-accent-foreground', isSelected && 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground', !isSelected && isTodayDate && 'bg-accent text-accent-foreground', (isOutside || isDisabled) && 'text-muted-foreground opacity-50', isDisabled && 'cursor-not-allowed');
2539
+ return cn('h-9 w-9 p-0 font-normal text-sm rounded-md transition-all flex items-center justify-center', !isSelected && !isDisabled && 'hover:bg-accent hover:text-accent-foreground', isSelected && 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground', !isSelected && isTodayDate && 'bg-accent text-accent-foreground', (isOutside || isDisabled) && 'text-muted-foreground opacity-50', isDisabled && 'cursor-not-allowed');
2540
+ }
2541
+ getMonthClass(monthIndex) {
2542
+ const isSelected = this.selectedDate &&
2543
+ this.selectedDate.getMonth() === monthIndex &&
2544
+ this.selectedDate.getFullYear() === this.viewDate.getFullYear();
2545
+ const isCurrent = new Date().getMonth() === monthIndex &&
2546
+ new Date().getFullYear() === this.viewDate.getFullYear();
2547
+ return cn('text-sm py-2.5 rounded-md transition-colors', isSelected ? 'bg-primary text-primary-foreground hover:bg-primary' :
2548
+ isCurrent ? 'border border-primary/30 text-primary' :
2549
+ 'hover:bg-accent hover:text-accent-foreground');
2550
+ }
2551
+ getYearClass(year) {
2552
+ const isSelected = this.selectedDate &&
2553
+ this.selectedDate.getFullYear() === year;
2554
+ const isCurrent = new Date().getFullYear() === year;
2555
+ return cn('text-sm py-2 rounded-md transition-colors', isSelected ? 'bg-primary text-primary-foreground hover:bg-primary' :
2556
+ isCurrent ? 'border border-primary/30 text-primary' :
2557
+ 'hover:bg-accent hover:text-accent-foreground');
2168
2558
  }
2169
2559
  isDateDisabled(date) {
2170
- return this.disablePastDates ? isBefore(date, startOfDay(new Date())) : false;
2560
+ if (this.disablePastDates && isBefore(date, startOfDay(new Date()))) {
2561
+ return true;
2562
+ }
2563
+ if (this.minDate && isBefore(date, this.minDate)) {
2564
+ return true;
2565
+ }
2566
+ if (this.maxDate && isBefore(this.maxDate, date)) {
2567
+ return true;
2568
+ }
2569
+ return false;
2570
+ }
2571
+ isTodayDisabled() {
2572
+ return this.isDateDisabled(new Date());
2573
+ }
2574
+ selectToday() {
2575
+ if (!this.isTodayDisabled()) {
2576
+ this.selectDate(new Date());
2577
+ }
2578
+ }
2579
+ clear() {
2580
+ this.selectedDate = null;
2581
+ this.onChange(null);
2582
+ this.onTouched();
2583
+ this.dateSelect.emit(null);
2171
2584
  }
2172
2585
  // CVA Implementation
2173
2586
  writeValue(obj) {
@@ -2184,27 +2597,26 @@ class CalendarComponent {
2184
2597
  registerOnChange(fn) { this.onChange = fn; }
2185
2598
  registerOnTouched(fn) { this.onTouched = fn; }
2186
2599
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2187
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CalendarComponent, isStandalone: true, selector: "tolle-calendar", inputs: { class: "class", disablePastDates: "disablePastDates" }, providers: [
2600
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CalendarComponent, isStandalone: true, selector: "tolle-calendar", inputs: { class: "class", mode: "mode", disablePastDates: "disablePastDates", showQuickActions: "showQuickActions", minDate: "minDate", maxDate: "maxDate", formatMonthFn: "formatMonthFn", formatYearFn: "formatYearFn", formatDateFn: "formatDateFn" }, outputs: { dateSelect: "dateSelect" }, providers: [
2188
2601
  {
2189
2602
  provide: NG_VALUE_ACCESSOR,
2190
2603
  useExisting: forwardRef(() => CalendarComponent),
2191
2604
  multi: true
2192
2605
  }
2193
2606
  ], ngImport: i0, template: `
2194
- <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block w-fit', class)">
2195
-
2607
+ <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block min-w-fit', class)">
2608
+ <!-- Header with Navigation -->
2196
2609
  <div class="flex items-center justify-between pt-1 pb-4 gap-2">
2197
-
2610
+ <!-- View Selector -->
2198
2611
  <div class="flex items-center gap-1">
2199
- <button
2612
+ <button *ngIf="mode !== 'year'"
2200
2613
  type="button"
2201
2614
  (click)="setView('month')"
2202
2615
  [class]="cn(
2203
2616
  'text-sm font-semibold px-2 py-1 rounded transition-colors',
2204
2617
  currentView === 'month' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
2205
- )"
2206
- >
2207
- {{ viewDate | date: 'MMMM' }}
2618
+ )">
2619
+ {{ formatMonthYear(viewDate, 'month') }}
2208
2620
  </button>
2209
2621
 
2210
2622
  <button
@@ -2213,12 +2625,12 @@ class CalendarComponent {
2213
2625
  [class]="cn(
2214
2626
  'text-sm font-semibold px-2 py-1 rounded transition-colors',
2215
2627
  currentView === 'year' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
2216
- )"
2217
- >
2218
- {{ viewDate | date: 'yyyy' }}
2628
+ )">
2629
+ {{ formatMonthYear(viewDate, 'year') }}
2219
2630
  </button>
2220
2631
  </div>
2221
2632
 
2633
+ <!-- Navigation Buttons -->
2222
2634
  <div class="flex items-center space-x-1">
2223
2635
  <button type="button" (click)="prev()" [class]="navBtnClass">
2224
2636
  <i class="ri-arrow-left-s-line text-lg"></i>
@@ -2229,7 +2641,8 @@ class CalendarComponent {
2229
2641
  </div>
2230
2642
  </div>
2231
2643
 
2232
- <div *ngIf="currentView === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
2644
+ <!-- DATE MODE -->
2645
+ <div *ngIf="currentView === 'date' && mode === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
2233
2646
  <div class="grid grid-cols-7 gap-1 w-full">
2234
2647
  <span *ngFor="let day of weekDays" class="text-[0.8rem] text-muted-foreground font-normal text-center w-9">
2235
2648
  {{ day }}
@@ -2243,47 +2656,66 @@ class CalendarComponent {
2243
2656
  [disabled]="isDateDisabled(date)"
2244
2657
  [class]="getDayClass(date)"
2245
2658
  >
2246
- {{ date | date: 'd' }}
2659
+ {{ formatDate(date, 'day') }}
2247
2660
  </button>
2248
2661
  </div>
2249
2662
  </div>
2250
2663
 
2251
- <div *ngIf="currentView === 'month'" class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2664
+ <!-- MONTH SELECTOR (for date mode and month mode) -->
2665
+ <div *ngIf="(currentView === 'month')"
2666
+ class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2252
2667
  <button
2253
2668
  *ngFor="let month of months; let i = index"
2254
2669
  type="button"
2255
2670
  (click)="selectMonth(i)"
2256
- [class]="cn(
2257
- 'text-sm py-2.5 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
2258
- i === viewDate.getMonth() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
2259
- )"
2671
+ [class]="getMonthClass(i)"
2260
2672
  >
2261
2673
  {{ month }}
2262
2674
  </button>
2263
2675
  </div>
2264
2676
 
2265
- <div *ngIf="currentView === 'year'" class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2677
+ <!-- YEAR SELECTOR (for date mode and year mode) -->
2678
+ <div *ngIf="(currentView === 'year') "
2679
+ class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2266
2680
  <button
2267
2681
  *ngFor="let year of years"
2268
2682
  type="button"
2269
2683
  (click)="selectYear(year)"
2270
- [class]="cn(
2271
- 'text-sm py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
2272
- year === viewDate.getFullYear() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
2273
- )"
2684
+ [class]="getYearClass(year)"
2274
2685
  >
2275
2686
  {{ year }}
2276
2687
  </button>
2277
2688
  </div>
2689
+
2690
+ <!-- Quick Actions -->
2691
+ <div *ngIf="showQuickActions" class="border-t pt-3 mt-3">
2692
+ <div class="flex items-center justify-between gap-2">
2693
+ <button
2694
+ type="button"
2695
+ (click)="selectToday()"
2696
+ [class]="quickActionBtnClass"
2697
+ [disabled]="isTodayDisabled()"
2698
+ >
2699
+ Today
2700
+ </button>
2701
+ <button
2702
+ type="button"
2703
+ (click)="clear()"
2704
+ [class]="quickActionBtnClass"
2705
+ >
2706
+ Clear
2707
+ </button>
2708
+ </div>
2709
+ </div>
2278
2710
  </div>
2279
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }] });
2711
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }] });
2280
2712
  }
2281
2713
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarComponent, decorators: [{
2282
2714
  type: Component,
2283
2715
  args: [{
2284
2716
  selector: 'tolle-calendar',
2285
2717
  standalone: true,
2286
- imports: [CommonModule],
2718
+ imports: [CommonModule, FormsModule],
2287
2719
  providers: [
2288
2720
  {
2289
2721
  provide: NG_VALUE_ACCESSOR,
@@ -2292,20 +2724,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2292
2724
  }
2293
2725
  ],
2294
2726
  template: `
2295
- <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block w-fit', class)">
2296
-
2727
+ <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block min-w-fit', class)">
2728
+ <!-- Header with Navigation -->
2297
2729
  <div class="flex items-center justify-between pt-1 pb-4 gap-2">
2298
-
2730
+ <!-- View Selector -->
2299
2731
  <div class="flex items-center gap-1">
2300
- <button
2732
+ <button *ngIf="mode !== 'year'"
2301
2733
  type="button"
2302
2734
  (click)="setView('month')"
2303
2735
  [class]="cn(
2304
2736
  'text-sm font-semibold px-2 py-1 rounded transition-colors',
2305
2737
  currentView === 'month' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
2306
- )"
2307
- >
2308
- {{ viewDate | date: 'MMMM' }}
2738
+ )">
2739
+ {{ formatMonthYear(viewDate, 'month') }}
2309
2740
  </button>
2310
2741
 
2311
2742
  <button
@@ -2314,12 +2745,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2314
2745
  [class]="cn(
2315
2746
  'text-sm font-semibold px-2 py-1 rounded transition-colors',
2316
2747
  currentView === 'year' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
2317
- )"
2318
- >
2319
- {{ viewDate | date: 'yyyy' }}
2748
+ )">
2749
+ {{ formatMonthYear(viewDate, 'year') }}
2320
2750
  </button>
2321
2751
  </div>
2322
2752
 
2753
+ <!-- Navigation Buttons -->
2323
2754
  <div class="flex items-center space-x-1">
2324
2755
  <button type="button" (click)="prev()" [class]="navBtnClass">
2325
2756
  <i class="ri-arrow-left-s-line text-lg"></i>
@@ -2330,7 +2761,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2330
2761
  </div>
2331
2762
  </div>
2332
2763
 
2333
- <div *ngIf="currentView === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
2764
+ <!-- DATE MODE -->
2765
+ <div *ngIf="currentView === 'date' && mode === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
2334
2766
  <div class="grid grid-cols-7 gap-1 w-full">
2335
2767
  <span *ngFor="let day of weekDays" class="text-[0.8rem] text-muted-foreground font-normal text-center w-9">
2336
2768
  {{ day }}
@@ -2344,45 +2776,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2344
2776
  [disabled]="isDateDisabled(date)"
2345
2777
  [class]="getDayClass(date)"
2346
2778
  >
2347
- {{ date | date: 'd' }}
2779
+ {{ formatDate(date, 'day') }}
2348
2780
  </button>
2349
2781
  </div>
2350
2782
  </div>
2351
2783
 
2352
- <div *ngIf="currentView === 'month'" class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2784
+ <!-- MONTH SELECTOR (for date mode and month mode) -->
2785
+ <div *ngIf="(currentView === 'month')"
2786
+ class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2353
2787
  <button
2354
2788
  *ngFor="let month of months; let i = index"
2355
2789
  type="button"
2356
2790
  (click)="selectMonth(i)"
2357
- [class]="cn(
2358
- 'text-sm py-2.5 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
2359
- i === viewDate.getMonth() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
2360
- )"
2791
+ [class]="getMonthClass(i)"
2361
2792
  >
2362
2793
  {{ month }}
2363
2794
  </button>
2364
2795
  </div>
2365
2796
 
2366
- <div *ngIf="currentView === 'year'" class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2797
+ <!-- YEAR SELECTOR (for date mode and year mode) -->
2798
+ <div *ngIf="(currentView === 'year') "
2799
+ class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
2367
2800
  <button
2368
2801
  *ngFor="let year of years"
2369
2802
  type="button"
2370
2803
  (click)="selectYear(year)"
2371
- [class]="cn(
2372
- 'text-sm py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
2373
- year === viewDate.getFullYear() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
2374
- )"
2804
+ [class]="getYearClass(year)"
2375
2805
  >
2376
2806
  {{ year }}
2377
2807
  </button>
2378
2808
  </div>
2809
+
2810
+ <!-- Quick Actions -->
2811
+ <div *ngIf="showQuickActions" class="border-t pt-3 mt-3">
2812
+ <div class="flex items-center justify-between gap-2">
2813
+ <button
2814
+ type="button"
2815
+ (click)="selectToday()"
2816
+ [class]="quickActionBtnClass"
2817
+ [disabled]="isTodayDisabled()"
2818
+ >
2819
+ Today
2820
+ </button>
2821
+ <button
2822
+ type="button"
2823
+ (click)="clear()"
2824
+ [class]="quickActionBtnClass"
2825
+ >
2826
+ Clear
2827
+ </button>
2828
+ </div>
2829
+ </div>
2379
2830
  </div>
2380
2831
  `
2381
2832
  }]
2382
- }], propDecorators: { class: [{
2833
+ }], ctorParameters: () => [], propDecorators: { class: [{
2834
+ type: Input
2835
+ }], mode: [{
2383
2836
  type: Input
2384
2837
  }], disablePastDates: [{
2385
2838
  type: Input
2839
+ }], showQuickActions: [{
2840
+ type: Input
2841
+ }], minDate: [{
2842
+ type: Input
2843
+ }], maxDate: [{
2844
+ type: Input
2845
+ }], formatMonthFn: [{
2846
+ type: Input
2847
+ }], formatYearFn: [{
2848
+ type: Input
2849
+ }], formatDateFn: [{
2850
+ type: Input
2851
+ }], dateSelect: [{
2852
+ type: Output
2386
2853
  }] } });
2387
2854
 
2388
2855
  class MaskedInputComponent {
@@ -2728,6 +3195,15 @@ class DatePickerComponent {
2728
3195
  disabled = false;
2729
3196
  class = '';
2730
3197
  disablePastDates = false;
3198
+ showClear = true;
3199
+ showQuickActions = true;
3200
+ minDate;
3201
+ maxDate;
3202
+ mode = 'date';
3203
+ formatMonthFn;
3204
+ formatYearFn;
3205
+ // Format functions for display
3206
+ displayFormat;
2731
3207
  triggerContainer;
2732
3208
  popover;
2733
3209
  value = null;
@@ -2737,12 +3213,56 @@ class DatePickerComponent {
2737
3213
  constructor(cdr) {
2738
3214
  this.cdr = cdr;
2739
3215
  }
2740
- // --- Logic ---
3216
+ getMask() {
3217
+ switch (this.mode) {
3218
+ case 'date': return '00/00/0000';
3219
+ case 'month': return '00/0000';
3220
+ case 'year': return '0000';
3221
+ default: return '00/00/0000';
3222
+ }
3223
+ }
3224
+ getPlaceholder() {
3225
+ switch (this.mode) {
3226
+ case 'date': return 'MM/DD/YYYY';
3227
+ case 'month': return 'MM/YYYY';
3228
+ case 'year': return 'YYYY';
3229
+ default: return 'MM/DD/YYYY';
3230
+ }
3231
+ }
3232
+ getFormatString() {
3233
+ switch (this.mode) {
3234
+ case 'date': return 'MM/dd/yyyy';
3235
+ case 'month': return 'MM/yyyy';
3236
+ case 'year': return 'yyyy';
3237
+ default: return 'MM/dd/yyyy';
3238
+ }
3239
+ }
3240
+ formatDate(date) {
3241
+ if (this.displayFormat) {
3242
+ return this.displayFormat(date, this.mode);
3243
+ }
3244
+ switch (this.mode) {
3245
+ case 'date': return format(date, 'MM/dd/yyyy');
3246
+ case 'month': return format(date, 'MM/yyyy');
3247
+ case 'year': return format(date, 'yyyy');
3248
+ default: return format(date, 'MM/dd/yyyy');
3249
+ }
3250
+ }
3251
+ parseDate(str) {
3252
+ try {
3253
+ const parsed = parse(str, this.getFormatString(), new Date());
3254
+ return isValid(parsed) ? startOfDay(parsed) : null;
3255
+ }
3256
+ catch {
3257
+ return null;
3258
+ }
3259
+ }
2741
3260
  onInputChange(str) {
2742
- if (str?.length === 10) {
2743
- const parsed = parse(str, 'MM/dd/yyyy', new Date());
2744
- if (isValid(parsed)) {
2745
- this.value = startOfDay(parsed);
3261
+ const expectedLength = this.getFormatString().replace(/[^0]/g, '').length;
3262
+ if (str?.length === expectedLength) {
3263
+ const parsed = this.parseDate(str);
3264
+ if (parsed) {
3265
+ this.value = parsed;
2746
3266
  this.onChange(this.value);
2747
3267
  }
2748
3268
  }
@@ -2753,12 +3273,17 @@ class DatePickerComponent {
2753
3273
  }
2754
3274
  onCalendarChange(date) {
2755
3275
  this.value = date;
2756
- this.inputValue = format(date, 'MM/dd/yyyy');
3276
+ if (date) {
3277
+ this.inputValue = this.formatDate(date);
3278
+ }
3279
+ else {
3280
+ this.inputValue = '';
3281
+ }
2757
3282
  this.onChange(this.value);
2758
3283
  this.close();
2759
3284
  }
2760
3285
  togglePopover(event) {
2761
- event.stopPropagation(); // Prevent bubbling to document
3286
+ event.stopPropagation();
2762
3287
  if (this.disabled)
2763
3288
  return;
2764
3289
  this.isOpen ? this.close() : this.open();
@@ -2773,22 +3298,27 @@ class DatePickerComponent {
2773
3298
  this.cleanupAutoUpdate();
2774
3299
  }
2775
3300
  clear(event) {
2776
- event.stopPropagation(); // CRITICAL: Stop the calendar from opening
3301
+ event.stopPropagation();
2777
3302
  this.value = null;
2778
3303
  this.inputValue = '';
2779
3304
  this.onChange(null);
2780
3305
  this.cdr.markForCheck();
2781
3306
  }
2782
- // --- Positioning ---
2783
3307
  updatePosition() {
2784
3308
  if (!this.triggerContainer || !this.popover)
2785
3309
  return;
2786
3310
  this.cleanupAutoUpdate = autoUpdate(this.triggerContainer.nativeElement, this.popover.nativeElement, () => {
2787
3311
  computePosition(this.triggerContainer.nativeElement, this.popover.nativeElement, {
2788
- placement: 'bottom-end', // Aligned to the right where the icon is
2789
- middleware: [offset(4), flip(), shift({ padding: 8 })],
2790
- }).then(({ x, y }) => {
3312
+ strategy: 'fixed', // ADDED: Fixed strategy
3313
+ placement: 'bottom-start', // Changed to bottom-start to align with input left edge
3314
+ middleware: [
3315
+ offset(4),
3316
+ flip(),
3317
+ shift({ padding: 8 })
3318
+ ],
3319
+ }).then(({ x, y, strategy }) => {
2791
3320
  Object.assign(this.popover.nativeElement.style, {
3321
+ position: strategy,
2792
3322
  left: `${x}px`,
2793
3323
  top: `${y}px`,
2794
3324
  visibility: 'visible',
@@ -2803,7 +3333,7 @@ class DatePickerComponent {
2803
3333
  this.close();
2804
3334
  }
2805
3335
  }
2806
- // --- CVA ---
3336
+ // CVA Implementation
2807
3337
  onChange = () => { };
2808
3338
  onTouched = () => { };
2809
3339
  writeValue(val) {
@@ -2811,7 +3341,7 @@ class DatePickerComponent {
2811
3341
  const date = new Date(val);
2812
3342
  if (isValid(date)) {
2813
3343
  this.value = startOfDay(date);
2814
- this.inputValue = format(this.value, 'MM/dd/yyyy');
3344
+ this.inputValue = this.formatDate(this.value);
2815
3345
  }
2816
3346
  }
2817
3347
  else {
@@ -2825,7 +3355,7 @@ class DatePickerComponent {
2825
3355
  setDisabledState(isDisabled) { this.disabled = isDisabled; }
2826
3356
  cn = cn;
2827
3357
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
2828
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DatePickerComponent, isStandalone: true, selector: "tolle-date-picker", inputs: { placeholder: "placeholder", disabled: "disabled", class: "class", disablePastDates: "disablePastDates" }, host: { listeners: { "document:mousedown": "onClickOutside($event)" } }, providers: [
3358
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DatePickerComponent, isStandalone: true, selector: "tolle-date-picker", inputs: { placeholder: "placeholder", disabled: "disabled", class: "class", disablePastDates: "disablePastDates", showClear: "showClear", showQuickActions: "showQuickActions", minDate: "minDate", maxDate: "maxDate", mode: "mode", formatMonthFn: "formatMonthFn", formatYearFn: "formatYearFn", displayFormat: "displayFormat" }, host: { listeners: { "document:mousedown": "onClickOutside($event)" } }, providers: [
2829
3359
  {
2830
3360
  provide: NG_VALUE_ACCESSOR,
2831
3361
  useExisting: forwardRef(() => DatePickerComponent),
@@ -2835,8 +3365,8 @@ class DatePickerComponent {
2835
3365
  <div class="relative w-full" #triggerContainer>
2836
3366
  <tolle-masked-input
2837
3367
  #maskInput
2838
- [mask]="'00/00/0000'"
2839
- [placeholder]="placeholder"
3368
+ [mask]="getMask()"
3369
+ [placeholder]="getPlaceholder()"
2840
3370
  [disabled]="disabled"
2841
3371
  [(ngModel)]="inputValue"
2842
3372
  (ngModelChange)="onInputChange($event)"
@@ -2844,14 +3374,17 @@ class DatePickerComponent {
2844
3374
  >
2845
3375
  <div suffix class="flex items-center gap-1.5 cursor-pointer">
2846
3376
  <i
2847
- *ngIf="value && !disabled"
3377
+ *ngIf="value && !disabled && showClear"
2848
3378
  (click)="clear($event)"
2849
3379
  class="ri-close-line cursor-pointer text-muted-foreground hover:text-foreground transition-colors"
2850
3380
  ></i>
2851
3381
 
2852
3382
  <i
2853
3383
  (click)="togglePopover($event)"
2854
- class="ri-calendar-line cursor-pointer text-muted-foreground hover:text-primary transition-colors"
3384
+ [class]="cn(
3385
+ 'cursor-pointer text-muted-foreground transition-colors',
3386
+ 'ri-calendar-line'
3387
+ )"
2855
3388
  ></i>
2856
3389
  </div>
2857
3390
  </tolle-masked-input>
@@ -2859,17 +3392,23 @@ class DatePickerComponent {
2859
3392
  <div
2860
3393
  #popover
2861
3394
  *ngIf="isOpen"
2862
- class="absolute bg-popover z-50 max-w-max left-0 right-0 overflow-hidden rounded-md border border-border text-popover-foreground bg-background shadow-md"
3395
+ class="fixed z-[50]"
2863
3396
  style="visibility: hidden; top: 0; left: 0;"
2864
3397
  >
2865
- <tolle-calendar
3398
+ <tolle-calendar class="shadow-lg"
2866
3399
  [(ngModel)]="value"
2867
3400
  (ngModelChange)="onCalendarChange($event)"
3401
+ [mode]="mode"
2868
3402
  [disablePastDates]="disablePastDates"
3403
+ [minDate]="minDate"
3404
+ [maxDate]="maxDate"
3405
+ [showQuickActions]="showQuickActions"
3406
+ [formatMonthFn]="formatMonthFn"
3407
+ [formatYearFn]="formatYearFn"
2869
3408
  ></tolle-calendar>
2870
3409
  </div>
2871
3410
  </div>
2872
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "disablePastDates"] }] });
3411
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "mode", "disablePastDates", "showQuickActions", "minDate", "maxDate", "formatMonthFn", "formatYearFn", "formatDateFn"], outputs: ["dateSelect"] }] });
2873
3412
  }
2874
3413
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, decorators: [{
2875
3414
  type: Component,
@@ -2888,8 +3427,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2888
3427
  <div class="relative w-full" #triggerContainer>
2889
3428
  <tolle-masked-input
2890
3429
  #maskInput
2891
- [mask]="'00/00/0000'"
2892
- [placeholder]="placeholder"
3430
+ [mask]="getMask()"
3431
+ [placeholder]="getPlaceholder()"
2893
3432
  [disabled]="disabled"
2894
3433
  [(ngModel)]="inputValue"
2895
3434
  (ngModelChange)="onInputChange($event)"
@@ -2897,14 +3436,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2897
3436
  >
2898
3437
  <div suffix class="flex items-center gap-1.5 cursor-pointer">
2899
3438
  <i
2900
- *ngIf="value && !disabled"
3439
+ *ngIf="value && !disabled && showClear"
2901
3440
  (click)="clear($event)"
2902
3441
  class="ri-close-line cursor-pointer text-muted-foreground hover:text-foreground transition-colors"
2903
3442
  ></i>
2904
3443
 
2905
3444
  <i
2906
3445
  (click)="togglePopover($event)"
2907
- class="ri-calendar-line cursor-pointer text-muted-foreground hover:text-primary transition-colors"
3446
+ [class]="cn(
3447
+ 'cursor-pointer text-muted-foreground transition-colors',
3448
+ 'ri-calendar-line'
3449
+ )"
2908
3450
  ></i>
2909
3451
  </div>
2910
3452
  </tolle-masked-input>
@@ -2912,13 +3454,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2912
3454
  <div
2913
3455
  #popover
2914
3456
  *ngIf="isOpen"
2915
- class="absolute bg-popover z-50 max-w-max left-0 right-0 overflow-hidden rounded-md border border-border text-popover-foreground bg-background shadow-md"
3457
+ class="fixed z-[50]"
2916
3458
  style="visibility: hidden; top: 0; left: 0;"
2917
3459
  >
2918
- <tolle-calendar
3460
+ <tolle-calendar class="shadow-lg"
2919
3461
  [(ngModel)]="value"
2920
3462
  (ngModelChange)="onCalendarChange($event)"
3463
+ [mode]="mode"
2921
3464
  [disablePastDates]="disablePastDates"
3465
+ [minDate]="minDate"
3466
+ [maxDate]="maxDate"
3467
+ [showQuickActions]="showQuickActions"
3468
+ [formatMonthFn]="formatMonthFn"
3469
+ [formatYearFn]="formatYearFn"
2922
3470
  ></tolle-calendar>
2923
3471
  </div>
2924
3472
  </div>
@@ -2932,6 +3480,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2932
3480
  type: Input
2933
3481
  }], disablePastDates: [{
2934
3482
  type: Input
3483
+ }], showClear: [{
3484
+ type: Input
3485
+ }], showQuickActions: [{
3486
+ type: Input
3487
+ }], minDate: [{
3488
+ type: Input
3489
+ }], maxDate: [{
3490
+ type: Input
3491
+ }], mode: [{
3492
+ type: Input
3493
+ }], formatMonthFn: [{
3494
+ type: Input
3495
+ }], formatYearFn: [{
3496
+ type: Input
3497
+ }], displayFormat: [{
3498
+ type: Input
2935
3499
  }], triggerContainer: [{
2936
3500
  type: ViewChild,
2937
3501
  args: ['triggerContainer']
@@ -3113,7 +3677,7 @@ class PaginationComponent {
3113
3677
  </div>
3114
3678
  </div>
3115
3679
  </div>
3116
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectComponent, selector: "tolle-select", inputs: ["placeholder", "class", "disabled", "searchable", "size", "readonly"] }, { kind: "component", type: SelectItemComponent, selector: "tolle-select-item", inputs: ["value", "class", "selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3680
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: SelectComponent, selector: "tolle-select", inputs: ["placeholder", "class", "disabled", "searchable", "size", "readonly"] }, { kind: "component", type: SelectItemComponent, selector: "tolle-select-item", inputs: ["value", "class", "selected", "disabled", "multiSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3117
3681
  }
3118
3682
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PaginationComponent, decorators: [{
3119
3683
  type: Component,
@@ -3386,7 +3950,7 @@ class DataTableComponent {
3386
3950
  </tolle-input>
3387
3951
  </div>
3388
3952
 
3389
- <div class="rounded-md border border-border bg-background overflow-hidden shadow-sm">
3953
+ <div class="rounded-md border border-border overflow-hidden shadow-sm">
3390
3954
  <table class="w-full text-sm">
3391
3955
  <thead class="border-b bg-muted/30">
3392
3956
  <tr>
@@ -3482,7 +4046,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3482
4046
  </tolle-input>
3483
4047
  </div>
3484
4048
 
3485
- <div class="rounded-md border border-border bg-background overflow-hidden shadow-sm">
4049
+ <div class="rounded-md border border-border overflow-hidden shadow-sm">
3486
4050
  <table class="w-full text-sm">
3487
4051
  <thead class="border-b bg-muted/30">
3488
4052
  <tr>
@@ -4248,7 +4812,6 @@ class DateRangePickerComponent {
4248
4812
  placeholder = 'Pick a date range';
4249
4813
  class = '';
4250
4814
  disablePastDates = false;
4251
- // Standardized Sizes
4252
4815
  size = 'default';
4253
4816
  triggerContainer;
4254
4817
  popover;
@@ -4261,7 +4824,7 @@ class DateRangePickerComponent {
4261
4824
  get displayValue() {
4262
4825
  if (!this.value.start)
4263
4826
  return '';
4264
- const startStr = format(this.value.start, 'MMM dd, yyyy'); // Using date-fns format
4827
+ const startStr = format(this.value.start, 'MMM dd, yyyy');
4265
4828
  if (!this.value.end)
4266
4829
  return startStr;
4267
4830
  const endStr = format(this.value.end, 'MMM dd, yyyy');
@@ -4272,12 +4835,11 @@ class DateRangePickerComponent {
4272
4835
  this.onChange(this.value);
4273
4836
  // Close only if range is complete
4274
4837
  if (range.start && range.end) {
4275
- this.onChange(this.value);
4276
- // Small delay for UX
4277
4838
  setTimeout(() => this.close(), 150);
4278
4839
  }
4279
4840
  }
4280
- togglePopover(_) {
4841
+ togglePopover(event) {
4842
+ event.stopPropagation();
4281
4843
  if (this.disabled)
4282
4844
  return;
4283
4845
  this.isOpen ? this.close() : this.open();
@@ -4288,39 +4850,75 @@ class DateRangePickerComponent {
4288
4850
  }
4289
4851
  close() {
4290
4852
  this.isOpen = false;
4291
- if (this.cleanupAutoUpdate)
4853
+ if (this.cleanupAutoUpdate) {
4292
4854
  this.cleanupAutoUpdate();
4855
+ this.cleanupAutoUpdate = undefined;
4856
+ }
4293
4857
  }
4294
4858
  clear(event) {
4295
- event.stopPropagation(); // Stop button click
4859
+ event.stopPropagation();
4296
4860
  this.value = { start: null, end: null };
4297
4861
  this.onChange(this.value);
4862
+ this.cdr.markForCheck();
4298
4863
  }
4299
- // --- Floating UI Positioning ---
4864
+ // --- Floating UI Positioning with Fixed Strategy ---
4300
4865
  updatePosition() {
4301
4866
  if (!this.triggerContainer || !this.popover)
4302
4867
  return;
4303
4868
  this.cleanupAutoUpdate = autoUpdate(this.triggerContainer.nativeElement, this.popover.nativeElement, () => {
4304
4869
  computePosition(this.triggerContainer.nativeElement, this.popover.nativeElement, {
4305
- placement: 'bottom-start', // Aligned to the right where the icon is
4306
- middleware: [offset(4), flip(), shift({ padding: 8 })],
4307
- }).then(({ x, y }) => {
4870
+ placement: 'bottom-end',
4871
+ strategy: 'fixed', // Use fixed to escape column layout
4872
+ middleware: [
4873
+ offset(4),
4874
+ flip({
4875
+ fallbackAxisSideDirection: 'start',
4876
+ padding: 8
4877
+ }),
4878
+ shift({ padding: 8 }),
4879
+ size({
4880
+ apply({ rects, elements, availableHeight }) {
4881
+ // Constrain popover to available space
4882
+ Object.assign(elements.floating.style, {
4883
+ maxHeight: `${Math.min(400, availableHeight)}px`,
4884
+ minWidth: `${Math.max(rects.reference.width, 320)}px`, // Calendar minimum width
4885
+ });
4886
+ }
4887
+ })
4888
+ ],
4889
+ }).then(({ x, y, placement }) => {
4308
4890
  Object.assign(this.popover.nativeElement.style, {
4309
4891
  left: `${x}px`,
4310
4892
  top: `${y}px`,
4311
4893
  visibility: 'visible',
4312
4894
  });
4895
+ // Optional: Add placement class for styling
4896
+ this.popover.nativeElement.classList.remove('calendar-top', 'calendar-bottom');
4897
+ if (placement.includes('top')) {
4898
+ this.popover.nativeElement.classList.add('calendar-top');
4899
+ }
4900
+ else {
4901
+ this.popover.nativeElement.classList.add('calendar-bottom');
4902
+ }
4313
4903
  });
4314
4904
  });
4315
4905
  }
4316
4906
  onClickOutside(event) {
4317
4907
  if (this.isOpen &&
4318
4908
  !this.triggerContainer.nativeElement.contains(event.target) &&
4319
- !this.popover.nativeElement.contains(event.target)) {
4909
+ !this.popover?.nativeElement.contains(event.target)) {
4320
4910
  this.close();
4321
4911
  }
4322
4912
  }
4323
- // CVA
4913
+ onWindowResize() {
4914
+ if (this.isOpen) {
4915
+ this.close(); // Close on resize for simplicity
4916
+ }
4917
+ }
4918
+ onWindowScroll() {
4919
+ // Floating-UI's autoUpdate handles scroll repositioning
4920
+ }
4921
+ // --- Control Value Accessor ---
4324
4922
  onChange = () => { };
4325
4923
  onTouched = () => { };
4326
4924
  writeValue(val) {
@@ -4337,7 +4935,7 @@ class DateRangePickerComponent {
4337
4935
  setDisabledState(isDisabled) { this.disabled = isDisabled; }
4338
4936
  cn = cn;
4339
4937
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DateRangePickerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4340
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DateRangePickerComponent, isStandalone: true, selector: "tolle-date-range-picker", inputs: { disabled: "disabled", placeholder: "placeholder", class: "class", disablePastDates: "disablePastDates", size: "size" }, host: { listeners: { "document:mousedown": "onClickOutside($event)" } }, providers: [
4938
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DateRangePickerComponent, isStandalone: true, selector: "tolle-date-range-picker", inputs: { disabled: "disabled", placeholder: "placeholder", class: "class", disablePastDates: "disablePastDates", size: "size" }, host: { listeners: { "document:mousedown": "onClickOutside($event)", "window:resize": "onWindowResize()", "window:scroll": "onWindowScroll()" } }, providers: [
4341
4939
  {
4342
4940
  provide: NG_VALUE_ACCESSOR,
4343
4941
  useExisting: forwardRef(() => DateRangePickerComponent),
@@ -4345,7 +4943,12 @@ class DateRangePickerComponent {
4345
4943
  }
4346
4944
  ], viewQueries: [{ propertyName: "triggerContainer", first: true, predicate: ["triggerContainer"], descendants: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true }], ngImport: i0, template: `
4347
4945
  <div class="relative w-full" #triggerContainer>
4348
- <tolle-input [placeholder]="placeholder" [disabled]="disabled" [ngModel]="displayValue">
4946
+ <tolle-input
4947
+ [placeholder]="placeholder"
4948
+ [disabled]="disabled"
4949
+ [ngModel]="displayValue"
4950
+ [class]="class"
4951
+ >
4349
4952
  <div suffix class="flex items-center gap-1.5 cursor-pointer">
4350
4953
  <i
4351
4954
  *ngIf="(value.start || value.end) && !disabled"
@@ -4359,13 +4962,14 @@ class DateRangePickerComponent {
4359
4962
  ></i>
4360
4963
  </div>
4361
4964
  </tolle-input>
4965
+
4362
4966
  <div
4363
4967
  #popover
4364
4968
  *ngIf="isOpen"
4365
- class="absolute z-50 min-w-72"
4366
- style="visibility: hidden; top: 0; left: 0;"
4969
+ class="fixed z-50"
4970
+ style="visibility: hidden;"
4367
4971
  >
4368
- <tolle-range-calendar
4972
+ <tolle-range-calendar class="shadow-lg"
4369
4973
  [ngModel]="value"
4370
4974
  (rangeSelect)="onCalendarSelect($event)"
4371
4975
  [disablePastDates]="disablePastDates"
@@ -4389,7 +4993,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4389
4993
  ],
4390
4994
  template: `
4391
4995
  <div class="relative w-full" #triggerContainer>
4392
- <tolle-input [placeholder]="placeholder" [disabled]="disabled" [ngModel]="displayValue">
4996
+ <tolle-input
4997
+ [placeholder]="placeholder"
4998
+ [disabled]="disabled"
4999
+ [ngModel]="displayValue"
5000
+ [class]="class"
5001
+ >
4393
5002
  <div suffix class="flex items-center gap-1.5 cursor-pointer">
4394
5003
  <i
4395
5004
  *ngIf="(value.start || value.end) && !disabled"
@@ -4403,13 +5012,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4403
5012
  ></i>
4404
5013
  </div>
4405
5014
  </tolle-input>
5015
+
4406
5016
  <div
4407
5017
  #popover
4408
5018
  *ngIf="isOpen"
4409
- class="absolute z-50 min-w-72"
4410
- style="visibility: hidden; top: 0; left: 0;"
5019
+ class="fixed z-50"
5020
+ style="visibility: hidden;"
4411
5021
  >
4412
- <tolle-range-calendar
5022
+ <tolle-range-calendar class="shadow-lg"
4413
5023
  [ngModel]="value"
4414
5024
  (rangeSelect)="onCalendarSelect($event)"
4415
5025
  [disablePastDates]="disablePastDates"
@@ -4437,6 +5047,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4437
5047
  }], onClickOutside: [{
4438
5048
  type: HostListener,
4439
5049
  args: ['document:mousedown', ['$event']]
5050
+ }], onWindowResize: [{
5051
+ type: HostListener,
5052
+ args: ['window:resize']
5053
+ }], onWindowScroll: [{
5054
+ type: HostListener,
5055
+ args: ['window:scroll']
4440
5056
  }] } });
4441
5057
 
4442
5058
  class DropdownItemComponent {