@tolle_/tolle-ui 0.0.1-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.
Files changed (77) hide show
  1. package/README.md +35 -0
  2. package/esm2022/lib/accordion-item.component.mjs +78 -0
  3. package/esm2022/lib/accordion.component.mjs +60 -0
  4. package/esm2022/lib/badge.component.mjs +76 -0
  5. package/esm2022/lib/button-group.component.mjs +25 -0
  6. package/esm2022/lib/button.component.mjs +70 -0
  7. package/esm2022/lib/calendar.component.mjs +315 -0
  8. package/esm2022/lib/card.component.mjs +94 -0
  9. package/esm2022/lib/checkbox.component.mjs +100 -0
  10. package/esm2022/lib/data-table.component.mjs +332 -0
  11. package/esm2022/lib/date-picker.component.mjs +232 -0
  12. package/esm2022/lib/date-range-picker.component.mjs +208 -0
  13. package/esm2022/lib/input.component.mjs +134 -0
  14. package/esm2022/lib/masked-input.component.mjs +179 -0
  15. package/esm2022/lib/modal-ref.mjs +31 -0
  16. package/esm2022/lib/modal-stack.service.mjs +26 -0
  17. package/esm2022/lib/modal.component.mjs +98 -0
  18. package/esm2022/lib/modal.mjs +27 -0
  19. package/esm2022/lib/modal.service.mjs +65 -0
  20. package/esm2022/lib/multi-select.component.mjs +231 -0
  21. package/esm2022/lib/pagination.component.mjs +279 -0
  22. package/esm2022/lib/range-calendar.component.mjs +285 -0
  23. package/esm2022/lib/select-group.component.mjs +28 -0
  24. package/esm2022/lib/select-item.component.mjs +84 -0
  25. package/esm2022/lib/select-separator.component.mjs +24 -0
  26. package/esm2022/lib/select.component.mjs +261 -0
  27. package/esm2022/lib/select.service.mjs +21 -0
  28. package/esm2022/lib/skeleton.component.mjs +34 -0
  29. package/esm2022/lib/switch.component.mjs +133 -0
  30. package/esm2022/lib/toast.service.mjs +59 -0
  31. package/esm2022/lib/tolle-cell.directive.mjs +22 -0
  32. package/esm2022/lib/tolle-config.mjs +11 -0
  33. package/esm2022/lib/tooltip.directive.mjs +71 -0
  34. package/esm2022/lib/types/date-range.mjs +2 -0
  35. package/esm2022/lib/utils/cn.mjs +6 -0
  36. package/esm2022/public-api.mjs +36 -0
  37. package/esm2022/tolle_-tolle-ui.mjs +5 -0
  38. package/fesm2022/tolle_-tolle-ui.mjs +3553 -0
  39. package/fesm2022/tolle_-tolle-ui.mjs.map +1 -0
  40. package/index.d.ts +5 -0
  41. package/lib/accordion-item.component.d.ts +13 -0
  42. package/lib/accordion.component.d.ts +14 -0
  43. package/lib/badge.component.d.ts +14 -0
  44. package/lib/button-group.component.d.ts +8 -0
  45. package/lib/button.component.d.ts +16 -0
  46. package/lib/calendar.component.d.ts +35 -0
  47. package/lib/card.component.d.ts +32 -0
  48. package/lib/checkbox.component.d.ts +23 -0
  49. package/lib/data-table.component.d.ts +45 -0
  50. package/lib/date-picker.component.d.ts +35 -0
  51. package/lib/date-range-picker.component.d.ts +36 -0
  52. package/lib/input.component.d.ts +27 -0
  53. package/lib/masked-input.component.d.ts +36 -0
  54. package/lib/modal-ref.d.ts +16 -0
  55. package/lib/modal-stack.service.d.ts +12 -0
  56. package/lib/modal.component.d.ts +19 -0
  57. package/lib/modal.d.ts +29 -0
  58. package/lib/modal.service.d.ts +18 -0
  59. package/lib/multi-select.component.d.ts +47 -0
  60. package/lib/pagination.component.d.ts +36 -0
  61. package/lib/range-calendar.component.d.ts +37 -0
  62. package/lib/select-group.component.d.ts +8 -0
  63. package/lib/select-item.component.d.ts +18 -0
  64. package/lib/select-separator.component.d.ts +8 -0
  65. package/lib/select.component.d.ts +45 -0
  66. package/lib/select.service.d.ts +10 -0
  67. package/lib/skeleton.component.d.ts +10 -0
  68. package/lib/switch.component.d.ts +39 -0
  69. package/lib/toast.service.d.ts +24 -0
  70. package/lib/tolle-cell.directive.d.ts +9 -0
  71. package/lib/tolle-config.d.ts +9 -0
  72. package/lib/tooltip.directive.d.ts +15 -0
  73. package/lib/types/date-range.d.ts +4 -0
  74. package/lib/utils/cn.d.ts +2 -0
  75. package/package.json +32 -0
  76. package/public-api.d.ts +32 -0
  77. package/theme.css +211 -0
