@tolle_/tolle-ui 0.0.17-beta → 0.0.19-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 (50) hide show
  1. package/esm2022/lib/alert.component.mjs +116 -0
  2. package/esm2022/lib/avatar-fallback.component.mjs +19 -0
  3. package/esm2022/lib/avatar.component.mjs +86 -0
  4. package/esm2022/lib/breadcrumb-item.component.mjs +19 -0
  5. package/esm2022/lib/breadcrumb-link.component.mjs +60 -0
  6. package/esm2022/lib/breadcrumb-separator.component.mjs +23 -0
  7. package/esm2022/lib/breadcrumb.component.mjs +28 -0
  8. package/esm2022/lib/button.component.mjs +44 -36
  9. package/esm2022/lib/date-picker.component.mjs +2 -2
  10. package/esm2022/lib/empty-state.component.mjs +111 -0
  11. package/esm2022/lib/input.component.mjs +2 -2
  12. package/esm2022/lib/masked-input.component.mjs +119 -43
  13. package/esm2022/lib/otp-group.component.mjs +15 -0
  14. package/esm2022/lib/otp-slot.component.mjs +62 -0
  15. package/esm2022/lib/otp.component.mjs +127 -0
  16. package/esm2022/lib/popover-content.component.mjs +44 -0
  17. package/esm2022/lib/popover.component.mjs +105 -0
  18. package/esm2022/lib/radio-group.component.mjs +78 -0
  19. package/esm2022/lib/radio-item.component.mjs +112 -0
  20. package/esm2022/lib/radio-service.mjs +23 -0
  21. package/esm2022/lib/textarea.component.mjs +2 -2
  22. package/esm2022/lib/theme.service.mjs +25 -1
  23. package/esm2022/public-api.mjs +16 -1
  24. package/esm2022/tolle-ui.mjs +5 -0
  25. package/fesm2022/{tolle_-tolle-ui.mjs → tolle-ui.mjs} +1150 -83
  26. package/fesm2022/tolle-ui.mjs.map +1 -0
  27. package/lib/alert.component.d.ts +25 -0
  28. package/lib/avatar-fallback.component.d.ts +5 -0
  29. package/lib/avatar.component.d.ts +17 -0
  30. package/lib/breadcrumb-item.component.d.ts +5 -0
  31. package/lib/breadcrumb-link.component.d.ts +6 -0
  32. package/lib/breadcrumb-separator.component.d.ts +5 -0
  33. package/lib/breadcrumb.component.d.ts +8 -0
  34. package/lib/button.component.d.ts +5 -7
  35. package/lib/empty-state.component.d.ts +20 -0
  36. package/lib/masked-input.component.d.ts +8 -1
  37. package/lib/otp-group.component.d.ts +5 -0
  38. package/lib/otp-slot.component.d.ts +12 -0
  39. package/lib/otp.component.d.ts +21 -0
  40. package/lib/popover-content.component.d.ts +8 -0
  41. package/lib/popover.component.d.ts +19 -0
  42. package/lib/radio-group.component.d.ts +23 -0
  43. package/lib/radio-item.component.d.ts +22 -0
  44. package/lib/radio-service.d.ts +11 -0
  45. package/lib/theme.service.d.ts +2 -0
  46. package/package.json +7 -6
  47. package/public-api.d.ts +15 -0
  48. package/theme.css +9 -0
  49. package/esm2022/tolle_-tolle-ui.mjs +0 -5
  50. package/fesm2022/tolle_-tolle-ui.mjs.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import * as i0 from '@angular/core';
4
- import { Component, Input, forwardRef, Injectable, Optional, HostListener, ViewChild, ContentChildren, EventEmitter, Output, Directive, inject, PLATFORM_ID, Inject, InjectionToken, APP_INITIALIZER, ChangeDetectorRef, ChangeDetectionStrategy, TemplateRef, Injector } from '@angular/core';
4
+ import { Component, Input, forwardRef, Injectable, Optional, HostListener, ViewChild, ContentChildren, EventEmitter, Output, Directive, inject, PLATFORM_ID, Inject, InjectionToken, APP_INITIALIZER, ChangeDetectorRef, ChangeDetectionStrategy, TemplateRef, Injector, HostBinding } from '@angular/core';
5
5
  import * as i1 from '@angular/common';
6
- import { CommonModule, isPlatformBrowser, DOCUMENT } from '@angular/common';
6
+ import { CommonModule, isPlatformBrowser, DOCUMENT, NgIf, NgTemplateOutlet } from '@angular/common';
7
7
  import { cva } from 'class-variance-authority';
8
8
  import * as i2 from '@angular/forms';
9
9
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
@@ -18,10 +18,7 @@ function cn(...inputs) {
18
18
  return twMerge(clsx(inputs));
19
19
  }
20
20
 
