@tolle_/tolle-ui 0.0.19-beta → 0.0.22-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.
- package/esm2022/lib/badge.component.mjs +4 -4
- package/esm2022/lib/button.component.mjs +18 -13
- package/esm2022/lib/card.component.mjs +3 -3
- package/esm2022/lib/data-table.component.mjs +1 -1
- package/esm2022/lib/date-picker.component.mjs +2 -2
- package/esm2022/lib/date-range-picker.component.mjs +1 -1
- package/esm2022/lib/input.component.mjs +125 -32
- package/esm2022/lib/masked-input.component.mjs +118 -30
- package/esm2022/lib/multi-select.component.mjs +1 -1
- package/esm2022/lib/otp-slot.component.mjs +35 -23
- package/esm2022/lib/select.component.mjs +38 -9
- package/esm2022/lib/textarea.component.mjs +130 -26
- package/esm2022/lib/theme.service.mjs +283 -75
- package/fesm2022/tolle-ui.mjs +751 -212
- package/fesm2022/tolle-ui.mjs.map +1 -1
- package/lib/button.component.d.ts +2 -3
- package/lib/input.component.d.ts +11 -3
- package/lib/masked-input.component.d.ts +7 -1
- package/lib/otp-slot.component.d.ts +1 -0
- package/lib/select.component.d.ts +1 -0
- package/lib/textarea.component.d.ts +8 -1
- package/lib/theme.service.d.ts +46 -3
- package/package.json +1 -1
- package/preset.js +47 -33
- package/theme.css +204 -168
package/fesm2022/tolle-ui.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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,
|
|
4
|
+
import { Component, Input, forwardRef, ViewChild, Injectable, Optional, HostListener, 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
6
|
import { CommonModule, isPlatformBrowser, DOCUMENT, NgIf, NgTemplateOutlet } from '@angular/common';
|
|
7
7
|
import { cva } from 'class-variance-authority';
|
|
@@ -18,15 +18,15 @@ function cn(...inputs) {
|
|
|
18
18
|
return twMerge(clsx(inputs));
|
|
19
19
|
}
|
|
20
20
|
|
|
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]", {
|
|
21
|
+
const buttonVariants = cva("tolle-button-base 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]", {
|
|
22
22
|
variants: {
|
|
23
23
|
variant: {
|
|
24
24
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
25
25
|
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
26
|
-
outline: "border border-input hover:bg-accent hover:text-accent-foreground",
|
|
26
|
+
outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
27
27
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
28
28
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
29
|
-
link: "underline-offset-4 hover:underline
|
|
29
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
30
30
|
},
|
|
31
31
|
size: {
|
|
32
32
|
default: "h-10 px-4 py-2",
|
|
@@ -38,7 +38,7 @@ const buttonVariants = cva("inline-flex items-center justify-center rounded-md t
|
|
|
38
38
|
icon: "h-10 w-10",
|
|
39
39
|
"icon-lg": "h-11 w-11",
|
|
40
40
|
},
|
|
41
|
-
busy: { true: "relative !cursor-wait !pointer-events-none" }
|
|
41
|
+
busy: { true: "tolle-button--busy relative !cursor-wait !pointer-events-none" }
|
|
42
42
|
},
|
|
43
43
|
defaultVariants: {
|
|
44
44
|
variant: "default",
|
|
@@ -51,16 +51,15 @@ class ButtonComponent {
|
|
|
51
51
|
size = 'default';
|
|
52
52
|
disabled = false;
|
|
53
53
|
busy = false;
|
|
54
|
-
readonly = false;
|
|
55
54
|
get computedClass() {
|
|
56
55
|
return cn(buttonVariants({
|
|
57
56
|
variant: this.variant,
|
|
58
57
|
size: this.size,
|
|
59
58
|
busy: this.busy
|
|
60
|
-
}),
|
|
59
|
+
}), this.class);
|
|
61
60
|
}
|
|
62
61
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
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",
|
|
62
|
+
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" }, host: { classAttribute: "tolle-button-wrapper inline-block align-middle" }, ngImport: i0, template: `
|
|
64
63
|
<button
|
|
65
64
|
[class]="computedClass"
|
|
66
65
|
[disabled]="disabled || busy"
|
|
@@ -77,11 +76,15 @@ class ButtonComponent {
|
|
|
77
76
|
<ng-content></ng-content>
|
|
78
77
|
</span>
|
|
79
78
|
</button>
|
|
80
|
-
`, isInline: true,
|
|
79
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
81
80
|
}
|
|
82
81
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
83
82
|
type: Component,
|
|
84
|
-
args: [{
|
|
83
|
+
args: [{
|
|
84
|
+
selector: 'tolle-button',
|
|
85
|
+
standalone: true,
|
|
86
|
+
imports: [CommonModule],
|
|
87
|
+
template: `
|
|
85
88
|
<button
|
|
86
89
|
[class]="computedClass"
|
|
87
90
|
[disabled]="disabled || busy"
|
|
@@ -98,7 +101,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
98
101
|
<ng-content></ng-content>
|
|
99
102
|
</span>
|
|
100
103
|
</button>
|
|
101
|
-
`,
|
|
104
|
+
`,
|
|
105
|
+
host: {
|
|
106
|
+
'class': 'tolle-button-wrapper inline-block align-middle'
|
|
107
|
+
}
|
|
108
|
+
}]
|
|
102
109
|
}], propDecorators: { class: [{
|
|
103
110
|
type: Input
|
|
104
111
|
}], variant: [{
|
|
@@ -109,12 +116,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
109
116
|
type: Input
|
|
110
117
|
}], busy: [{
|
|
111
118
|
type: Input
|
|
112
|
-
}], readonly: [{
|
|
113
|
-
type: Input
|
|
114
119
|
}] } });
|
|
115
120
|
|
|
116
121
|
class InputComponent {
|
|
117
122
|
cdr;
|
|
123
|
+
inputElement;
|
|
118
124
|
id = `input-${Math.random().toString(36).substr(2, 9)}`;
|
|
119
125
|
label = '';
|
|
120
126
|
hint = '';
|
|
@@ -128,12 +134,20 @@ class InputComponent {
|
|
|
128
134
|
disabled = false;
|
|
129
135
|
readonly = false;
|
|
130
136
|
error = false;
|
|
137
|
+
// Focus behavior
|
|
138
|
+
hideHintOnFocus = true;
|
|
131
139
|
value = '';
|
|
132
140
|
onChange = () => { };
|
|
133
141
|
onTouched = () => { };
|
|
142
|
+
isFocused = false;
|
|
134
143
|
constructor(cdr) {
|
|
135
144
|
this.cdr = cdr;
|
|
136
145
|
}
|
|
146
|
+
ngAfterViewInit() {
|
|
147
|
+
if (this.inputElement?.nativeElement.hasAttribute('autofocus')) {
|
|
148
|
+
setTimeout(() => this.inputElement.nativeElement.focus());
|
|
149
|
+
}
|
|
150
|
+
}
|
|
137
151
|
writeValue(value) {
|
|
138
152
|
this.value = value;
|
|
139
153
|
this.cdr.markForCheck();
|
|
@@ -151,66 +165,132 @@ class InputComponent {
|
|
|
151
165
|
this.value = val;
|
|
152
166
|
this.onChange(val);
|
|
153
167
|
}
|
|
168
|
+
onFocus() {
|
|
169
|
+
this.isFocused = true;
|
|
170
|
+
}
|
|
171
|
+
onBlur() {
|
|
172
|
+
this.isFocused = false;
|
|
173
|
+
this.onTouched();
|
|
174
|
+
}
|
|
175
|
+
focusInput() {
|
|
176
|
+
if (!this.disabled && this.inputElement) {
|
|
177
|
+
this.inputElement.nativeElement.focus();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
154
180
|
cn = cn;
|
|
181
|
+
get computedLabelClass() {
|
|
182
|
+
return cn("text-sm font-medium text-foreground leading-none transition-opacity duration-200", this.disabled && "opacity-50");
|
|
183
|
+
}
|
|
155
184
|
get computedContainerClass() {
|
|
156
|
-
return cn(
|
|
185
|
+
return cn(
|
|
186
|
+
// Base styles
|
|
187
|
+
"group relative flex items-center w-full rounded-md border transition-all duration-200", "bg-background",
|
|
188
|
+
// Border and shadow
|
|
189
|
+
"border-input shadow-sm",
|
|
157
190
|
// Sizing
|
|
158
|
-
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",
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
191
|
+
this.size === 'xs' && "h-8 px-2 gap-1.5 text-xs", this.size === 'sm' && "h-9 px-3 gap-2 text-sm", this.size === 'default' && "h-10 px-3 gap-2 text-sm", this.size === 'lg' && "h-11 px-4 gap-3 text-base",
|
|
192
|
+
// Focus state - SIMPLE AND ELEGANT LIKE ZARDUI
|
|
193
|
+
// The magic happens in CSS: border darkens automatically on focus
|
|
194
|
+
!(this.readonly || this.disabled) && [
|
|
195
|
+
"focus-within:border-primary/80",
|
|
196
|
+
"focus-within:ring-4",
|
|
197
|
+
"focus-within:ring-ring/30",
|
|
198
|
+
"focus-within:ring-offset-0",
|
|
199
|
+
"focus-within:shadow-none",
|
|
200
|
+
],
|
|
201
|
+
// Error state
|
|
202
|
+
this.error && [
|
|
203
|
+
"border-destructive",
|
|
204
|
+
!(this.readonly || this.disabled) && [
|
|
205
|
+
"focus-within:border-destructive/80",
|
|
206
|
+
"focus-within:ring-destructive/30"
|
|
207
|
+
]
|
|
208
|
+
],
|
|
209
|
+
// Disabled state
|
|
210
|
+
this.disabled && [
|
|
211
|
+
"cursor-not-allowed opacity-50",
|
|
212
|
+
"border-opacity-50"
|
|
213
|
+
],
|
|
214
|
+
// Readonly state
|
|
215
|
+
this.readonly && [
|
|
216
|
+
"cursor-default",
|
|
217
|
+
"border-dashed",
|
|
218
|
+
!this.disabled && "focus-within:ring-0 focus-within:border-opacity-100"
|
|
219
|
+
], this.containerClass);
|
|
165
220
|
}
|
|
166
221
|
get computedInputClass() {
|
|
167
|
-
return cn(
|
|
222
|
+
return cn(
|
|
223
|
+
// Base styles
|
|
224
|
+
"flex-1 bg-transparent border-none p-0", "placeholder:text-muted-foreground",
|
|
225
|
+
// Remove all default focus styles
|
|
226
|
+
"focus:outline-none focus:ring-0 focus:shadow-none",
|
|
227
|
+
// Text sizing
|
|
228
|
+
this.size === 'xs' && "text-xs", this.size === 'sm' && "text-sm", this.size === 'default' && "text-sm", this.size === 'lg' && "text-base",
|
|
229
|
+
// Cursor states
|
|
230
|
+
this.disabled && "cursor-not-allowed", this.readonly && "cursor-default",
|
|
231
|
+
// Text color
|
|
232
|
+
"text-foreground",
|
|
233
|
+
// Selection color
|
|
234
|
+
"selection:bg-primary/20 selection:text-foreground", this.class);
|
|
168
235
|
}
|
|
169
236
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InputComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
170
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: InputComponent, isStandalone: true, selector: "tolle-input", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", type: "type", placeholder: "placeholder", size: "size", containerClass: "containerClass", class: "class", disabled: "disabled", readonly: "readonly", error: "error" }, providers: [
|
|
237
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: InputComponent, isStandalone: true, selector: "tolle-input", inputs: { id: "id", label: "label", hint: "hint", errorMessage: "errorMessage", type: "type", placeholder: "placeholder", size: "size", containerClass: "containerClass", class: "class", disabled: "disabled", readonly: "readonly", error: "error", hideHintOnFocus: "hideHintOnFocus" }, providers: [
|
|
171
238
|
{
|
|
172
239
|
provide: NG_VALUE_ACCESSOR,
|
|
173
240
|
useExisting: forwardRef(() => InputComponent),
|
|
174
241
|
multi: true
|
|
175
242
|
}
|
|
176
|
-
], ngImport: i0, template: `
|
|
243
|
+
], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }], ngImport: i0, template: `
|
|
177
244
|
<div class="flex flex-col gap-1.5 w-full">
|
|
178
245
|
<label
|
|
179
246
|
*ngIf="label"
|
|
180
247
|
[for]="id"
|
|
181
|
-
[class
|
|
182
|
-
class="text-sm font-medium text-foreground leading-none transition-opacity"
|
|
183
|
-
>
|
|
248
|
+
[class]="computedLabelClass">
|
|
184
249
|
{{ label }}
|
|
185
250
|
</label>
|
|
186
251
|
|
|
187
|
-
<div
|
|
188
|
-
|
|
252
|
+
<div
|
|
253
|
+
[class]="computedContainerClass"
|
|
254
|
+
(click)="focusInput()"
|
|
255
|
+
>
|
|
256
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
189
257
|
<ng-content select="[prefix]"></ng-content>
|
|
190
258
|
</div>
|
|
191
259
|
|
|
192
260
|
<input
|
|
261
|
+
#inputElement
|
|
193
262
|
[id]="id"
|
|
194
263
|
[type]="type"
|
|
195
264
|
[placeholder]="placeholder"
|
|
196
265
|
[disabled]="disabled"
|
|
197
266
|
[readOnly]="readonly"
|
|
198
267
|
[(ngModel)]="value"
|
|
199
|
-
(blur)="
|
|
268
|
+
(blur)="onBlur()"
|
|
269
|
+
(focus)="onFocus()"
|
|
200
270
|
(input)="onInputChange($event)"
|
|
201
271
|
[class]="computedInputClass"
|
|
272
|
+
[attr.aria-invalid]="error"
|
|
273
|
+
[attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
|
|
202
274
|
/>
|
|
203
275
|
|
|
204
|
-
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
|
|
276
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
205
277
|
<ng-content select="[suffix]"></ng-content>
|
|
206
278
|
</div>
|
|
207
279
|
</div>
|
|
208
280
|
|
|
209
281
|
<ng-container *ngIf="!disabled">
|
|
210
|
-
<p
|
|
282
|
+
<p
|
|
283
|
+
*ngIf="hint && !error"
|
|
284
|
+
class="text-xs text-muted-foreground px-1 transition-opacity duration-200"
|
|
285
|
+
[class.opacity-0]="isFocused && hideHintOnFocus"
|
|
286
|
+
>
|
|
211
287
|
{{ hint }}
|
|
212
288
|
</p>
|
|
213
|
-
<p
|
|
289
|
+
<p
|
|
290
|
+
*ngIf="error && errorMessage"
|
|
291
|
+
[id]="id + '-error'"
|
|
292
|
+
class="text-xs text-destructive px-1"
|
|
293
|
+
>
|
|
214
294
|
{{ errorMessage }}
|
|
215
295
|
</p>
|
|
216
296
|
</ng-container>
|
|
@@ -235,46 +315,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
235
315
|
<label
|
|
236
316
|
*ngIf="label"
|
|
237
317
|
[for]="id"
|
|
238
|
-
[class
|
|
239
|
-
class="text-sm font-medium text-foreground leading-none transition-opacity"
|
|
240
|
-
>
|
|
318
|
+
[class]="computedLabelClass">
|
|
241
319
|
{{ label }}
|
|
242
320
|
</label>
|
|
243
321
|
|
|
244
|
-
<div
|
|
245
|
-
|
|
322
|
+
<div
|
|
323
|
+
[class]="computedContainerClass"
|
|
324
|
+
(click)="focusInput()"
|
|
325
|
+
>
|
|
326
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
246
327
|
<ng-content select="[prefix]"></ng-content>
|
|
247
328
|
</div>
|
|
248
329
|
|
|
249
330
|
<input
|
|
331
|
+
#inputElement
|
|
250
332
|
[id]="id"
|
|
251
333
|
[type]="type"
|
|
252
334
|
[placeholder]="placeholder"
|
|
253
335
|
[disabled]="disabled"
|
|
254
336
|
[readOnly]="readonly"
|
|
255
337
|
[(ngModel)]="value"
|
|
256
|
-
(blur)="
|
|
338
|
+
(blur)="onBlur()"
|
|
339
|
+
(focus)="onFocus()"
|
|
257
340
|
(input)="onInputChange($event)"
|
|
258
341
|
[class]="computedInputClass"
|
|
342
|
+
[attr.aria-invalid]="error"
|
|
343
|
+
[attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
|
|
259
344
|
/>
|
|
260
345
|
|
|
261
|
-
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
|
|
346
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
262
347
|
<ng-content select="[suffix]"></ng-content>
|
|
263
348
|
</div>
|
|
264
349
|
</div>
|
|
265
350
|
|
|
266
351
|
<ng-container *ngIf="!disabled">
|
|
267
|
-
<p
|
|
352
|
+
<p
|
|
353
|
+
*ngIf="hint && !error"
|
|
354
|
+
class="text-xs text-muted-foreground px-1 transition-opacity duration-200"
|
|
355
|
+
[class.opacity-0]="isFocused && hideHintOnFocus"
|
|
356
|
+
>
|
|
268
357
|
{{ hint }}
|
|
269
358
|
</p>
|
|
270
|
-
<p
|
|
359
|
+
<p
|
|
360
|
+
*ngIf="error && errorMessage"
|
|
361
|
+
[id]="id + '-error'"
|
|
362
|
+
class="text-xs text-destructive px-1"
|
|
363
|
+
>
|
|
271
364
|
{{ errorMessage }}
|
|
272
365
|
</p>
|
|
273
366
|
</ng-container>
|
|
274
367
|
</div>
|
|
275
368
|
`,
|
|
276
369
|
}]
|
|
277
|
-
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: {
|
|
370
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { inputElement: [{
|
|
371
|
+
type: ViewChild,
|
|
372
|
+
args: ['inputElement']
|
|
373
|
+
}], id: [{
|
|
278
374
|
type: Input
|
|
279
375
|
}], label: [{
|
|
280
376
|
type: Input
|
|
@@ -298,6 +394,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
298
394
|
type: Input
|
|
299
395
|
}], error: [{
|
|
300
396
|
type: Input
|
|
397
|
+
}], hideHintOnFocus: [{
|
|
398
|
+
type: Input
|
|
301
399
|
}] } });
|
|
302
400
|
|
|
303
401
|
class CardComponent {
|
|
@@ -305,7 +403,7 @@ class CardComponent {
|
|
|
305
403
|
cn = cn;
|
|
306
404
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
307
405
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CardComponent, isStandalone: true, selector: "tolle-card", inputs: { class: "class" }, ngImport: i0, template: `
|
|
308
|
-
<div [class]="cn('rounded-
|
|
406
|
+
<div [class]="cn('rounded-md border border-border bg-card text-card-foreground shadow', class)">
|
|
309
407
|
<ng-content></ng-content>
|
|
310
408
|
</div>
|
|
311
409
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
@@ -317,7 +415,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
317
415
|
standalone: true,
|
|
318
416
|
imports: [CommonModule],
|
|
319
417
|
template: `
|
|
320
|
-
<div [class]="cn('rounded-
|
|
418
|
+
<div [class]="cn('rounded-md border border-border bg-card text-card-foreground shadow', class)">
|
|
321
419
|
<ng-content></ng-content>
|
|
322
420
|
</div>
|
|
323
421
|
`,
|
|
@@ -496,6 +594,7 @@ class SelectComponent {
|
|
|
496
594
|
readonly = false;
|
|
497
595
|
trigger;
|
|
498
596
|
popover;
|
|
597
|
+
container;
|
|
499
598
|
items;
|
|
500
599
|
sub = new Subscription();
|
|
501
600
|
searchQuery = '';
|
|
@@ -519,15 +618,38 @@ class SelectComponent {
|
|
|
519
618
|
this.close();
|
|
520
619
|
}));
|
|
521
620
|
}
|
|
522
|
-
//
|
|
621
|
+
// SIMPLIFIED: Zardui-inspired trigger styling
|
|
523
622
|
get computedTriggerClass() {
|
|
524
|
-
return cn(
|
|
623
|
+
return cn(
|
|
624
|
+
// Base styles
|
|
625
|
+
'flex w-full items-center justify-between rounded-md border transition-all duration-200', 'bg-background text-foreground',
|
|
626
|
+
// Border and shadow
|
|
627
|
+
'border-input shadow-sm',
|
|
525
628
|
// Sizing
|
|
526
629
|
this.size === 'xs' && 'h-8 px-2 text-xs', this.size === 'sm' && 'h-9 px-3 text-sm', this.size === 'default' && 'h-10 px-3 text-sm', this.size === 'lg' && 'h-11 px-4 text-base',
|
|
527
|
-
//
|
|
528
|
-
!(this.readonly || this.disabled) &&
|
|
529
|
-
|
|
530
|
-
|
|
630
|
+
// Focus state - SIMPLE LIKE ZARDUI
|
|
631
|
+
!(this.readonly || this.disabled) && [
|
|
632
|
+
'focus:outline-none',
|
|
633
|
+
'focus:ring-4',
|
|
634
|
+
'focus:ring-ring/30',
|
|
635
|
+
'focus:ring-offset-0',
|
|
636
|
+
'focus:shadow-none',
|
|
637
|
+
// Border darkens on focus automatically
|
|
638
|
+
'focus:border-primary/80'
|
|
639
|
+
],
|
|
640
|
+
// Hover state
|
|
641
|
+
!(this.readonly || this.disabled) && 'hover:border-accent',
|
|
642
|
+
// Disabled state
|
|
643
|
+
this.disabled && [
|
|
644
|
+
'cursor-not-allowed opacity-50',
|
|
645
|
+
'border-opacity-50'
|
|
646
|
+
],
|
|
647
|
+
// Readonly state
|
|
648
|
+
this.readonly && [
|
|
649
|
+
'cursor-default',
|
|
650
|
+
'border-dashed',
|
|
651
|
+
!this.disabled && 'focus:ring-0 focus:border-opacity-100'
|
|
652
|
+
], this.class);
|
|
531
653
|
}
|
|
532
654
|
// UPDATED: Dynamic icon sizing relative to the trigger size
|
|
533
655
|
get iconClass() {
|
|
@@ -553,6 +675,8 @@ class SelectComponent {
|
|
|
553
675
|
}
|
|
554
676
|
open() {
|
|
555
677
|
this.isOpen = true;
|
|
678
|
+
// Trigger focus on the button for focus styling
|
|
679
|
+
this.trigger.nativeElement.focus();
|
|
556
680
|
// Tick to ensure DOM is rendered before positioning
|
|
557
681
|
setTimeout(() => this.updatePosition());
|
|
558
682
|
}
|
|
@@ -621,7 +745,7 @@ class SelectComponent {
|
|
|
621
745
|
useExisting: forwardRef(() => SelectComponent),
|
|
622
746
|
multi: true
|
|
623
747
|
}
|
|
624
|
-
], 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: `
|
|
748
|
+
], queries: [{ propertyName: "items", predicate: SelectItemComponent, descendants: true }], viewQueries: [{ propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "popover", first: true, predicate: ["popover"], descendants: true }, { propertyName: "container", first: true, predicate: ["container"], descendants: true }], ngImport: i0, template: `
|
|
625
749
|
<div [class]="cn('w-full', 'size-' + size)" #container>
|
|
626
750
|
<button
|
|
627
751
|
type="button"
|
|
@@ -661,7 +785,7 @@ class SelectComponent {
|
|
|
661
785
|
</div>
|
|
662
786
|
</div>
|
|
663
787
|
</div>
|
|
664
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error"] }] });
|
|
788
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error", "hideHintOnFocus"] }] });
|
|
665
789
|
}
|
|
666
790
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SelectComponent, decorators: [{
|
|
667
791
|
type: Component,
|
|
@@ -737,6 +861,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
737
861
|
}], popover: [{
|
|
738
862
|
type: ViewChild,
|
|
739
863
|
args: ['popover']
|
|
864
|
+
}], container: [{
|
|
865
|
+
type: ViewChild,
|
|
866
|
+
args: ['container']
|
|
740
867
|
}], items: [{
|
|
741
868
|
type: ContentChildren,
|
|
742
869
|
args: [SelectItemComponent, { descendants: true }]
|
|
@@ -929,7 +1056,7 @@ class BadgeComponent {
|
|
|
929
1056
|
get computedClass() {
|
|
930
1057
|
return cn(
|
|
931
1058
|
// Base styles - Pills are always rounded-full
|
|
932
|
-
'inline-flex items-center justify-center rounded-
|
|
1059
|
+
'inline-flex items-center justify-center rounded-md border px-2 py-0.5 font-medium transition-colors gap-1',
|
|
933
1060
|
// Variants (Google Dark Mode theme)
|
|
934
1061
|
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',
|
|
935
1062
|
// Sizing
|
|
@@ -947,7 +1074,7 @@ class BadgeComponent {
|
|
|
947
1074
|
<button
|
|
948
1075
|
*ngIf="removable"
|
|
949
1076
|
(click)="onRemove.emit($event)"
|
|
950
|
-
class="ml-1 -mr-1 rounded-
|
|
1077
|
+
class="ml-1 -mr-1 rounded-md p-0.5 hover:bg-foreground/20 transition-colors outline-none focus:ring-1 focus:ring-ring"
|
|
951
1078
|
>
|
|
952
1079
|
<i class="ri-close-line text-[1.1em]"></i>
|
|
953
1080
|
</button>
|
|
@@ -971,7 +1098,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
971
1098
|
<button
|
|
972
1099
|
*ngIf="removable"
|
|
973
1100
|
(click)="onRemove.emit($event)"
|
|
974
|
-
class="ml-1 -mr-1 rounded-
|
|
1101
|
+
class="ml-1 -mr-1 rounded-md p-0.5 hover:bg-foreground/20 transition-colors outline-none focus:ring-1 focus:ring-ring"
|
|
975
1102
|
>
|
|
976
1103
|
<i class="ri-close-line text-[1.1em]"></i>
|
|
977
1104
|
</button>
|
|
@@ -1343,116 +1470,255 @@ class ThemeService {
|
|
|
1343
1470
|
initializeTheme() {
|
|
1344
1471
|
if (!isPlatformBrowser(this.platformId))
|
|
1345
1472
|
return;
|
|
1346
|
-
//
|
|
1473
|
+
// LOGIC: User Saved Preferences > Config Defaults > System Preference
|
|
1347
1474
|
const savedTheme = localStorage.getItem('tolle-theme');
|
|
1475
|
+
const savedPrimary = localStorage.getItem('tolle-primary-color');
|
|
1476
|
+
const savedRadius = localStorage.getItem('tolle-radius');
|
|
1348
1477
|
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1478
|
+
console.log('Theme initialization:', {
|
|
1479
|
+
savedTheme,
|
|
1480
|
+
savedPrimary,
|
|
1481
|
+
savedRadius,
|
|
1482
|
+
config: this.config,
|
|
1483
|
+
systemPrefersDark
|
|
1484
|
+
});
|
|
1485
|
+
// 1. Determine Dark/Light Mode
|
|
1486
|
+
// Priority: Saved Theme > Config Default > System Preference
|
|
1487
|
+
let shouldBeDark = systemPrefersDark; // Start with system
|
|
1488
|
+
if (savedTheme) {
|
|
1489
|
+
// User has saved preference
|
|
1490
|
+
shouldBeDark = savedTheme === 'dark';
|
|
1491
|
+
}
|
|
1492
|
+
else if (this.config?.darkByDefault !== undefined) {
|
|
1493
|
+
// Use config default if no user preference
|
|
1494
|
+
shouldBeDark = this.config.darkByDefault;
|
|
1495
|
+
}
|
|
1496
|
+
// Apply theme mode
|
|
1353
1497
|
if (shouldBeDark) {
|
|
1354
|
-
this.enableDarkMode();
|
|
1498
|
+
this.enableDarkMode(false); // Don't save, we'll save after checking all preferences
|
|
1355
1499
|
}
|
|
1356
1500
|
else {
|
|
1357
|
-
this.disableDarkMode();
|
|
1501
|
+
this.disableDarkMode(false);
|
|
1358
1502
|
}
|
|
1359
|
-
|
|
1503
|
+
// 2. Apply Primary Color
|
|
1504
|
+
// Priority: Saved Color > Config Color
|
|
1360
1505
|
if (savedPrimary) {
|
|
1361
|
-
|
|
1506
|
+
// User has saved color preference
|
|
1507
|
+
this.setPrimaryColor(savedPrimary, false); // Don't save again
|
|
1362
1508
|
}
|
|
1363
1509
|
else if (this.config?.primaryColor) {
|
|
1364
|
-
|
|
1510
|
+
// Use config default if no user preference
|
|
1511
|
+
this.setPrimaryColor(this.config.primaryColor, true); // Save this as user preference
|
|
1512
|
+
}
|
|
1513
|
+
// 3. Apply Radius
|
|
1514
|
+
// Priority: Saved Radius > Config Radius
|
|
1515
|
+
if (savedRadius) {
|
|
1516
|
+
// User has saved radius preference
|
|
1517
|
+
this.setRadius(savedRadius, false);
|
|
1365
1518
|
}
|
|
1366
|
-
if (this.config?.radius) {
|
|
1367
|
-
|
|
1519
|
+
else if (this.config?.radius) {
|
|
1520
|
+
// Use config default if no user preference
|
|
1521
|
+
this.setRadius(this.config.radius, true);
|
|
1368
1522
|
}
|
|
1369
|
-
//
|
|
1370
|
-
if (
|
|
1371
|
-
|
|
1523
|
+
// Save theme mode preference if it came from config or system
|
|
1524
|
+
if (!savedTheme) {
|
|
1525
|
+
localStorage.setItem('tolle-theme', shouldBeDark ? 'dark' : 'light');
|
|
1372
1526
|
}
|
|
1373
1527
|
}
|
|
1374
1528
|
/**
|
|
1375
|
-
*
|
|
1529
|
+
* Sets the border radius for all components
|
|
1376
1530
|
*/
|
|
1377
|
-
|
|
1531
|
+
setRadius(radius, persist = true) {
|
|
1378
1532
|
if (!isPlatformBrowser(this.platformId))
|
|
1379
1533
|
return;
|
|
1380
1534
|
const root = this.document.documentElement;
|
|
1381
|
-
// Set
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1535
|
+
// Set the CSS variable
|
|
1536
|
+
this.renderer.setStyle(root, '--radius', radius);
|
|
1537
|
+
// Also update the dynamic styles to include radius calculations
|
|
1538
|
+
this.updateRadiusInDynamicStyles(radius);
|
|
1539
|
+
// Persist if needed
|
|
1540
|
+
if (persist) {
|
|
1541
|
+
localStorage.setItem('tolle-radius', radius);
|
|
1385
1542
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Updates the radius calculations in dynamic styles
|
|
1546
|
+
*/
|
|
1547
|
+
updateRadiusInDynamicStyles(radius) {
|
|
1548
|
+
const existingStyle = this.document.getElementById(this.styleId);
|
|
1549
|
+
if (existingStyle) {
|
|
1550
|
+
let css = existingStyle.textContent || '';
|
|
1551
|
+
// Update or add radius calculations
|
|
1552
|
+
if (css.includes('--radius:')) {
|
|
1553
|
+
// Replace existing radius declarations
|
|
1554
|
+
css = css.replace(/--radius:[^;]+;/g, `--radius: ${radius};`);
|
|
1555
|
+
}
|
|
1556
|
+
else {
|
|
1557
|
+
// Add radius to the beginning of :root
|
|
1558
|
+
css = css.replace(/:root\s*{/, `:root {\n --radius: ${radius};`);
|
|
1559
|
+
}
|
|
1560
|
+
// Update the calculated radius values in the CSS
|
|
1561
|
+
const radiusCalcRegex = /calc\(var\(--radius[^)]+\)/g;
|
|
1562
|
+
css = css.replace(radiusCalcRegex, (match) => {
|
|
1563
|
+
if (match.includes('- 2px')) {
|
|
1564
|
+
return `calc(${radius} - 2px)`;
|
|
1565
|
+
}
|
|
1566
|
+
else if (match.includes('- 4px')) {
|
|
1567
|
+
return `calc(${radius} - 4px)`;
|
|
1568
|
+
}
|
|
1569
|
+
return match;
|
|
1570
|
+
});
|
|
1571
|
+
existingStyle.textContent = css;
|
|
1389
1572
|
}
|
|
1390
1573
|
}
|
|
1391
1574
|
/**
|
|
1392
1575
|
* Generates full primary color palette (50-900) based on base color
|
|
1393
|
-
* Uses color-mix() for consistency with your existing approach
|
|
1394
1576
|
*/
|
|
1395
1577
|
generatePrimaryShades(baseColor) {
|
|
1578
|
+
// Convert hex to RGB
|
|
1579
|
+
const rgb = this.hexToRgb(baseColor);
|
|
1580
|
+
const rgbString = rgb ? `${rgb.r} ${rgb.g} ${rgb.b}` : '37 99 235';
|
|
1581
|
+
// Create lighter ring colors in RGB
|
|
1582
|
+
const ringLight = this.lightenColor(baseColor, 40);
|
|
1583
|
+
const ringLightRgb = this.hexToRgb(ringLight);
|
|
1584
|
+
const ringLightRgbString = ringLightRgb ? `${ringLightRgb.r} ${ringLightRgb.g} ${ringLightRgb.b}` : '96 165 250';
|
|
1585
|
+
const ringDark = this.lightenColor(baseColor, 20);
|
|
1586
|
+
const ringDarkRgb = this.hexToRgb(ringDark);
|
|
1587
|
+
const ringDarkRgbString = ringDarkRgb ? `${ringDarkRgb.r} ${ringDarkRgb.g} ${ringDarkRgb.b}` : '147 197 253';
|
|
1588
|
+
// Get current radius or use default
|
|
1589
|
+
const root = this.document.documentElement;
|
|
1590
|
+
const currentRadius = getComputedStyle(root).getPropertyValue('--radius').trim() || '0.5rem';
|
|
1396
1591
|
const css = `
|
|
1592
|
+
/* Override primary colors - this needs to come AFTER your main CSS */
|
|
1397
1593
|
:root {
|
|
1398
|
-
/* Primary
|
|
1399
|
-
--primary
|
|
1400
|
-
--primary-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
--
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
--primary-
|
|
1407
|
-
--primary-
|
|
1408
|
-
--primary-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
--primary-
|
|
1412
|
-
--
|
|
1413
|
-
--
|
|
1414
|
-
--
|
|
1415
|
-
--
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
--ring:
|
|
1594
|
+
/* Primary in RGB format for Tailwind opacity support */
|
|
1595
|
+
--primary: ${rgbString};
|
|
1596
|
+
--primary-foreground: ${this.getContrastColorRgb(baseColor)};
|
|
1597
|
+
|
|
1598
|
+
/* Radius */
|
|
1599
|
+
--radius: ${currentRadius};
|
|
1600
|
+
|
|
1601
|
+
/* Primary shades for light mode */
|
|
1602
|
+
--primary-50: ${this.hexToRgbString(this.lightenColor(baseColor, 90))};
|
|
1603
|
+
--primary-100: ${this.hexToRgbString(this.lightenColor(baseColor, 80))};
|
|
1604
|
+
--primary-200: ${this.hexToRgbString(this.lightenColor(baseColor, 60))};
|
|
1605
|
+
--primary-300: ${this.hexToRgbString(this.lightenColor(baseColor, 40))};
|
|
1606
|
+
--primary-400: ${this.hexToRgbString(this.lightenColor(baseColor, 20))};
|
|
1607
|
+
--primary-500: ${rgbString};
|
|
1608
|
+
--primary-600: ${this.hexToRgbString(this.darkenColor(baseColor, 20))};
|
|
1609
|
+
--primary-700: ${this.hexToRgbString(this.darkenColor(baseColor, 40))};
|
|
1610
|
+
--primary-800: ${this.hexToRgbString(this.darkenColor(baseColor, 60))};
|
|
1611
|
+
--primary-900: ${this.hexToRgbString(this.darkenColor(baseColor, 80))};
|
|
1612
|
+
|
|
1613
|
+
/* Update ring color to be lighter */
|
|
1614
|
+
--ring: ${ringLightRgbString};
|
|
1419
1615
|
}
|
|
1420
1616
|
|
|
1421
1617
|
.dark {
|
|
1422
|
-
/*
|
|
1423
|
-
--primary
|
|
1424
|
-
--primary-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
--primary-
|
|
1428
|
-
--primary-
|
|
1429
|
-
--primary-
|
|
1430
|
-
--primary-
|
|
1431
|
-
--primary-
|
|
1432
|
-
--primary-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
--primary-
|
|
1436
|
-
--
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
--
|
|
1440
|
-
--accent: color-mix(in srgb, var(--primary-800), transparent 80%);
|
|
1441
|
-
--accent-foreground: var(--primary-200);
|
|
1442
|
-
--ring: color-mix(in srgb, var(--primary-600), transparent 60%);
|
|
1618
|
+
/* For dark mode, we keep the primary color but adjust shades */
|
|
1619
|
+
--primary: ${rgbString};
|
|
1620
|
+
--primary-foreground: ${this.getContrastColorRgb(baseColor)};
|
|
1621
|
+
|
|
1622
|
+
/* Dark mode shades */
|
|
1623
|
+
--primary-50: ${this.hexToRgbString(this.darkenColor(baseColor, 85))};
|
|
1624
|
+
--primary-100: ${this.hexToRgbString(this.darkenColor(baseColor, 75))};
|
|
1625
|
+
--primary-200: ${this.hexToRgbString(this.darkenColor(baseColor, 65))};
|
|
1626
|
+
--primary-300: ${this.hexToRgbString(this.darkenColor(baseColor, 55))};
|
|
1627
|
+
--primary-400: ${this.hexToRgbString(this.darkenColor(baseColor, 45))};
|
|
1628
|
+
--primary-500: ${rgbString};
|
|
1629
|
+
--primary-600: ${this.hexToRgbString(this.lightenColor(baseColor, 20))};
|
|
1630
|
+
--primary-700: ${this.hexToRgbString(this.lightenColor(baseColor, 35))};
|
|
1631
|
+
--primary-800: ${this.hexToRgbString(this.lightenColor(baseColor, 50))};
|
|
1632
|
+
--primary-900: ${this.hexToRgbString(this.lightenColor(baseColor, 65))};
|
|
1633
|
+
|
|
1634
|
+
/* Update ring color for dark mode - lighter variant */
|
|
1635
|
+
--ring: ${ringDarkRgbString};
|
|
1443
1636
|
}
|
|
1444
1637
|
`;
|
|
1445
1638
|
this.injectDynamicStyles(css);
|
|
1446
1639
|
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Convert hex color to RGB object
|
|
1642
|
+
*/
|
|
1643
|
+
hexToRgb(hex) {
|
|
1644
|
+
// Remove # if present
|
|
1645
|
+
const cleanedHex = hex.replace('#', '');
|
|
1646
|
+
let r = 0, g = 0, b = 0;
|
|
1647
|
+
if (cleanedHex.length === 3) {
|
|
1648
|
+
r = parseInt(cleanedHex[0] + cleanedHex[0], 16);
|
|
1649
|
+
g = parseInt(cleanedHex[1] + cleanedHex[1], 16);
|
|
1650
|
+
b = parseInt(cleanedHex[2] + cleanedHex[2], 16);
|
|
1651
|
+
return { r, g, b };
|
|
1652
|
+
}
|
|
1653
|
+
if (cleanedHex.length === 6) {
|
|
1654
|
+
r = parseInt(cleanedHex.substring(0, 2), 16);
|
|
1655
|
+
g = parseInt(cleanedHex.substring(2, 4), 16);
|
|
1656
|
+
b = parseInt(cleanedHex.substring(4, 6), 16);
|
|
1657
|
+
return { r, g, b };
|
|
1658
|
+
}
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Convert hex to RGB string format for CSS (space-separated)
|
|
1663
|
+
*/
|
|
1664
|
+
hexToRgbString(hexColor) {
|
|
1665
|
+
const rgb = this.hexToRgb(hexColor);
|
|
1666
|
+
return rgb ? `${rgb.r} ${rgb.g} ${rgb.b}` : '37 99 235';
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Get contrast color in RGB format
|
|
1670
|
+
*/
|
|
1671
|
+
getContrastColorRgb(hexColor) {
|
|
1672
|
+
const contrast = this.getContrastColor(hexColor);
|
|
1673
|
+
const rgb = this.hexToRgb(contrast);
|
|
1674
|
+
return rgb ? `${rgb.r} ${rgb.g} ${rgb.b}` : '255 255 255';
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Lighten a hex color by a percentage
|
|
1678
|
+
*/
|
|
1679
|
+
lightenColor(color, percent) {
|
|
1680
|
+
const rgb = this.hexToRgb(color);
|
|
1681
|
+
if (!rgb)
|
|
1682
|
+
return color;
|
|
1683
|
+
// Lighten by percentage
|
|
1684
|
+
const r = Math.min(255, Math.floor(rgb.r + (255 - rgb.r) * (percent / 100)));
|
|
1685
|
+
const g = Math.min(255, Math.floor(rgb.g + (255 - rgb.g) * (percent / 100)));
|
|
1686
|
+
const b = Math.min(255, Math.floor(rgb.b + (255 - rgb.b) * (percent / 100)));
|
|
1687
|
+
// Convert back to hex
|
|
1688
|
+
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Darken a hex color by a percentage
|
|
1692
|
+
*/
|
|
1693
|
+
darkenColor(color, percent) {
|
|
1694
|
+
const rgb = this.hexToRgb(color);
|
|
1695
|
+
if (!rgb)
|
|
1696
|
+
return color;
|
|
1697
|
+
const factor = 1 - (percent / 100);
|
|
1698
|
+
const r = Math.max(0, Math.floor(rgb.r * factor));
|
|
1699
|
+
const g = Math.max(0, Math.floor(rgb.g * factor));
|
|
1700
|
+
const b = Math.max(0, Math.floor(rgb.b * factor));
|
|
1701
|
+
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Calculate contrast color (black or white) based on background color
|
|
1705
|
+
*/
|
|
1706
|
+
getContrastColor(hexColor) {
|
|
1707
|
+
const rgb = this.hexToRgb(hexColor);
|
|
1708
|
+
if (!rgb)
|
|
1709
|
+
return '#ffffff';
|
|
1710
|
+
// Calculate relative luminance
|
|
1711
|
+
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
|
1712
|
+
// Return black for light colors, white for dark colors
|
|
1713
|
+
return luminance > 0.5 ? '#000000' : '#ffffff';
|
|
1714
|
+
}
|
|
1447
1715
|
injectDynamicStyles(css) {
|
|
1448
1716
|
if (!isPlatformBrowser(this.platformId))
|
|
1449
1717
|
return;
|
|
1450
|
-
// Remove existing dynamic styles
|
|
1451
1718
|
const existingStyle = this.document.getElementById(this.styleId);
|
|
1452
1719
|
if (existingStyle) {
|
|
1453
1720
|
existingStyle.remove();
|
|
1454
1721
|
}
|
|
1455
|
-
// Create and inject new styles
|
|
1456
1722
|
const styleElement = this.document.createElement('style');
|
|
1457
1723
|
styleElement.id = this.styleId;
|
|
1458
1724
|
styleElement.textContent = css;
|
|
@@ -1462,23 +1728,29 @@ class ThemeService {
|
|
|
1462
1728
|
const isCurrentlyDark = this.document.documentElement.classList.contains('dark');
|
|
1463
1729
|
isCurrentlyDark ? this.disableDarkMode() : this.enableDarkMode();
|
|
1464
1730
|
}
|
|
1465
|
-
enableDarkMode() {
|
|
1731
|
+
enableDarkMode(saveToStorage = true) {
|
|
1466
1732
|
this.renderer.addClass(this.document.documentElement, 'dark');
|
|
1467
|
-
|
|
1733
|
+
if (saveToStorage) {
|
|
1734
|
+
localStorage.setItem('tolle-theme', 'dark');
|
|
1735
|
+
}
|
|
1468
1736
|
this.isDarkSubject.next(true);
|
|
1469
1737
|
}
|
|
1470
|
-
disableDarkMode() {
|
|
1738
|
+
disableDarkMode(saveToStorage = true) {
|
|
1471
1739
|
this.renderer.removeClass(this.document.documentElement, 'dark');
|
|
1472
|
-
|
|
1740
|
+
if (saveToStorage) {
|
|
1741
|
+
localStorage.setItem('tolle-theme', 'light');
|
|
1742
|
+
}
|
|
1473
1743
|
this.isDarkSubject.next(false);
|
|
1474
1744
|
}
|
|
1475
1745
|
setPrimaryColor(color, persist = true) {
|
|
1476
1746
|
if (!isPlatformBrowser(this.platformId))
|
|
1477
1747
|
return;
|
|
1478
|
-
// Update CSS variables + palette
|
|
1479
|
-
this.renderer.setStyle(this.document.documentElement, '--primary', color);
|
|
1480
1748
|
this.generatePrimaryShades(color);
|
|
1481
|
-
//
|
|
1749
|
+
// Also set inline for immediate update
|
|
1750
|
+
const rgb = this.hexToRgb(color);
|
|
1751
|
+
if (rgb) {
|
|
1752
|
+
this.renderer.setStyle(this.document.documentElement, '--primary', `${rgb.r} ${rgb.g} ${rgb.b}`);
|
|
1753
|
+
}
|
|
1482
1754
|
if (persist) {
|
|
1483
1755
|
localStorage.setItem('tolle-primary-color', color);
|
|
1484
1756
|
}
|
|
@@ -1487,8 +1759,71 @@ class ThemeService {
|
|
|
1487
1759
|
return this.isDarkSubject.value ? 'dark' : 'light';
|
|
1488
1760
|
}
|
|
1489
1761
|
get primaryColor() {
|
|
1762
|
+
if (!isPlatformBrowser(this.platformId))
|
|
1763
|
+
return null;
|
|
1764
|
+
const root = this.document.documentElement;
|
|
1765
|
+
const cssValue = getComputedStyle(root).getPropertyValue('--primary').trim();
|
|
1766
|
+
if (cssValue && cssValue !== '') {
|
|
1767
|
+
// Convert RGB string back to hex for external use
|
|
1768
|
+
const rgbParts = cssValue.split(' ').map(Number);
|
|
1769
|
+
if (rgbParts.length === 3) {
|
|
1770
|
+
return `#${rgbParts[0].toString(16).padStart(2, '0')}${rgbParts[1].toString(16).padStart(2, '0')}${rgbParts[2].toString(16).padStart(2, '0')}`;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1490
1773
|
return localStorage.getItem('tolle-primary-color');
|
|
1491
1774
|
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Reset to config defaults (clears user preferences)
|
|
1777
|
+
*/
|
|
1778
|
+
resetToConfigDefaults() {
|
|
1779
|
+
if (!isPlatformBrowser(this.platformId))
|
|
1780
|
+
return;
|
|
1781
|
+
// Clear user preferences
|
|
1782
|
+
localStorage.removeItem('tolle-theme');
|
|
1783
|
+
localStorage.removeItem('tolle-primary-color');
|
|
1784
|
+
localStorage.removeItem('tolle-radius');
|
|
1785
|
+
// Re-initialize with config defaults
|
|
1786
|
+
this.initializeTheme();
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Get current user preferences
|
|
1790
|
+
*/
|
|
1791
|
+
getUserPreferences() {
|
|
1792
|
+
if (!isPlatformBrowser(this.platformId))
|
|
1793
|
+
return null;
|
|
1794
|
+
return {
|
|
1795
|
+
theme: localStorage.getItem('tolle-theme'),
|
|
1796
|
+
primaryColor: localStorage.getItem('tolle-primary-color'),
|
|
1797
|
+
radius: localStorage.getItem('tolle-radius')
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Clear all user preferences
|
|
1802
|
+
*/
|
|
1803
|
+
clearUserPreferences() {
|
|
1804
|
+
if (!isPlatformBrowser(this.platformId))
|
|
1805
|
+
return;
|
|
1806
|
+
localStorage.removeItem('tolle-theme');
|
|
1807
|
+
localStorage.removeItem('tolle-primary-color');
|
|
1808
|
+
localStorage.removeItem('tolle-radius');
|
|
1809
|
+
// Reset to system defaults (not config defaults)
|
|
1810
|
+
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
1811
|
+
if (systemPrefersDark) {
|
|
1812
|
+
this.enableDarkMode(false);
|
|
1813
|
+
}
|
|
1814
|
+
else {
|
|
1815
|
+
this.disableDarkMode(false);
|
|
1816
|
+
}
|
|
1817
|
+
// Remove CSS variables
|
|
1818
|
+
const root = this.document.documentElement;
|
|
1819
|
+
this.renderer.removeStyle(root, '--primary');
|
|
1820
|
+
this.renderer.removeStyle(root, '--radius');
|
|
1821
|
+
// Clear dynamic styles
|
|
1822
|
+
const existingStyle = this.document.getElementById(this.styleId);
|
|
1823
|
+
if (existingStyle) {
|
|
1824
|
+
existingStyle.remove();
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1492
1827
|
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 });
|
|
1493
1828
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThemeService, providedIn: 'root' });
|
|
1494
1829
|
}
|
|
@@ -1670,7 +2005,7 @@ class MultiSelectComponent {
|
|
|
1670
2005
|
</div>
|
|
1671
2006
|
</div>
|
|
1672
2007
|
</div>
|
|
1673
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BadgeComponent, selector: "tolle-badge", inputs: ["variant", "size", "removable", "class"], outputs: ["onRemove"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error"] }] });
|
|
2008
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: BadgeComponent, selector: "tolle-badge", inputs: ["variant", "size", "removable", "class"], outputs: ["onRemove"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error", "hideHintOnFocus"] }] });
|
|
1674
2009
|
}
|
|
1675
2010
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MultiSelectComponent, decorators: [{
|
|
1676
2011
|
type: Component,
|
|
@@ -2067,10 +2402,12 @@ class MaskedInputComponent {
|
|
|
2067
2402
|
error = false;
|
|
2068
2403
|
size = 'default';
|
|
2069
2404
|
returnRaw = false;
|
|
2405
|
+
hideHintOnFocus = true;
|
|
2070
2406
|
inputEl;
|
|
2071
2407
|
hasPrefix = false;
|
|
2072
2408
|
hasSuffix = false;
|
|
2073
2409
|
displayValue = '';
|
|
2410
|
+
isFocused = false;
|
|
2074
2411
|
tokens = {
|
|
2075
2412
|
'0': /\d/, '9': /\d/, 'a': /[a-z]/i, 'A': /[a-z]/i, '*': /[a-z0-9]/i
|
|
2076
2413
|
};
|
|
@@ -2089,19 +2426,71 @@ class MaskedInputComponent {
|
|
|
2089
2426
|
this.cdr.detectChanges();
|
|
2090
2427
|
}
|
|
2091
2428
|
}
|
|
2429
|
+
get computedLabelClass() {
|
|
2430
|
+
return cn("text-sm font-medium text-foreground leading-none transition-opacity duration-200", this.disabled && "opacity-50");
|
|
2431
|
+
}
|
|
2092
2432
|
get computedContainerClass() {
|
|
2093
|
-
return cn(
|
|
2433
|
+
return cn(
|
|
2434
|
+
// Base styles
|
|
2435
|
+
"group relative flex items-center w-full rounded-md border transition-all duration-200", "bg-background",
|
|
2436
|
+
// Border and shadow
|
|
2437
|
+
"border-input shadow-sm",
|
|
2094
2438
|
// 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
|
-
//
|
|
2097
|
-
!(this.readonly || this.disabled) &&
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2439
|
+
this.size === 'xs' && "h-8 px-2 gap-1.5 text-xs", this.size === 'sm' && "h-9 px-3 gap-2 text-sm", this.size === 'default' && "h-10 px-3 gap-2 text-sm", this.size === 'lg' && "h-11 px-4 gap-3 text-base",
|
|
2440
|
+
// Focus state - SIMPLE LIKE ZARDUI
|
|
2441
|
+
!(this.readonly || this.disabled) && [
|
|
2442
|
+
"focus-within:border-primary/80",
|
|
2443
|
+
"focus-within:ring-4",
|
|
2444
|
+
"focus-within:ring-ring/30",
|
|
2445
|
+
"focus-within:ring-offset-0",
|
|
2446
|
+
"focus-within:shadow-none",
|
|
2447
|
+
],
|
|
2448
|
+
// Error state
|
|
2449
|
+
this.error && [
|
|
2450
|
+
"border-destructive",
|
|
2451
|
+
!(this.readonly || this.disabled) && [
|
|
2452
|
+
"focus-within:border-destructive/80",
|
|
2453
|
+
"focus-within:ring-destructive/30"
|
|
2454
|
+
]
|
|
2455
|
+
],
|
|
2456
|
+
// Disabled state
|
|
2457
|
+
this.disabled && [
|
|
2458
|
+
"cursor-not-allowed opacity-50",
|
|
2459
|
+
"border-opacity-50"
|
|
2460
|
+
],
|
|
2461
|
+
// Readonly state
|
|
2462
|
+
this.readonly && [
|
|
2463
|
+
"cursor-default",
|
|
2464
|
+
"border-dashed",
|
|
2465
|
+
!this.disabled && "focus-within:ring-0 focus-within:border-opacity-100"
|
|
2466
|
+
], this.containerClass);
|
|
2102
2467
|
}
|
|
2103
2468
|
get computedInputClass() {
|
|
2104
|
-
return cn(
|
|
2469
|
+
return cn(
|
|
2470
|
+
// Base styles
|
|
2471
|
+
"flex-1 bg-transparent border-none p-0", "placeholder:text-muted-foreground",
|
|
2472
|
+
// Remove all default focus styles
|
|
2473
|
+
"focus:outline-none focus:ring-0 focus:shadow-none",
|
|
2474
|
+
// Text sizing
|
|
2475
|
+
this.size === 'xs' && "text-xs", this.size === 'sm' && "text-sm", this.size === 'default' && "text-sm", this.size === 'lg' && "text-base",
|
|
2476
|
+
// Cursor states
|
|
2477
|
+
this.disabled && "cursor-not-allowed", this.readonly && "cursor-default",
|
|
2478
|
+
// Text color
|
|
2479
|
+
"text-foreground",
|
|
2480
|
+
// Selection color
|
|
2481
|
+
"selection:bg-primary/20 selection:text-foreground", this.class);
|
|
2482
|
+
}
|
|
2483
|
+
focusInput() {
|
|
2484
|
+
if (!this.disabled && this.inputEl) {
|
|
2485
|
+
this.inputEl.nativeElement.focus();
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
onFocus() {
|
|
2489
|
+
this.isFocused = true;
|
|
2490
|
+
}
|
|
2491
|
+
onBlur() {
|
|
2492
|
+
this.isFocused = false;
|
|
2493
|
+
this.onTouched();
|
|
2105
2494
|
}
|
|
2106
2495
|
// --- Masking Logic ---
|
|
2107
2496
|
onInput(event) {
|
|
@@ -2140,20 +2529,26 @@ class MaskedInputComponent {
|
|
|
2140
2529
|
}
|
|
2141
2530
|
return formatted;
|
|
2142
2531
|
}
|
|
2143
|
-
unmask(val) {
|
|
2532
|
+
unmask(val) {
|
|
2533
|
+
return val.replace(/[^a-zA-Z0-9]/g, '');
|
|
2534
|
+
}
|
|
2144
2535
|
writeValue(value) {
|
|
2145
2536
|
this.displayValue = value ? this.applyMask(this.unmask(value.toString())) : '';
|
|
2146
2537
|
this.cdr.markForCheck();
|
|
2147
2538
|
}
|
|
2148
|
-
registerOnChange(fn) {
|
|
2149
|
-
|
|
2539
|
+
registerOnChange(fn) {
|
|
2540
|
+
this.onChange = fn;
|
|
2541
|
+
}
|
|
2542
|
+
registerOnTouched(fn) {
|
|
2543
|
+
this.onTouched = fn;
|
|
2544
|
+
}
|
|
2150
2545
|
setDisabledState(isDisabled) {
|
|
2151
2546
|
this.disabled = isDisabled;
|
|
2152
2547
|
this.cdr.markForCheck();
|
|
2153
2548
|
}
|
|
2154
2549
|
cn = cn;
|
|
2155
2550
|
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 });
|
|
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: [
|
|
2551
|
+
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", hideHintOnFocus: "hideHintOnFocus" }, providers: [
|
|
2157
2552
|
{
|
|
2158
2553
|
provide: NG_VALUE_ACCESSOR,
|
|
2159
2554
|
useExisting: forwardRef(() => MaskedInputComponent),
|
|
@@ -2164,15 +2559,17 @@ class MaskedInputComponent {
|
|
|
2164
2559
|
<label
|
|
2165
2560
|
*ngIf="label"
|
|
2166
2561
|
[for]="id"
|
|
2167
|
-
[class
|
|
2168
|
-
class="text-sm font-medium text-foreground leading-none transition-opacity"
|
|
2562
|
+
[class]="computedLabelClass"
|
|
2169
2563
|
>
|
|
2170
2564
|
{{ label }}
|
|
2171
2565
|
</label>
|
|
2172
2566
|
|
|
2173
|
-
<div
|
|
2567
|
+
<div
|
|
2568
|
+
[class]="computedContainerClass"
|
|
2569
|
+
(click)="focusInput()"
|
|
2570
|
+
>
|
|
2174
2571
|
<!-- Prefix Icon -->
|
|
2175
|
-
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
|
|
2572
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
2176
2573
|
<ng-content select="[prefix]"></ng-content>
|
|
2177
2574
|
</div>
|
|
2178
2575
|
|
|
@@ -2185,21 +2582,32 @@ class MaskedInputComponent {
|
|
|
2185
2582
|
[readOnly]="readonly"
|
|
2186
2583
|
[value]="displayValue"
|
|
2187
2584
|
(input)="onInput($event)"
|
|
2188
|
-
(blur)="
|
|
2585
|
+
(blur)="onBlur()"
|
|
2586
|
+
(focus)="onFocus()"
|
|
2189
2587
|
[class]="computedInputClass"
|
|
2588
|
+
[attr.aria-invalid]="error"
|
|
2589
|
+
[attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
|
|
2190
2590
|
/>
|
|
2191
2591
|
|
|
2192
2592
|
<!-- Suffix Icon -->
|
|
2193
|
-
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
|
|
2593
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
2194
2594
|
<ng-content select="[suffix]"></ng-content>
|
|
2195
2595
|
</div>
|
|
2196
2596
|
</div>
|
|
2197
2597
|
|
|
2198
2598
|
<ng-container *ngIf="!disabled">
|
|
2199
|
-
<p
|
|
2599
|
+
<p
|
|
2600
|
+
*ngIf="hint && !error"
|
|
2601
|
+
class="text-xs text-muted-foreground px-1 transition-opacity duration-200"
|
|
2602
|
+
[class.opacity-0]="isFocused && hideHintOnFocus"
|
|
2603
|
+
>
|
|
2200
2604
|
{{ hint }}
|
|
2201
2605
|
</p>
|
|
2202
|
-
<p
|
|
2606
|
+
<p
|
|
2607
|
+
*ngIf="error && errorMessage"
|
|
2608
|
+
[id]="id + '-error'"
|
|
2609
|
+
class="text-xs text-destructive px-1"
|
|
2610
|
+
>
|
|
2203
2611
|
{{ errorMessage }}
|
|
2204
2612
|
</p>
|
|
2205
2613
|
</ng-container>
|
|
@@ -2224,15 +2632,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
2224
2632
|
<label
|
|
2225
2633
|
*ngIf="label"
|
|
2226
2634
|
[for]="id"
|
|
2227
|
-
[class
|
|
2228
|
-
class="text-sm font-medium text-foreground leading-none transition-opacity"
|
|
2635
|
+
[class]="computedLabelClass"
|
|
2229
2636
|
>
|
|
2230
2637
|
{{ label }}
|
|
2231
2638
|
</label>
|
|
2232
2639
|
|
|
2233
|
-
<div
|
|
2640
|
+
<div
|
|
2641
|
+
[class]="computedContainerClass"
|
|
2642
|
+
(click)="focusInput()"
|
|
2643
|
+
>
|
|
2234
2644
|
<!-- Prefix Icon -->
|
|
2235
|
-
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
|
|
2645
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
2236
2646
|
<ng-content select="[prefix]"></ng-content>
|
|
2237
2647
|
</div>
|
|
2238
2648
|
|
|
@@ -2245,21 +2655,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
2245
2655
|
[readOnly]="readonly"
|
|
2246
2656
|
[value]="displayValue"
|
|
2247
2657
|
(input)="onInput($event)"
|
|
2248
|
-
(blur)="
|
|
2658
|
+
(blur)="onBlur()"
|
|
2659
|
+
(focus)="onFocus()"
|
|
2249
2660
|
[class]="computedInputClass"
|
|
2661
|
+
[attr.aria-invalid]="error"
|
|
2662
|
+
[attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
|
|
2250
2663
|
/>
|
|
2251
2664
|
|
|
2252
2665
|
<!-- Suffix Icon -->
|
|
2253
|
-
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors">
|
|
2666
|
+
<div class="flex items-center text-muted-foreground group-focus-within:text-primary transition-colors duration-200">
|
|
2254
2667
|
<ng-content select="[suffix]"></ng-content>
|
|
2255
2668
|
</div>
|
|
2256
2669
|
</div>
|
|
2257
2670
|
|
|
2258
2671
|
<ng-container *ngIf="!disabled">
|
|
2259
|
-
<p
|
|
2672
|
+
<p
|
|
2673
|
+
*ngIf="hint && !error"
|
|
2674
|
+
class="text-xs text-muted-foreground px-1 transition-opacity duration-200"
|
|
2675
|
+
[class.opacity-0]="isFocused && hideHintOnFocus"
|
|
2676
|
+
>
|
|
2260
2677
|
{{ hint }}
|
|
2261
2678
|
</p>
|
|
2262
|
-
<p
|
|
2679
|
+
<p
|
|
2680
|
+
*ngIf="error && errorMessage"
|
|
2681
|
+
[id]="id + '-error'"
|
|
2682
|
+
class="text-xs text-destructive px-1"
|
|
2683
|
+
>
|
|
2263
2684
|
{{ errorMessage }}
|
|
2264
2685
|
</p>
|
|
2265
2686
|
</ng-container>
|
|
@@ -2294,6 +2715,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
2294
2715
|
type: Input
|
|
2295
2716
|
}], returnRaw: [{
|
|
2296
2717
|
type: Input
|
|
2718
|
+
}], hideHintOnFocus: [{
|
|
2719
|
+
type: Input
|
|
2297
2720
|
}], inputEl: [{
|
|
2298
2721
|
type: ViewChild,
|
|
2299
2722
|
args: ['inputEl', { static: true }]
|
|
@@ -2446,7 +2869,7 @@ class DatePickerComponent {
|
|
|
2446
2869
|
></tolle-calendar>
|
|
2447
2870
|
</div>
|
|
2448
2871
|
</div>
|
|
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"] }] });
|
|
2872
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MaskedInputComponent, selector: "tolle-masked-input", inputs: ["id", "label", "hint", "errorMessage", "mask", "placeholder", "type", "disabled", "readonly", "class", "containerClass", "error", "size", "returnRaw", "hideHintOnFocus"] }, { kind: "component", type: CalendarComponent, selector: "tolle-calendar", inputs: ["class", "disablePastDates"] }] });
|
|
2450
2873
|
}
|
|
2451
2874
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DatePickerComponent, decorators: [{
|
|
2452
2875
|
type: Component,
|
|
@@ -3038,7 +3461,7 @@ class DataTableComponent {
|
|
|
3038
3461
|
(onPageSizeChange)="updatePage()"
|
|
3039
3462
|
></tolle-pagination>
|
|
3040
3463
|
</div>
|
|
3041
|
-
`, 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: "directive", type: i1.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, selector: "tolle-pagination", inputs: ["class", "showPageLinks", "showPageOptions", "showCurrentPageInfo", "currentPageInfoTemplate", "totalRecords", "currentPageSize", "currentPage", "pageSizeOptions"], outputs: ["onPageNumberChange", "onPageSizeChange"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error"] }] });
|
|
3464
|
+
`, 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: "directive", type: i1.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, selector: "tolle-pagination", inputs: ["class", "showPageLinks", "showPageOptions", "showCurrentPageInfo", "currentPageInfoTemplate", "totalRecords", "currentPageSize", "currentPage", "pageSizeOptions"], outputs: ["onPageNumberChange", "onPageSizeChange"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error", "hideHintOnFocus"] }] });
|
|
3042
3465
|
}
|
|
3043
3466
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DataTableComponent, decorators: [{
|
|
3044
3467
|
type: Component,
|
|
@@ -3948,7 +4371,7 @@ class DateRangePickerComponent {
|
|
|
3948
4371
|
></tolle-range-calendar>
|
|
3949
4372
|
</div>
|
|
3950
4373
|
</div>
|
|
3951
|
-
`, 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: RangeCalendarComponent, selector: "tolle-range-calendar", inputs: ["class", "disablePastDates"], outputs: ["rangeSelect"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error"] }] });
|
|
4374
|
+
`, 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: RangeCalendarComponent, selector: "tolle-range-calendar", inputs: ["class", "disablePastDates"], outputs: ["rangeSelect"] }, { kind: "component", type: InputComponent, selector: "tolle-input", inputs: ["id", "label", "hint", "errorMessage", "type", "placeholder", "size", "containerClass", "class", "disabled", "readonly", "error", "hideHintOnFocus"] }] });
|
|
3952
4375
|
}
|
|
3953
4376
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DateRangePickerComponent, decorators: [{
|
|
3954
4377
|
type: Component,
|
|
@@ -4166,30 +4589,69 @@ class TextareaComponent {
|
|
|
4166
4589
|
label = '';
|
|
4167
4590
|
placeholder = '';
|
|
4168
4591
|
hint = '';
|
|
4592
|
+
errorMessage = '';
|
|
4169
4593
|
rows = 3;
|
|
4170
4594
|
maxLength;
|
|
4171
4595
|
showCharacterCount = false;
|
|
4172
4596
|
autoGrow = false;
|
|
4173
4597
|
error = false;
|
|
4174
4598
|
className = '';
|
|
4599
|
+
// Focus behavior
|
|
4600
|
+
hideHintOnFocus = true;
|
|
4601
|
+
hideCharacterCountOnFocus = false;
|
|
4175
4602
|
// New States
|
|
4176
4603
|
disabled = false;
|
|
4177
4604
|
readonly = false;
|
|
4178
4605
|
value = '';
|
|
4606
|
+
isFocused = false;
|
|
4179
4607
|
onChange = () => { };
|
|
4180
4608
|
onTouched = () => { };
|
|
4181
4609
|
ngAfterViewInit() {
|
|
4182
4610
|
if (this.autoGrow)
|
|
4183
4611
|
this.resize();
|
|
4184
4612
|
}
|
|
4613
|
+
get computedLabelClass() {
|
|
4614
|
+
return cn("text-sm font-medium text-foreground leading-none transition-opacity duration-200", this.disabled && "opacity-50");
|
|
4615
|
+
}
|
|
4185
4616
|
get textareaClasses() {
|
|
4186
|
-
return cn(
|
|
4187
|
-
//
|
|
4188
|
-
|
|
4189
|
-
// Border
|
|
4190
|
-
|
|
4191
|
-
//
|
|
4192
|
-
|
|
4617
|
+
return cn(
|
|
4618
|
+
// Base styles
|
|
4619
|
+
'flex w-full rounded-md border bg-background px-3 py-2 text-sm', 'ring-offset-background transition-all duration-200', 'placeholder:text-muted-foreground',
|
|
4620
|
+
// Border and shadow
|
|
4621
|
+
'border-input shadow-sm',
|
|
4622
|
+
// Minimum height
|
|
4623
|
+
'min-h-[80px]',
|
|
4624
|
+
// Focus state - SIMPLE LIKE ZARDUI
|
|
4625
|
+
!(this.readonly || this.disabled) && [
|
|
4626
|
+
'focus:outline-none',
|
|
4627
|
+
'focus:ring-4',
|
|
4628
|
+
'focus:ring-ring/30',
|
|
4629
|
+
'focus:ring-offset-0',
|
|
4630
|
+
'focus:shadow-none',
|
|
4631
|
+
// Border darkens on focus automatically
|
|
4632
|
+
'focus:border-primary/80'
|
|
4633
|
+
],
|
|
4634
|
+
// Error state
|
|
4635
|
+
this.error && [
|
|
4636
|
+
'border-destructive',
|
|
4637
|
+
!(this.readonly || this.disabled) && [
|
|
4638
|
+
'focus:border-destructive/80',
|
|
4639
|
+
'focus:ring-destructive/30'
|
|
4640
|
+
]
|
|
4641
|
+
],
|
|
4642
|
+
// Disabled state
|
|
4643
|
+
this.disabled && [
|
|
4644
|
+
'cursor-not-allowed opacity-50',
|
|
4645
|
+
'border-opacity-50'
|
|
4646
|
+
],
|
|
4647
|
+
// Readonly state
|
|
4648
|
+
this.readonly && [
|
|
4649
|
+
'cursor-default',
|
|
4650
|
+
'border-dashed',
|
|
4651
|
+
!this.disabled && 'focus:ring-0 focus:border-opacity-100'
|
|
4652
|
+
],
|
|
4653
|
+
// Scrollbar styling
|
|
4654
|
+
'scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent', this.className);
|
|
4193
4655
|
}
|
|
4194
4656
|
handleInput(event) {
|
|
4195
4657
|
if (this.readonly || this.disabled)
|
|
@@ -4200,6 +4662,13 @@ class TextareaComponent {
|
|
|
4200
4662
|
if (this.autoGrow)
|
|
4201
4663
|
this.resize();
|
|
4202
4664
|
}
|
|
4665
|
+
onFocus() {
|
|
4666
|
+
this.isFocused = true;
|
|
4667
|
+
}
|
|
4668
|
+
onBlur() {
|
|
4669
|
+
this.isFocused = false;
|
|
4670
|
+
this.onTouched();
|
|
4671
|
+
}
|
|
4203
4672
|
resize() {
|
|
4204
4673
|
const textarea = this.textareaElement.nativeElement;
|
|
4205
4674
|
textarea.style.height = 'auto';
|
|
@@ -4210,11 +4679,17 @@ class TextareaComponent {
|
|
|
4210
4679
|
if (this.autoGrow)
|
|
4211
4680
|
setTimeout(() => this.resize());
|
|
4212
4681
|
}
|
|
4213
|
-
registerOnChange(fn) {
|
|
4214
|
-
|
|
4215
|
-
|
|
4682
|
+
registerOnChange(fn) {
|
|
4683
|
+
this.onChange = fn;
|
|
4684
|
+
}
|
|
4685
|
+
registerOnTouched(fn) {
|
|
4686
|
+
this.onTouched = fn;
|
|
4687
|
+
}
|
|
4688
|
+
setDisabledState(isDisabled) {
|
|
4689
|
+
this.disabled = isDisabled;
|
|
4690
|
+
}
|
|
4216
4691
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4217
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: TextareaComponent, isStandalone: true, selector: "tolle-textarea", inputs: { id: "id", label: "label", placeholder: "placeholder", hint: "hint", rows: "rows", maxLength: "maxLength", showCharacterCount: "showCharacterCount", autoGrow: "autoGrow", error: "error", className: "className", disabled: "disabled", readonly: "readonly" }, providers: [
|
|
4692
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: TextareaComponent, isStandalone: true, selector: "tolle-textarea", inputs: { id: "id", label: "label", placeholder: "placeholder", hint: "hint", errorMessage: "errorMessage", rows: "rows", maxLength: "maxLength", showCharacterCount: "showCharacterCount", autoGrow: "autoGrow", error: "error", className: "className", hideHintOnFocus: "hideHintOnFocus", hideCharacterCountOnFocus: "hideCharacterCountOnFocus", disabled: "disabled", readonly: "readonly" }, providers: [
|
|
4218
4693
|
{
|
|
4219
4694
|
provide: NG_VALUE_ACCESSOR,
|
|
4220
4695
|
useExisting: forwardRef(() => TextareaComponent),
|
|
@@ -4222,9 +4697,11 @@ class TextareaComponent {
|
|
|
4222
4697
|
}
|
|
4223
4698
|
], viewQueries: [{ propertyName: "textareaElement", first: true, predicate: ["textareaElement"], descendants: true }], ngImport: i0, template: `
|
|
4224
4699
|
<div class="flex flex-col gap-1.5 w-full">
|
|
4225
|
-
<label
|
|
4226
|
-
|
|
4227
|
-
|
|
4700
|
+
<label
|
|
4701
|
+
*ngIf="label"
|
|
4702
|
+
[for]="id"
|
|
4703
|
+
[class]="computedLabelClass"
|
|
4704
|
+
>
|
|
4228
4705
|
{{ label }}
|
|
4229
4706
|
</label>
|
|
4230
4707
|
|
|
@@ -4238,16 +4715,37 @@ class TextareaComponent {
|
|
|
4238
4715
|
[rows]="rows"
|
|
4239
4716
|
[(ngModel)]="value"
|
|
4240
4717
|
(input)="handleInput($event)"
|
|
4241
|
-
(blur)="
|
|
4718
|
+
(blur)="onBlur()"
|
|
4719
|
+
(focus)="onFocus()"
|
|
4242
4720
|
[class]="textareaClasses"
|
|
4243
4721
|
[style.resize]="(autoGrow || readonly || disabled) ? 'none' : 'vertical'"
|
|
4244
4722
|
[style.overflow]="autoGrow ? 'hidden' : 'auto'"
|
|
4723
|
+
[attr.aria-invalid]="error"
|
|
4724
|
+
[attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
|
|
4725
|
+
[attr.maxlength]="maxLength"
|
|
4245
4726
|
></textarea>
|
|
4246
4727
|
</div>
|
|
4247
4728
|
|
|
4248
|
-
<div *ngIf="(showCharacterCount || hint) && !disabled" class="flex justify-between items-center px-1">
|
|
4249
|
-
<p
|
|
4250
|
-
|
|
4729
|
+
<div *ngIf="(showCharacterCount || hint || errorMessage) && !disabled" class="flex justify-between items-center px-1">
|
|
4730
|
+
<p
|
|
4731
|
+
*ngIf="hint && !error"
|
|
4732
|
+
class="text-xs text-muted-foreground transition-opacity duration-200"
|
|
4733
|
+
[class.opacity-0]="isFocused && hideHintOnFocus"
|
|
4734
|
+
>
|
|
4735
|
+
{{ hint }}
|
|
4736
|
+
</p>
|
|
4737
|
+
<p
|
|
4738
|
+
*ngIf="error && errorMessage"
|
|
4739
|
+
[id]="id + '-error'"
|
|
4740
|
+
class="text-xs text-destructive"
|
|
4741
|
+
>
|
|
4742
|
+
{{ errorMessage }}
|
|
4743
|
+
</p>
|
|
4744
|
+
<p
|
|
4745
|
+
*ngIf="showCharacterCount"
|
|
4746
|
+
class="text-[10px] uppercase tracking-wider text-muted-foreground ml-auto font-medium transition-opacity duration-200"
|
|
4747
|
+
[class.opacity-0]="isFocused && hideCharacterCountOnFocus"
|
|
4748
|
+
>
|
|
4251
4749
|
{{ value.length || 0 }}{{ maxLength ? ' / ' + maxLength : '' }}
|
|
4252
4750
|
</p>
|
|
4253
4751
|
</div>
|
|
@@ -4269,9 +4767,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4269
4767
|
],
|
|
4270
4768
|
template: `
|
|
4271
4769
|
<div class="flex flex-col gap-1.5 w-full">
|
|
4272
|
-
<label
|
|
4273
|
-
|
|
4274
|
-
|
|
4770
|
+
<label
|
|
4771
|
+
*ngIf="label"
|
|
4772
|
+
[for]="id"
|
|
4773
|
+
[class]="computedLabelClass"
|
|
4774
|
+
>
|
|
4275
4775
|
{{ label }}
|
|
4276
4776
|
</label>
|
|
4277
4777
|
|
|
@@ -4285,16 +4785,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4285
4785
|
[rows]="rows"
|
|
4286
4786
|
[(ngModel)]="value"
|
|
4287
4787
|
(input)="handleInput($event)"
|
|
4288
|
-
(blur)="
|
|
4788
|
+
(blur)="onBlur()"
|
|
4789
|
+
(focus)="onFocus()"
|
|
4289
4790
|
[class]="textareaClasses"
|
|
4290
4791
|
[style.resize]="(autoGrow || readonly || disabled) ? 'none' : 'vertical'"
|
|
4291
4792
|
[style.overflow]="autoGrow ? 'hidden' : 'auto'"
|
|
4793
|
+
[attr.aria-invalid]="error"
|
|
4794
|
+
[attr.aria-describedby]="error && errorMessage ? id + '-error' : null"
|
|
4795
|
+
[attr.maxlength]="maxLength"
|
|
4292
4796
|
></textarea>
|
|
4293
4797
|
</div>
|
|
4294
4798
|
|
|
4295
|
-
<div *ngIf="(showCharacterCount || hint) && !disabled" class="flex justify-between items-center px-1">
|
|
4296
|
-
<p
|
|
4297
|
-
|
|
4799
|
+
<div *ngIf="(showCharacterCount || hint || errorMessage) && !disabled" class="flex justify-between items-center px-1">
|
|
4800
|
+
<p
|
|
4801
|
+
*ngIf="hint && !error"
|
|
4802
|
+
class="text-xs text-muted-foreground transition-opacity duration-200"
|
|
4803
|
+
[class.opacity-0]="isFocused && hideHintOnFocus"
|
|
4804
|
+
>
|
|
4805
|
+
{{ hint }}
|
|
4806
|
+
</p>
|
|
4807
|
+
<p
|
|
4808
|
+
*ngIf="error && errorMessage"
|
|
4809
|
+
[id]="id + '-error'"
|
|
4810
|
+
class="text-xs text-destructive"
|
|
4811
|
+
>
|
|
4812
|
+
{{ errorMessage }}
|
|
4813
|
+
</p>
|
|
4814
|
+
<p
|
|
4815
|
+
*ngIf="showCharacterCount"
|
|
4816
|
+
class="text-[10px] uppercase tracking-wider text-muted-foreground ml-auto font-medium transition-opacity duration-200"
|
|
4817
|
+
[class.opacity-0]="isFocused && hideCharacterCountOnFocus"
|
|
4818
|
+
>
|
|
4298
4819
|
{{ value.length || 0 }}{{ maxLength ? ' / ' + maxLength : '' }}
|
|
4299
4820
|
</p>
|
|
4300
4821
|
</div>
|
|
@@ -4312,6 +4833,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4312
4833
|
type: Input
|
|
4313
4834
|
}], hint: [{
|
|
4314
4835
|
type: Input
|
|
4836
|
+
}], errorMessage: [{
|
|
4837
|
+
type: Input
|
|
4315
4838
|
}], rows: [{
|
|
4316
4839
|
type: Input
|
|
4317
4840
|
}], maxLength: [{
|
|
@@ -4324,6 +4847,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4324
4847
|
type: Input
|
|
4325
4848
|
}], className: [{
|
|
4326
4849
|
type: Input
|
|
4850
|
+
}], hideHintOnFocus: [{
|
|
4851
|
+
type: Input
|
|
4852
|
+
}], hideCharacterCountOnFocus: [{
|
|
4853
|
+
type: Input
|
|
4327
4854
|
}], disabled: [{
|
|
4328
4855
|
type: Input
|
|
4329
4856
|
}], readonly: [{
|
|
@@ -4887,23 +5414,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4887
5414
|
class OtpSlotComponent {
|
|
4888
5415
|
char = '';
|
|
4889
5416
|
isActive = false;
|
|
4890
|
-
isFirst = false;
|
|
4891
|
-
isLast = false;
|
|
5417
|
+
isFirst = false;
|
|
5418
|
+
isLast = false;
|
|
4892
5419
|
class = '';
|
|
4893
5420
|
cn = cn;
|
|
5421
|
+
get computedClass() {
|
|
5422
|
+
return cn(
|
|
5423
|
+
// Base styles (matching input container)
|
|
5424
|
+
'relative flex h-10 w-10 items-center justify-center', 'transition-all duration-200', 'text-center font-medium select-none',
|
|
5425
|
+
// Border styling - exactly like input
|
|
5426
|
+
'border border-input shadow-sm bg-background',
|
|
5427
|
+
// FOCUS STYLING - EXACTLY LIKE INPUT COMPONENT
|
|
5428
|
+
this.isActive && [
|
|
5429
|
+
// Darker border on focus
|
|
5430
|
+
'border-primary/80',
|
|
5431
|
+
// Remove shadow on focus
|
|
5432
|
+
'shadow-none',
|
|
5433
|
+
// Focus ring with same styling as input
|
|
5434
|
+
'ring-4',
|
|
5435
|
+
'ring-ring/30',
|
|
5436
|
+
'ring-offset-0',
|
|
5437
|
+
// Ensure it's above other slots
|
|
5438
|
+
'z-10'
|
|
5439
|
+
],
|
|
5440
|
+
// Filled state
|
|
5441
|
+
this.char && !this.isActive && 'border-primary/60',
|
|
5442
|
+
// Border radius
|
|
5443
|
+
this.isFirst && 'rounded-l-md', this.isLast && 'rounded-r-md',
|
|
5444
|
+
// Remove left border for all but first slot
|
|
5445
|
+
!this.isFirst && '-ml-px', this.class);
|
|
5446
|
+
}
|
|
4894
5447
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OtpSlotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4895
5448
|
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]="
|
|
4897
|
-
|
|
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 || '' }}
|
|
5449
|
+
<div [class]="computedClass">
|
|
5450
|
+
<span class="text-lg font-medium">{{ char || '' }}</span>
|
|
4905
5451
|
<div *ngIf="isActive && !char" class="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
4906
|
-
<div class="h-
|
|
5452
|
+
<div class="h-6 w-0.5 animate-caret-blink bg-foreground"></div>
|
|
4907
5453
|
</div>
|
|
4908
5454
|
</div>
|
|
4909
5455
|
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
@@ -4915,17 +5461,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
4915
5461
|
standalone: true,
|
|
4916
5462
|
imports: [NgIf],
|
|
4917
5463
|
template: `
|
|
4918
|
-
<div [class]="
|
|
4919
|
-
|
|
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 || '' }}
|
|
5464
|
+
<div [class]="computedClass">
|
|
5465
|
+
<span class="text-lg font-medium">{{ char || '' }}</span>
|
|
4927
5466
|
<div *ngIf="isActive && !char" class="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
4928
|
-
<div class="h-
|
|
5467
|
+
<div class="h-6 w-0.5 animate-caret-blink bg-foreground"></div>
|
|
4929
5468
|
</div>
|
|
4930
5469
|
</div>
|
|
4931
5470
|
`
|