@@ -0,0 +1,3553 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import * as i0 from '@angular/core';
4
+ import { Component, Input, forwardRef, Optional, HostListener, Injectable, ViewChild, ContentChildren, EventEmitter, Output, Directive, InjectionToken, inject, ChangeDetectorRef, ChangeDetectionStrategy, TemplateRef, Injector } from '@angular/core';
5
+ import * as i1$1 from '@angular/common';
6
+ import { CommonModule } from '@angular/common';
7
+ import { cva } from 'class-variance-authority';
8
+ import * as i2 from '@angular/forms';
9
+ import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
10
+ import { autoUpdate, computePosition, offset, flip, shift } from '@floating-ui/dom';
11
+ import * as i1 from '@tolle/ui/select.service';
12
+ import { Subject, Subscription } from 'rxjs';
13
+ import { BadgeComponent as BadgeComponent$1 } from '@tolle/ui/badge.component';
14
+ import { InputComponent as InputComponent$1 } from '@tolle/ui/input.component';
15
+ import { startOfWeek, startOfMonth, endOfWeek, endOfMonth, eachDayOfInterval, subMonths, subYears, addMonths, addYears, isSameMonth, setMonth, setYear, isSameDay, isToday, isBefore, startOfDay, parse, isValid, format, isWithinInterval } from 'date-fns';
16
+ import { SelectComponent as SelectComponent$1, SelectItemComponent as SelectItemComponent$1 } from '@tolle/ui';
17
+ import { PaginationComponent as PaginationComponent$1 } from '@tolle/ui/pagination.component';
18
+ import { TolleCellDirective as TolleCellDirective$1 } from '@tolle/ui/tolle-cell.directive';
19
+ import * as i1$2 from '@angular/cdk/overlay';
20
+ import { OverlayConfig } from '@angular/cdk/overlay';
21
+ import { ComponentPortal } from '@angular/cdk/portal';
22
+
23
+ function cn(...inputs) {
24
+ return twMerge(clsx(inputs));
25
+ }
26
+
27
+ // 1. Define Component Variants (The "Recipe")
28
+ const buttonVariants = cva(
29
+ // Base styles applied to ALL buttons
30
+ "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 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", {
31
+ variants: {
32
+ variant: {
33
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
34
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
35
+ outline: "border border-input hover:bg-accent hover:text-accent-foreground",
36
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
37
+ ghost: "hover:bg-accent hover:text-accent-foreground",
38
+ link: "underline-offset-4 hover:underline text-primary",
39
+ },
40
+ size: {
41
+ default: "h-10 px-4 py-2",
42
+ xs: "h-8 px-2 py-1 text-xs",
43
+ sm: "h-9 rounded-md px-3",
44
+ lg: "h-11 rounded-md px-8",
45
+ "icon-xs": "h-8 w-8",
46
+ "icon-sm": "h-9 w-9",
47
+ icon: "h-10 w-10",
48
+ "icon-lg": "h-11 w-11",
49
+ },
50
+ },
51
+ defaultVariants: {
52
+ variant: "default",
53
+ size: "default",
54
+ },
55
+ });
56
+ class ButtonComponent {
57
+ // Allow consumers to pass a custom class to override styles
58
+ class = '';
59
+ // Expose the variants as Inputs
60
+ variant = 'default';
61
+ size = 'default';
62
+ // Calculate the final string of classes
63
+ get computedClass() {
64
+ return cn(buttonVariants({ variant: this.variant, size: this.size }), this.class);
65
+ }
66
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
67
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ButtonComponent, isStandalone: true, selector: "tolle-button", inputs: { class: "class", variant: "variant", size: "size" }, ngImport: i0, template: `
68
+ <button [class]="computedClass">
69
+ <span class="flex items-center justify-center gap-2 w-full h-full">
70
+ <ng-content></ng-content>
71
+ </span>
72
+ </button>
73
+ `, isInline: true, styles: [":host ::ng-deep i{display:inline-flex;align-items:center;line-height:1;font-size:1.25em}:host ::ng-deep i:not(:only-child){margin-right:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
74
+ }
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonComponent, decorators: [{
76
+ type: Component,
77
+ args: [{ selector: 'tolle-button', standalone: true, imports: [CommonModule], template: `
78
+ <button [class]="computedClass">
79
+ <span class="flex items-center justify-center gap-2 w-full h-full">
80
+ <ng-content></ng-content>
81
+ </span>
82
+ </button>
83
+ `, styles: [":host ::ng-deep i{display:inline-flex;align-items:center;line-height:1;font-size:1.25em}:host ::ng-deep i:not(:only-child){margin-right:.5rem}\n"] }]
84
+ }], propDecorators: { class: [{
85
+ type: Input
86
+ }], variant: [{
87
+ type: Input
88
+ }], size: [{
89
+ type: Input
90
+ }] } });
91
+
92
+ class InputComponent {
93
+ cdr;
94
+ type = 'text';
95
+ placeholder = '';
96
+ disabled = false;
97
+ error = false;
98
+ size = 'default';
99
+ containerClass = '';
100
+ class = '';
101
+ // Internal State
102
+ value = '';
103
+ // CVA Callbacks
104
+ onChange = () => { };
105
+ onTouched = () => { };
106
+ constructor(cdr) {
107
+ this.cdr = cdr;
108
+ }
109
+ // --- ControlValueAccessor Implementation ---
110
+ writeValue(value) {
111
+ this.value = value;
112
+ this.cdr.markForCheck();
113
+ }
114
+ registerOnChange(fn) {
115
+ this.onChange = fn;
116
+ }
117
+ registerOnTouched(fn) {
118
+ this.onTouched = fn;
119
+ }
120
+ setDisabledState(isDisabled) {
121
+ this.disabled = isDisabled;
122
+ this.cdr.markForCheck();
123
+ }
124
+ onInputChange(event) {
125
+ const val = event.target.value;
126
+ this.value = val;
127
+ this.onChange(val);
128
+ }
129
+ // --- Styling Logic ---
130
+ cn = cn;
131
+ get computedInputClass() {
132
+ return cn("flex w-full rounded-md border border-input bg-background text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring focus-visible:ring-ring focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 transition-all", 'disabled:opacity-50 shadow-sm transition-shadow', this.size === 'xs' && "h-8 text-xs px-2", this.size === 'sm' && "h-9 px-3", this.size === 'default' && "h-10 px-3", this.size === 'lg' && "h-11 px-4 text-base", "group-has-[[prefix]]:pl-10 group-has-[[suffix]]:pr-10", this.size === 'xs' && "group-has-[[prefix]]:pl-8 group-has-[[suffix]]:pr-8", this.error && "border-destructive focus-visible:ring-destructive", this.class);
133
+ }
134
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InputComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
135
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: InputComponent, isStandalone: true, selector: "tolle-input", inputs: { type: "type", placeholder: "placeholder", disabled: "disabled", error: "error", size: "size", containerClass: "containerClass", class: "class" }, providers: [
136
+ {
137
+ provide: NG_VALUE_ACCESSOR,
138
+ useExisting: forwardRef(() => InputComponent),
139
+ multi: true
140
+ }
141
+ ], ngImport: i0, template: `
142
+ <div [class]="cn('relative flex items-center w-full group', 'size-' + size, containerClass)">
143
+
144
+ <div class="absolute left-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
145
+ [class.left-2.5]="size === 'xs'">
146
+ <ng-content select="[prefix]"></ng-content>
147
+ </div>
148
+
149
+ <input
150
+ [type]="type"
151
+ [placeholder]="placeholder"
152
+ [disabled]="disabled"
153
+ [(ngModel)]="value"
154
+ (blur)="onTouched()"
155
+ (input)="onInputChange($event)"
156
+ [class]="computedInputClass"
157
+ />
158
+
159
+ <div class="absolute right-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
160
+ [class.right-2.5]="size === 'xs'">
161
+ <ng-content select="[suffix]"></ng-content>
162
+ </div>
163
+ </div>
164
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { 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"] }] });
165
+ }
166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InputComponent, decorators: [{
167
+ type: Component,
168
+ args: [{
169
+ selector: 'tolle-input',
170
+ standalone: true,
171
+ imports: [CommonModule, FormsModule],
172
+ providers: [
173
+ {
174
+ provide: NG_VALUE_ACCESSOR,
175
+ useExisting: forwardRef(() => InputComponent),
176
+ multi: true
177
+ }
178
+ ],
179
+ template: `
180
+ <div [class]="cn('relative flex items-center w-full group', 'size-' + size, containerClass)">
181
+
182
+ <div class="absolute left-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
183
+ [class.left-2.5]="size === 'xs'">
184
+ <ng-content select="[prefix]"></ng-content>
185
+ </div>
186
+
187
+ <input
188
+ [type]="type"
189
+ [placeholder]="placeholder"
190
+ [disabled]="disabled"
191
+ [(ngModel)]="value"
192
+ (blur)="onTouched()"
193
+ (input)="onInputChange($event)"
194
+ [class]="computedInputClass"
195
+ />
196
+
197
+ <div class="absolute right-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
198
+ [class.right-2.5]="size === 'xs'">
199
+ <ng-content select="[suffix]"></ng-content>
200
+ </div>
201
+ </div>
202
+ `,
203
+ }]
204
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { type: [{
205
+ type: Input
206
+ }], placeholder: [{
207
+ type: Input
208
+ }], disabled: [{
209
+ type: Input
210
+ }], error: [{
211
+ type: Input
212
+ }], size: [{
213
+ type: Input
214
+ }], containerClass: [{
215
+ type: Input
216
+ }], class: [{
217
+ type: Input
218
+ }] } });
219
+
220
+ class CardComponent {
221
+ class = '';
222
+ cn = cn;
223
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
224
+ 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: `
225
+ <div [class]="cn('rounded-xl border border-border bg-card text-card-foreground shadow', class)">
226
+ <ng-content></ng-content>
227
+ </div>
228
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
229
+ }
230
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardComponent, decorators: [{
231
+ type: Component,
232
+ args: [{
233
+ selector: 'tolle-card',
234
+ standalone: true,
235
+ imports: [CommonModule],
236
+ template: `
237
+ <div [class]="cn('rounded-xl border border-border bg-card text-card-foreground shadow', class)">
238
+ <ng-content></ng-content>
239
+ </div>
240
+ `,
241
+ }]
242
+ }], propDecorators: { class: [{
243
+ type: Input
244
+ }] } });
245
+ class CardHeaderComponent {
246
+ class = '';
247
+ cn = cn;
248
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
249
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CardHeaderComponent, isStandalone: true, selector: "tolle-card-header", inputs: { class: "class" }, ngImport: i0, template: `<div [class]="cn('flex flex-col space-y-1.5 p-6', class)"><ng-content></ng-content></div>`, isInline: true });
250
+ }
251
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardHeaderComponent, decorators: [{
252
+ type: Component,
253
+ args: [{
254
+ selector: 'tolle-card-header',
255
+ standalone: true,
256
+ template: `<div [class]="cn('flex flex-col space-y-1.5 p-6', class)"><ng-content></ng-content></div>`,
257
+ }]
258
+ }], propDecorators: { class: [{
259
+ type: Input
260
+ }] } });
261
+ class CardTitleComponent {
262
+ class = '';
263
+ cn = cn;
264
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
265
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CardTitleComponent, isStandalone: true, selector: "tolle-card-title", inputs: { class: "class" }, ngImport: i0, template: `<h3 [class]="cn('font-semibold leading-none tracking-tight', class)"><ng-content></ng-content></h3>`, isInline: true });
266
+ }
267
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardTitleComponent, decorators: [{
268
+ type: Component,
269
+ args: [{
270
+ selector: 'tolle-card-title',
271
+ standalone: true,
272
+ template: `<h3 [class]="cn('font-semibold leading-none tracking-tight', class)"><ng-content></ng-content></h3>`,
273
+ }]
274
+ }], propDecorators: { class: [{
275
+ type: Input
276
+ }] } });
277
+ class CardContentComponent {
278
+ class = '';
279
+ cn = cn;
280
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
281
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CardContentComponent, isStandalone: true, selector: "tolle-card-content", inputs: { class: "class" }, ngImport: i0, template: `<div [class]="cn('p-6 pt-0', class)"><ng-content></ng-content></div>`, isInline: true });
282
+ }
283
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardContentComponent, decorators: [{
284
+ type: Component,
285
+ args: [{
286
+ selector: 'tolle-card-content',
287
+ standalone: true,
288
+ template: `<div [class]="cn('p-6 pt-0', class)"><ng-content></ng-content></div>`,
289
+ }]
290
+ }], propDecorators: { class: [{
291
+ type: Input
292
+ }] } });
293
+ class CardFooterComponent {
294
+ class = '';
295
+ cn = cn;
296
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
297
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CardFooterComponent, isStandalone: true, selector: "tolle-card-footer", inputs: { class: "class" }, ngImport: i0, template: `<div [class]="cn('flex items-center p-6 pt-0', class)"><ng-content></ng-content></div>`, isInline: true });
298
+ }
299
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardFooterComponent, decorators: [{
300
+ type: Component,
301
+ args: [{
302
+ selector: 'tolle-card-footer',
303
+ standalone: true,
304
+ template: `<div [class]="cn('flex items-center p-6 pt-0', class)"><ng-content></ng-content></div>`,
305
+ }]
306
+ }], propDecorators: { class: [{
307
+ type: Input
308
+ }] } });
309
+
310
+ class SelectItemComponent {
311
+ selectService;
312
+ el;
313
+ value;
314
+ class = '';
315
+ selected = false;
316
+ hidden = false;
317
+ constructor(selectService, el) {
318
+ this.selectService = selectService;
319
+ this.el = el;
320
+ }
321
+ // Helper method for the parent to get the searchable text
322
+ getLabel() {
323
+ return this.el.nativeElement.innerText || '';
324
+ }
325
+ onClick(event) {
326
+ if (this.hidden)
327
+ return;
328
+ event.stopPropagation();
329
+ if (this.selectService) {
330
+ // Get the text content to show in the trigger button
331
+ const label = this.el.nativeElement.innerText.trim();
332
+ this.selectService.registerClick(this.value, label);
333
+ }
334
+ }
335
+ cn = cn;
336
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectItemComponent, deps: [{ token: i1.SelectService, optional: true }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
337
+ 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: `
338
+ <div
339
+ *ngIf="!hidden"
340
+ [class]="cn(
341
+ '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',
342
+ selected ? 'bg-accent text-accent-foreground' : '',
343
+ class
344
+ )"
345
+ >
346
+ <span *ngIf="selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
347
+ <i class="ri-check-line text-primary"></i>
348
+ </span>
349
+ <ng-content></ng-content>
350
+ </div>
351
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
352
+ }
353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectItemComponent, decorators: [{
354
+ type: Component,
355
+ args: [{
356
+ selector: 'tolle-select-item',
357
+ standalone: true,
358
+ imports: [CommonModule],
359
+ template: `
360
+ <div
361
+ *ngIf="!hidden"
362
+ [class]="cn(
363
+ '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',
364
+ selected ? 'bg-accent text-accent-foreground' : '',
365
+ class
366
+ )"
367
+ >
368
+ <span *ngIf="selected" class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
369
+ <i class="ri-check-line text-primary"></i>
370
+ </span>
371
+ <ng-content></ng-content>
372
+ </div>
373
+ `,
374
+ }]
375
+ }], ctorParameters: () => [{ type: i1.SelectService, decorators: [{
376
+ type: Optional
377
+ }] }, { type: i0.ElementRef }], propDecorators: { value: [{
378
+ type: Input
379
+ }], class: [{
380
+ type: Input
381
+ }], selected: [{
382
+ type: Input
383
+ }], onClick: [{
384
+ type: HostListener,
385
+ args: ['click', ['$event']]
386
+ }] } });
387
+
388
+ class SelectService {
389
+ // Emits the value of the clicked item
390
+ selectedValueSource = new Subject();
391
+ selectedValue$ = this.selectedValueSource.asObservable();
392
+ // Emits the label/text of the clicked item
393
+ selectedLabelSource = new Subject();
394
+ selectedLabel$ = this.selectedLabelSource.asObservable();
395
+ registerClick(value, label) {
396
+ this.selectedValueSource.next(value);
397
+ this.selectedLabelSource.next(label);
398
+ }
399
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
400
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectService });
401
+ }
402
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectService, decorators: [{
403
+ type: Injectable
404
+ }] });
405
+
406
+ class SelectComponent {
407
+ selectService;
408
+ placeholder = 'Select an option';
409
+ class = '';
410
+ disabled = false;
411
+ searchable = false;
412
+ size = 'default';
413
+ trigger;
414
+ popover;
415
+ items;
416
+ sub = new Subscription();
417
+ searchQuery = '';
418
+ noResults = false;
419
+ isOpen = false;
420
+ value = null;
421
+ selectedLabel = '';
422
+ cleanupAutoUpdate;
423
+ onChange = () => { };
424
+ onTouched = () => { };
425
+ cn = cn;
426
+ constructor(selectService) {
427
+ this.selectService = selectService;
428
+ this.sub.add(this.selectService.selectedValue$.subscribe(val => {
429
+ this.value = val;
430
+ this.onChange(val);
431
+ this.updateItemSelection();
432
+ }));
433
+ this.sub.add(this.selectService.selectedLabel$.subscribe(label => {
434
+ this.selectedLabel = label;
435
+ this.close();
436
+ }));
437
+ }
438
+ // UPDATED: Centralized sizing logic for the trigger
439
+ get computedTriggerClass() {
440
+ return cn('flex w-full items-center justify-between rounded-md border border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring focus:ring-ring focus:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 transition-all', 'disabled:opacity-50 shadow-sm transition-shadow', 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.class);
441
+ }
442
+ // UPDATED: Dynamic icon sizing relative to the trigger size
443
+ get iconClass() {
444
+ 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]');
445
+ }
446
+ ngAfterContentInit() {
447
+ this.updateItemSelection();
448
+ this.items.changes.subscribe(() => this.updateItemSelection());
449
+ }
450
+ updateItemSelection() {
451
+ if (this.items) {
452
+ this.items.forEach(item => {
453
+ item.selected = item.value === this.value;
454
+ });
455
+ }
456
+ }
457
+ toggle() {
458
+ if (this.disabled)
459
+ return;
460
+ this.isOpen ? this.close() : this.open();
461
+ }
462
+ open() {
463
+ this.isOpen = true;
464
+ // Tick to ensure DOM is rendered before positioning
465
+ setTimeout(() => this.updatePosition());
466
+ }
467
+ close() {
468
+ this.isOpen = false;
469
+ this.searchQuery = '';
470
+ this.onSearchChange('');
471
+ if (this.cleanupAutoUpdate)
472
+ this.cleanupAutoUpdate();
473
+ }
474
+ updatePosition() {
475
+ if (!this.trigger || !this.popover)
476
+ return;
477
+ this.cleanupAutoUpdate = autoUpdate(this.trigger.nativeElement, this.popover.nativeElement, () => {
478
+ computePosition(this.trigger.nativeElement, this.popover.nativeElement, {
479
+ placement: 'bottom-start',
480
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
481
+ }).then(({ x, y }) => {
482
+ Object.assign(this.popover.nativeElement.style, {
483
+ left: `${x}px`,
484
+ top: `${y}px`,
485
+ visibility: 'visible',
486
+ });
487
+ });
488
+ });
489
+ }
490
+ onSearchChange(query) {
491
+ const filter = (query || '').toLowerCase().trim();
492
+ let visibleCount = 0;
493
+ this.items.forEach(item => {
494
+ const text = item.getLabel().toLowerCase();
495
+ const isVisible = text.includes(filter);
496
+ item.hidden = !isVisible;
497
+ if (isVisible)
498
+ visibleCount++;
499
+ });
500
+ this.noResults = visibleCount === 0 && filter !== '';
501
+ }
502
+ onDocumentClick(event) {
503
+ if (this.isOpen && !this.trigger.nativeElement.contains(event.target) && !this.popover.nativeElement.contains(event.target)) {
504
+ this.close();
505
+ }
506
+ }
507
+ writeValue(value) {
508
+ this.value = value;
509
+ this.updateItemSelection();
510
+ if (this.items) {
511
+ const found = this.items.find(i => i.value === value);
512
+ if (found)
513
+ this.selectedLabel = found.getLabel();
514
+ }
515
+ }
516
+ registerOnChange(fn) { this.onChange = fn; }
517
+ registerOnTouched(fn) { this.onTouched = fn; }
518
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
519
+ ngOnDestroy() {
520
+ this.sub.unsubscribe();
521
+ if (this.cleanupAutoUpdate)
522
+ this.cleanupAutoUpdate();
523
+ }
524
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, deps: [{ token: SelectService }], target: i0.ɵɵFactoryTarget.Component });
525
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SelectComponent, isStandalone: true, selector: "tolle-select", inputs: { placeholder: "placeholder", class: "class", disabled: "disabled", searchable: "searchable", size: "size" }, host: { listeners: { "document:mousedown": "onDocumentClick($event)" } }, providers: [
526
+ SelectService,
527
+ {
528
+ provide: NG_VALUE_ACCESSOR,
529
+ useExisting: forwardRef(() => SelectComponent),
530
+ multi: true
531
+ }
532
+ ], 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: `
533
+ <div [class]="cn('relative w-full', 'size-' + size)" #container>
534
+ <button
535
+ type="button"
536
+ #trigger
537
+ (click)="toggle()"
538
+ [disabled]="disabled"
539
+ [class]="computedTriggerClass"
540
+ >
541
+ <span class="truncate" [class.text-muted-foreground]="!selectedLabel">
542
+ {{ selectedLabel || placeholder }}
543
+ </span>
544
+ <i [class]="iconClass"></i>
545
+ </button>
546
+
547
+ <div
548
+ #popover
549
+ *ngIf="isOpen"
550
+ class="absolute bg-popover z-50 min-w-full overflow-hidden rounded-md border border-border text-popover-foreground bg-background shadow-md"
551
+ style="visibility: hidden; top: 0; left: 0;"
552
+ >
553
+ <div *ngIf="searchable" class="p-2 border-b border-border bg-popover">
554
+ <tolle-input
555
+ size="xs"
556
+ placeholder="Search..."
557
+ [(ngModel)]="searchQuery"
558
+ (ngModelChange)="onSearchChange($event)"
559
+ class="w-full">
560
+ <i prefix class="ri-search-line"></i>
561
+ </tolle-input>
562
+ </div>
563
+
564
+ <div class="p-1 max-h-60 overflow-y-auto">
565
+ <ng-content></ng-content>
566
+ <div *ngIf="noResults" class="py-6 text-center text-sm text-muted-foreground">
567
+ No results found.
568
+ </div>
569
+ </div>
570
+ </div>
571
+ </div>
572
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.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: ["type", "placeholder", "disabled", "error", "size", "containerClass", "class"] }] });
573
+ }
574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, decorators: [{
575
+ type: Component,
576
+ args: [{
577
+ selector: 'tolle-select',
578
+ standalone: true,
579
+ imports: [CommonModule, FormsModule, InputComponent],
580
+ providers: [
581
+ SelectService,
582
+ {
583
+ provide: NG_VALUE_ACCESSOR,
584
+ useExisting: forwardRef(() => SelectComponent),
585
+ multi: true
586
+ }
587
+ ],
588
+ template: `
589
+ <div [class]="cn('relative w-full', 'size-' + size)" #container>
590
+ <button
591
+ type="button"
592
+ #trigger
593
+ (click)="toggle()"
594
+ [disabled]="disabled"
595
+ [class]="computedTriggerClass"
596
+ >
597
+ <span class="truncate" [class.text-muted-foreground]="!selectedLabel">
598
+ {{ selectedLabel || placeholder }}
599
+ </span>
600
+ <i [class]="iconClass"></i>
601
+ </button>
602
+
603
+ <div
604
+ #popover
605
+ *ngIf="isOpen"
606
+ class="absolute bg-popover z-50 min-w-full overflow-hidden rounded-md border border-border text-popover-foreground bg-background shadow-md"
607
+ style="visibility: hidden; top: 0; left: 0;"
608
+ >
609
+ <div *ngIf="searchable" class="p-2 border-b border-border bg-popover">
610
+ <tolle-input
611
+ size="xs"
612
+ placeholder="Search..."
613
+ [(ngModel)]="searchQuery"
614
+ (ngModelChange)="onSearchChange($event)"
615
+ class="w-full">
616
+ <i prefix class="ri-search-line"></i>
617
+ </tolle-input>
618
+ </div>
619
+
620
+ <div class="p-1 max-h-60 overflow-y-auto">
621
+ <ng-content></ng-content>
622
+ <div *ngIf="noResults" class="py-6 text-center text-sm text-muted-foreground">
623
+ No results found.
624
+ </div>
625
+ </div>
626
+ </div>
627
+ </div>
628
+ `,
629
+ }]
630
+ }], ctorParameters: () => [{ type: SelectService }], propDecorators: { placeholder: [{
631
+ type: Input
632
+ }], class: [{
633
+ type: Input
634
+ }], disabled: [{
635
+ type: Input
636
+ }], searchable: [{
637
+ type: Input
638
+ }], size: [{
639
+ type: Input
640
+ }], trigger: [{
641
+ type: ViewChild,
642
+ args: ['trigger']
643
+ }], popover: [{
644
+ type: ViewChild,
645
+ args: ['popover']
646
+ }], items: [{
647
+ type: ContentChildren,
648
+ args: [SelectItemComponent, { descendants: true }]
649
+ }], onDocumentClick: [{
650
+ type: HostListener,
651
+ args: ['document:mousedown', ['$event']]
652
+ }] } });
653
+
654
+ class SelectGroupComponent {
655
+ class = '';
656
+ cn = cn;
657
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
658
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SelectGroupComponent, isStandalone: true, selector: "tolle-select-group", inputs: { class: "class" }, ngImport: i0, template: `
659
+ <div [class]="cn('px-2 py-1.5 text-sm font-semibold text-muted-foreground', class)">
660
+ <ng-content></ng-content>
661
+ </div>
662
+ `, isInline: true });
663
+ }
664
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectGroupComponent, decorators: [{
665
+ type: Component,
666
+ args: [{
667
+ selector: 'tolle-select-group',
668
+ standalone: true,
669
+ template: `
670
+ <div [class]="cn('px-2 py-1.5 text-sm font-semibold text-muted-foreground', class)">
671
+ <ng-content></ng-content>
672
+ </div>
673
+ `,
674
+ }]
675
+ }], propDecorators: { class: [{
676
+ type: Input
677
+ }] } });
678
+
679
+ class SelectSeparatorComponent {
680
+ class = '';
681
+ cn = cn;
682
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectSeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
683
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SelectSeparatorComponent, isStandalone: true, selector: "tolle-select-separator", inputs: { class: "class" }, ngImport: i0, template: `
684
+ <div [class]="cn('-mx-1 my-1 h-px bg-border', class)"></div>
685
+ `, isInline: true });
686
+ }
687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectSeparatorComponent, decorators: [{
688
+ type: Component,
689
+ args: [{
690
+ selector: 'tolle-select-separator',
691
+ standalone: true,
692
+ template: `
693
+ <div [class]="cn('-mx-1 my-1 h-px bg-border', class)"></div>
694
+ `,
695
+ }]
696
+ }], propDecorators: { class: [{
697
+ type: Input
698
+ }] } });
699
+
700
+ class SwitchComponent {
701
+ cdr;
702
+ class = '';
703
+ disabled = false;
704
+ size = 'default';
705
+ checked = false;
706
+ onChange = () => { };
707
+ onTouched = () => { };
708
+ constructor(cdr) {
709
+ this.cdr = cdr;
710
+ }
711
+ get sizeClasses() {
712
+ const sizes = {
713
+ xs: {
714
+ track: 'h-4 w-7',
715
+ thumb: 'h-3 w-3',
716
+ translate: 'translate-x-3'
717
+ },
718
+ sm: {
719
+ track: 'h-5 w-9',
720
+ thumb: 'h-4 w-4',
721
+ translate: 'translate-x-4'
722
+ },
723
+ default: {
724
+ track: 'h-6 w-11',
725
+ thumb: 'h-5 w-5',
726
+ translate: 'translate-x-5'
727
+ },
728
+ lg: {
729
+ track: 'h-7 w-[3.25rem]',
730
+ thumb: 'h-6 w-6',
731
+ translate: 'translate-x-6'
732
+ }
733
+ };
734
+ return sizes[this.size];
735
+ }
736
+ cn = cn;
737
+ toggle() {
738
+ if (this.disabled)
739
+ return;
740
+ this.checked = !this.checked;
741
+ this.onChange(this.checked);
742
+ this.onTouched();
743
+ }
744
+ // --- ControlValueAccessor ---
745
+ writeValue(value) {
746
+ this.checked = value;
747
+ this.cdr.markForCheck();
748
+ }
749
+ registerOnChange(fn) { this.onChange = fn; }
750
+ registerOnTouched(fn) { this.onTouched = fn; }
751
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
752
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SwitchComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
753
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SwitchComponent, isStandalone: true, selector: "tolle-switch", inputs: { class: "class", disabled: "disabled", size: "size" }, providers: [
754
+ {
755
+ provide: NG_VALUE_ACCESSOR,
756
+ useExisting: forwardRef(() => SwitchComponent),
757
+ multi: true
758
+ }
759
+ ], ngImport: i0, template: `
760
+ <button
761
+ type="button"
762
+ role="switch"
763
+ [attr.aria-checked]="checked"
764
+ [disabled]="disabled"
765
+ (click)="toggle()"
766
+ [class]="cn(
767
+ 'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50',
768
+ sizeClasses.track,
769
+ checked ? 'bg-primary' : 'bg-input',
770
+ class
771
+ )"
772
+ >
773
+ <span
774
+ [class]="cn(
775
+ 'pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform',
776
+ sizeClasses.thumb,
777
+ checked ? sizeClasses.translate : 'translate-x-0'
778
+ )"
779
+ ></span>
780
+ </button>
781
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
782
+ }
783
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SwitchComponent, decorators: [{
784
+ type: Component,
785
+ args: [{
786
+ selector: 'tolle-switch',
787
+ standalone: true,
788
+ imports: [CommonModule],
789
+ providers: [
790
+ {
791
+ provide: NG_VALUE_ACCESSOR,
792
+ useExisting: forwardRef(() => SwitchComponent),
793
+ multi: true
794
+ }
795
+ ],
796
+ template: `
797
+ <button
798
+ type="button"
799
+ role="switch"
800
+ [attr.aria-checked]="checked"
801
+ [disabled]="disabled"
802
+ (click)="toggle()"
803
+ [class]="cn(
804
+ 'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50',
805
+ sizeClasses.track,
806
+ checked ? 'bg-primary' : 'bg-input',
807
+ class
808
+ )"
809
+ >
810
+ <span
811
+ [class]="cn(
812
+ 'pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform',
813
+ sizeClasses.thumb,
814
+ checked ? sizeClasses.translate : 'translate-x-0'
815
+ )"
816
+ ></span>
817
+ </button>
818
+ `
819
+ }]
820
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { class: [{
821
+ type: Input
822
+ }], disabled: [{
823
+ type: Input
824
+ }], size: [{
825
+ type: Input
826
+ }] } });
827
+
828
+ class BadgeComponent {
829
+ variant = 'default';
830
+ size = 'default';
831
+ removable = false; // Toggle the "Pill" remove icon
832
+ class = '';
833
+ onRemove = new EventEmitter();
834
+ cn = cn;
835
+ get computedClass() {
836
+ return cn(
837
+ // Base styles - Pills are always rounded-full
838
+ 'inline-flex items-center justify-center rounded-full border px-2 py-0.5 font-medium transition-colors gap-1',
839
+ // Variants (Google Dark Mode theme)
840
+ this.variant === 'default' && 'border-transparent bg-primary text-primary-foreground', this.variant === 'secondary' && 'border-transparent bg-secondary text-secondary-foreground', this.variant === 'outline' && 'text-foreground border-border bg-transparent', this.variant === 'destructive' && 'border-transparent bg-destructive text-destructive-foreground',
841
+ // Sizing
842
+ this.size === 'xs' && 'px-1.5 py-0 text-[10px]', this.size === 'sm' && 'px-2 py-0 text-[11px]', this.size === 'default' && 'text-xs', this.class);
843
+ }
844
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
845
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BadgeComponent, isStandalone: true, selector: "tolle-badge", inputs: { variant: "variant", size: "size", removable: "removable", class: "class" }, outputs: { onRemove: "onRemove" }, ngImport: i0, template: `
846
+ <div [class]="computedClass">
847
+ <ng-content select="[prefix]"></ng-content>
848
+
849
+ <span class="truncate">
850
+ <ng-content></ng-content>
851
+ </span>
852
+
853
+ <button
854
+ *ngIf="removable"
855
+ (click)="onRemove.emit($event)"
856
+ class="ml-1 -mr-1 rounded-full p-0.5 hover:bg-foreground/20 transition-colors outline-none focus:ring-1 focus:ring-ring"
857
+ >
858
+ <i class="ri-close-line text-[1.1em]"></i>
859
+ </button>
860
+ </div>
861
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
862
+ }
863
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BadgeComponent, decorators: [{
864
+ type: Component,
865
+ args: [{
866
+ selector: 'tolle-badge',
867
+ standalone: true,
868
+ imports: [CommonModule],
869
+ template: `
870
+ <div [class]="computedClass">
871
+ <ng-content select="[prefix]"></ng-content>
872
+
873
+ <span class="truncate">
874
+ <ng-content></ng-content>
875
+ </span>
876
+
877
+ <button
878
+ *ngIf="removable"
879
+ (click)="onRemove.emit($event)"
880
+ class="ml-1 -mr-1 rounded-full p-0.5 hover:bg-foreground/20 transition-colors outline-none focus:ring-1 focus:ring-ring"
881
+ >
882
+ <i class="ri-close-line text-[1.1em]"></i>
883
+ </button>
884
+ </div>
885
+ `,
886
+ }]
887
+ }], propDecorators: { variant: [{
888
+ type: Input
889
+ }], size: [{
890
+ type: Input
891
+ }], removable: [{
892
+ type: Input
893
+ }], class: [{
894
+ type: Input
895
+ }], onRemove: [{
896
+ type: Output
897
+ }] } });
898
+
899
+ class SkeletonComponent {
900
+ variant = 'rect';
901
+ class = '';
902
+ cn = cn;
903
+ get computedClass() {
904
+ return cn(
905
+ // The background matches the Google Dark Mode "Muted" color
906
+ 'animate-pulse bg-muted rounded-md', this.variant === 'circle' && 'rounded-full', this.variant === 'pill' && 'rounded-full', this.class);
907
+ }
908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: SkeletonComponent, isStandalone: true, selector: "tolle-skeleton", inputs: { variant: "variant", class: "class" }, ngImport: i0, template: `
910
+ <div [class]="computedClass"></div>
911
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
912
+ }
913
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SkeletonComponent, decorators: [{
914
+ type: Component,
915
+ args: [{
916
+ selector: 'tolle-skeleton',
917
+ standalone: true,
918
+ imports: [CommonModule],
919
+ template: `
920
+ <div [class]="computedClass"></div>
921
+ `,
922
+ }]
923
+ }], propDecorators: { variant: [{
924
+ type: Input
925
+ }], class: [{
926
+ type: Input
927
+ }] } });
928
+
929
+ class CheckboxComponent {
930
+ cdr;
931
+ class = '';
932
+ disabled = false;
933
+ size = 'default';
934
+ checked = false;
935
+ onChange = () => { };
936
+ onTouched = () => { };
937
+ constructor(cdr) {
938
+ this.cdr = cdr;
939
+ }
940
+ cn = cn;
941
+ get sizeClasses() {
942
+ return cn(this.size === 'xs' && 'h-3.5 w-3.5', this.size === 'sm' && 'h-4 w-4', this.size === 'default' && 'h-5 w-5', this.size === 'lg' && 'h-6 w-6');
943
+ }
944
+ toggle() {
945
+ if (this.disabled)
946
+ return;
947
+ this.checked = !this.checked;
948
+ this.onChange(this.checked);
949
+ this.onTouched();
950
+ }
951
+ // --- ControlValueAccessor ---
952
+ writeValue(value) {
953
+ this.checked = value;
954
+ this.cdr.markForCheck();
955
+ }
956
+ registerOnChange(fn) { this.onChange = fn; }
957
+ registerOnTouched(fn) { this.onTouched = fn; }
958
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
959
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CheckboxComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
960
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CheckboxComponent, isStandalone: true, selector: "tolle-checkbox", inputs: { class: "class", disabled: "disabled", size: "size" }, providers: [
961
+ {
962
+ provide: NG_VALUE_ACCESSOR,
963
+ useExisting: forwardRef(() => CheckboxComponent),
964
+ multi: true
965
+ }
966
+ ], ngImport: i0, template: `
967
+ <div
968
+ (click)="toggle()"
969
+ [class]="cn(
970
+ 'group flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border border-primary ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
971
+ checked ? 'bg-primary text-primary-foreground' : 'bg-transparent',
972
+ sizeClasses,
973
+ class
974
+ )"
975
+ >
976
+ <i
977
+ *ngIf="checked"
978
+ class="ri-check-line font-bold"
979
+ [class]="size === 'xs' ? 'text-[10px]' : 'text-[14px]'"
980
+ ></i>
981
+ </div>
982
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
983
+ }
984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CheckboxComponent, decorators: [{
985
+ type: Component,
986
+ args: [{
987
+ selector: 'tolle-checkbox',
988
+ standalone: true,
989
+ imports: [CommonModule],
990
+ providers: [
991
+ {
992
+ provide: NG_VALUE_ACCESSOR,
993
+ useExisting: forwardRef(() => CheckboxComponent),
994
+ multi: true
995
+ }
996
+ ],
997
+ template: `
998
+ <div
999
+ (click)="toggle()"
1000
+ [class]="cn(
1001
+ 'group flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border border-primary ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
1002
+ checked ? 'bg-primary text-primary-foreground' : 'bg-transparent',
1003
+ sizeClasses,
1004
+ class
1005
+ )"
1006
+ >
1007
+ <i
1008
+ *ngIf="checked"
1009
+ class="ri-check-line font-bold"
1010
+ [class]="size === 'xs' ? 'text-[10px]' : 'text-[14px]'"
1011
+ ></i>
1012
+ </div>
1013
+ `
1014
+ }]
1015
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { class: [{
1016
+ type: Input
1017
+ }], disabled: [{
1018
+ type: Input
1019
+ }], size: [{
1020
+ type: Input
1021
+ }] } });
1022
+
1023
+ class TooltipDirective {
1024
+ el;
1025
+ content = '';
1026
+ placement = 'top';
1027
+ tooltipEl = null;
1028
+ cleanup;
1029
+ constructor(el) {
1030
+ this.el = el;
1031
+ }
1032
+ show() {
1033
+ if (!this.content)
1034
+ return;
1035
+ // 1. Create Tooltip Element
1036
+ this.tooltipEl = document.createElement('div');
1037
+ this.tooltipEl.className = cn('absolute z-[100] px-2 py-1 text-xs font-medium text-white bg-black rounded shadow-md pointer-events-none');
1038
+ this.tooltipEl.innerText = this.content;
1039
+ document.body.appendChild(this.tooltipEl);
1040
+ // 2. Position it using Floating UI
1041
+ this.cleanup = autoUpdate(this.el.nativeElement, this.tooltipEl, () => {
1042
+ computePosition(this.el.nativeElement, this.tooltipEl, {
1043
+ placement: this.placement,
1044
+ middleware: [offset(8), flip(), shift({ padding: 5 })],
1045
+ }).then(({ x, y }) => {
1046
+ Object.assign(this.tooltipEl.style, {
1047
+ left: `${x}px`,
1048
+ top: `${y}px`,
1049
+ });
1050
+ });
1051
+ });
1052
+ }
1053
+ hide() {
1054
+ if (this.tooltipEl) {
1055
+ this.tooltipEl.remove();
1056
+ this.tooltipEl = null;
1057
+ }
1058
+ if (this.cleanup) {
1059
+ this.cleanup();
1060
+ }
1061
+ }
1062
+ ngOnDestroy() {
1063
+ this.hide();
1064
+ }
1065
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TooltipDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
1066
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: TooltipDirective, isStandalone: true, selector: "[tolleTooltip]", inputs: { content: ["tolleTooltip", "content"], placement: "placement" }, host: { listeners: { "mouseenter": "show()", "mouseleave": "hide()", "focusout": "hide()" } }, ngImport: i0 });
1067
+ }
1068
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TooltipDirective, decorators: [{
1069
+ type: Directive,
1070
+ args: [{
1071
+ selector: '[tolleTooltip]',
1072
+ standalone: true
1073
+ }]
1074
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { content: [{
1075
+ type: Input,
1076
+ args: ['tolleTooltip']
1077
+ }], placement: [{
1078
+ type: Input
1079
+ }], show: [{
1080
+ type: HostListener,
1081
+ args: ['mouseenter']
1082
+ }], hide: [{
1083
+ type: HostListener,
1084
+ args: ['mouseleave']
1085
+ }, {
1086
+ type: HostListener,
1087
+ args: ['focusout']
1088
+ }] } });
1089
+
1090
+ // projects/tolle/src/lib/toast.service.ts
1091
+ class ToastService {
1092
+ toasts = [];
1093
+ toastSubject = new Subject();
1094
+ toasts$ = this.toastSubject.asObservable();
1095
+ constructor() {
1096
+ setInterval(() => this.tick(), 100);
1097
+ }
1098
+ show(toast) {
1099
+ const duration = toast.duration || 3000;
1100
+ const newToast = {
1101
+ ...toast,
1102
+ id: Date.now(),
1103
+ duration,
1104
+ remainingTime: duration,
1105
+ isPaused: false
1106
+ };
1107
+ this.toasts.push(newToast);
1108
+ this.notify();
1109
+ }
1110
+ tick() {
1111
+ let changed = false;
1112
+ this.toasts.forEach(t => {
1113
+ if (!t.isPaused) {
1114
+ t.remainingTime -= 100;
1115
+ changed = true;
1116
+ }
1117
+ });
1118
+ const initialCount = this.toasts.length;
1119
+ this.toasts = this.toasts.filter(t => t.remainingTime > 0);
1120
+ if (changed || this.toasts.length !== initialCount) {
1121
+ this.notify();
1122
+ }
1123
+ }
1124
+ setPaused(id, paused) {
1125
+ const toast = this.toasts.find(t => t.id === id);
1126
+ if (toast) {
1127
+ toast.isPaused = paused;
1128
+ this.notify();
1129
+ }
1130
+ }
1131
+ remove(id) {
1132
+ this.toasts = this.toasts.filter(t => t.id !== id);
1133
+ this.notify();
1134
+ }
1135
+ notify() {
1136
+ this.toastSubject.next([...this.toasts]);
1137
+ }
1138
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1139
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ToastService, providedIn: 'root' });
1140
+ }
1141
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ToastService, decorators: [{
1142
+ type: Injectable,
1143
+ args: [{ providedIn: 'root' }]
1144
+ }], ctorParameters: () => [] });
1145
+
1146
+ const TOLLE_CONFIG = new InjectionToken('TolleConfig');
1147
+ function provideTolleConfig(config) {
1148
+ return [
1149
+ {
1150
+ provide: TOLLE_CONFIG,
1151
+ useValue: config
1152
+ }
1153
+ ];
1154
+ }
1155
+
1156
+ class MultiSelectComponent {
1157
+ selectService;
1158
+ placeholder = 'Select options...';
1159
+ size = 'default';
1160
+ searchable = false;
1161
+ disabled = false;
1162
+ class = '';
1163
+ trigger;
1164
+ popover;
1165
+ items;
1166
+ value = [];
1167
+ selectedItems = [];
1168
+ isOpen = false;
1169
+ searchQuery = '';
1170
+ noResults = false;
1171
+ cleanup;
1172
+ constructor(selectService) {
1173
+ this.selectService = selectService;
1174
+ this.selectService.selectedValue$.subscribe(val => {
1175
+ if (val !== undefined && this.isOpen)
1176
+ this.toggleValue(val);
1177
+ });
1178
+ }
1179
+ ngAfterContentInit() {
1180
+ this.syncItems();
1181
+ this.items.changes.subscribe(() => this.syncItems());
1182
+ }
1183
+ toggle() {
1184
+ if (this.disabled)
1185
+ return;
1186
+ this.isOpen ? this.close() : this.open();
1187
+ }
1188
+ open() {
1189
+ this.isOpen = true;
1190
+ setTimeout(() => this.updatePosition());
1191
+ }
1192
+ close() {
1193
+ this.isOpen = false;
1194
+ if (this.cleanup)
1195
+ this.cleanup();
1196
+ }
1197
+ updatePosition() {
1198
+ this.cleanup = autoUpdate(this.trigger.nativeElement, this.popover.nativeElement, () => {
1199
+ computePosition(this.trigger.nativeElement, this.popover.nativeElement, {
1200
+ placement: 'bottom-start',
1201
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
1202
+ }).then(({ x, y }) => {
1203
+ Object.assign(this.popover.nativeElement.style, {
1204
+ left: `${x}px`,
1205
+ top: `${y}px`,
1206
+ visibility: 'visible',
1207
+ });
1208
+ });
1209
+ });
1210
+ }
1211
+ toggleValue(val) {
1212
+ const index = this.value.indexOf(val);
1213
+ index > -1 ? this.value.splice(index, 1) : this.value.push(val);
1214
+ this.syncItems();
1215
+ this.onChange([...this.value]); // Return new array reference for Change Detection
1216
+ }
1217
+ selectAll() {
1218
+ this.value = this.items.map(i => i.value);
1219
+ this.syncItems();
1220
+ this.onChange([...this.value]);
1221
+ }
1222
+ clearAll() {
1223
+ this.value = [];
1224
+ this.syncItems();
1225
+ this.onChange([]);
1226
+ }
1227
+ removeValue(event, val) {
1228
+ event.stopPropagation();
1229
+ this.toggleValue(val);
1230
+ }
1231
+ syncItems() {
1232
+ if (!this.items)
1233
+ return;
1234
+ this.selectedItems = [];
1235
+ this.items.forEach(item => {
1236
+ item.selected = this.value.includes(item.value);
1237
+ if (item.selected)
1238
+ this.selectedItems.push({ label: item.getLabel(), value: item.value });
1239
+ });
1240
+ }
1241
+ onSearchChange(q) {
1242
+ const filter = (q || '').toLowerCase();
1243
+ let visibleCount = 0;
1244
+ this.items.forEach(item => {
1245
+ const isVisible = item.getLabel().toLowerCase().includes(filter);
1246
+ item.hidden = !isVisible;
1247
+ if (isVisible)
1248
+ visibleCount++;
1249
+ });
1250
+ this.noResults = visibleCount === 0 && filter !== '';
1251
+ }
1252
+ onClickOutside(event) {
1253
+ if (this.isOpen && !this.trigger.nativeElement.contains(event.target) && !this.popover?.nativeElement.contains(event.target)) {
1254
+ this.close();
1255
+ }
1256
+ }
1257
+ // ControlValueAccessor
1258
+ onChange = () => { };
1259
+ onTouched = () => { };
1260
+ writeValue(v) { this.value = Array.isArray(v) ? v : []; this.syncItems(); }
1261
+ registerOnChange(fn) { this.onChange = fn; }
1262
+ registerOnTouched(fn) { this.onTouched = fn; }
1263
+ cn = cn;
1264
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MultiSelectComponent, deps: [{ token: SelectService }], target: i0.ɵɵFactoryTarget.Component });
1265
+ 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: [
1266
+ SelectService,
1267
+ { provide: NG_VALUE_ACCESSOR, useExisting: MultiSelectComponent, multi: true }
1268
+ ], 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: `
1269
+ <div [class]="cn('relative w-full', 'size-' + size)" #container>
1270
+ <button #trigger type="button" (click)="toggle()" [disabled]="disabled"
1271
+ [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)">
1272
+ <div class="flex flex-wrap gap-1 items-center max-w-[95%]">
1273
+ <ng-container *ngIf="value?.length; else placeholderTpl">
1274
+ <tolle-badge *ngFor="let item of selectedItems" size="xs" variant="secondary" [removable]="true" (onRemove)="removeValue($event, item.value)">
1275
+ {{ item.label }}
1276
+ </tolle-badge>
1277
+ </ng-container>
1278
+ <ng-template #placeholderTpl><span class="text-muted-foreground">{{ placeholder }}</span></ng-template>
1279
+ </div>
1280
+ <i [class]="cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform', isOpen ? 'rotate-180' : '')"></i>
1281
+ </button>
1282
+
1283
+ <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;">
1284
+
1285
+ <div class="p-2 border-b border-border space-y-2 bg-popover">
1286
+ <tolle-input *ngIf="searchable" size="xs" placeholder="Search..." [(ngModel)]="searchQuery" (ngModelChange)="onSearchChange($event)">
1287
+ <i prefix class="ri-search-line"></i>
1288
+ </tolle-input>
1289
+
1290
+ <div class="flex items-center justify-between px-1">
1291
+ <button type="button" (click)="selectAll()" class="text-[10px] font-bold uppercase text-primary hover:underline">Select All</button>
1292
+ <button type="button" (click)="clearAll()" class="text-[10px] font-bold uppercase text-muted-foreground hover:underline">Clear</button>
1293
+ </div>
1294
+ </div>
1295
+
1296
+ <div class="p-1 max-h-60 overflow-y-auto">
1297
+ <ng-content></ng-content>
1298
+ <div *ngIf="noResults" class="py-4 text-center text-xs text-muted-foreground">No results found for "{{searchQuery}}"</div>
1299
+ </div>
1300
+ </div>
1301
+ </div>
1302
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.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: BadgeComponent$1, selector: "tolle-badge", inputs: ["variant", "size", "removable", "class"], outputs: ["onRemove"] }, { kind: "component", type: InputComponent$1, selector: "tolle-input", inputs: ["type", "placeholder", "disabled", "error", "size", "containerClass", "class"] }] });
1303
+ }
1304
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MultiSelectComponent, decorators: [{
1305
+ type: Component,
1306
+ args: [{
1307
+ selector: 'tolle-multi-select',
1308
+ standalone: true,
1309
+ imports: [CommonModule, FormsModule, BadgeComponent$1, InputComponent$1],
1310
+ providers: [
1311
+ SelectService,
1312
+ { provide: NG_VALUE_ACCESSOR, useExisting: MultiSelectComponent, multi: true }
1313
+ ],
1314
+ template: `
1315
+ <div [class]="cn('relative w-full', 'size-' + size)" #container>
1316
+ <button #trigger type="button" (click)="toggle()" [disabled]="disabled"
1317
+ [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)">
1318
+ <div class="flex flex-wrap gap-1 items-center max-w-[95%]">
1319
+ <ng-container *ngIf="value?.length; else placeholderTpl">
1320
+ <tolle-badge *ngFor="let item of selectedItems" size="xs" variant="secondary" [removable]="true" (onRemove)="removeValue($event, item.value)">
1321
+ {{ item.label }}
1322
+ </tolle-badge>
1323
+ </ng-container>
1324
+ <ng-template #placeholderTpl><span class="text-muted-foreground">{{ placeholder }}</span></ng-template>
1325
+ </div>
1326
+ <i [class]="cn('ri-arrow-down-s-line text-muted-foreground ml-2 transition-transform', isOpen ? 'rotate-180' : '')"></i>
1327
+ </button>
1328
+
1329
+ <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;">
1330
+
1331
+ <div class="p-2 border-b border-border space-y-2 bg-popover">
1332
+ <tolle-input *ngIf="searchable" size="xs" placeholder="Search..." [(ngModel)]="searchQuery" (ngModelChange)="onSearchChange($event)">
1333
+ <i prefix class="ri-search-line"></i>
1334
+ </tolle-input>
1335
+
1336
+ <div class="flex items-center justify-between px-1">
1337
+ <button type="button" (click)="selectAll()" class="text-[10px] font-bold uppercase text-primary hover:underline">Select All</button>
1338
+ <button type="button" (click)="clearAll()" class="text-[10px] font-bold uppercase text-muted-foreground hover:underline">Clear</button>
1339
+ </div>
1340
+ </div>
1341
+
1342
+ <div class="p-1 max-h-60 overflow-y-auto">
1343
+ <ng-content></ng-content>
1344
+ <div *ngIf="noResults" class="py-4 text-center text-xs text-muted-foreground">No results found for "{{searchQuery}}"</div>
1345
+ </div>
1346
+ </div>
1347
+ </div>
1348
+ `
1349
+ }]
1350
+ }], ctorParameters: () => [{ type: SelectService }], propDecorators: { placeholder: [{
1351
+ type: Input
1352
+ }], size: [{
1353
+ type: Input
1354
+ }], searchable: [{
1355
+ type: Input
1356
+ }], disabled: [{
1357
+ type: Input
1358
+ }], class: [{
1359
+ type: Input
1360
+ }], trigger: [{
1361
+ type: ViewChild,
1362
+ args: ['trigger']
1363
+ }], popover: [{
1364
+ type: ViewChild,
1365
+ args: ['popover']
1366
+ }], items: [{
1367
+ type: ContentChildren,
1368
+ args: [SelectItemComponent, { descendants: true }]
1369
+ }], onClickOutside: [{
1370
+ type: HostListener,
1371
+ args: ['document:mousedown', ['$event']]
1372
+ }] } });
1373
+
1374
+ class CalendarComponent {
1375
+ class = '';
1376
+ disablePastDates = false;
1377
+ currentView = 'date';
1378
+ viewDate = new Date();
1379
+ selectedDate = null;
1380
+ weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
1381
+ daysInMonth = [];
1382
+ months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
1383
+ years = [];
1384
+ 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');
1385
+ onTouched = () => { };
1386
+ onChange = () => { };
1387
+ cn = cn;
1388
+ ngOnInit() {
1389
+ this.generateDays();
1390
+ this.generateYears();
1391
+ }
1392
+ generateDays() {
1393
+ const start = startOfWeek(startOfMonth(this.viewDate));
1394
+ const end = endOfWeek(endOfMonth(this.viewDate));
1395
+ this.daysInMonth = eachDayOfInterval({ start, end });
1396
+ }
1397
+ generateYears() {
1398
+ const currentYear = this.viewDate.getFullYear();
1399
+ // Generates a 16-year window centered roughly on current view
1400
+ this.years = Array.from({ length: 16 }, (_, i) => currentYear - 6 + i);
1401
+ }
1402
+ setView(view) {
1403
+ this.currentView = view;
1404
+ // If switching to year view, ensure the year grid is centered on current view year
1405
+ if (view === 'year') {
1406
+ this.generateYears();
1407
+ }
1408
+ }
1409
+ prev() {
1410
+ if (this.currentView === 'date') {
1411
+ this.viewDate = subMonths(this.viewDate, 1);
1412
+ this.generateDays();
1413
+ }
1414
+ else if (this.currentView === 'year') {
1415
+ this.viewDate = subYears(this.viewDate, 16);
1416
+ this.generateYears();
1417
+ }
1418
+ else if (this.currentView === 'month') {
1419
+ this.viewDate = subYears(this.viewDate, 1);
1420
+ }
1421
+ }
1422
+ next() {
1423
+ if (this.currentView === 'date') {
1424
+ this.viewDate = addMonths(this.viewDate, 1);
1425
+ this.generateDays();
1426
+ }
1427
+ else if (this.currentView === 'year') {
1428
+ this.viewDate = addYears(this.viewDate, 16);
1429
+ this.generateYears();
1430
+ }
1431
+ else if (this.currentView === 'month') {
1432
+ this.viewDate = addYears(this.viewDate, 1);
1433
+ }
1434
+ }
1435
+ selectDate(date) {
1436
+ if (this.isDateDisabled(date))
1437
+ return;
1438
+ this.selectedDate = date;
1439
+ if (!isSameMonth(date, this.viewDate)) {
1440
+ this.viewDate = date;
1441
+ this.generateDays();
1442
+ }
1443
+ this.onChange(date);
1444
+ this.onTouched();
1445
+ }
1446
+ selectMonth(monthIndex) {
1447
+ this.viewDate = setMonth(this.viewDate, monthIndex);
1448
+ this.currentView = 'date';
1449
+ this.generateDays();
1450
+ }
1451
+ selectYear(year) {
1452
+ this.viewDate = setYear(this.viewDate, year);
1453
+ this.currentView = 'date'; // Jump straight back to date view for efficiency
1454
+ this.generateDays();
1455
+ }
1456
+ getDayClass(date) {
1457
+ const isSelected = this.selectedDate && isSameDay(date, this.selectedDate);
1458
+ const isTodayDate = isToday(date);
1459
+ const isOutside = !isSameMonth(date, this.viewDate);
1460
+ const isDisabled = this.isDateDisabled(date);
1461
+ 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');
1462
+ }
1463
+ isDateDisabled(date) {
1464
+ return this.disablePastDates ? isBefore(date, startOfDay(new Date())) : false;
1465
+ }
1466
+ // CVA Implementation
1467
+ writeValue(obj) {
1468
+ if (obj) {
1469
+ const date = new Date(obj);
1470
+ if (!isNaN(date.getTime())) {
1471
+ this.selectedDate = date;
1472
+ this.viewDate = date;
1473
+ this.generateDays();
1474
+ this.generateYears();
1475
+ }
1476
+ }
1477
+ }
1478
+ registerOnChange(fn) { this.onChange = fn; }
1479
+ registerOnTouched(fn) { this.onTouched = fn; }
1480
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1481
+ 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: [
1482
+ {
1483
+ provide: NG_VALUE_ACCESSOR,
1484
+ useExisting: forwardRef(() => CalendarComponent),
1485
+ multi: true
1486
+ }
1487
+ ], ngImport: i0, template: `
1488
+ <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block w-fit', class)">
1489
+
1490
+ <div class="flex items-center justify-between pt-1 pb-4 gap-2">
1491
+
1492
+ <div class="flex items-center gap-1">
1493
+ <button
1494
+ type="button"
1495
+ (click)="setView('month')"
1496
+ [class]="cn(
1497
+ 'text-sm font-semibold px-2 py-1 rounded transition-colors',
1498
+ currentView === 'month' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
1499
+ )"
1500
+ >
1501
+ {{ viewDate | date: 'MMMM' }}
1502
+ </button>
1503
+
1504
+ <button
1505
+ type="button"
1506
+ (click)="setView('year')"
1507
+ [class]="cn(
1508
+ 'text-sm font-semibold px-2 py-1 rounded transition-colors',
1509
+ currentView === 'year' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
1510
+ )"
1511
+ >
1512
+ {{ viewDate | date: 'yyyy' }}
1513
+ </button>
1514
+ </div>
1515
+
1516
+ <div class="flex items-center space-x-1">
1517
+ <button type="button" (click)="prev()" [class]="navBtnClass">
1518
+ <i class="ri-arrow-left-s-line text-lg"></i>
1519
+ </button>
1520
+ <button type="button" (click)="next()" [class]="navBtnClass">
1521
+ <i class="ri-arrow-right-s-line text-lg"></i>
1522
+ </button>
1523
+ </div>
1524
+ </div>
1525
+
1526
+ <div *ngIf="currentView === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
1527
+ <div class="grid grid-cols-7 gap-1 w-full">
1528
+ <span *ngFor="let day of weekDays" class="text-[0.8rem] text-muted-foreground font-normal text-center w-9">
1529
+ {{ day }}
1530
+ </span>
1531
+ </div>
1532
+ <div class="grid grid-cols-7 gap-1 w-full">
1533
+ <button
1534
+ *ngFor="let date of daysInMonth"
1535
+ type="button"
1536
+ (click)="selectDate(date)"
1537
+ [disabled]="isDateDisabled(date)"
1538
+ [class]="getDayClass(date)"
1539
+ >
1540
+ {{ date | date: 'd' }}
1541
+ </button>
1542
+ </div>
1543
+ </div>
1544
+
1545
+ <div *ngIf="currentView === 'month'" class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
1546
+ <button
1547
+ *ngFor="let month of months; let i = index"
1548
+ type="button"
1549
+ (click)="selectMonth(i)"
1550
+ [class]="cn(
1551
+ 'text-sm py-2.5 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
1552
+ i === viewDate.getMonth() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
1553
+ )"
1554
+ >
1555
+ {{ month }}
1556
+ </button>
1557
+ </div>
1558
+
1559
+ <div *ngIf="currentView === 'year'" class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
1560
+ <button
1561
+ *ngFor="let year of years"
1562
+ type="button"
1563
+ (click)="selectYear(year)"
1564
+ [class]="cn(
1565
+ 'text-sm py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
1566
+ year === viewDate.getFullYear() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
1567
+ )"
1568
+ >
1569
+ {{ year }}
1570
+ </button>
1571
+ </div>
1572
+ </div>
1573
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
1574
+ }
1575
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CalendarComponent, decorators: [{
1576
+ type: Component,
1577
+ args: [{
1578
+ selector: 'tolle-calendar',
1579
+ standalone: true,
1580
+ imports: [CommonModule],
1581
+ providers: [
1582
+ {
1583
+ provide: NG_VALUE_ACCESSOR,
1584
+ useExisting: forwardRef(() => CalendarComponent),
1585
+ multi: true
1586
+ }
1587
+ ],
1588
+ template: `
1589
+ <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block w-fit', class)">
1590
+
1591
+ <div class="flex items-center justify-between pt-1 pb-4 gap-2">
1592
+
1593
+ <div class="flex items-center gap-1">
1594
+ <button
1595
+ type="button"
1596
+ (click)="setView('month')"
1597
+ [class]="cn(
1598
+ 'text-sm font-semibold px-2 py-1 rounded transition-colors',
1599
+ currentView === 'month' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
1600
+ )"
1601
+ >
1602
+ {{ viewDate | date: 'MMMM' }}
1603
+ </button>
1604
+
1605
+ <button
1606
+ type="button"
1607
+ (click)="setView('year')"
1608
+ [class]="cn(
1609
+ 'text-sm font-semibold px-2 py-1 rounded transition-colors',
1610
+ currentView === 'year' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground'
1611
+ )"
1612
+ >
1613
+ {{ viewDate | date: 'yyyy' }}
1614
+ </button>
1615
+ </div>
1616
+
1617
+ <div class="flex items-center space-x-1">
1618
+ <button type="button" (click)="prev()" [class]="navBtnClass">
1619
+ <i class="ri-arrow-left-s-line text-lg"></i>
1620
+ </button>
1621
+ <button type="button" (click)="next()" [class]="navBtnClass">
1622
+ <i class="ri-arrow-right-s-line text-lg"></i>
1623
+ </button>
1624
+ </div>
1625
+ </div>
1626
+
1627
+ <div *ngIf="currentView === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
1628
+ <div class="grid grid-cols-7 gap-1 w-full">
1629
+ <span *ngFor="let day of weekDays" class="text-[0.8rem] text-muted-foreground font-normal text-center w-9">
1630
+ {{ day }}
1631
+ </span>
1632
+ </div>
1633
+ <div class="grid grid-cols-7 gap-1 w-full">
1634
+ <button
1635
+ *ngFor="let date of daysInMonth"
1636
+ type="button"
1637
+ (click)="selectDate(date)"
1638
+ [disabled]="isDateDisabled(date)"
1639
+ [class]="getDayClass(date)"
1640
+ >
1641
+ {{ date | date: 'd' }}
1642
+ </button>
1643
+ </div>
1644
+ </div>
1645
+
1646
+ <div *ngIf="currentView === 'month'" class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
1647
+ <button
1648
+ *ngFor="let month of months; let i = index"
1649
+ type="button"
1650
+ (click)="selectMonth(i)"
1651
+ [class]="cn(
1652
+ 'text-sm py-2.5 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
1653
+ i === viewDate.getMonth() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
1654
+ )"
1655
+ >
1656
+ {{ month }}
1657
+ </button>
1658
+ </div>
1659
+
1660
+ <div *ngIf="currentView === 'year'" class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
1661
+ <button
1662
+ *ngFor="let year of years"
1663
+ type="button"
1664
+ (click)="selectYear(year)"
1665
+ [class]="cn(
1666
+ 'text-sm py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors',
1667
+ year === viewDate.getFullYear() ? 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground' : ''
1668
+ )"
1669
+ >
1670
+ {{ year }}
1671
+ </button>
1672
+ </div>
1673
+ </div>
1674
+ `
1675
+ }]
1676
+ }], propDecorators: { class: [{
1677
+ type: Input
1678
+ }], disablePastDates: [{
1679
+ type: Input
1680
+ }] } });
1681
+
1682
+ class MaskedInputComponent {
1683
+ el;
1684
+ cdr;
1685
+ mask = '';
1686
+ placeholder = '';
1687
+ type = 'text';
1688
+ disabled = false;
1689
+ class = '';
1690
+ error = false;
1691
+ size = 'default';
1692
+ returnRaw = false;
1693
+ inputEl;
1694
+ hasPrefix = false;
1695
+ hasSuffix = false;
1696
+ displayValue = '';
1697
+ tokens = {
1698
+ '0': /\d/, '9': /\d/, 'a': /[a-z]/i, 'A': /[a-z]/i, '*': /[a-z0-9]/i
1699
+ };
1700
+ onChange = () => { };
1701
+ onTouched = () => { };
1702
+ constructor(el, cdr) {
1703
+ this.el = el;
1704
+ this.cdr = cdr;
1705
+ }
1706
+ // FIXED DETECTION: Check the actual DOM nodes projected into the component
1707
+ ngAfterContentChecked() {
1708
+ const prefix = this.el.nativeElement.querySelector('[prefix]');
1709
+ const suffix = this.el.nativeElement.querySelector('[suffix]');
1710
+ if (this.hasPrefix !== !!prefix || this.hasSuffix !== !!suffix) {
1711
+ this.hasPrefix = !!prefix;
1712
+ this.hasSuffix = !!suffix;
1713
+ this.cdr.detectChanges();
1714
+ }
1715
+ }
1716
+ get computedInputClass() {
1717
+ return cn("flex w-full rounded-md border border-input bg-background text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring focus-visible:ring-ring focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 transition-all", 'disabled:opacity-50 shadow-sm transition-shadow', this.size === 'xs' && "h-8 text-xs px-2", this.size === 'sm' && "h-9 px-3", this.size === 'default' && "h-10 px-3", this.size === 'lg' && "h-11 px-4 text-base", "group-has-[[prefix]]:pl-10 group-has-[[suffix]]:pr-10", this.size === 'xs' && "group-has-[[prefix]]:pl-8 group-has-[[suffix]]:pr-8", this.error && "border-destructive focus-visible:ring-destructive", this.class);
1718
+ }
1719
+ // --- Masking Logic ---
1720
+ onInput(event) {
1721
+ const input = event.target;
1722
+ const raw = this.unmask(input.value);
1723
+ const masked = this.applyMask(raw);
1724
+ this.displayValue = masked;
1725
+ input.value = masked;
1726
+ this.returnRaw ? this.onChange(raw) : this.onChange(masked);
1727
+ }
1728
+ applyMask(rawValue) {
1729
+ let rawIndex = 0;
1730
+ let formatted = '';
1731
+ for (let i = 0; i < this.mask.length; i++) {
1732
+ if (rawIndex >= rawValue.length)
1733
+ break;
1734
+ const maskChar = this.mask[i];
1735
+ const rawChar = rawValue[rawIndex];
1736
+ if (this.tokens[maskChar]) {
1737
+ if (this.tokens[maskChar].test(rawChar)) {
1738
+ formatted += rawChar;
1739
+ rawIndex++;
1740
+ }
1741
+ else {
1742
+ rawIndex++;
1743
+ i--;
1744
+ }
1745
+ }
1746
+ else {
1747
+ formatted += maskChar;
1748
+ if (rawChar === maskChar)
1749
+ rawIndex++;
1750
+ }
1751
+ }
1752
+ return formatted;
1753
+ }
1754
+ unmask(val) { return val.replace(/[^a-zA-Z0-9]/g, ''); }
1755
+ writeValue(value) {
1756
+ this.displayValue = value ? this.applyMask(this.unmask(value.toString())) : '';
1757
+ this.cdr.markForCheck();
1758
+ }
1759
+ registerOnChange(fn) { this.onChange = fn; }
1760
+ registerOnTouched(fn) { this.onTouched = fn; }
1761
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
1762
+ cn = cn;
1763
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskedInputComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1764
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MaskedInputComponent, isStandalone: true, selector: "tolle-masked-input", inputs: { mask: "mask", placeholder: "placeholder", type: "type", disabled: "disabled", class: "class", error: "error", size: "size", returnRaw: "returnRaw" }, providers: [
1765
+ {
1766
+ provide: NG_VALUE_ACCESSOR,
1767
+ useExisting: forwardRef(() => MaskedInputComponent),
1768
+ multi: true
1769
+ }
1770
+ ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }], ngImport: i0, template: `
1771
+ <div [class]="cn('relative flex items-center w-full group', 'size-' + size, class)">
1772
+
1773
+ <div class="absolute left-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
1774
+ [class.left-2.5]="size === 'xs'">
1775
+ <ng-content select="[prefix]"></ng-content>
1776
+ </div>
1777
+
1778
+ <input
1779
+ #inputEl
1780
+ [type]="type"
1781
+ [placeholder]="placeholder"
1782
+ [disabled]="disabled"
1783
+ [value]="displayValue"
1784
+ (input)="onInput($event)"
1785
+ (blur)="onTouched()"
1786
+ [class]="computedInputClass"
1787
+ />
1788
+
1789
+ <div class="absolute right-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
1790
+ [class.right-2.5]="size === 'xs'">
1791
+ <ng-content select="[suffix]"></ng-content>
1792
+ </div>
1793
+ </div>
1794
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] });
1795
+ }
1796
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskedInputComponent, decorators: [{
1797
+ type: Component,
1798
+ args: [{
1799
+ selector: 'tolle-masked-input',
1800
+ standalone: true,
1801
+ imports: [CommonModule, FormsModule],
1802
+ providers: [
1803
+ {
1804
+ provide: NG_VALUE_ACCESSOR,
1805
+ useExisting: forwardRef(() => MaskedInputComponent),
1806
+ multi: true
1807
+ }
1808
+ ],
1809
+ template: `
1810
+ <div [class]="cn('relative flex items-center w-full group', 'size-' + size, class)">
1811
+
1812
+ <div class="absolute left-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
1813
+ [class.left-2.5]="size === 'xs'">
1814
+ <ng-content select="[prefix]"></ng-content>
1815
+ </div>
1816
+
1817
+ <input
1818
+ #inputEl
1819
+ [type]="type"
1820
+ [placeholder]="placeholder"
1821
+ [disabled]="disabled"
1822
+ [value]="displayValue"
1823
+ (input)="onInput($event)"
1824
+ (blur)="onTouched()"
1825
+ [class]="computedInputClass"
1826
+ />
1827
+
1828
+ <div class="absolute right-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
1829
+ [class.right-2.5]="size === 'xs'">
1830
+ <ng-content select="[suffix]"></ng-content>
1831
+ </div>
1832
+ </div>
1833
+ `
1834
+ }]
1835
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { mask: [{
1836
+ type: Input
1837
+ }], placeholder: [{
1838
+ type: Input
1839
+ }], type: [{
1840
+ type: Input
1841
+ }], disabled: [{
1842
+ type: Input
1843
+ }], class: [{
1844
+ type: Input
1845
+ }], error: [{
1846
+ type: Input
1847
+ }], size: [{
1848
+ type: Input
1849
+ }], returnRaw: [{
1850
+ type: Input
1851
+ }], inputEl: [{
1852
+ type: ViewChild,
1853
+ args: ['inputEl', { static: true }]
1854
+ }] } });
1855
+
1856
+ class DatePickerComponent {
1857
+ cdr;
1858
+ placeholder = 'MM/DD/YYYY';
1859
+ disabled = false;
1860
+ class = '';
1861
+ disablePastDates = false;
1862
+ triggerContainer;
1863
+ popover;
1864
+ value = null;
1865
+ inputValue = '';
1866
+ isOpen = false;
1867
+ cleanupAutoUpdate;
1868
+ constructor(cdr) {
1869
+ this.cdr = cdr;
1870
+ }
1871
+ // --- Logic ---
1872
+ onInputChange(str) {
1873
+ if (str?.length === 10) {
1874
+ const parsed = parse(str, 'MM/dd/yyyy', new Date());
1875
+ if (isValid(parsed)) {
1876
+ this.value = startOfDay(parsed);
1877
+ this.onChange(this.value);
1878
+ }
1879
+ }
1880
+ else if (!str) {
1881
+ this.value = null;
1882
+ this.onChange(null);
1883
+ }
1884
+ }
1885
+ onCalendarChange(date) {
1886
+ this.value = date;
1887
+ this.inputValue = format(date, 'MM/dd/yyyy');
1888
+ this.onChange(this.value);
1889
+ this.close();
1890
+ }
1891
+ togglePopover(event) {
1892
+ event.stopPropagation(); // Prevent bubbling to document
1893
+ if (this.disabled)
1894
+ return;
1895
+ this.isOpen ? this.close() : this.open();
1896
+ }
1897
+ open() {
1898
+ this.isOpen = true;
1899
+ setTimeout(() => this.updatePosition());
1900
+ }
1901
+ close() {
1902
+ this.isOpen = false;
1903
+ if (this.cleanupAutoUpdate)
1904
+ this.cleanupAutoUpdate();
1905
+ }
1906
+ clear(event) {
1907
+ event.stopPropagation(); // CRITICAL: Stop the calendar from opening
1908
+ this.value = null;
1909
+ this.inputValue = '';
1910
+ this.onChange(null);
1911
+ this.cdr.markForCheck();
1912
+ }
1913
+ // --- Positioning ---
1914
+ updatePosition() {
1915
+ if (!this.triggerContainer || !this.popover)
1916
+ return;
1917
+ this.cleanupAutoUpdate = autoUpdate(this.triggerContainer.nativeElement, this.popover.nativeElement, () => {
1918
+ computePosition(this.triggerContainer.nativeElement, this.popover.nativeElement, {
1919
+ placement: 'bottom-end', // Aligned to the right where the icon is
1920
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
1921
+ }).then(({ x, y }) => {
1922
+ Object.assign(this.popover.nativeElement.style, {
1923
+ left: `${x}px`,
1924
+ top: `${y}px`,
1925
+ visibility: 'visible',
1926
+ });
1927
+ });
1928
+ });
1929
+ }
1930
+ onClickOutside(event) {
1931
+ if (this.isOpen &&
1932
+ !this.triggerContainer.nativeElement.contains(event.target) &&
1933
+ !this.popover.nativeElement.contains(event.target)) {
1934
+ this.close();
1935
+ }
1936
+ }
1937
+ // --- CVA ---
1938
+ onChange = () => { };
1939
+ onTouched = () => { };
1940
+ writeValue(val) {
1941
+ if (val) {
1942
+ const date = new Date(val);
1943
+ if (isValid(date)) {
1944
+ this.value = startOfDay(date);
1945
+ this.inputValue = format(this.value, 'MM/dd/yyyy');
1946
+ }
1947
+ }
1948
+ else {
1949
+ this.value = null;
1950
+ this.inputValue = '';
1951
+ }
1952
+ this.cdr.markForCheck();
1953
+ }
1954
+ registerOnChange(fn) { this.onChange = fn; }
1955
+ registerOnTouched(fn) { this.onTouched = fn; }
1956
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
1957
+ cn = cn;
1958
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1959
+ 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: [
1960
+ {
1961
+ provide: NG_VALUE_ACCESSOR,
1962
+ useExisting: forwardRef(() => DatePickerComponent),
1963
+ multi: true
1964
+ }
1965
+ ], viewQueries: [{ propertyName: "triggerContainer", first: true, predicate: ["triggerContainer"], descendants: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true }], ngImport: i0, template: `
1966
+ <div class="relative w-full" #triggerContainer>
1967
+ <tolle-masked-input
1968
+ #maskInput
1969
+ [mask]="'00/00/0000'"
1970
+ [placeholder]="placeholder"
1971
+ [disabled]="disabled"
1972
+ [(ngModel)]="inputValue"
1973
+ (ngModelChange)="onInputChange($event)"
1974
+ [class]="cn(class)"
1975
+ >
1976
+ <div suffix class="flex items-center gap-1.5 cursor-pointer">
1977
+ <i
1978
+ *ngIf="value && !disabled"
1979
+ (click)="clear($event)"
1980
+ class="ri-close-line cursor-pointer text-muted-foreground hover:text-foreground transition-colors"
1981
+ ></i>
1982
+
1983
+ <i
1984
+ (click)="togglePopover($event)"
1985
+ class="ri-calendar-line cursor-pointer text-muted-foreground hover:text-primary transition-colors"
1986
+ ></i>
1987
+ </div>
1988
+ </tolle-masked-input>
1989
+
1990
+ <div
1991
+ #popover
1992
+ *ngIf="isOpen"
1993
+ 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"
1994
+ style="visibility: hidden; top: 0; left: 0;"
1995
+ >
1996
+ <tolle-calendar
1997
+ [(ngModel)]="value"
1998
+ (ngModelChange)="onCalendarChange($event)"
1999
+ [disablePastDates]="disablePastDates"
2000
+ ></tolle-calendar>
2001
+ </div>
2002
+ </div>
2003
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.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: ["mask", "placeholder", "type", "disabled", "class", "error", "size", "returnRaw"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "disablePastDates"] }] });
2004
+ }
2005
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, decorators: [{
2006
+ type: Component,
2007
+ args: [{
2008
+ selector: 'tolle-date-picker',
2009
+ standalone: true,
2010
+ imports: [CommonModule, FormsModule, MaskedInputComponent, CalendarComponent],
2011
+ providers: [
2012
+ {
2013
+ provide: NG_VALUE_ACCESSOR,
2014
+ useExisting: forwardRef(() => DatePickerComponent),
2015
+ multi: true
2016
+ }
2017
+ ],
2018
+ template: `
2019
+ <div class="relative w-full" #triggerContainer>
2020
+ <tolle-masked-input
2021
+ #maskInput
2022
+ [mask]="'00/00/0000'"
2023
+ [placeholder]="placeholder"
2024
+ [disabled]="disabled"
2025
+ [(ngModel)]="inputValue"
2026
+ (ngModelChange)="onInputChange($event)"
2027
+ [class]="cn(class)"
2028
+ >
2029
+ <div suffix class="flex items-center gap-1.5 cursor-pointer">
2030
+ <i
2031
+ *ngIf="value && !disabled"
2032
+ (click)="clear($event)"
2033
+ class="ri-close-line cursor-pointer text-muted-foreground hover:text-foreground transition-colors"
2034
+ ></i>
2035
+
2036
+ <i
2037
+ (click)="togglePopover($event)"
2038
+ class="ri-calendar-line cursor-pointer text-muted-foreground hover:text-primary transition-colors"
2039
+ ></i>
2040
+ </div>
2041
+ </tolle-masked-input>
2042
+
2043
+ <div
2044
+ #popover
2045
+ *ngIf="isOpen"
2046
+ 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"
2047
+ style="visibility: hidden; top: 0; left: 0;"
2048
+ >
2049
+ <tolle-calendar
2050
+ [(ngModel)]="value"
2051
+ (ngModelChange)="onCalendarChange($event)"
2052
+ [disablePastDates]="disablePastDates"
2053
+ ></tolle-calendar>
2054
+ </div>
2055
+ </div>
2056
+ `
2057
+ }]
2058
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { placeholder: [{
2059
+ type: Input
2060
+ }], disabled: [{
2061
+ type: Input
2062
+ }], class: [{
2063
+ type: Input
2064
+ }], disablePastDates: [{
2065
+ type: Input
2066
+ }], triggerContainer: [{
2067
+ type: ViewChild,
2068
+ args: ['triggerContainer']
2069
+ }], popover: [{
2070
+ type: ViewChild,
2071
+ args: ['popover']
2072
+ }], onClickOutside: [{
2073
+ type: HostListener,
2074
+ args: ['document:mousedown', ['$event']]
2075
+ }] } });
2076
+
2077
+ class PaginationComponent {
2078
+ class = '';
2079
+ showPageLinks = true;
2080
+ showPageOptions = true;
2081
+ showCurrentPageInfo = true;
2082
+ currentPageInfoTemplate;
2083
+ totalRecords = 0;
2084
+ currentPageSize = 10;
2085
+ currentPage = 1;
2086
+ pageSizeOptions = [10, 20, 30, 50];
2087
+ onPageNumberChange = new EventEmitter();
2088
+ onPageSizeChange = new EventEmitter();
2089
+ totalPages = 0;
2090
+ first = 0;
2091
+ last = 0;
2092
+ displayPageIndex = [];
2093
+ pageReport = '';
2094
+ initialized = false;
2095
+ cd = inject(ChangeDetectorRef);
2096
+ cn = cn;
2097
+ navBtnClass = 'h-8 w-8 p-0 flex items-center justify-center rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:cursor-not-allowed';
2098
+ ngOnInit() {
2099
+ this.initializePagination();
2100
+ }
2101
+ ngOnChanges(changes) {
2102
+ // Only re-init if meaningful data changes
2103
+ if (changes['totalRecords'] || changes['currentPage'] || changes['currentPageSize']) {
2104
+ this.initializePagination();
2105
+ }
2106
+ }
2107
+ initializePagination() {
2108
+ this.calcPagination();
2109
+ if (!this.initialized) {
2110
+ this.initialized = true;
2111
+ }
2112
+ this.cd.detectChanges();
2113
+ }
2114
+ nextPage() {
2115
+ if (this.totalPages > this.currentPage) {
2116
+ this.currentPage++;
2117
+ this.emitChange();
2118
+ }
2119
+ }
2120
+ previousPage() {
2121
+ if (this.currentPage > 1) {
2122
+ this.currentPage--;
2123
+ this.emitChange();
2124
+ }
2125
+ }
2126
+ selectPage(page) {
2127
+ if (this.currentPage === page)
2128
+ return;
2129
+ this.currentPage = page;
2130
+ this.emitChange();
2131
+ }
2132
+ sizeChange(size) {
2133
+ this.currentPageSize = size;
2134
+ this.currentPage = 1; // Reset to page 1 on size change
2135
+ this.emitChange();
2136
+ }
2137
+ emitChange() {
2138
+ this.calcPagination();
2139
+ this.onPageNumberChange.emit(this.currentPage);
2140
+ this.onPageSizeChange.emit(this.currentPageSize);
2141
+ this.cd.detectChanges();
2142
+ }
2143
+ calcPagination() {
2144
+ this.totalPages = Math.ceil(this.totalRecords / this.currentPageSize) || 0;
2145
+ // Bounds check
2146
+ if (this.currentPage > this.totalPages && this.totalPages > 0) {
2147
+ this.currentPage = this.totalPages;
2148
+ }
2149
+ this.first = this.totalRecords === 0 ? 0 : (this.currentPage - 1) * this.currentPageSize + 1;
2150
+ this.last = Math.min(this.totalRecords, this.currentPage * this.currentPageSize);
2151
+ // Calculate Sliding Window for Page Numbers (Max 5 visible)
2152
+ const pages = Array.from({ length: this.totalPages }, (_, i) => i + 1);
2153
+ if (this.totalPages <= 5) {
2154
+ this.displayPageIndex = pages;
2155
+ }
2156
+ else {
2157
+ let start = Math.max(0, this.currentPage - 3);
2158
+ let end = start + 5;
2159
+ if (end > this.totalPages) {
2160
+ end = this.totalPages;
2161
+ start = end - 5;
2162
+ }
2163
+ this.displayPageIndex = pages.slice(start, end);
2164
+ }
2165
+ // Template Parsing
2166
+ if (this.currentPageInfoTemplate) {
2167
+ this.pageReport = this.currentPageInfoTemplate.replace(/{first}|{last}|{totalRecords}|{currentPage}|{currentPageSize}|{totalPages}/g, (match) => {
2168
+ switch (match) {
2169
+ case '{first}': return `${this.first}`;
2170
+ case '{last}': return `${this.last}`;
2171
+ case '{totalRecords}': return `${this.totalRecords}`;
2172
+ case '{totalPages}': return `${this.totalPages}`;
2173
+ case '{currentPage}': return `${this.currentPage}`;
2174
+ case '{currentPageSize}': return `${this.currentPageSize}`;
2175
+ default: return match;
2176
+ }
2177
+ });
2178
+ }
2179
+ }
2180
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2181
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: PaginationComponent, isStandalone: true, selector: "tolle-pagination", inputs: { class: "class", showPageLinks: "showPageLinks", showPageOptions: "showPageOptions", showCurrentPageInfo: "showCurrentPageInfo", currentPageInfoTemplate: "currentPageInfoTemplate", totalRecords: "totalRecords", currentPageSize: "currentPageSize", currentPage: "currentPage", pageSizeOptions: "pageSizeOptions" }, outputs: { onPageNumberChange: "onPageNumberChange", onPageSizeChange: "onPageSizeChange" }, usesOnChanges: true, ngImport: i0, template: `
2182
+ <div [class]="cn('flex items-center justify-between px-2 py-4', class)">
2183
+
2184
+ <div *ngIf="showCurrentPageInfo" class="text-sm text-muted-foreground">
2185
+ <ng-container *ngIf="currentPageInfoTemplate; else defaultReport">
2186
+ {{ pageReport }}
2187
+ </ng-container>
2188
+ <ng-template #defaultReport>
2189
+ Showing {{ first }} to {{ last }} of {{ totalRecords }} entries
2190
+ </ng-template>
2191
+ </div>
2192
+
2193
+ <div class="flex items-center space-x-6 lg:space-x-8">
2194
+
2195
+ <div *ngIf="showPageOptions" class="flex items-center space-x-2">
2196
+ <p class="text-sm font-medium">Rows per page</p>
2197
+ <tolle-select
2198
+ class="w-[70px]"
2199
+ size="sm"
2200
+ [ngModel]="currentPageSize"
2201
+ (ngModelChange)="sizeChange($event)"
2202
+ >
2203
+ <tolle-select-item *ngFor="let opt of pageSizeOptions" [value]="opt">
2204
+ {{ opt }}
2205
+ </tolle-select-item>
2206
+ </tolle-select>
2207
+ </div>
2208
+
2209
+ <div class="flex items-center space-x-2">
2210
+ <div *ngIf="!showPageLinks" class="flex w-[100px] items-center justify-center text-sm font-medium">
2211
+ Page {{ currentPage }} of {{ totalPages }}
2212
+ </div>
2213
+
2214
+ <div *ngIf="showPageLinks" class="flex items-center space-x-1">
2215
+ <button
2216
+ (click)="previousPage()"
2217
+ [disabled]="currentPage === 1"
2218
+ [class]="navBtnClass"
2219
+ >
2220
+ <i class="ri-arrow-left-s-line"></i>
2221
+ </button>
2222
+
2223
+ <button
2224
+ *ngFor="let page of displayPageIndex"
2225
+ (click)="selectPage(page)"
2226
+ [class]="cn(
2227
+ 'h-8 w-8 text-sm rounded-md flex items-center justify-center transition-colors',
2228
+ currentPage === page
2229
+ ? 'bg-primary text-primary-foreground font-medium'
2230
+ : 'hover:bg-accent hover:text-accent-foreground'
2231
+ )"
2232
+ >
2233
+ {{ page }}
2234
+ </button>
2235
+
2236
+ <button
2237
+ (click)="nextPage()"
2238
+ [disabled]="currentPage === totalPages || totalPages === 0"
2239
+ [class]="navBtnClass"
2240
+ >
2241
+ <i class="ri-arrow-right-s-line"></i>
2242
+ </button>
2243
+ </div>
2244
+ </div>
2245
+ </div>
2246
+ </div>
2247
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SelectComponent$1, selector: "tolle-select", inputs: ["placeholder", "class", "disabled", "searchable", "size"] }, { kind: "component", type: SelectItemComponent$1, selector: "tolle-select-item", inputs: ["value", "class", "selected"] }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2248
+ }
2249
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PaginationComponent, decorators: [{
2250
+ type: Component,
2251
+ args: [{
2252
+ selector: 'tolle-pagination',
2253
+ standalone: true,
2254
+ imports: [CommonModule, SelectComponent$1, SelectItemComponent$1, FormsModule],
2255
+ template: `
2256
+ <div [class]="cn('flex items-center justify-between px-2 py-4', class)">
2257
+
2258
+ <div *ngIf="showCurrentPageInfo" class="text-sm text-muted-foreground">
2259
+ <ng-container *ngIf="currentPageInfoTemplate; else defaultReport">
2260
+ {{ pageReport }}
2261
+ </ng-container>
2262
+ <ng-template #defaultReport>
2263
+ Showing {{ first }} to {{ last }} of {{ totalRecords }} entries
2264
+ </ng-template>
2265
+ </div>
2266
+
2267
+ <div class="flex items-center space-x-6 lg:space-x-8">
2268
+
2269
+ <div *ngIf="showPageOptions" class="flex items-center space-x-2">
2270
+ <p class="text-sm font-medium">Rows per page</p>
2271
+ <tolle-select
2272
+ class="w-[70px]"
2273
+ size="sm"
2274
+ [ngModel]="currentPageSize"
2275
+ (ngModelChange)="sizeChange($event)"
2276
+ >
2277
+ <tolle-select-item *ngFor="let opt of pageSizeOptions" [value]="opt">
2278
+ {{ opt }}
2279
+ </tolle-select-item>
2280
+ </tolle-select>
2281
+ </div>
2282
+
2283
+ <div class="flex items-center space-x-2">
2284
+ <div *ngIf="!showPageLinks" class="flex w-[100px] items-center justify-center text-sm font-medium">
2285
+ Page {{ currentPage }} of {{ totalPages }}
2286
+ </div>
2287
+
2288
+ <div *ngIf="showPageLinks" class="flex items-center space-x-1">
2289
+ <button
2290
+ (click)="previousPage()"
2291
+ [disabled]="currentPage === 1"
2292
+ [class]="navBtnClass"
2293
+ >
2294
+ <i class="ri-arrow-left-s-line"></i>
2295
+ </button>
2296
+
2297
+ <button
2298
+ *ngFor="let page of displayPageIndex"
2299
+ (click)="selectPage(page)"
2300
+ [class]="cn(
2301
+ 'h-8 w-8 text-sm rounded-md flex items-center justify-center transition-colors',
2302
+ currentPage === page
2303
+ ? 'bg-primary text-primary-foreground font-medium'
2304
+ : 'hover:bg-accent hover:text-accent-foreground'
2305
+ )"
2306
+ >
2307
+ {{ page }}
2308
+ </button>
2309
+
2310
+ <button
2311
+ (click)="nextPage()"
2312
+ [disabled]="currentPage === totalPages || totalPages === 0"
2313
+ [class]="navBtnClass"
2314
+ >
2315
+ <i class="ri-arrow-right-s-line"></i>
2316
+ </button>
2317
+ </div>
2318
+ </div>
2319
+ </div>
2320
+ </div>
2321
+ `,
2322
+ changeDetection: ChangeDetectionStrategy.OnPush
2323
+ }]
2324
+ }], propDecorators: { class: [{
2325
+ type: Input
2326
+ }], showPageLinks: [{
2327
+ type: Input
2328
+ }], showPageOptions: [{
2329
+ type: Input
2330
+ }], showCurrentPageInfo: [{
2331
+ type: Input
2332
+ }], currentPageInfoTemplate: [{
2333
+ type: Input
2334
+ }], totalRecords: [{
2335
+ type: Input
2336
+ }], currentPageSize: [{
2337
+ type: Input
2338
+ }], currentPage: [{
2339
+ type: Input
2340
+ }], pageSizeOptions: [{
2341
+ type: Input
2342
+ }], onPageNumberChange: [{
2343
+ type: Output
2344
+ }], onPageSizeChange: [{
2345
+ type: Output
2346
+ }] } });
2347
+
2348
+ class DataTableComponent {
2349
+ data = [];
2350
+ columns = [];
2351
+ searchable = true;
2352
+ paginate = true;
2353
+ pageSize = 10;
2354
+ expandable = false;
2355
+ size = 'default';
2356
+ // Track which rows are open
2357
+ expandedRows = new Set();
2358
+ // Use ContentChildren to grab the tolleCell templates from the user's HTML
2359
+ cellTemplates;
2360
+ // Keep this as an Input for the main expansion slot
2361
+ expandedTemplate;
2362
+ filteredData = [];
2363
+ pagedData = [];
2364
+ searchTerm = '';
2365
+ currentPage = 1;
2366
+ sortKey = '';
2367
+ sortDir = null;
2368
+ // 2. Map Size to Padding for Cells
2369
+ get cellPaddingClass() {
2370
+ switch (this.size) {
2371
+ case 'xs': return 'p-1 px-4'; // Ultra-compact
2372
+ case 'sm': return 'p-2 px-4'; // Dense
2373
+ case 'lg': return 'p-6 px-4'; // Spacious
2374
+ default: return 'p-4'; // Standard (16px)
2375
+ }
2376
+ }
2377
+ // 3. Map Size to Padding for Header (usually slightly shorter than cells)
2378
+ get headerPaddingClass() {
2379
+ switch (this.size) {
2380
+ case 'xs': return 'h-7 px-4';
2381
+ case 'sm': return 'h-9 px-4';
2382
+ case 'lg': return 'h-14 px-4';
2383
+ default: return 'h-12 px-4';
2384
+ }
2385
+ }
2386
+ // 4. Map Size to Font Sizes
2387
+ get fontSizeClass() {
2388
+ switch (this.size) {
2389
+ case 'xs': return 'text-[11px]';
2390
+ case 'sm': return 'text-xs';
2391
+ case 'lg': return 'text-base';
2392
+ default: return 'text-sm';
2393
+ }
2394
+ }
2395
+ cn = cn;
2396
+ ngOnInit() { this.refreshTable(); }
2397
+ ngOnChanges(changes) {
2398
+ if (changes['data']) {
2399
+ this.refreshTable();
2400
+ }
2401
+ }
2402
+ // --- Search & Sort & Page Logic ---
2403
+ // (Your existing implementation of applySearch, applySort, and updatePage goes here)
2404
+ refreshTable() { this.applySearch(); this.applySort(); this.updatePage(); }
2405
+ onSearch() { this.currentPage = 1; this.refreshTable(); }
2406
+ applySearch() {
2407
+ if (!this.searchTerm) {
2408
+ this.filteredData = [...this.data];
2409
+ return;
2410
+ }
2411
+ const q = this.searchTerm.toLowerCase();
2412
+ this.filteredData = this.data.filter(row => Object.values(row).some(val => String(val).toLowerCase().includes(q)));
2413
+ }
2414
+ applySort() {
2415
+ if (!this.sortKey || !this.sortDir)
2416
+ return;
2417
+ this.filteredData.sort((a, b) => {
2418
+ const valA = a[this.sortKey];
2419
+ const valB = b[this.sortKey];
2420
+ if (valA < valB)
2421
+ return this.sortDir === 'asc' ? -1 : 1;
2422
+ if (valA > valB)
2423
+ return this.sortDir === 'asc' ? 1 : -1;
2424
+ return 0;
2425
+ });
2426
+ }
2427
+ updatePage() {
2428
+ if (!this.paginate) {
2429
+ this.pagedData = this.filteredData;
2430
+ return;
2431
+ }
2432
+ const start = (this.currentPage - 1) * this.pageSize;
2433
+ const end = start + this.pageSize;
2434
+ this.pagedData = this.filteredData.slice(start, end);
2435
+ }
2436
+ // --- Helpers ---
2437
+ toggleSort(key) {
2438
+ if (this.sortKey === key) {
2439
+ this.sortDir = this.sortDir === 'asc' ? 'desc' : this.sortDir === 'desc' ? null : 'asc';
2440
+ }
2441
+ else {
2442
+ this.sortKey = key;
2443
+ this.sortDir = 'asc';
2444
+ }
2445
+ this.refreshTable();
2446
+ }
2447
+ getSortIcon(key) {
2448
+ if (this.sortKey !== key || !this.sortDir)
2449
+ return 'ri-arrow-up-down-line opacity-30';
2450
+ return this.sortDir === 'asc' ? 'ri-arrow-up-line' : 'ri-arrow-down-line';
2451
+ }
2452
+ toggleRow(index) {
2453
+ if (this.expandedRows.has(index))
2454
+ this.expandedRows.delete(index);
2455
+ else
2456
+ this.expandedRows.add(index);
2457
+ }
2458
+ // Helper to find the right cell template
2459
+ getTemplate(key) {
2460
+ return this.cellTemplates?.find(t => t.name === key);
2461
+ }
2462
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2463
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DataTableComponent, isStandalone: true, selector: "tolle-data-table", inputs: { data: "data", columns: "columns", searchable: "searchable", paginate: "paginate", pageSize: "pageSize", expandable: "expandable", size: "size", expandedTemplate: "expandedTemplate" }, queries: [{ propertyName: "cellTemplates", predicate: TolleCellDirective$1 }], usesOnChanges: true, ngImport: i0, template: `
2464
+ <div class="space-y-4">
2465
+ <div *ngIf="searchable" class="flex items-center py-2">
2466
+ <tolle-input
2467
+ [size]="size === 'lg' ? 'default' : 'sm'"
2468
+ class="max-w-sm"
2469
+ placeholder="Filter records..."
2470
+ [(ngModel)]="searchTerm"
2471
+ (ngModelChange)="onSearch()">
2472
+ <i prefix class="ri-search-line"></i>
2473
+ </tolle-input>
2474
+ </div>
2475
+
2476
+ <div class="rounded-md border border-border bg-background overflow-hidden shadow-sm">
2477
+ <table class="w-full text-sm">
2478
+ <thead class="border-b bg-muted/30">
2479
+ <tr>
2480
+ <th *ngIf="expandable" [class]="cn('px-4', size === 'xs' ? 'w-[32px]' : 'w-[48px]')"></th>
2481
+ <th *ngFor="let col of columns"
2482
+ [class]="cn(
2483
+ 'font-medium text-muted-foreground transition-all',
2484
+ headerPaddingClass,
2485
+ fontSizeClass,
2486
+ col.class
2487
+ )">
2488
+ <div *ngIf="col.sortable; else simpleHeader" (click)="toggleSort(col.key)" class="flex items-center gap-1 cursor-pointer hover:text-foreground">
2489
+ {{ col.label }}
2490
+ <i [class]="getSortIcon(col.key)"></i>
2491
+ </div>
2492
+ <ng-template #simpleHeader>{{ col.label }}</ng-template>
2493
+ </th>
2494
+ </tr>
2495
+ </thead>
2496
+ <tbody class="divide-y divide-border">
2497
+ <ng-container *ngFor="let row of pagedData; let i = index">
2498
+ <tr class="hover:bg-muted/50 transition-colors">
2499
+ <td *ngIf="expandable" class="px-4">
2500
+ <button (click)="toggleRow(i)"
2501
+ [class]="cn(
2502
+ 'flex items-center justify-center rounded-md hover:bg-accent text-muted-foreground hover:text-foreground',
2503
+ size === 'xs' ? 'h-6 w-6' : 'h-8 w-8'
2504
+ )">
2505
+ <i [class]="expandedRows.has(i) ? 'ri-arrow-down-s-line' : 'ri-arrow-right-s-line'"></i>
2506
+ </button>
2507
+ </td>
2508
+
2509
+ <td *ngFor="let col of columns"
2510
+ [class]="cn(
2511
+ 'align-middle transition-all',
2512
+ cellPaddingClass,
2513
+ fontSizeClass,
2514
+ col.class
2515
+ )">
2516
+ <ng-container *ngIf="getTemplate(col.key) as cell; else defaultValue">
2517
+ <ng-container *ngTemplateOutlet="cell.template; context: { $implicit: row[col.key], row: row }"></ng-container>
2518
+ </ng-container>
2519
+ <ng-template #defaultValue>
2520
+ <span class="text-foreground">{{ row[col.key] }}</span>
2521
+ </ng-template>
2522
+ </td>
2523
+ </tr>
2524
+
2525
+ <tr *ngIf="expandedRows.has(i)" class="bg-muted/10">
2526
+ <td [attr.colspan]="columns.length + (expandable ? 1 : 0)" class="p-0">
2527
+ <div class="p-6 border-b border-dashed border-border">
2528
+ <ng-container *ngIf="expandedTemplate; else defaultExpanded">
2529
+ <ng-container *ngTemplateOutlet="expandedTemplate; context: { row: row }"></ng-container>
2530
+ </ng-container>
2531
+ <ng-template #defaultExpanded>
2532
+ <div class="text-xs text-muted-foreground italic">No details available.</div>
2533
+ </ng-template>
2534
+ </div>
2535
+ </td>
2536
+ </tr>
2537
+ </ng-container>
2538
+ </tbody>
2539
+ </table>
2540
+ </div>
2541
+
2542
+ <tolle-pagination
2543
+ *ngIf="paginate"
2544
+ [totalRecords]="filteredData.length"
2545
+ [currentPage]="currentPage"
2546
+ [currentPageSize]="pageSize"
2547
+ (onPageNumberChange)="updatePage()"
2548
+ (onPageSizeChange)="updatePage()"
2549
+ ></tolle-pagination>
2550
+ </div>
2551
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: PaginationComponent$1, selector: "tolle-pagination", inputs: ["class", "showPageLinks", "showPageOptions", "showCurrentPageInfo", "currentPageInfoTemplate", "totalRecords", "currentPageSize", "currentPage", "pageSizeOptions"], outputs: ["onPageNumberChange", "onPageSizeChange"] }, { kind: "component", type: InputComponent$1, selector: "tolle-input", inputs: ["type", "placeholder", "disabled", "error", "size", "containerClass", "class"] }] });
2552
+ }
2553
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataTableComponent, decorators: [{
2554
+ type: Component,
2555
+ args: [{
2556
+ selector: 'tolle-data-table',
2557
+ standalone: true,
2558
+ imports: [CommonModule, FormsModule, PaginationComponent$1, InputComponent$1],
2559
+ template: `
2560
+ <div class="space-y-4">
2561
+ <div *ngIf="searchable" class="flex items-center py-2">
2562
+ <tolle-input
2563
+ [size]="size === 'lg' ? 'default' : 'sm'"
2564
+ class="max-w-sm"
2565
+ placeholder="Filter records..."
2566
+ [(ngModel)]="searchTerm"
2567
+ (ngModelChange)="onSearch()">
2568
+ <i prefix class="ri-search-line"></i>
2569
+ </tolle-input>
2570
+ </div>
2571
+
2572
+ <div class="rounded-md border border-border bg-background overflow-hidden shadow-sm">
2573
+ <table class="w-full text-sm">
2574
+ <thead class="border-b bg-muted/30">
2575
+ <tr>
2576
+ <th *ngIf="expandable" [class]="cn('px-4', size === 'xs' ? 'w-[32px]' : 'w-[48px]')"></th>
2577
+ <th *ngFor="let col of columns"
2578
+ [class]="cn(
2579
+ 'font-medium text-muted-foreground transition-all',
2580
+ headerPaddingClass,
2581
+ fontSizeClass,
2582
+ col.class
2583
+ )">
2584
+ <div *ngIf="col.sortable; else simpleHeader" (click)="toggleSort(col.key)" class="flex items-center gap-1 cursor-pointer hover:text-foreground">
2585
+ {{ col.label }}
2586
+ <i [class]="getSortIcon(col.key)"></i>
2587
+ </div>
2588
+ <ng-template #simpleHeader>{{ col.label }}</ng-template>
2589
+ </th>
2590
+ </tr>
2591
+ </thead>
2592
+ <tbody class="divide-y divide-border">
2593
+ <ng-container *ngFor="let row of pagedData; let i = index">
2594
+ <tr class="hover:bg-muted/50 transition-colors">
2595
+ <td *ngIf="expandable" class="px-4">
2596
+ <button (click)="toggleRow(i)"
2597
+ [class]="cn(
2598
+ 'flex items-center justify-center rounded-md hover:bg-accent text-muted-foreground hover:text-foreground',
2599
+ size === 'xs' ? 'h-6 w-6' : 'h-8 w-8'
2600
+ )">
2601
+ <i [class]="expandedRows.has(i) ? 'ri-arrow-down-s-line' : 'ri-arrow-right-s-line'"></i>
2602
+ </button>
2603
+ </td>
2604
+
2605
+ <td *ngFor="let col of columns"
2606
+ [class]="cn(
2607
+ 'align-middle transition-all',
2608
+ cellPaddingClass,
2609
+ fontSizeClass,
2610
+ col.class
2611
+ )">
2612
+ <ng-container *ngIf="getTemplate(col.key) as cell; else defaultValue">
2613
+ <ng-container *ngTemplateOutlet="cell.template; context: { $implicit: row[col.key], row: row }"></ng-container>
2614
+ </ng-container>
2615
+ <ng-template #defaultValue>
2616
+ <span class="text-foreground">{{ row[col.key] }}</span>
2617
+ </ng-template>
2618
+ </td>
2619
+ </tr>
2620
+
2621
+ <tr *ngIf="expandedRows.has(i)" class="bg-muted/10">
2622
+ <td [attr.colspan]="columns.length + (expandable ? 1 : 0)" class="p-0">
2623
+ <div class="p-6 border-b border-dashed border-border">
2624
+ <ng-container *ngIf="expandedTemplate; else defaultExpanded">
2625
+ <ng-container *ngTemplateOutlet="expandedTemplate; context: { row: row }"></ng-container>
2626
+ </ng-container>
2627
+ <ng-template #defaultExpanded>
2628
+ <div class="text-xs text-muted-foreground italic">No details available.</div>
2629
+ </ng-template>
2630
+ </div>
2631
+ </td>
2632
+ </tr>
2633
+ </ng-container>
2634
+ </tbody>
2635
+ </table>
2636
+ </div>
2637
+
2638
+ <tolle-pagination
2639
+ *ngIf="paginate"
2640
+ [totalRecords]="filteredData.length"
2641
+ [currentPage]="currentPage"
2642
+ [currentPageSize]="pageSize"
2643
+ (onPageNumberChange)="updatePage()"
2644
+ (onPageSizeChange)="updatePage()"
2645
+ ></tolle-pagination>
2646
+ </div>
2647
+ `
2648
+ }]
2649
+ }], propDecorators: { data: [{
2650
+ type: Input
2651
+ }], columns: [{
2652
+ type: Input
2653
+ }], searchable: [{
2654
+ type: Input
2655
+ }], paginate: [{
2656
+ type: Input
2657
+ }], pageSize: [{
2658
+ type: Input
2659
+ }], expandable: [{
2660
+ type: Input
2661
+ }], size: [{
2662
+ type: Input
2663
+ }], cellTemplates: [{
2664
+ type: ContentChildren,
2665
+ args: [TolleCellDirective$1]
2666
+ }], expandedTemplate: [{
2667
+ type: Input
2668
+ }] } });
2669
+
2670
+ class TolleCellDirective {
2671
+ template;
2672
+ name; // The column key this template belongs to
2673
+ constructor(template) {
2674
+ this.template = template;
2675
+ }
2676
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TolleCellDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
2677
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: TolleCellDirective, isStandalone: true, selector: "[tolleCell]", inputs: { name: ["tolleCell", "name"] }, ngImport: i0 });
2678
+ }
2679
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TolleCellDirective, decorators: [{
2680
+ type: Directive,
2681
+ args: [{
2682
+ selector: '[tolleCell]',
2683
+ standalone: true
2684
+ }]
2685
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { name: [{
2686
+ type: Input,
2687
+ args: ['tolleCell']
2688
+ }] } });
2689
+
2690
+ class AccordionItemComponent {
2691
+ title = '';
2692
+ class = '';
2693
+ id;
2694
+ isOpen = false;
2695
+ // This will be set by the parent Accordion component
2696
+ onToggle;
2697
+ toggle() {
2698
+ if (this.onToggle) {
2699
+ this.onToggle(this.id);
2700
+ }
2701
+ else {
2702
+ this.isOpen = !this.isOpen;
2703
+ }
2704
+ }
2705
+ cn = cn;
2706
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccordionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2707
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AccordionItemComponent, isStandalone: true, selector: "tolle-accordion-item", inputs: { title: "title", class: "class", id: "id" }, ngImport: i0, template: `
2708
+ <div [class]="cn('flex flex-col border-b border-border', class)">
2709
+ <button
2710
+ type="button"
2711
+ (click)="toggle()"
2712
+ class="flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>i]:rotate-180"
2713
+ [attr.data-state]="isOpen ? 'open' : 'closed'"
2714
+ >
2715
+ <span class="text-left">{{ title }}</span>
2716
+ <i class="ri-arrow-down-s-line text-muted-foreground transition-transform"></i>
2717
+ </button>
2718
+
2719
+ <div
2720
+ *ngIf="isOpen"
2721
+ class="pb-4 text-sm text-muted-foreground overflow-hidden"
2722
+ >
2723
+ <ng-content></ng-content>
2724
+ </div>
2725
+ </div>
2726
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2727
+ }
2728
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccordionItemComponent, decorators: [{
2729
+ type: Component,
2730
+ args: [{
2731
+ selector: 'tolle-accordion-item',
2732
+ standalone: true,
2733
+ imports: [CommonModule],
2734
+ template: `
2735
+ <div [class]="cn('flex flex-col border-b border-border', class)">
2736
+ <button
2737
+ type="button"
2738
+ (click)="toggle()"
2739
+ class="flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>i]:rotate-180"
2740
+ [attr.data-state]="isOpen ? 'open' : 'closed'"
2741
+ >
2742
+ <span class="text-left">{{ title }}</span>
2743
+ <i class="ri-arrow-down-s-line text-muted-foreground transition-transform"></i>
2744
+ </button>
2745
+
2746
+ <div
2747
+ *ngIf="isOpen"
2748
+ class="pb-4 text-sm text-muted-foreground overflow-hidden"
2749
+ >
2750
+ <ng-content></ng-content>
2751
+ </div>
2752
+ </div>
2753
+ `
2754
+ }]
2755
+ }], propDecorators: { title: [{
2756
+ type: Input
2757
+ }], class: [{
2758
+ type: Input
2759
+ }], id: [{
2760
+ type: Input
2761
+ }] } });
2762
+
2763
+ class AccordionComponent {
2764
+ type = 'single';
2765
+ class = '';
2766
+ items;
2767
+ ngAfterContentInit() {
2768
+ this.items.forEach((item, index) => {
2769
+ // Assign unique ID if none provided
2770
+ if (item.id === undefined)
2771
+ item.id = index;
2772
+ // Hook into the item's toggle event
2773
+ item.onToggle = (id) => this.handleToggle(id);
2774
+ });
2775
+ }
2776
+ handleToggle(selectedId) {
2777
+ this.items.forEach(item => {
2778
+ if (item.id === selectedId) {
2779
+ item.isOpen = !item.isOpen;
2780
+ }
2781
+ else {
2782
+ // If type is 'single', close all other items
2783
+ if (this.type === 'single') {
2784
+ item.isOpen = false;
2785
+ }
2786
+ }
2787
+ });
2788
+ }
2789
+ cn = cn;
2790
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2791
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AccordionComponent, isStandalone: true, selector: "tolle-accordion", inputs: { type: "type", class: "class" }, queries: [{ propertyName: "items", predicate: AccordionItemComponent }], ngImport: i0, template: `
2792
+ <div [class]="cn('w-full border-t border-border', class)">
2793
+ <ng-content></ng-content>
2794
+ </div>
2795
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
2796
+ }
2797
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccordionComponent, decorators: [{
2798
+ type: Component,
2799
+ args: [{
2800
+ selector: 'tolle-accordion',
2801
+ standalone: true,
2802
+ imports: [CommonModule],
2803
+ template: `
2804
+ <div [class]="cn('w-full border-t border-border', class)">
2805
+ <ng-content></ng-content>
2806
+ </div>
2807
+ `
2808
+ }]
2809
+ }], propDecorators: { type: [{
2810
+ type: Input
2811
+ }], class: [{
2812
+ type: Input
2813
+ }], items: [{
2814
+ type: ContentChildren,
2815
+ args: [AccordionItemComponent]
2816
+ }] } });
2817
+
2818
+ class ModalRef {
2819
+ overlay;
2820
+ modal;
2821
+ stack;
2822
+ _afterClosed$ = new Subject();
2823
+ afterClosed$ = this._afterClosed$.asObservable();
2824
+ constructor(overlay, modal, stack) {
2825
+ this.overlay = overlay;
2826
+ this.modal = modal;
2827
+ this.stack = stack;
2828
+ this.stack.register(this);
2829
+ // Handle Backdrop Click
2830
+ this.overlay.backdropClick().subscribe(() => {
2831
+ if (this.modal.backdropClose) {
2832
+ this.close();
2833
+ }
2834
+ });
2835
+ }
2836
+ /**
2837
+ * Closes the modal instantly.
2838
+ * @param result Data to pass back to the caller
2839
+ */
2840
+ close(result) {
2841
+ this._afterClosed$.next(result);
2842
+ this._afterClosed$.complete();
2843
+ this.overlay.dispose(); // Instant disposal (No animation timer)
2844
+ this.stack.unregister(this);
2845
+ }
2846
+ }
2847
+
2848
+ class ModalComponent {
2849
+ ref;
2850
+ contentType = 'string';
2851
+ content;
2852
+ modalClasses = '';
2853
+ constructor(ref) {
2854
+ this.ref = ref;
2855
+ }
2856
+ ngOnInit() {
2857
+ this.content = this.ref.modal.content;
2858
+ this.modalClasses = this.getModalSizeCss();
2859
+ this.determineContentType();
2860
+ }
2861
+ getModalSizeCss() {
2862
+ const { size } = this.ref.modal;
2863
+ return cn(
2864
+ // Base classes: Added 'w-full' and 'mx-auto'
2865
+ 'bg-background border border-border shadow-lg relative flex flex-col w-full mx-auto overflow-hidden', size === 'fullscreen' ? 'h-screen w-screen rounded-none' : 'rounded-lg',
2866
+ // Sizing scale with explicit max-widths
2867
+ size === 'xs' && 'max-w-[320px]', size === 'sm' && 'max-w-[425px]', size === 'default' && 'max-w-[544px]', size === 'lg' && 'max-w-[1024px]');
2868
+ }
2869
+ determineContentType() {
2870
+ if (typeof this.content === 'string')
2871
+ this.contentType = 'string';
2872
+ else if (this.content instanceof TemplateRef)
2873
+ this.contentType = 'template';
2874
+ else
2875
+ this.contentType = 'component';
2876
+ }
2877
+ get asTemplate() { return this.content; }
2878
+ get asComponent() { return this.content; }
2879
+ cn = cn;
2880
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalComponent, deps: [{ token: ModalRef }], target: i0.ɵɵFactoryTarget.Component });
2881
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ModalComponent, isStandalone: true, selector: "tolle-modal", ngImport: i0, template: `
2882
+ <div [class]="modalClasses" class="pointer-events-auto" (mousedown)="$event.stopPropagation()">
2883
+
2884
+ <div class="flex items-center justify-between p-6 border-b border-border">
2885
+ <h3 *ngIf="ref.modal.title" class="text-lg font-semibold text-foreground tracking-tight">
2886
+ {{ ref.modal.title }}
2887
+ </h3>
2888
+ <button (click)="ref.close()" class="ml-auto p-1 text-muted-foreground hover:text-foreground hover:bg-accent rounded-md transition-colors">
2889
+ <i class="ri-close-line text-2xl"></i>
2890
+ </button>
2891
+ </div>
2892
+
2893
+ <div class="p-6 overflow-y-auto max-h-[80vh] text-foreground">
2894
+ <ng-container [ngSwitch]="contentType">
2895
+ <div *ngSwitchCase="'string'">{{ content }}</div>
2896
+
2897
+ <ng-container *ngSwitchCase="'template'">
2898
+ <ng-container *ngTemplateOutlet="asTemplate; context: ref.modal.context"></ng-container>
2899
+ </ng-container>
2900
+
2901
+ <ng-container *ngSwitchCase="'component'">
2902
+ <ng-container *ngComponentOutlet="asComponent"></ng-container>
2903
+ </ng-container>
2904
+ </ng-container>
2905
+ </div>
2906
+ </div>
2907
+ `, isInline: true, styles: [":host{display:contents}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }] });
2908
+ }
2909
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalComponent, decorators: [{
2910
+ type: Component,
2911
+ args: [{ selector: 'tolle-modal', standalone: true, imports: [CommonModule], template: `
2912
+ <div [class]="modalClasses" class="pointer-events-auto" (mousedown)="$event.stopPropagation()">
2913
+
2914
+ <div class="flex items-center justify-between p-6 border-b border-border">
2915
+ <h3 *ngIf="ref.modal.title" class="text-lg font-semibold text-foreground tracking-tight">
2916
+ {{ ref.modal.title }}
2917
+ </h3>
2918
+ <button (click)="ref.close()" class="ml-auto p-1 text-muted-foreground hover:text-foreground hover:bg-accent rounded-md transition-colors">
2919
+ <i class="ri-close-line text-2xl"></i>
2920
+ </button>
2921
+ </div>
2922
+
2923
+ <div class="p-6 overflow-y-auto max-h-[80vh] text-foreground">
2924
+ <ng-container [ngSwitch]="contentType">
2925
+ <div *ngSwitchCase="'string'">{{ content }}</div>
2926
+
2927
+ <ng-container *ngSwitchCase="'template'">
2928
+ <ng-container *ngTemplateOutlet="asTemplate; context: ref.modal.context"></ng-container>
2929
+ </ng-container>
2930
+
2931
+ <ng-container *ngSwitchCase="'component'">
2932
+ <ng-container *ngComponentOutlet="asComponent"></ng-container>
2933
+ </ng-container>
2934
+ </ng-container>
2935
+ </div>
2936
+ </div>
2937
+ `, styles: [":host{display:contents}\n"] }]
2938
+ }], ctorParameters: () => [{ type: ModalRef }] });
2939
+
2940
+ class ModalStackService {
2941
+ _stack = new Set();
2942
+ register(ref) {
2943
+ this._stack.add(ref);
2944
+ }
2945
+ unregister(ref) {
2946
+ this._stack.delete(ref);
2947
+ }
2948
+ /** Instantly closes all open modals */
2949
+ closeAll() {
2950
+ this._stack.forEach(ref => ref.close());
2951
+ this._stack.clear();
2952
+ }
2953
+ get activeCount() {
2954
+ return this._stack.size;
2955
+ }
2956
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalStackService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2957
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalStackService, providedIn: 'root' });
2958
+ }
2959
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalStackService, decorators: [{
2960
+ type: Injectable,
2961
+ args: [{ providedIn: 'root' }]
2962
+ }] });
2963
+
2964
+ class ModalService {
2965
+ overlay;
2966
+ injector;
2967
+ stack;
2968
+ constructor(overlay, injector, stack) {
2969
+ this.overlay = overlay;
2970
+ this.injector = injector;
2971
+ this.stack = stack;
2972
+ }
2973
+ open(config) {
2974
+ // 1. Create the Overlay (DOM placeholder)
2975
+ const overlayRef = this.createOverlay();
2976
+ // 2. Create the Controller (Ref)
2977
+ const modalRef = new ModalRef(overlayRef, config, this.stack);
2978
+ // 3. Create Injector to allow the Component to access the Ref
2979
+ const injector = Injector.create({
2980
+ parent: this.injector,
2981
+ providers: [{ provide: ModalRef, useValue: modalRef }]
2982
+ });
2983
+ // 4. Attach the UI Component to the Overlay
2984
+ const portal = new ComponentPortal(ModalComponent, null, injector);
2985
+ overlayRef.attach(portal);
2986
+ return modalRef;
2987
+ }
2988
+ /** Global helper to close everything */
2989
+ closeAll() {
2990
+ this.stack.closeAll();
2991
+ }
2992
+ // modal.service.ts
2993
+ createOverlay() {
2994
+ const config = new OverlayConfig({
2995
+ hasBackdrop: true,
2996
+ backdropClass: ['cdk-overlay-backdrop', 'bg-black/80', 'backdrop-blur-sm'],
2997
+ panelClass: [
2998
+ 'w-full',
2999
+ 'h-full',
3000
+ 'flex',
3001
+ 'items-center',
3002
+ 'justify-center',
3003
+ 'pointer-events-none'
3004
+ ],
3005
+ scrollStrategy: this.overlay.scrollStrategies.block(),
3006
+ positionStrategy: this.overlay.position()
3007
+ .global()
3008
+ .centerHorizontally()
3009
+ .centerVertically()
3010
+ });
3011
+ return this.overlay.create(config);
3012
+ }
3013
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalService, deps: [{ token: i1$2.Overlay }, { token: i0.Injector }, { token: ModalStackService }], target: i0.ɵɵFactoryTarget.Injectable });
3014
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalService, providedIn: 'root' });
3015
+ }
3016
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ModalService, decorators: [{
3017
+ type: Injectable,
3018
+ args: [{ providedIn: 'root' }]
3019
+ }], ctorParameters: () => [{ type: i1$2.Overlay }, { type: i0.Injector }, { type: ModalStackService }] });
3020
+
3021
+ class Modal {
3022
+ /** The content to display (String, Component, or Template) */
3023
+ content;
3024
+ /** Optional title for the standard header */
3025
+ title;
3026
+ /** * Predefined size scale.
3027
+ * - xs: 320px (Mobile alerts)
3028
+ * - sm: 425px (Standard dialogs)
3029
+ * - default: 544px (Forms)
3030
+ * - lg: 90% / 1024px (Data tables)
3031
+ * - fullscreen: 100vw/100vh (Complex workflows)
3032
+ */
3033
+ size = 'default';
3034
+ /** * If true (default), clicking the backdrop closes the modal.
3035
+ * Set to false for "blocking" modals (e.g., Terms of Service).
3036
+ */
3037
+ backdropClose = true;
3038
+ /** * Data to pass to a Component content.
3039
+ * Accessed via @Input() in the child component.
3040
+ */
3041
+ data;
3042
+ /** * Context to pass to a TemplateRef content.
3043
+ * Accessed via `let-val` in the HTML.
3044
+ */
3045
+ context;
3046
+ }
3047
+
3048
+ class ButtonGroupComponent {
3049
+ class = '';
3050
+ cn = cn;
3051
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3052
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ButtonGroupComponent, isStandalone: true, selector: "tolle-button-group", inputs: { class: "class" }, ngImport: i0, template: `
3053
+ <div [class]="cn('inline-flex items-center -space-x-px rounded-md shadow-sm', class)">
3054
+ <ng-content></ng-content>
3055
+ </div>
3056
+ `, isInline: true, styles: [":host{display:inline-block}:host ::ng-deep tolle-button:first-child button{@apply rounded-r-none;}:host ::ng-deep tolle-button:not(:first-child):not(:last-child) button{@apply rounded-none border-l-0;}:host ::ng-deep tolle-button:last-child:not(:first-child) button{@apply rounded-l-none border-l-0;}:host ::ng-deep tolle-button button:hover,:host ::ng-deep tolle-button button:focus{@apply z-10 relative;}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
3057
+ }
3058
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonGroupComponent, decorators: [{
3059
+ type: Component,
3060
+ args: [{ selector: 'tolle-button-group', standalone: true, imports: [CommonModule], template: `
3061
+ <div [class]="cn('inline-flex items-center -space-x-px rounded-md shadow-sm', class)">
3062
+ <ng-content></ng-content>
3063
+ </div>
3064
+ `, styles: [":host{display:inline-block}:host ::ng-deep tolle-button:first-child button{@apply rounded-r-none;}:host ::ng-deep tolle-button:not(:first-child):not(:last-child) button{@apply rounded-none border-l-0;}:host ::ng-deep tolle-button:last-child:not(:first-child) button{@apply rounded-l-none border-l-0;}:host ::ng-deep tolle-button button:hover,:host ::ng-deep tolle-button button:focus{@apply z-10 relative;}\n"] }]
3065
+ }], propDecorators: { class: [{
3066
+ type: Input
3067
+ }] } });
3068
+
3069
+ class RangeCalendarComponent {
3070
+ class = '';
3071
+ disablePastDates = false;
3072
+ rangeSelect = new EventEmitter(); // Emits whenever selection changes
3073
+ currentView = 'date';
3074
+ viewDate = new Date();
3075
+ // The Range Value
3076
+ value = { start: null, end: null };
3077
+ weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
3078
+ daysInMonth = [];
3079
+ months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
3080
+ years = [];
3081
+ 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');
3082
+ onTouched = () => { };
3083
+ onChange = () => { };
3084
+ cn = cn;
3085
+ ngOnInit() {
3086
+ this.generateDays();
3087
+ this.generateYears();
3088
+ }
3089
+ // --- Date Generation Logic (Same as Calendar) ---
3090
+ generateDays() {
3091
+ const start = startOfWeek(startOfMonth(this.viewDate));
3092
+ const end = endOfWeek(endOfMonth(this.viewDate));
3093
+ this.daysInMonth = eachDayOfInterval({ start, end });
3094
+ }
3095
+ generateYears() {
3096
+ const currentYear = this.viewDate.getFullYear();
3097
+ this.years = Array.from({ length: 16 }, (_, i) => currentYear - 6 + i);
3098
+ }
3099
+ setView(view) {
3100
+ this.currentView = view;
3101
+ if (view === 'year')
3102
+ this.generateYears();
3103
+ }
3104
+ prev() {
3105
+ if (this.currentView === 'date') {
3106
+ this.viewDate = subMonths(this.viewDate, 1);
3107
+ this.generateDays();
3108
+ }
3109
+ else if (this.currentView === 'year') {
3110
+ this.viewDate = subYears(this.viewDate, 16);
3111
+ this.generateYears();
3112
+ }
3113
+ else if (this.currentView === 'month') {
3114
+ this.viewDate = subYears(this.viewDate, 1);
3115
+ }
3116
+ }
3117
+ next() {
3118
+ if (this.currentView === 'date') {
3119
+ this.viewDate = addMonths(this.viewDate, 1);
3120
+ this.generateDays();
3121
+ }
3122
+ else if (this.currentView === 'year') {
3123
+ this.viewDate = addYears(this.viewDate, 16);
3124
+ this.generateYears();
3125
+ }
3126
+ else if (this.currentView === 'month') {
3127
+ this.viewDate = addYears(this.viewDate, 1);
3128
+ }
3129
+ }
3130
+ // --- Range Selection Logic ---
3131
+ selectDate(date) {
3132
+ if (this.isDateDisabled(date))
3133
+ return;
3134
+ const { start, end } = this.value;
3135
+ // 1. If start exists but end doesn't
3136
+ if (start && !end) {
3137
+ if (isBefore(date, start)) {
3138
+ // User clicked earlier date -> Reset start
3139
+ this.value = { start: date, end: null };
3140
+ }
3141
+ else {
3142
+ // User clicked later date -> Complete range
3143
+ this.value = { start, end: date };
3144
+ }
3145
+ }
3146
+ // 2. If neither exist OR both exist (reset)
3147
+ else {
3148
+ this.value = { start: date, end: null };
3149
+ }
3150
+ if (!isSameMonth(date, this.viewDate)) {
3151
+ this.viewDate = date;
3152
+ this.generateDays();
3153
+ }
3154
+ this.onChange(this.value);
3155
+ this.rangeSelect.emit(this.value);
3156
+ this.onTouched();
3157
+ }
3158
+ selectMonth(monthIndex) {
3159
+ this.viewDate = setMonth(this.viewDate, monthIndex);
3160
+ this.currentView = 'date';
3161
+ this.generateDays();
3162
+ }
3163
+ selectYear(year) {
3164
+ this.viewDate = setYear(this.viewDate, year);
3165
+ this.currentView = 'date';
3166
+ this.generateDays();
3167
+ }
3168
+ // --- Visual Styling for Range ---
3169
+ getDayClass(date) {
3170
+ const { start, end } = this.value;
3171
+ const isOutside = !isSameMonth(date, this.viewDate);
3172
+ const isDisabled = this.isDateDisabled(date);
3173
+ // Range Checks
3174
+ const isStart = start && isSameDay(date, start);
3175
+ const isEnd = end && isSameDay(date, end);
3176
+ const isInside = start && end && isWithinInterval(date, { start, end });
3177
+ const isTodayDate = isToday(date);
3178
+ return cn(
3179
+ // Base: h-9 w-9, but we remove margins/rounding for the 'strip' effect
3180
+ 'h-9 w-9 p-0 font-normal text-sm transition-all flex items-center justify-center relative z-10',
3181
+ // Default State (Not selected, Not disabled)
3182
+ !isInside && !isStart && !isEnd && !isDisabled && 'hover:bg-accent hover:text-accent-foreground rounded-md',
3183
+ // The "Caps": Start and End
3184
+ (isStart || isEnd) && 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
3185
+ // The "Strip": Dates between start and end
3186
+ isInside && !isStart && !isEnd && 'bg-accent text-accent-foreground rounded-none',
3187
+ // Connecting the Caps to the Strip (Rectangular inside edges)
3188
+ isStart && end && 'rounded-l-md rounded-r-none', isEnd && start && 'rounded-r-md rounded-l-none',
3189
+ // If no end date yet, Start should be fully rounded
3190
+ isStart && !end && 'rounded-md',
3191
+ // Muted/Disabled logic
3192
+ !isInside && isTodayDate && !isStart && !isEnd && 'bg-accent/50 text-accent-foreground rounded-md', (isOutside || isDisabled) && 'text-muted-foreground opacity-50', isDisabled && 'cursor-not-allowed');
3193
+ }
3194
+ isDateDisabled(date) {
3195
+ return this.disablePastDates ? isBefore(date, startOfDay(new Date())) : false;
3196
+ }
3197
+ // --- CVA Implementation ---
3198
+ writeValue(val) {
3199
+ if (val) {
3200
+ this.value = val;
3201
+ if (val.start)
3202
+ this.viewDate = val.start;
3203
+ this.generateDays();
3204
+ this.generateYears();
3205
+ }
3206
+ }
3207
+ registerOnChange(fn) { this.onChange = fn; }
3208
+ registerOnTouched(fn) { this.onTouched = fn; }
3209
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RangeCalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3210
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: RangeCalendarComponent, isStandalone: true, selector: "tolle-range-calendar", inputs: { class: "class", disablePastDates: "disablePastDates" }, outputs: { rangeSelect: "rangeSelect" }, providers: [
3211
+ {
3212
+ provide: NG_VALUE_ACCESSOR,
3213
+ useExisting: forwardRef(() => RangeCalendarComponent),
3214
+ multi: true
3215
+ }
3216
+ ], ngImport: i0, template: `
3217
+ <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block min-w-fit', class)">
3218
+
3219
+ <div class="flex items-center justify-between pt-1 pb-4 gap-2">
3220
+ <div class="flex items-center gap-1">
3221
+ <button type="button" (click)="setView('month')"
3222
+ [class]="cn('text-sm font-semibold px-2 py-1 rounded transition-colors', currentView === 'month' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground')">
3223
+ {{ viewDate | date: 'MMMM' }}
3224
+ </button>
3225
+ <button type="button" (click)="setView('year')"
3226
+ [class]="cn('text-sm font-semibold px-2 py-1 rounded transition-colors', currentView === 'year' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground')">
3227
+ {{ viewDate | date: 'yyyy' }}
3228
+ </button>
3229
+ </div>
3230
+ <div class="flex items-center space-x-1">
3231
+ <button type="button" (click)="prev()" [class]="navBtnClass"><i class="ri-arrow-left-s-line text-lg"></i></button>
3232
+ <button type="button" (click)="next()" [class]="navBtnClass"><i class="ri-arrow-right-s-line text-lg"></i></button>
3233
+ </div>
3234
+ </div>
3235
+
3236
+ <div *ngIf="currentView === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
3237
+ <div class="grid grid-cols-7 gap-y-1 w-full">
3238
+ <span *ngFor="let day of weekDays" class="text-[0.8rem] text-muted-foreground font-normal text-center w-9">
3239
+ {{ day }}
3240
+ </span>
3241
+ </div>
3242
+ <div class="grid grid-cols-7 gap-y-1 w-full">
3243
+ <button
3244
+ *ngFor="let date of daysInMonth"
3245
+ type="button"
3246
+ (click)="selectDate(date)"
3247
+ [disabled]="isDateDisabled(date)"
3248
+ [class]="getDayClass(date)"
3249
+ >
3250
+ {{ date | date: 'd' }}
3251
+ </button>
3252
+ </div>
3253
+ </div>
3254
+
3255
+ <div *ngIf="currentView === 'month'" class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
3256
+ <button *ngFor="let month of months; let i = index" type="button" (click)="selectMonth(i)"
3257
+ [class]="cn('text-sm py-2.5 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors', i === viewDate.getMonth() ? 'bg-primary text-primary-foreground' : '')">
3258
+ {{ month }}
3259
+ </button>
3260
+ </div>
3261
+
3262
+ <div *ngIf="currentView === 'year'" class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
3263
+ <button *ngFor="let year of years" type="button" (click)="selectYear(year)"
3264
+ [class]="cn('text-sm py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors', year === viewDate.getFullYear() ? 'bg-primary text-primary-foreground' : '')">
3265
+ {{ year }}
3266
+ </button>
3267
+ </div>
3268
+ </div>
3269
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
3270
+ }
3271
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RangeCalendarComponent, decorators: [{
3272
+ type: Component,
3273
+ args: [{
3274
+ selector: 'tolle-range-calendar',
3275
+ standalone: true,
3276
+ imports: [CommonModule],
3277
+ providers: [
3278
+ {
3279
+ provide: NG_VALUE_ACCESSOR,
3280
+ useExisting: forwardRef(() => RangeCalendarComponent),
3281
+ multi: true
3282
+ }
3283
+ ],
3284
+ template: `
3285
+ <div [class]="cn('p-3 border rounded-md bg-background text-popover-foreground shadow-sm inline-block min-w-fit', class)">
3286
+
3287
+ <div class="flex items-center justify-between pt-1 pb-4 gap-2">
3288
+ <div class="flex items-center gap-1">
3289
+ <button type="button" (click)="setView('month')"
3290
+ [class]="cn('text-sm font-semibold px-2 py-1 rounded transition-colors', currentView === 'month' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground')">
3291
+ {{ viewDate | date: 'MMMM' }}
3292
+ </button>
3293
+ <button type="button" (click)="setView('year')"
3294
+ [class]="cn('text-sm font-semibold px-2 py-1 rounded transition-colors', currentView === 'year' ? 'bg-secondary text-secondary-foreground' : 'hover:bg-accent hover:text-accent-foreground')">
3295
+ {{ viewDate | date: 'yyyy' }}
3296
+ </button>
3297
+ </div>
3298
+ <div class="flex items-center space-x-1">
3299
+ <button type="button" (click)="prev()" [class]="navBtnClass"><i class="ri-arrow-left-s-line text-lg"></i></button>
3300
+ <button type="button" (click)="next()" [class]="navBtnClass"><i class="ri-arrow-right-s-line text-lg"></i></button>
3301
+ </div>
3302
+ </div>
3303
+
3304
+ <div *ngIf="currentView === 'date'" class="space-y-2 animate-in fade-in zoom-in-95 duration-200">
3305
+ <div class="grid grid-cols-7 gap-y-1 w-full">
3306
+ <span *ngFor="let day of weekDays" class="text-[0.8rem] text-muted-foreground font-normal text-center w-9">
3307
+ {{ day }}
3308
+ </span>
3309
+ </div>
3310
+ <div class="grid grid-cols-7 gap-y-1 w-full">
3311
+ <button
3312
+ *ngFor="let date of daysInMonth"
3313
+ type="button"
3314
+ (click)="selectDate(date)"
3315
+ [disabled]="isDateDisabled(date)"
3316
+ [class]="getDayClass(date)"
3317
+ >
3318
+ {{ date | date: 'd' }}
3319
+ </button>
3320
+ </div>
3321
+ </div>
3322
+
3323
+ <div *ngIf="currentView === 'month'" class="grid grid-cols-3 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
3324
+ <button *ngFor="let month of months; let i = index" type="button" (click)="selectMonth(i)"
3325
+ [class]="cn('text-sm py-2.5 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors', i === viewDate.getMonth() ? 'bg-primary text-primary-foreground' : '')">
3326
+ {{ month }}
3327
+ </button>
3328
+ </div>
3329
+
3330
+ <div *ngIf="currentView === 'year'" class="grid grid-cols-4 gap-2 w-64 animate-in fade-in zoom-in-95 duration-200">
3331
+ <button *ngFor="let year of years" type="button" (click)="selectYear(year)"
3332
+ [class]="cn('text-sm py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors', year === viewDate.getFullYear() ? 'bg-primary text-primary-foreground' : '')">
3333
+ {{ year }}
3334
+ </button>
3335
+ </div>
3336
+ </div>
3337
+ `
3338
+ }]
3339
+ }], propDecorators: { class: [{
3340
+ type: Input
3341
+ }], disablePastDates: [{
3342
+ type: Input
3343
+ }], rangeSelect: [{
3344
+ type: Output
3345
+ }] } });
3346
+
3347
+ class DateRangePickerComponent {
3348
+ cdr;
3349
+ disabled = false;
3350
+ placeholder = 'Pick a date range';
3351
+ class = '';
3352
+ disablePastDates = false;
3353
+ // Standardized Sizes
3354
+ size = 'default';
3355
+ triggerContainer;
3356
+ popover;
3357
+ value = { start: null, end: null };
3358
+ isOpen = false;
3359
+ cleanupAutoUpdate;
3360
+ constructor(cdr) {
3361
+ this.cdr = cdr;
3362
+ }
3363
+ get displayValue() {
3364
+ if (!this.value.start)
3365
+ return '';
3366
+ const startStr = format(this.value.start, 'MMM dd, yyyy'); // Using date-fns format
3367
+ if (!this.value.end)
3368
+ return startStr;
3369
+ const endStr = format(this.value.end, 'MMM dd, yyyy');
3370
+ return `${startStr} - ${endStr}`;
3371
+ }
3372
+ onCalendarSelect(range) {
3373
+ this.value = range;
3374
+ this.onChange(this.value);
3375
+ // Close only if range is complete
3376
+ if (range.start && range.end) {
3377
+ this.onChange(this.value);
3378
+ // Small delay for UX
3379
+ setTimeout(() => this.close(), 150);
3380
+ }
3381
+ }
3382
+ togglePopover(event) {
3383
+ if (this.disabled)
3384
+ return;
3385
+ this.isOpen ? this.close() : this.open();
3386
+ }
3387
+ open() {
3388
+ this.isOpen = true;
3389
+ setTimeout(() => this.updatePosition());
3390
+ }
3391
+ close() {
3392
+ this.isOpen = false;
3393
+ if (this.cleanupAutoUpdate)
3394
+ this.cleanupAutoUpdate();
3395
+ }
3396
+ clear(event) {
3397
+ event.stopPropagation(); // Stop button click
3398
+ this.value = { start: null, end: null };
3399
+ this.onChange(this.value);
3400
+ }
3401
+ // --- Floating UI Positioning ---
3402
+ updatePosition() {
3403
+ if (!this.triggerContainer || !this.popover)
3404
+ return;
3405
+ this.cleanupAutoUpdate = autoUpdate(this.triggerContainer.nativeElement, this.popover.nativeElement, () => {
3406
+ computePosition(this.triggerContainer.nativeElement, this.popover.nativeElement, {
3407
+ placement: 'bottom-start', // Aligned to the right where the icon is
3408
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
3409
+ }).then(({ x, y }) => {
3410
+ Object.assign(this.popover.nativeElement.style, {
3411
+ left: `${x}px`,
3412
+ top: `${y}px`,
3413
+ visibility: 'visible',
3414
+ });
3415
+ });
3416
+ });
3417
+ }
3418
+ onClickOutside(event) {
3419
+ if (this.isOpen &&
3420
+ !this.triggerContainer.nativeElement.contains(event.target) &&
3421
+ !this.popover.nativeElement.contains(event.target)) {
3422
+ this.close();
3423
+ }
3424
+ }
3425
+ // CVA
3426
+ onChange = () => { };
3427
+ onTouched = () => { };
3428
+ writeValue(val) {
3429
+ if (val) {
3430
+ this.value = { ...val };
3431
+ }
3432
+ else {
3433
+ this.value = { start: null, end: null };
3434
+ }
3435
+ this.cdr.markForCheck();
3436
+ }
3437
+ registerOnChange(fn) { this.onChange = fn; }
3438
+ registerOnTouched(fn) { this.onTouched = fn; }
3439
+ setDisabledState(isDisabled) { this.disabled = isDisabled; }
3440
+ cn = cn;
3441
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DateRangePickerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
3442
+ 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: [
3443
+ {
3444
+ provide: NG_VALUE_ACCESSOR,
3445
+ useExisting: forwardRef(() => DateRangePickerComponent),
3446
+ multi: true
3447
+ }
3448
+ ], viewQueries: [{ propertyName: "triggerContainer", first: true, predicate: ["triggerContainer"], descendants: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true }], ngImport: i0, template: `
3449
+ <div class="relative w-full" #triggerContainer>
3450
+ <tolle-input [placeholder]="placeholder" [disabled]="disabled" [ngModel]="displayValue">
3451
+ <div suffix class="flex items-center gap-1.5 cursor-pointer">
3452
+ <i
3453
+ *ngIf="(value.start || value.end) && !disabled"
3454
+ (click)="clear($event)"
3455
+ class="ri-close-line cursor-pointer text-muted-foreground hover:text-foreground transition-colors"
3456
+ ></i>
3457
+
3458
+ <i
3459
+ (click)="togglePopover($event)"
3460
+ class="ri-calendar-line cursor-pointer text-muted-foreground hover:text-primary transition-colors"
3461
+ ></i>
3462
+ </div>
3463
+ </tolle-input>
3464
+ <div
3465
+ #popover
3466
+ *ngIf="isOpen"
3467
+ class="absolute z-50 min-w-72"
3468
+ style="visibility: hidden; top: 0; left: 0;"
3469
+ >
3470
+ <tolle-range-calendar
3471
+ [ngModel]="value"
3472
+ (rangeSelect)="onCalendarSelect($event)"
3473
+ [disablePastDates]="disablePastDates"
3474
+ ></tolle-range-calendar>
3475
+ </div>
3476
+ </div>
3477
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.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: RangeCalendarComponent, selector: "tolle-range-calendar", inputs: ["class", "disablePastDates"], outputs: ["rangeSelect"] }, { kind: "component", type: InputComponent$1, selector: "tolle-input", inputs: ["type", "placeholder", "disabled", "error", "size", "containerClass", "class"] }] });
3478
+ }
3479
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DateRangePickerComponent, decorators: [{
3480
+ type: Component,
3481
+ args: [{
3482
+ selector: 'tolle-date-range-picker',
3483
+ standalone: true,
3484
+ imports: [CommonModule, FormsModule, RangeCalendarComponent, InputComponent$1],
3485
+ providers: [
3486
+ {
3487
+ provide: NG_VALUE_ACCESSOR,
3488
+ useExisting: forwardRef(() => DateRangePickerComponent),
3489
+ multi: true
3490
+ }
3491
+ ],
3492
+ template: `
3493
+ <div class="relative w-full" #triggerContainer>
3494
+ <tolle-input [placeholder]="placeholder" [disabled]="disabled" [ngModel]="displayValue">
3495
+ <div suffix class="flex items-center gap-1.5 cursor-pointer">
3496
+ <i
3497
+ *ngIf="(value.start || value.end) && !disabled"
3498
+ (click)="clear($event)"
3499
+ class="ri-close-line cursor-pointer text-muted-foreground hover:text-foreground transition-colors"
3500
+ ></i>
3501
+
3502
+ <i
3503
+ (click)="togglePopover($event)"
3504
+ class="ri-calendar-line cursor-pointer text-muted-foreground hover:text-primary transition-colors"
3505
+ ></i>
3506
+ </div>
3507
+ </tolle-input>
3508
+ <div
3509
+ #popover
3510
+ *ngIf="isOpen"
3511
+ class="absolute z-50 min-w-72"
3512
+ style="visibility: hidden; top: 0; left: 0;"
3513
+ >
3514
+ <tolle-range-calendar
3515
+ [ngModel]="value"
3516
+ (rangeSelect)="onCalendarSelect($event)"
3517
+ [disablePastDates]="disablePastDates"
3518
+ ></tolle-range-calendar>
3519
+ </div>
3520
+ </div>
3521
+ `
3522
+ }]
3523
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { disabled: [{
3524
+ type: Input
3525
+ }], placeholder: [{
3526
+ type: Input
3527
+ }], class: [{
3528
+ type: Input
3529
+ }], disablePastDates: [{
3530
+ type: Input
3531
+ }], size: [{
3532
+ type: Input
3533
+ }], triggerContainer: [{
3534
+ type: ViewChild,
3535
+ args: ['triggerContainer']
3536
+ }], popover: [{
3537
+ type: ViewChild,
3538
+ args: ['popover']
3539
+ }], onClickOutside: [{
3540
+ type: HostListener,
3541
+ args: ['document:mousedown', ['$event']]
3542
+ }] } });
3543
+
3544
+ /*
3545
+ * Public API Surface of tolle
3546
+ */
3547
+
3548
+ /**
3549
+ * Generated bundle index. Do not edit.
3550
+ */
3551
+
3552
+ export { AccordionComponent, AccordionItemComponent, BadgeComponent, ButtonComponent, ButtonGroupComponent, CalendarComponent, CardComponent, CardContentComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CheckboxComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, InputComponent, MaskedInputComponent, Modal, ModalComponent, ModalRef, ModalService, ModalStackService, MultiSelectComponent, PaginationComponent, RangeCalendarComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SkeletonComponent, SwitchComponent, TOLLE_CONFIG, ToastService, TolleCellDirective, TooltipDirective, cn, provideTolleConfig };
3553
+ //# sourceMappingURL=tolle_-tolle-ui.mjs.map