21
- // 1. Define Component Variants (The "Recipe")
22
- const buttonVariants = cva(
23
- // Base styles applied to ALL buttons
24
- "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", {
21
+ const buttonVariants = cva("inline-flex items-center justify-center rounded-md text-sm font-medium transition-all 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 active:scale-[0.98]", {
25
22
  variants: {
26
23
  variant: {
27
24
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
@@ -41,68 +38,78 @@ const buttonVariants = cva(
41
38
  icon: "h-10 w-10",
42
39
  "icon-lg": "h-11 w-11",
43
40
  },
44
- // Added block variant
45
- block: {
46
- true: "w-full flex",
47
- },
48
- // Added readonly variant
49
- readonly: {
50
- true: "pointer-events-none opacity-80",
51
- },
52
- disabled: {
53
- true: "pointer-events-none",
54
- }
41
+ busy: { true: "relative !cursor-wait !pointer-events-none" }
55
42
  },
56
43
  defaultVariants: {
57
44
  variant: "default",
58
45
  size: "default",
59
- block: false,
60
- readonly: false,
61
46
  },
62
47
  });
63
48
  class ButtonComponent {
64
- // Allow consumers to pass a custom class to override styles
65
49
  class = '';
66
- // Expose the variants as Inputs
67
50
  variant = 'default';
68
51
  size = 'default';
69
- // Added new inputs
70
- block = false;
71
- readonly = false;
72
52
  disabled = false;
73
- // Calculate the final string of classes
53
+ busy = false;
54
+ readonly = false;
74
55
  get computedClass() {
75
- return cn(buttonVariants({ variant: this.variant, size: this.size, block: this.block, disabled: this.disabled }), this.class);
56
+ return cn(buttonVariants({
57
+ variant: this.variant,
58
+ size: this.size,
59
+ busy: this.busy
60
+ }), 'size-' + this.size, this.class);
76
61
  }
77
62
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
78
- 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", block: "block", readonly: "readonly", disabled: "disabled" }, ngImport: i0, template: `
79
- <button [class]="computedClass" [disabled]="disabled" [attr.aria-readonly]="readonly">
80
- <span class="flex items-center justify-center gap-2 w-full h-full">
63
+ 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", disabled: "disabled", busy: "busy", readonly: "readonly" }, ngImport: i0, template: `
64
+ <button
65
+ [class]="computedClass"
66
+ [disabled]="disabled || busy"
67
+ [attr.aria-busy]="busy"
68
+ >
69
+ <div *ngIf="busy" class="absolute inset-0 flex items-center justify-center">
70
+ <svg class="animate-spin h-5 w-5 text-current" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
71
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
72
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
73
+ </svg>
74
+ </div>
75
+
76
+ <span class="flex items-center justify-center w-full h-full pointer-events-none" [class.invisible]="busy">
81
77
  <ng-content></ng-content>
82
78
  </span>
83
79
  </button>
84
- `, 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 }] });
80
+ `, isInline: true, styles: [":host{display:inline-block;vertical-align:middle}:host ::ng-deep i{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host-context(.size-xs) ::ng-deep i,:host-context(.size-icon-xs) ::ng-deep i,:host-context(.size-sm) ::ng-deep i,:host-context(.size-icon-sm) ::ng-deep i{font-size:1rem}:host-context(.size-default) ::ng-deep i,:host-context(.size-icon) ::ng-deep i,:host-context(.size-lg) ::ng-deep i,:host-context(.size-icon-lg) ::ng-deep i{font-size:1.2rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
85
81
  }
86
82
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonComponent, decorators: [{
87
83
  type: Component,
88
84
  args: [{ selector: 'tolle-button', standalone: true, imports: [CommonModule], template: `
89
- <button [class]="computedClass" [disabled]="disabled" [attr.aria-readonly]="readonly">
90
- <span class="flex items-center justify-center gap-2 w-full h-full">
85
+ <button
86
+ [class]="computedClass"
87
+ [disabled]="disabled || busy"
88
+ [attr.aria-busy]="busy"
89
+ >
90
+ <div *ngIf="busy" class="absolute inset-0 flex items-center justify-center">
91
+ <svg class="animate-spin h-5 w-5 text-current" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
92
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
93
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
94
+ </svg>
95
+ </div>
96
+
97
+ <span class="flex items-center justify-center w-full h-full pointer-events-none" [class.invisible]="busy">
91
98
  <ng-content></ng-content>
92
99
  </span>
93
100
  </button>
94
- `, 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"] }]
101
+ `, styles: [":host{display:inline-block;vertical-align:middle}:host ::ng-deep i{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host-context(.size-xs) ::ng-deep i,:host-context(.size-icon-xs) ::ng-deep i,:host-context(.size-sm) ::ng-deep i,:host-context(.size-icon-sm) ::ng-deep i{font-size:1rem}:host-context(.size-default) ::ng-deep i,:host-context(.size-icon) ::ng-deep i,:host-context(.size-lg) ::ng-deep i,:host-context(.size-icon-lg) ::ng-deep i{font-size:1.2rem}\n"] }]
95
102
  }], propDecorators: { class: [{
96
103
  type: Input
97
104
  }], variant: [{
98
105
  type: Input
99
106
  }], size: [{
100
107
  type: Input
101
- }], block: [{
108
+ }], disabled: [{
102
109
  type: Input
103
- }], readonly: [{
110
+ }], busy: [{
104
111
  type: Input
105
- }], disabled: [{
112
+ }], readonly: [{
106
113
  type: Input
107
114
  }] } });
108
115
 
@@ -154,7 +161,7 @@ class InputComponent {
154
161
  // Colors & Borders
155
162
  this.error ? "border-destructive focus-within:ring-destructive" : "border-input",
156
163
  // Disabled vs Readonly styling
157
- this.disabled && "cursor-not-allowed opacity-50 bg-muted/30", this.readonly && "cursor-default bg-muted/10 border-dashed focus-within:ring-0", this.containerClass);
164
+ this.disabled && "cursor-not-allowed opacity-50", this.readonly && "cursor-default border-dashed focus-within:ring-0", this.containerClass);
158
165
  }
159
166
  get computedInputClass() {
160
167
  return cn("flex-1 bg-transparent border-none p-0 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-0", this.size === 'xs' && "text-xs", this.size === 'lg' && "text-base", this.disabled && "cursor-not-allowed", this.readonly && "cursor-default", this.class);
@@ -1349,6 +1356,16 @@ class ThemeService {
1349
1356
  else {
1350
1357
  this.disableDarkMode();
1351
1358
  }
1359
+ const savedPrimary = localStorage.getItem('tolle-primary-color');
1360
+ if (savedPrimary) {
1361
+ this.setPrimaryColor(savedPrimary, false);
1362
+ }
1363
+ else if (this.config?.primaryColor) {
1364
+ this.setPrimaryColor(this.config.primaryColor, false);
1365
+ }
1366
+ if (this.config?.radius) {
1367
+ this.renderer.setStyle(this.document.documentElement, '--radius', this.config.radius);
1368
+ }
1352
1369
  // 2. Apply Brand Config - This will generate full palette
1353
1370
  if (this.config) {
1354
1371
  this.applyBrandConfig(this.config);
@@ -1455,9 +1472,23 @@ class ThemeService {
1455
1472
  localStorage.setItem('tolle-theme', 'light');
1456
1473
  this.isDarkSubject.next(false);
1457
1474
  }
1475
+ setPrimaryColor(color, persist = true) {
1476
+ if (!isPlatformBrowser(this.platformId))
1477
+ return;
1478
+ // Update CSS variables + palette
1479
+ this.renderer.setStyle(this.document.documentElement, '--primary', color);
1480
+ this.generatePrimaryShades(color);
1481
+ // Persist user preference
1482
+ if (persist) {
1483
+ localStorage.setItem('tolle-primary-color', color);
1484
+ }
1485
+ }
1458
1486
  get currentTheme() {
1459
1487
  return this.isDarkSubject.value ? 'dark' : 'light';
1460
1488
  }
1489
+ get primaryColor() {
1490
+ return localStorage.getItem('tolle-primary-color');
1491
+ }
1461
1492
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThemeService, deps: [{ token: DOCUMENT }, { token: PLATFORM_ID }, { token: TOLLE_CONFIG, optional: true }, { token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable });
1462
1493
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThemeService, providedIn: 'root' });
1463
1494
  }
@@ -2022,11 +2053,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2022
2053
  class MaskedInputComponent {
2023
2054
  el;
2024
2055
  cdr;
2056
+ id = `masked-input-${Math.random().toString(36).substr(2, 9)}`;
2057
+ label = '';
2058
+ hint = '';
2059
+ errorMessage = '';
2025
2060
  mask = '';
2026
2061
  placeholder = '';
2027
2062
  type = 'text';
2028
2063
  disabled = false;
2064
+ readonly = false;
2029
2065
  class = '';
2066
+ containerClass = '';
2030
2067
  error = false;
2031
2068
  size = 'default';
2032
2069
  returnRaw = false;
@@ -2043,7 +2080,6 @@ class MaskedInputComponent {
2043
2080
  this.el = el;
2044
2081
  this.cdr = cdr;
2045
2082
  }
2046
- // FIXED DETECTION: Check the actual DOM nodes projected into the component
2047
2083
  ngAfterContentChecked() {
2048
2084
  const prefix = this.el.nativeElement.querySelector('[prefix]');
2049
2085
  const suffix = this.el.nativeElement.querySelector('[suffix]');
@@ -2053,11 +2089,24 @@ class MaskedInputComponent {
2053
2089
  this.cdr.detectChanges();
2054
2090
  }
2055
2091
  }
2092
+ get computedContainerClass() {
2093
+ return cn("group relative flex items-center w-full rounded-md border transition-all shadow-sm", "bg-background ring-offset-background",
2094
+ // Sizing
2095
+ this.size === 'xs' && "h-8 px-2 gap-1.5", this.size === 'sm' && "h-9 px-3 gap-2", this.size === 'default' && "h-10 px-3 gap-2", this.size === 'lg' && "h-11 px-4 gap-3",
2096
+ // Interaction States (Inherited from Input focus style)
2097
+ !(this.readonly || this.disabled) && "focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-1",
2098
+ // Colors & Borders
2099
+ this.error ? "border-destructive focus-within:ring-destructive" : "border-input",
2100
+ // Disabled vs Readonly styling
2101
+ this.disabled && "cursor-not-allowed opacity-50", this.readonly && "cursor-default border-dashed focus-within:ring-0", this.containerClass);
2102
+ }
2056
2103
  get computedInputClass() {
2057
- 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);
2104
+ return cn("flex-1 bg-transparent border-none p-0 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-0", this.size === 'xs' && "text-xs", this.size === 'lg' && "text-base", this.disabled && "cursor-not-allowed", this.readonly && "cursor-default", this.class);
2058
2105
  }
2059
2106
  // --- Masking Logic ---
2060
2107
  onInput(event) {
2108
+ if (this.readonly || this.disabled)
2109
+ return;
2061
2110
  const input = event.target;
2062
2111
  const raw = this.unmask(input.value);
2063
2112
  const masked = this.applyMask(raw);
@@ -2098,40 +2147,64 @@ class MaskedInputComponent {
2098
2147
  }
2099
2148
  registerOnChange(fn) { this.onChange = fn; }
2100
2149
  registerOnTouched(fn) { this.onTouched = fn; }
2101
- setDisabledState(isDisabled) { this.disabled = isDisabled; }
2150
+ setDisabledState(isDisabled) {
2151
+ this.disabled = isDisabled;
2152
+ this.cdr.markForCheck();
2153
+ }
2102
2154
  cn = cn;
2103
2155
  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 });
2104
- 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: [
2156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: MaskedInputComponent, isStandalone: true, selector: "tolle-masked-input", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", mask: "mask", placeholder: "placeholder", type: "type", disabled: "disabled", readonly: "readonly", class: "class", containerClass: "containerClass", error: "error", size: "size", returnRaw: "returnRaw" }, providers: [
2105
2157
  {
2106
2158
  provide: NG_VALUE_ACCESSOR,
2107
2159
  useExisting: forwardRef(() => MaskedInputComponent),
2108
2160
  multi: true
2109
2161
  }
2110
2162
  ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }], ngImport: i0, template: `
2111
- <div [class]="cn('relative flex items-center w-full group', 'size-' + size, class)">
2163
+ <div class="flex flex-col gap-1.5 w-full">
2164
+ <label
2165
+ *ngIf="label"
2166
+ [for]="id"
2167
+ [class.opacity-50]="disabled"
2168
+ class="text-sm font-medium text-foreground leading-none transition-opacity"
2169
+ >
2170
+ {{ label }}
2171
+ </label>
2112
2172
 
2113
- <div class="absolute left-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
2114
- [class.left-2.5]="size === 'xs'">
2115
- <ng-content select="[prefix]"></ng-content>
2116
- </div>
2173
+ <div [class]="computedContainerClass">
2174
+ <!-- Prefix Icon -->
2175
+ <div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
2176
+ <ng-content select="[prefix]"></ng-content>
2177
+ </div>
2117
2178
 
2118
- <input
2119
- #inputEl
2120
- [type]="type"
2121
- [placeholder]="placeholder"
2122
- [disabled]="disabled"
2123
- [value]="displayValue"
2124
- (input)="onInput($event)"
2125
- (blur)="onTouched()"
2126
- [class]="computedInputClass"
2127
- />
2179
+ <input
2180
+ #inputEl
2181
+ [id]="id"
2182
+ [type]="type"
2183
+ [placeholder]="placeholder"
2184
+ [disabled]="disabled"
2185
+ [readOnly]="readonly"
2186
+ [value]="displayValue"
2187
+ (input)="onInput($event)"
2188
+ (blur)="onTouched()"
2189
+ [class]="computedInputClass"
2190
+ />
2128
2191
 
2129
- <div class="absolute right-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
2130
- [class.right-2.5]="size === 'xs'">
2131
- <ng-content select="[suffix]"></ng-content>
2192
+ <!-- Suffix Icon -->
2193
+ <div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
2194
+ <ng-content select="[suffix]"></ng-content>
2195
+ </div>
2132
2196
  </div>
2197
+
2198
+ <ng-container *ngIf="!disabled">
2199
+ <p *ngIf="hint && !error" class="text-xs text-muted-foreground px-1">
2200
+ {{ hint }}
2201
+ </p>
2202
+ <p *ngIf="error && errorMessage" class="text-xs text-destructive px-1">
2203
+ {{ errorMessage }}
2204
+ </p>
2205
+ </ng-container>
2133
2206
  </div>
2134
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] });
2207
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }] });
2135
2208
  }
2136
2209
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MaskedInputComponent, decorators: [{
2137
2210
  type: Component,
@@ -2147,32 +2220,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2147
2220
  }
2148
2221
  ],
2149
2222
  template: `
2150
- <div [class]="cn('relative flex items-center w-full group', 'size-' + size, class)">
2223
+ <div class="flex flex-col gap-1.5 w-full">
2224
+ <label
2225
+ *ngIf="label"
2226
+ [for]="id"
2227
+ [class.opacity-50]="disabled"
2228
+ class="text-sm font-medium text-foreground leading-none transition-opacity"
2229
+ >
2230
+ {{ label }}
2231
+ </label>
2151
2232
 
2152
- <div class="absolute left-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
2153
- [class.left-2.5]="size === 'xs'">
2154
- <ng-content select="[prefix]"></ng-content>
2155
- </div>
2233
+ <div [class]="computedContainerClass">
2234
+ <!-- Prefix Icon -->
2235
+ <div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
2236
+ <ng-content select="[prefix]"></ng-content>
2237
+ </div>
2156
2238
 
2157
- <input
2158
- #inputEl
2159
- [type]="type"
2160
- [placeholder]="placeholder"
2161
- [disabled]="disabled"
2162
- [value]="displayValue"
2163
- (input)="onInput($event)"
2164
- (blur)="onTouched()"
2165
- [class]="computedInputClass"
2166
- />
2239
+ <input
2240
+ #inputEl
2241
+ [id]="id"
2242
+ [type]="type"
2243
+ [placeholder]="placeholder"
2244
+ [disabled]="disabled"
2245
+ [readOnly]="readonly"
2246
+ [value]="displayValue"
2247
+ (input)="onInput($event)"
2248
+ (blur)="onTouched()"
2249
+ [class]="computedInputClass"
2250
+ />
2167
2251
 
2168
- <div class="absolute right-3 flex items-center justify-center text-muted-foreground group-focus-within:text-primary transition-colors"
2169
- [class.right-2.5]="size === 'xs'">
2170
- <ng-content select="[suffix]"></ng-content>
2252
+ <!-- Suffix Icon -->
2253
+ <div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
2254
+ <ng-content select="[suffix]"></ng-content>
2255
+ </div>
2171
2256
  </div>
2257
+
2258
+ <ng-container *ngIf="!disabled">
2259
+ <p *ngIf="hint && !error" class="text-xs text-muted-foreground px-1">
2260
+ {{ hint }}
2261
+ </p>
2262
+ <p *ngIf="error && errorMessage" class="text-xs text-destructive px-1">
2263
+ {{ errorMessage }}
2264
+ </p>
2265
+ </ng-container>
2172
2266
  </div>
2173
2267
  `
2174
2268
  }]
2175
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { mask: [{
2269
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { id: [{
2270
+ type: Input
2271
+ }], label: [{
2272
+ type: Input
2273
+ }], hint: [{
2274
+ type: Input
2275
+ }], errorMessage: [{
2276
+ type: Input
2277
+ }], mask: [{
2176
2278
  type: Input
2177
2279
  }], placeholder: [{
2178
2280
  type: Input
@@ -2180,8 +2282,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2180
2282
  type: Input
2181
2283
  }], disabled: [{
2182
2284
  type: Input
2285
+ }], readonly: [{
2286
+ type: Input
2183
2287
  }], class: [{
2184
2288
  type: Input
2289
+ }], containerClass: [{
2290
+ type: Input
2185
2291
  }], error: [{
2186
2292
  type: Input
2187
2293
  }], size: [{
@@ -2340,7 +2446,7 @@ class DatePickerComponent {
2340
2446
  ></tolle-calendar>
2341
2447
  </div>
2342
2448
  </div>
2343
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["mask", "placeholder", "type", "disabled", "class", "error", "size", "returnRaw"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "disablePastDates"] }] });
2449
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "disablePastDates"] }] });
2344
2450
  }
2345
2451
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, decorators: [{
2346
2452
  type: Component,
@@ -4083,7 +4189,7 @@ class TextareaComponent {
4083
4189
  // Border colors
4084
4190
  this.error ? 'border-destructive' : 'border-input',
4085
4191
  // Disabled vs Readonly styles
4086
- this.disabled && 'cursor-not-allowed opacity-50 bg-muted/30', this.readonly && 'cursor-default bg-muted/10 border-dashed focus-visible:ring-0', 'scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent', 'min-h-[80px]', this.className);
4192
+ this.disabled && 'cursor-not-allowed opacity-50', this.readonly && 'cursor-default border-dashed focus-visible:ring-0', 'scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent', 'min-h-[80px]', this.className);
4087
4193
  }
4088
4194
  handleInput(event) {
4089
4195
  if (this.readonly || this.disabled)
@@ -4224,6 +4330,967 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4224
4330
  type: Input
4225
4331
  }] } });
4226
4332
 
4333
+ const alertVariants = cva("relative w-full rounded-lg border p-4 transition-all duration-300 [&>i~div]:pl-7 [&>i]:absolute [&>i]:left-4 [&>i]:top-4 [&>i]:text-foreground", {
4334
+ variants: {
4335
+ variant: {
4336
+ default: "bg-background text-foreground",
4337
+ destructive: "border-destructive/50 text-destructive dark:border-destructive [&>i]:text-destructive",
4338
+ success: "border-emerald-500/50 text-emerald-700 dark:text-emerald-400 [&>i]:text-emerald-600",
4339
+ warning: "border-amber-500/50 text-amber-700 dark:text-amber-400 [&>i]:text-amber-600",
4340
+ },
4341
+ },
4342
+ defaultVariants: {
4343
+ variant: "default",
4344
+ },
4345
+ });
4346
+ class AlertComponent {
4347
+ variant = 'default';
4348
+ title;
4349
+ class = '';
4350
+ dismissible = false;
4351
+ onClose = new EventEmitter();
4352
+ dismissed = false;
4353
+ isDismissing = false;
4354
+ alertVariants = alertVariants;
4355
+ cn = cn;
4356
+ dismiss() {
4357
+ this.isDismissing = true;
4358
+ // Wait for animation to finish before removing from DOM
4359
+ setTimeout(() => {
4360
+ this.dismissed = true;
4361
+ this.onClose.emit();
4362
+ }, 300);
4363
+ }
4364
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4365
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AlertComponent, isStandalone: true, selector: "tolle-alert", inputs: { variant: "variant", title: "title", class: "class", dismissible: "dismissible" }, outputs: { onClose: "onClose" }, ngImport: i0, template: `
4366
+ <div
4367
+ *ngIf="!dismissed"
4368
+ [class]="cn(alertVariants({ variant }), class)"
4369
+ [class.opacity-0]="isDismissing"
4370
+ [class.scale-95]="isDismissing"
4371
+ role="alert"
4372
+ >
4373
+ <ng-content select="[icon]"></ng-content>
4374
+
4375
+ <button
4376
+ *ngIf="dismissible"
4377
+ (click)="dismiss()"
4378
+ class="absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 group-hover:opacity-100"
4379
+ [class.opacity-100]="dismissible"
4380
+ >
4381
+ <i class="ri-close-line text-lg"></i>
4382
+ </button>
4383
+
4384
+ <div>
4385
+ <h5 *ngIf="title" class="mb-1 font-medium leading-none tracking-tight">
4386
+ {{ title }}
4387
+ </h5>
4388
+ <div class="text-sm [&_p]:leading-relaxed">
4389
+ <ng-content></ng-content>
4390
+ </div>
4391
+ </div>
4392
+ </div>
4393
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4394
+ }
4395
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AlertComponent, decorators: [{
4396
+ type: Component,
4397
+ args: [{
4398
+ selector: 'tolle-alert',
4399
+ standalone: true,
4400
+ imports: [CommonModule],
4401
+ template: `
4402
+ <div
4403
+ *ngIf="!dismissed"
4404
+ [class]="cn(alertVariants({ variant }), class)"
4405
+ [class.opacity-0]="isDismissing"
4406
+ [class.scale-95]="isDismissing"
4407
+ role="alert"
4408
+ >
4409
+ <ng-content select="[icon]"></ng-content>
4410
+
4411
+ <button
4412
+ *ngIf="dismissible"
4413
+ (click)="dismiss()"
4414
+ class="absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 group-hover:opacity-100"
4415
+ [class.opacity-100]="dismissible"
4416
+ >
4417
+ <i class="ri-close-line text-lg"></i>
4418
+ </button>
4419
+
4420
+ <div>
4421
+ <h5 *ngIf="title" class="mb-1 font-medium leading-none tracking-tight">
4422
+ {{ title }}
4423
+ </h5>
4424
+ <div class="text-sm [&_p]:leading-relaxed">
4425
+ <ng-content></ng-content>
4426
+ </div>
4427
+ </div>
4428
+ </div>
4429
+ `,
4430
+ }]
4431
+ }], propDecorators: { variant: [{
4432
+ type: Input
4433
+ }], title: [{
4434
+ type: Input
4435
+ }], class: [{
4436
+ type: Input
4437
+ }], dismissible: [{
4438
+ type: Input
4439
+ }], onClose: [{
4440
+ type: Output
4441
+ }] } });
4442
+
4443
+ class AvatarComponent {
4444
+ cdr;
4445
+ src;
4446
+ alt = '';
4447
+ size = 'default';
4448
+ shape = 'circle';
4449
+ // We remove @Input() class because standard class="" attributes
4450
+ // on the tag will now work automatically with the host bindings.
4451
+ isLoading = true;
4452
+ hasError = false;
4453
+ constructor(cdr) {
4454
+ this.cdr = cdr;
4455
+ }
4456
+ onLoad() {
4457
+ this.isLoading = false;
4458
+ this.cdr.detectChanges();
4459
+ }
4460
+ onError() {
4461
+ this.isLoading = false;
4462
+ this.hasError = true;
4463
+ this.cdr.detectChanges();
4464
+ }
4465
+ // Apply styles directly to the <tolle-avatar> tag
4466
+ get hostClasses() {
4467
+ return cn(
4468
+ // Layout & Shape
4469
+ "relative flex shrink-0 overflow-hidden bg-muted", this.shape === 'circle' ? 'rounded-full' : 'rounded-md',
4470
+ // Sizes
4471
+ this.size === 'sm' && "h-8 w-8 text-xs", this.size === 'default' && "h-10 w-10", this.size === 'lg' && "h-16 w-16 text-lg", this.size === 'xl' && "h-24 w-24 text-xl");
4472
+ }
4473
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AvatarComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4474
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AvatarComponent, isStandalone: true, selector: "tolle-avatar", inputs: { src: "src", alt: "alt", size: "size", shape: "shape" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: `
4475
+ <!-- Image Layer -->
4476
+ <img *ngIf="src && !hasError"
4477
+ [src]="src"
4478
+ [alt]="alt"
4479
+ (load)="onLoad()"
4480
+ (error)="onError()"
4481
+ [class.opacity-0]="isLoading"
4482
+ class="h-full w-full object-cover transition-opacity duration-300" />
4483
+
4484
+ <!-- Fallback Layer -->
4485
+ <div *ngIf="hasError || !src || isLoading" class="flex h-full w-full items-center justify-center bg-muted">
4486
+ <ng-content></ng-content>
4487
+ </div>
4488
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4489
+ }
4490
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AvatarComponent, decorators: [{
4491
+ type: Component,
4492
+ args: [{
4493
+ selector: 'tolle-avatar',
4494
+ standalone: true,
4495
+ imports: [NgIf],
4496
+ template: `
4497
+ <!-- Image Layer -->
4498
+ <img *ngIf="src && !hasError"
4499
+ [src]="src"
4500
+ [alt]="alt"
4501
+ (load)="onLoad()"
4502
+ (error)="onError()"
4503
+ [class.opacity-0]="isLoading"
4504
+ class="h-full w-full object-cover transition-opacity duration-300" />
4505
+
4506
+ <!-- Fallback Layer -->
4507
+ <div *ngIf="hasError || !src || isLoading" class="flex h-full w-full items-center justify-center bg-muted">
4508
+ <ng-content></ng-content>
4509
+ </div>
4510
+ `
4511
+ }]
4512
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { src: [{
4513
+ type: Input
4514
+ }], alt: [{
4515
+ type: Input
4516
+ }], size: [{
4517
+ type: Input
4518
+ }], shape: [{
4519
+ type: Input
4520
+ }], hostClasses: [{
4521
+ type: HostBinding,
4522
+ args: ['class']
4523
+ }] } });
4524
+
4525
+ class AvatarFallbackComponent {
4526
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AvatarFallbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4527
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AvatarFallbackComponent, isStandalone: true, selector: "tolle-avatar-fallback", ngImport: i0, template: `
4528
+ <div class="flex h-full w-full items-center justify-center bg-muted text-muted-foreground font-medium uppercase">
4529
+ <ng-content></ng-content>
4530
+ </div>
4531
+ `, isInline: true, styles: [""] });
4532
+ }
4533
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AvatarFallbackComponent, decorators: [{
4534
+ type: Component,
4535
+ args: [{ selector: 'tolle-avatar-fallback', standalone: true, imports: [], template: `
4536
+ <div class="flex h-full w-full items-center justify-center bg-muted text-muted-foreground font-medium uppercase">
4537
+ <ng-content></ng-content>
4538
+ </div>
4539
+ ` }]
4540
+ }] });
4541
+
4542
+ class BreadcrumbComponent {
4543
+ class = '';
4544
+ cn = cn;
4545
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4546
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BreadcrumbComponent, isStandalone: true, selector: "tolle-breadcrumb", inputs: { class: "class" }, ngImport: i0, template: `
4547
+ <nav aria-label="breadcrumb" [class]="cn('flex flex-wrap items-center break-words text-sm text-muted-foreground', class)">
4548
+ <ol class="flex flex-wrap items-center gap-1.5 break-words">
4549
+ <ng-content></ng-content>
4550
+ </ol>
4551
+ </nav>
4552
+ `, isInline: true, styles: [""] });
4553
+ }
4554
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbComponent, decorators: [{
4555
+ type: Component,
4556
+ args: [{ selector: 'tolle-breadcrumb', standalone: true, imports: [], template: `
4557
+ <nav aria-label="breadcrumb" [class]="cn('flex flex-wrap items-center break-words text-sm text-muted-foreground', class)">
4558
+ <ol class="flex flex-wrap items-center gap-1.5 break-words">
4559
+ <ng-content></ng-content>
4560
+ </ol>
4561
+ </nav>
4562
+ ` }]
4563
+ }], propDecorators: { class: [{
4564
+ type: Input
4565
+ }] } });
4566
+
4567
+ class BreadcrumbLinkComponent {
4568
+ active = false;
4569
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4570
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BreadcrumbLinkComponent, isStandalone: true, selector: "tolle-breadcrumb-link", inputs: { active: "active" }, ngImport: i0, template: `
4571
+ <ng-template #content>
4572
+ <ng-content></ng-content>
4573
+ </ng-template>
4574
+
4575
+ <ng-container *ngIf="active; else linkTemplate">
4576
+ <span
4577
+ role="link"
4578
+ aria-disabled="true"
4579
+ aria-current="page"
4580
+ class="font-normal text-foreground"
4581
+ >
4582
+ <ng-container *ngTemplateOutlet="content"></ng-container>
4583
+ </span>
4584
+ </ng-container>
4585
+
4586
+ <ng-template #linkTemplate>
4587
+ <div class="transition-colors hover:text-foreground cursor-pointer">
4588
+ <ng-container *ngTemplateOutlet="content"></ng-container>
4589
+ </div>
4590
+ </ng-template>
4591
+ `, isInline: true, styles: [""], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
4592
+ }
4593
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbLinkComponent, decorators: [{
4594
+ type: Component,
4595
+ args: [{ selector: 'tolle-breadcrumb-link', standalone: true, imports: [
4596
+ NgIf,
4597
+ NgTemplateOutlet
4598
+ ], template: `
4599
+ <ng-template #content>
4600
+ <ng-content></ng-content>
4601
+ </ng-template>
4602
+
4603
+ <ng-container *ngIf="active; else linkTemplate">
4604
+ <span
4605
+ role="link"
4606
+ aria-disabled="true"
4607
+ aria-current="page"
4608
+ class="font-normal text-foreground"
4609
+ >
4610
+ <ng-container *ngTemplateOutlet="content"></ng-container>
4611
+ </span>
4612
+ </ng-container>
4613
+
4614
+ <ng-template #linkTemplate>
4615
+ <div class="transition-colors hover:text-foreground cursor-pointer">
4616
+ <ng-container *ngTemplateOutlet="content"></ng-container>
4617
+ </div>
4618
+ </ng-template>
4619
+ ` }]
4620
+ }], propDecorators: { active: [{
4621
+ type: Input
4622
+ }] } });
4623
+
4624
+ class BreadcrumbSeparatorComponent {
4625
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbSeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4626
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BreadcrumbSeparatorComponent, isStandalone: true, selector: "tolle-breadcrumb-separator", ngImport: i0, template: `
4627
+ <li role="presentation" aria-hidden="true" class="[&>i]:size-3.5">
4628
+ <ng-content>
4629
+ <i class="ri-arrow-right-s-line"></i>
4630
+ </ng-content>
4631
+ </li>
4632
+ `, isInline: true, styles: [""] });
4633
+ }
4634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbSeparatorComponent, decorators: [{
4635
+ type: Component,
4636
+ args: [{ selector: 'tolle-breadcrumb-separator', standalone: true, imports: [], template: `
4637
+ <li role="presentation" aria-hidden="true" class="[&>i]:size-3.5">
4638
+ <ng-content>
4639
+ <i class="ri-arrow-right-s-line"></i>
4640
+ </ng-content>
4641
+ </li>
4642
+ ` }]
4643
+ }] });
4644
+
4645
+ class BreadcrumbItemComponent {
4646
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4647
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BreadcrumbItemComponent, isStandalone: true, selector: "tolle-breadcrumb-item", ngImport: i0, template: `
4648
+ <li class="inline-flex items-center gap-1.5">
4649
+ <ng-content></ng-content>
4650
+ </li>
4651
+ `, isInline: true, styles: [""] });
4652
+ }
4653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BreadcrumbItemComponent, decorators: [{
4654
+ type: Component,
4655
+ args: [{ selector: 'tolle-breadcrumb-item', standalone: true, imports: [], template: `
4656
+ <li class="inline-flex items-center gap-1.5">
4657
+ <ng-content></ng-content>
4658
+ </li>
4659
+ ` }]
4660
+ }] });
4661
+
4662
+ const emptyStateVariants = cva("flex flex-col items-center justify-center text-center animate-in fade-in duration-500", {
4663
+ variants: {
4664
+ variant: {
4665
+ default: "min-h-[400px] rounded-md border border-dashed border-border p-8 bg-background/50",
4666
+ minimal: "p-4 min-h-[200px]",
4667
+ },
4668
+ },
4669
+ defaultVariants: {
4670
+ variant: "default",
4671
+ },
4672
+ });
4673
+ class EmptyStateComponent {
4674
+ variant = 'default';
4675
+ title = 'No items found';
4676
+ description;
4677
+ class = '';
4678
+ emptyStateVariants = emptyStateVariants;
4679
+ cn = cn;
4680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4681
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EmptyStateComponent, isStandalone: true, selector: "tolle-empty-state", inputs: { variant: "variant", title: "title", description: "description", class: "class" }, ngImport: i0, template: `
4682
+ <div [class]="cn(emptyStateVariants({ variant }), class)">
4683
+
4684
+ <div [class]="cn(
4685
+ 'flex items-center justify-center rounded-full bg-muted',
4686
+ variant === 'minimal' ? 'h-12 w-12' : 'h-20 w-20'
4687
+ )">
4688
+ <ng-content select="[icon]">
4689
+ <i [class]="cn(
4690
+ 'ri-inbox-line text-muted-foreground/60',
4691
+ variant === 'minimal' ? 'text-xl' : 'text-3xl'
4692
+ )"></i>
4693
+ </ng-content>
4694
+ </div>
4695
+
4696
+ <h3 [class]="cn(
4697
+ 'font-semibold text-foreground',
4698
+ variant === 'minimal' ? 'mt-2 text-sm' : 'mt-4 text-lg'
4699
+ )">
4700
+ {{ title }}
4701
+ </h3>
4702
+
4703
+ <p *ngIf="description" [class]="cn(
4704
+ 'text-muted-foreground',
4705
+ variant === 'minimal' ? 'mt-1 text-xs' : 'mb-6 mt-2 max-w-sm text-sm'
4706
+ )">
4707
+ {{ description }}
4708
+ </p>
4709
+
4710
+ <div *ngIf="variant !== 'minimal'" class="flex items-center justify-center gap-3">
4711
+ <ng-content select="[actions]"></ng-content>
4712
+ </div>
4713
+ </div>
4714
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4715
+ }
4716
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EmptyStateComponent, decorators: [{
4717
+ type: Component,
4718
+ args: [{
4719
+ selector: 'tolle-empty-state',
4720
+ standalone: true,
4721
+ imports: [CommonModule],
4722
+ template: `
4723
+ <div [class]="cn(emptyStateVariants({ variant }), class)">
4724
+
4725
+ <div [class]="cn(
4726
+ 'flex items-center justify-center rounded-full bg-muted',
4727
+ variant === 'minimal' ? 'h-12 w-12' : 'h-20 w-20'
4728
+ )">
4729
+ <ng-content select="[icon]">
4730
+ <i [class]="cn(
4731
+ 'ri-inbox-line text-muted-foreground/60',
4732
+ variant === 'minimal' ? 'text-xl' : 'text-3xl'
4733
+ )"></i>
4734
+ </ng-content>
4735
+ </div>
4736
+
4737
+ <h3 [class]="cn(
4738
+ 'font-semibold text-foreground',
4739
+ variant === 'minimal' ? 'mt-2 text-sm' : 'mt-4 text-lg'
4740
+ )">
4741
+ {{ title }}
4742
+ </h3>
4743
+
4744
+ <p *ngIf="description" [class]="cn(
4745
+ 'text-muted-foreground',
4746
+ variant === 'minimal' ? 'mt-1 text-xs' : 'mb-6 mt-2 max-w-sm text-sm'
4747
+ )">
4748
+ {{ description }}
4749
+ </p>
4750
+
4751
+ <div *ngIf="variant !== 'minimal'" class="flex items-center justify-center gap-3">
4752
+ <ng-content select="[actions]"></ng-content>
4753
+ </div>
4754
+ </div>
4755
+ `
4756
+ }]
4757
+ }], propDecorators: { variant: [{
4758
+ type: Input
4759
+ }], title: [{
4760
+ type: Input
4761
+ }], description: [{
4762
+ type: Input
4763
+ }], class: [{
4764
+ type: Input
4765
+ }] } });
4766
+
4767
+ class OtpComponent {
4768
+ cdr;
4769
+ length = 6;
4770
+ disabled = false;
4771
+ auto = false; // The toggle for automatic slot generation
4772
+ value = '';
4773
+ isFocused = false;
4774
+ onChange = () => { };
4775
+ onTouched = () => { };
4776
+ constructor(cdr) {
4777
+ this.cdr = cdr;
4778
+ }
4779
+ onInputChange(event) {
4780
+ const val = event.target.value.replace(/\D/g, '');
4781
+ this.value = val;
4782
+ this.onChange(val);
4783
+ this.cdr.markForCheck();
4784
+ }
4785
+ // Visual logic for the automated slots
4786
+ getSlotClass(index) {
4787
+ const isActive = this.isFocused && this.value.length === index;
4788
+ return cn('relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all bg-background', index === 0 && 'rounded-l-md border-l', index === this.length - 1 && 'rounded-r-md', isActive && 'z-30 ring-2 ring-ring ring-offset-background');
4789
+ }
4790
+ writeValue(value) { this.value = value || ''; this.cdr.markForCheck(); }
4791
+ registerOnChange(fn) { this.onChange = fn; }
4792
+ registerOnTouched(fn) { this.onTouched = fn; }
4793
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
4794
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: OtpComponent, isStandalone: true, selector: "tolle-otp", inputs: { length: "length", disabled: "disabled", auto: "auto" }, providers: [{
4795
+ provide: NG_VALUE_ACCESSOR,
4796
+ useExisting: forwardRef(() => OtpComponent),
4797
+ multi: true
4798
+ }], ngImport: i0, template: `
4799
+ <div class="relative flex items-center gap-2">
4800
+ <input
4801
+ #hiddenInput
4802
+ type="text"
4803
+ inputmode="numeric"
4804
+ autocomplete="one-time-code"
4805
+ [maxLength]="length"
4806
+ class="absolute inset-0 opacity-0 cursor-default z-20"
4807
+ [(ngModel)]="value"
4808
+ (input)="onInputChange($event)"
4809
+ (focus)="isFocused = true"
4810
+ (blur)="isFocused = false"
4811
+ [disabled]="disabled"
4812
+ />
4813
+
4814
+ <div *ngIf="auto" class="flex items-center gap-2 pointer-events-none">
4815
+ <div class="flex items-center">
4816
+ <div *ngFor="let _ of [].constructor(length); let i = index"
4817
+ [class]="getSlotClass(i)"
4818
+ >
4819
+ {{ value[i] || '' }}
4820
+ <div *ngIf="isFocused && value.length === i" class="absolute inset-0 flex items-center justify-center">
4821
+ <div class="h-4 w-px animate-caret-blink bg-foreground duration-1000"></div>
4822
+ </div>
4823
+ </div>
4824
+ </div>
4825
+ </div>
4826
+
4827
+ <div *ngIf="!auto" class="flex items-center gap-2 pointer-events-none">
4828
+ <ng-content></ng-content>
4829
+ </div>
4830
+ </div>
4831
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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"] }] });
4832
+ }
4833
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpComponent, decorators: [{
4834
+ type: Component,
4835
+ args: [{
4836
+ selector: 'tolle-otp',
4837
+ standalone: true,
4838
+ imports: [CommonModule, FormsModule],
4839
+ providers: [{
4840
+ provide: NG_VALUE_ACCESSOR,
4841
+ useExisting: forwardRef(() => OtpComponent),
4842
+ multi: true
4843
+ }],
4844
+ template: `
4845
+ <div class="relative flex items-center gap-2">
4846
+ <input
4847
+ #hiddenInput
4848
+ type="text"
4849
+ inputmode="numeric"
4850
+ autocomplete="one-time-code"
4851
+ [maxLength]="length"
4852
+ class="absolute inset-0 opacity-0 cursor-default z-20"
4853
+ [(ngModel)]="value"
4854
+ (input)="onInputChange($event)"
4855
+ (focus)="isFocused = true"
4856
+ (blur)="isFocused = false"
4857
+ [disabled]="disabled"
4858
+ />
4859
+
4860
+ <div *ngIf="auto" class="flex items-center gap-2 pointer-events-none">
4861
+ <div class="flex items-center">
4862
+ <div *ngFor="let _ of [].constructor(length); let i = index"
4863
+ [class]="getSlotClass(i)"
4864
+ >
4865
+ {{ value[i] || '' }}
4866
+ <div *ngIf="isFocused && value.length === i" class="absolute inset-0 flex items-center justify-center">
4867
+ <div class="h-4 w-px animate-caret-blink bg-foreground duration-1000"></div>
4868
+ </div>
4869
+ </div>
4870
+ </div>
4871
+ </div>
4872
+
4873
+ <div *ngIf="!auto" class="flex items-center gap-2 pointer-events-none">
4874
+ <ng-content></ng-content>
4875
+ </div>
4876
+ </div>
4877
+ `
4878
+ }]
4879
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { length: [{
4880
+ type: Input
4881
+ }], disabled: [{
4882
+ type: Input
4883
+ }], auto: [{
4884
+ type: Input
4885
+ }] } });
4886
+
4887
+ class OtpSlotComponent {
4888
+ char = '';
4889
+ isActive = false;
4890
+ isFirst = false; // New Input
4891
+ isLast = false; // New Input
4892
+ class = '';
4893
+ cn = cn;
4894
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpSlotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4895
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: OtpSlotComponent, isStandalone: true, selector: "tolle-otp-slot", inputs: { char: "char", isActive: "isActive", isFirst: "isFirst", isLast: "isLast", class: "class" }, ngImport: i0, template: `
4896
+ <div [class]="cn(
4897
+ 'relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all',
4898
+ 'bg-background',
4899
+ isFirst ? 'rounded-l-md border-l' : '',
4900
+ isLast ? 'rounded-r-md' : '',
4901
+ isActive ? 'z-10 ring-2 ring-ring ring-offset-background' : '',
4902
+ class
4903
+ )">
4904
+ {{ char || '' }}
4905
+ <div *ngIf="isActive && !char" class="pointer-events-none absolute inset-0 flex items-center justify-center">
4906
+ <div class="h-4 w-px animate-caret-blink bg-foreground duration-1000"></div>
4907
+ </div>
4908
+ </div>
4909
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4910
+ }
4911
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpSlotComponent, decorators: [{
4912
+ type: Component,
4913
+ args: [{
4914
+ selector: 'tolle-otp-slot',
4915
+ standalone: true,
4916
+ imports: [NgIf],
4917
+ template: `
4918
+ <div [class]="cn(
4919
+ 'relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all',
4920
+ 'bg-background',
4921
+ isFirst ? 'rounded-l-md border-l' : '',
4922
+ isLast ? 'rounded-r-md' : '',
4923
+ isActive ? 'z-10 ring-2 ring-ring ring-offset-background' : '',
4924
+ class
4925
+ )">
4926
+ {{ char || '' }}
4927
+ <div *ngIf="isActive && !char" class="pointer-events-none absolute inset-0 flex items-center justify-center">
4928
+ <div class="h-4 w-px animate-caret-blink bg-foreground duration-1000"></div>
4929
+ </div>
4930
+ </div>
4931
+ `
4932
+ }]
4933
+ }], propDecorators: { char: [{
4934
+ type: Input
4935
+ }], isActive: [{
4936
+ type: Input
4937
+ }], isFirst: [{
4938
+ type: Input
4939
+ }], isLast: [{
4940
+ type: Input
4941
+ }], class: [{
4942
+ type: Input
4943
+ }] } });
4944
+
4945
+ class OtpGroupComponent {
4946
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4947
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: OtpGroupComponent, isStandalone: true, selector: "tolle-otp-group", ngImport: i0, template: `
4948
+ <div class="flex items-center"><ng-content></ng-content></div>
4949
+ `, isInline: true, styles: [""] });
4950
+ }
4951
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpGroupComponent, decorators: [{
4952
+ type: Component,
4953
+ args: [{ selector: 'tolle-otp-group', standalone: true, imports: [], template: `
4954
+ <div class="flex items-center"><ng-content></ng-content></div>
4955
+ ` }]
4956
+ }] });
4957
+
4958
+ class PopoverComponent {
4959
+ placement = 'bottom';
4960
+ onOpen = new EventEmitter();
4961
+ onClose = new EventEmitter();
4962
+ triggerEl;
4963
+ popoverEl;
4964
+ isOpen = false;
4965
+ cleanup;
4966
+ toggle() {
4967
+ this.isOpen ? this.close() : this.open();
4968
+ }
4969
+ open() {
4970
+ this.isOpen = true;
4971
+ this.onOpen.emit();
4972
+ setTimeout(() => this.updatePosition());
4973
+ }
4974
+ close() {
4975
+ this.isOpen = false;
4976
+ this.onClose.emit();
4977
+ if (this.cleanup)
4978
+ this.cleanup();
4979
+ }
4980
+ updatePosition() {
4981
+ if (!this.triggerEl || !this.popoverEl)
4982
+ return;
4983
+ this.cleanup = autoUpdate(this.triggerEl.nativeElement, this.popoverEl.nativeElement, () => {
4984
+ computePosition(this.triggerEl.nativeElement, this.popoverEl.nativeElement, {
4985
+ placement: this.placement,
4986
+ middleware: [offset(8), flip(), shift({ padding: 8 })],
4987
+ }).then(({ x, y }) => {
4988
+ Object.assign(this.popoverEl.nativeElement.style, {
4989
+ left: `${x}px`,
4990
+ top: `${y}px`,
4991
+ });
4992
+ });
4993
+ });
4994
+ }
4995
+ onClickOutside(event) {
4996
+ if (this.isOpen &&
4997
+ !this.triggerEl.nativeElement.contains(event.target) &&
4998
+ !this.popoverEl?.nativeElement.contains(event.target)) {
4999
+ this.close();
5000
+ }
5001
+ }
5002
+ ngOnDestroy() {
5003
+ if (this.cleanup)
5004
+ this.cleanup();
5005
+ }
5006
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5007
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: PopoverComponent, isStandalone: true, selector: "tolle-popover", inputs: { placement: "placement" }, outputs: { onOpen: "onOpen", onClose: "onClose" }, host: { listeners: { "document:mousedown": "onClickOutside($event)" } }, viewQueries: [{ propertyName: "triggerEl", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "popoverEl", first: true, predicate: ["popover"], descendants: true }], ngImport: i0, template: `
5008
+ <div class="inline-block" #trigger (click)="toggle()">
5009
+ <ng-content select="[trigger]"></ng-content>
5010
+ </div>
5011
+
5012
+ <div
5013
+ *ngIf="isOpen"
5014
+ #popover
5015
+ class="absolute top-0 left-0 w-max z-[9999]"
5016
+ >
5017
+ <ng-content></ng-content>
5018
+ </div>
5019
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
5020
+ }
5021
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PopoverComponent, decorators: [{
5022
+ type: Component,
5023
+ args: [{
5024
+ selector: 'tolle-popover',
5025
+ standalone: true,
5026
+ imports: [CommonModule],
5027
+ template: `
5028
+ <div class="inline-block" #trigger (click)="toggle()">
5029
+ <ng-content select="[trigger]"></ng-content>
5030
+ </div>
5031
+
5032
+ <div
5033
+ *ngIf="isOpen"
5034
+ #popover
5035
+ class="absolute top-0 left-0 w-max z-[9999]"
5036
+ >
5037
+ <ng-content></ng-content>
5038
+ </div>
5039
+ `,
5040
+ }]
5041
+ }], propDecorators: { placement: [{
5042
+ type: Input
5043
+ }], onOpen: [{
5044
+ type: Output
5045
+ }], onClose: [{
5046
+ type: Output
5047
+ }], triggerEl: [{
5048
+ type: ViewChild,
5049
+ args: ['trigger']
5050
+ }], popoverEl: [{
5051
+ type: ViewChild,
5052
+ args: ['popover']
5053
+ }], onClickOutside: [{
5054
+ type: HostListener,
5055
+ args: ['document:mousedown', ['$event']]
5056
+ }] } });
5057
+
5058
+ class PopoverContentComponent {
5059
+ class = '';
5060
+ cn = cn;
5061
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PopoverContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5062
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: PopoverContentComponent, isStandalone: true, selector: "tolle-popover-content", inputs: { class: "class" }, ngImport: i0, template: `
5063
+ <div
5064
+ [class]="cn(
5065
+ 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
5066
+ 'animate-in fade-in zoom-in-95 duration-200',
5067
+ class
5068
+ )"
5069
+ role="dialog"
5070
+ >
5071
+ <ng-content></ng-content>
5072
+ </div>
5073
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
5074
+ }
5075
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PopoverContentComponent, decorators: [{
5076
+ type: Component,
5077
+ args: [{
5078
+ selector: 'tolle-popover-content',
5079
+ standalone: true,
5080
+ imports: [CommonModule],
5081
+ template: `
5082
+ <div
5083
+ [class]="cn(
5084
+ 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
5085
+ 'animate-in fade-in zoom-in-95 duration-200',
5086
+ class
5087
+ )"
5088
+ role="dialog"
5089
+ >
5090
+ <ng-content></ng-content>
5091
+ </div>
5092
+ `
5093
+ }]
5094
+ }], propDecorators: { class: [{
5095
+ type: Input
5096
+ }] } });
5097
+
5098
+ class RadioService {
5099
+ selectedValueSource = new BehaviorSubject(null);
5100
+ selectedValue$ = this.selectedValueSource.asObservable();
5101
+ disabledSource = new BehaviorSubject(false);
5102
+ disabled$ = this.disabledSource.asObservable();
5103
+ select(value) {
5104
+ if (!this.disabledSource.value) {
5105
+ this.selectedValueSource.next(value);
5106
+ }
5107
+ }
5108
+ setDisabled(isDisabled) {
5109
+ this.disabledSource.next(isDisabled);
5110
+ }
5111
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5112
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioService });
5113
+ }
5114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioService, decorators: [{
5115
+ type: Injectable
5116
+ }] });
5117
+
5118
+ class RadioGroupComponent {
5119
+ radioService;
5120
+ class = '';
5121
+ disabled = false;
5122
+ name = `radio-group-${Math.random().toString(36).substring(2, 9)}`;
5123
+ value;
5124
+ onChange = () => { };
5125
+ onTouched = () => { };
5126
+ constructor(radioService) {
5127
+ this.radioService = radioService;
5128
+ this.radioService.selectedValue$.subscribe(val => {
5129
+ this.value = val;
5130
+ this.onChange(val);
5131
+ });
5132
+ }
5133
+ setDisabledState(isDisabled) {
5134
+ this.disabled = isDisabled;
5135
+ this.radioService.setDisabled(isDisabled);
5136
+ }
5137
+ ngOnChanges() {
5138
+ this.radioService.setDisabled(this.disabled);
5139
+ }
5140
+ writeValue(value) {
5141
+ this.value = value;
5142
+ this.radioService.select(value);
5143
+ }
5144
+ registerOnChange(fn) { this.onChange = fn; }
5145
+ registerOnTouched(fn) { this.onTouched = fn; }
5146
+ cn = cn;
5147
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioGroupComponent, deps: [{ token: RadioService }], target: i0.ɵɵFactoryTarget.Component });
5148
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: RadioGroupComponent, isStandalone: true, selector: "tolle-radio-group", inputs: { class: "class", disabled: "disabled", name: "name" }, providers: [
5149
+ RadioService,
5150
+ {
5151
+ provide: NG_VALUE_ACCESSOR,
5152
+ useExisting: forwardRef(() => RadioGroupComponent),
5153
+ multi: true
5154
+ }
5155
+ ], usesOnChanges: true, ngImport: i0, template: `
5156
+ <div [class]="cn('grid gap-2', class)" role="radiogroup">
5157
+ <ng-content></ng-content>
5158
+ </div>
5159
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
5160
+ }
5161
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioGroupComponent, decorators: [{
5162
+ type: Component,
5163
+ args: [{
5164
+ selector: 'tolle-radio-group',
5165
+ standalone: true,
5166
+ imports: [CommonModule],
5167
+ providers: [
5168
+ RadioService,
5169
+ {
5170
+ provide: NG_VALUE_ACCESSOR,
5171
+ useExisting: forwardRef(() => RadioGroupComponent),
5172
+ multi: true
5173
+ }
5174
+ ],
5175
+ template: `
5176
+ <div [class]="cn('grid gap-2', class)" role="radiogroup">
5177
+ <ng-content></ng-content>
5178
+ </div>
5179
+ `
5180
+ }]
5181
+ }], ctorParameters: () => [{ type: RadioService }], propDecorators: { class: [{
5182
+ type: Input
5183
+ }], disabled: [{
5184
+ type: Input
5185
+ }], name: [{
5186
+ type: Input
5187
+ }] } });
5188
+
5189
+ class RadioItemComponent {
5190
+ radioService;
5191
+ cdr;
5192
+ value;
5193
+ disabled = false;
5194
+ class = '';
5195
+ isSelected = false;
5196
+ groupDisabled = false;
5197
+ sub = new Subscription();
5198
+ get isEffectiveDisabled() {
5199
+ return this.disabled || this.groupDisabled;
5200
+ }
5201
+ // Inject ChangeDetectorRef to ensure UI updates when service emits
5202
+ constructor(radioService, cdr) {
5203
+ this.radioService = radioService;
5204
+ this.cdr = cdr;
5205
+ }
5206
+ ngOnInit() {
5207
+ // Listen for selection changes
5208
+ this.sub.add(this.radioService.selectedValue$.subscribe(val => {
5209
+ this.isSelected = (val === this.value);
5210
+ this.cdr.markForCheck(); // Trigger UI refresh
5211
+ }));
5212
+ // Listen for group-level disabled state
5213
+ this.sub.add(this.radioService.disabled$.subscribe(dis => {
5214
+ this.groupDisabled = dis;
5215
+ this.cdr.markForCheck(); // Trigger UI refresh
5216
+ }));
5217
+ }
5218
+ select() {
5219
+ this.radioService.select(this.value);
5220
+ }
5221
+ ngOnDestroy() {
5222
+ this.sub.unsubscribe(); // Clean up subscriptions
5223
+ }
5224
+ cn = cn;
5225
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioItemComponent, deps: [{ token: RadioService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
5226
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: RadioItemComponent, isStandalone: true, selector: "tolle-radio-item", inputs: { value: "value", disabled: "disabled", class: "class" }, ngImport: i0, template: `
5227
+ <div
5228
+ (click)="!isEffectiveDisabled && select()"
5229
+ [class]="cn(
5230
+ 'flex items-center space-x-2 group py-1',
5231
+ isEffectiveDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
5232
+ )"
5233
+ >
5234
+ <div [class]="cn(
5235
+ 'aspect-square h-4 w-4 rounded-full border border-primary text-primary transition-all flex items-center justify-center',
5236
+ 'ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
5237
+ !isEffectiveDisabled && 'group-hover:border-primary/70',
5238
+ isSelected ? 'bg-background' : 'bg-transparent',
5239
+ class
5240
+ )">
5241
+ <div
5242
+ *ngIf="isSelected"
5243
+ class="h-2.5 w-2.5 rounded-full bg-primary animate-in zoom-in-50 duration-200"
5244
+ ></div>
5245
+ </div>
5246
+
5247
+ <label class="text-sm font-medium leading-none select-none" [class.cursor-pointer]="!isEffectiveDisabled">
5248
+ <ng-content></ng-content>
5249
+ </label>
5250
+ </div>
5251
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
5252
+ }
5253
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RadioItemComponent, decorators: [{
5254
+ type: Component,
5255
+ args: [{
5256
+ selector: 'tolle-radio-item',
5257
+ standalone: true,
5258
+ imports: [CommonModule],
5259
+ template: `
5260
+ <div
5261
+ (click)="!isEffectiveDisabled && select()"
5262
+ [class]="cn(
5263
+ 'flex items-center space-x-2 group py-1',
5264
+ isEffectiveDisabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
5265
+ )"
5266
+ >
5267
+ <div [class]="cn(
5268
+ 'aspect-square h-4 w-4 rounded-full border border-primary text-primary transition-all flex items-center justify-center',
5269
+ 'ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
5270
+ !isEffectiveDisabled && 'group-hover:border-primary/70',
5271
+ isSelected ? 'bg-background' : 'bg-transparent',
5272
+ class
5273
+ )">
5274
+ <div
5275
+ *ngIf="isSelected"
5276
+ class="h-2.5 w-2.5 rounded-full bg-primary animate-in zoom-in-50 duration-200"
5277
+ ></div>
5278
+ </div>
5279
+
5280
+ <label class="text-sm font-medium leading-none select-none" [class.cursor-pointer]="!isEffectiveDisabled">
5281
+ <ng-content></ng-content>
5282
+ </label>
5283
+ </div>
5284
+ `
5285
+ }]
5286
+ }], ctorParameters: () => [{ type: RadioService }, { type: i0.ChangeDetectorRef }], propDecorators: { value: [{
5287
+ type: Input
5288
+ }], disabled: [{
5289
+ type: Input
5290
+ }], class: [{
5291
+ type: Input
5292
+ }] } });
5293
+
4227
5294
  /*
4228
5295
  * Public API Surface of tolle
4229
5296
  */
@@ -4232,5 +5299,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4232
5299
  * Generated bundle index. Do not edit.
4233
5300
  */
4234
5301
 
4235
- export { AccordionComponent, AccordionItemComponent, BadgeComponent, ButtonComponent, ButtonGroupComponent, CalendarComponent, CardComponent, CardContentComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CheckboxComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, DropdownItemComponent, DropdownLabelComponent, DropdownMenuComponent, DropdownSeparatorComponent, DropdownTriggerDirective, InputComponent, MaskedInputComponent, Modal, ModalComponent, ModalRef, ModalService, ModalStackService, MultiSelectComponent, PaginationComponent, RangeCalendarComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SkeletonComponent, SwitchComponent, TOLLE_CONFIG, TextareaComponent, ThemeService, ToastContainerComponent, ToastService, TolleCellDirective, TooltipDirective, cn, provideTolleConfig };
4236
- //# sourceMappingURL=tolle_-tolle-ui.mjs.map
5302
+ export { AccordionComponent, AccordionItemComponent, AlertComponent, AvatarComponent, AvatarFallbackComponent, BadgeComponent, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbLinkComponent, BreadcrumbSeparatorComponent, ButtonComponent, ButtonGroupComponent, CalendarComponent, CardComponent, CardContentComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CheckboxComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, DropdownItemComponent, DropdownLabelComponent, DropdownMenuComponent, DropdownSeparatorComponent, DropdownTriggerDirective, EmptyStateComponent, InputComponent, MaskedInputComponent, Modal, ModalComponent, ModalRef, ModalService, ModalStackService, MultiSelectComponent, OtpComponent, OtpGroupComponent, OtpSlotComponent, PaginationComponent, PopoverComponent, PopoverContentComponent, RadioGroupComponent, RadioItemComponent, RangeCalendarComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SkeletonComponent, SwitchComponent, TOLLE_CONFIG, TextareaComponent, ThemeService, ToastContainerComponent, ToastService, TolleCellDirective, TooltipDirective, cn, provideTolleConfig };
5303
+ //# sourceMappingURL=tolle-ui.mjs.map