@ng-cn/core 1.0.18 → 1.0.21
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/package.json +17 -17
- package/schematics/tsconfig.json +1 -0
- package/src/app/lib/components/ui/alert-dialog/alert-dialog-content.component.ts +1 -1
- package/src/app/lib/components/ui/calendar/calendar.component.ts +65 -12
- package/src/app/lib/components/ui/chart/chart-context.ts +8 -6
- package/src/app/lib/components/ui/collapsible/collapsible.component.ts +0 -5
- package/src/app/lib/components/ui/context-menu/context-menu-content.component.ts +1 -0
- package/src/app/lib/components/ui/country-selector/country-data.ts +63 -0
- package/src/app/lib/components/ui/country-selector/country-selector.component.ts +199 -0
- package/src/app/lib/components/ui/country-selector/index.ts +2 -0
- package/src/app/lib/components/ui/date-picker/date-picker.component.ts +47 -5
- package/src/app/lib/components/ui/dialog/dialog-content.component.ts +1 -1
- package/src/app/lib/components/ui/dropdown-menu/dropdown-menu-content.component.ts +23 -21
- package/src/app/lib/components/ui/field/field-content.component.ts +34 -0
- package/src/app/lib/components/ui/field/field-description.component.ts +35 -0
- package/src/app/lib/components/ui/field/field-error.component.ts +48 -0
- package/src/app/lib/components/ui/field/field-group.component.ts +34 -0
- package/src/app/lib/components/ui/field/field-label.component.ts +46 -0
- package/src/app/lib/components/ui/field/field-legend.component.ts +41 -0
- package/src/app/lib/components/ui/field/field-separator.component.ts +49 -0
- package/src/app/lib/components/ui/field/field-set.component.ts +37 -0
- package/src/app/lib/components/ui/field/field-title.component.ts +30 -0
- package/src/app/lib/components/ui/field/field.component.ts +66 -0
- package/src/app/lib/components/ui/field/index.ts +15 -0
- package/src/app/lib/components/ui/input/input.component.ts +3 -4
- package/src/app/lib/components/ui/item/index.ts +21 -0
- package/src/app/lib/components/ui/item/item-actions.component.ts +29 -0
- package/src/app/lib/components/ui/item/item-content.component.ts +31 -0
- package/src/app/lib/components/ui/item/item-description.component.ts +30 -0
- package/src/app/lib/components/ui/item/item-footer.component.ts +30 -0
- package/src/app/lib/components/ui/item/item-group.component.ts +32 -0
- package/src/app/lib/components/ui/item/item-header.component.ts +30 -0
- package/src/app/lib/components/ui/item/item-media.component.ts +63 -0
- package/src/app/lib/components/ui/item/item-separator.component.ts +33 -0
- package/src/app/lib/components/ui/item/item-title.component.ts +27 -0
- package/src/app/lib/components/ui/item/item.component.ts +77 -0
- package/src/app/lib/components/ui/phone-input/index.ts +1 -0
- package/src/app/lib/components/ui/phone-input/phone-input.component.ts +169 -0
- package/src/app/lib/components/ui/radio-group/radio-group.component.ts +0 -5
- package/src/app/lib/components/ui/resizable/resizable-handle.component.ts +2 -2
- package/src/app/lib/components/ui/select/select.component.ts +0 -8
- package/src/app/lib/components/ui/sheet/sheet-content.component.ts +1 -1
- package/src/app/lib/components/ui/sidebar/sidebar-provider.component.ts +0 -5
- package/src/app/lib/components/ui/slider/slider.component.ts +0 -4
- package/src/app/lib/components/ui/switch/switch.component.ts +0 -5
- package/src/app/lib/components/ui/tabs/tabs-list.component.ts +3 -1
- package/src/app/lib/components/ui/textarea/textarea.component.ts +110 -10
- package/src/app/lib/components/ui/toast/toast.service.ts +1 -1
- package/src/app/lib/components/ui/toggle/toggle.component.ts +0 -5
- package/src/app/lib/components/ui/toggle-group/toggle-group.component.ts +0 -5
|
@@ -168,8 +168,6 @@ export class Slider implements ControlValueAccessor {
|
|
|
168
168
|
private readonly thumb = viewChild<ElementRef<HTMLElement>>('thumb');
|
|
169
169
|
private readonly elementRef = viewChild<ElementRef<HTMLElement>>('slider');
|
|
170
170
|
|
|
171
|
-
/** Emitted when value changes */
|
|
172
|
-
readonly valueChange = output<number>();
|
|
173
171
|
/** Emitted when value change is committed (on release) */
|
|
174
172
|
readonly valueCommit = output<number>();
|
|
175
173
|
|
|
@@ -373,7 +371,6 @@ export class Slider implements ControlValueAccessor {
|
|
|
373
371
|
if (newValue !== this.value()) {
|
|
374
372
|
this.value.set(newValue);
|
|
375
373
|
this.onChange(newValue);
|
|
376
|
-
this.valueChange.emit(newValue);
|
|
377
374
|
}
|
|
378
375
|
}
|
|
379
376
|
|
|
@@ -406,7 +403,6 @@ export class Slider implements ControlValueAccessor {
|
|
|
406
403
|
if (newValue !== this.value()) {
|
|
407
404
|
this.value.set(newValue);
|
|
408
405
|
this.onChange(newValue);
|
|
409
|
-
this.valueChange.emit(newValue);
|
|
410
406
|
}
|
|
411
407
|
}
|
|
412
408
|
}
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
forwardRef,
|
|
8
8
|
input,
|
|
9
9
|
model,
|
|
10
|
-
output,
|
|
11
10
|
signal,
|
|
12
11
|
viewChild,
|
|
13
12
|
} from '@angular/core';
|
|
@@ -144,9 +143,6 @@ export class Switch implements ControlValueAccessor {
|
|
|
144
143
|
|
|
145
144
|
private readonly buttonElement = viewChild<ElementRef<HTMLButtonElement>>('buttonElement');
|
|
146
145
|
|
|
147
|
-
/** Emitted when checked state changes */
|
|
148
|
-
readonly checkedChange = output<boolean>();
|
|
149
|
-
|
|
150
146
|
/** Whether the switch is checked/on */
|
|
151
147
|
readonly checked = model<boolean>(false);
|
|
152
148
|
|
|
@@ -237,7 +233,6 @@ export class Switch implements ControlValueAccessor {
|
|
|
237
233
|
this.checked.set(newValue);
|
|
238
234
|
this.onChange(newValue);
|
|
239
235
|
this.onTouched();
|
|
240
|
-
this.checkedChange.emit(newValue);
|
|
241
236
|
}
|
|
242
237
|
}
|
|
243
238
|
|
|
@@ -187,6 +187,7 @@ export class TabsList {
|
|
|
187
187
|
|
|
188
188
|
if (handled) {
|
|
189
189
|
event.preventDefault();
|
|
190
|
+
if (typeof document === 'undefined') return;
|
|
190
191
|
|
|
191
192
|
// Skip disabled tabs — scan in the movement direction
|
|
192
193
|
const direction =
|
|
@@ -212,7 +213,7 @@ export class TabsList {
|
|
|
212
213
|
let index = startIndex;
|
|
213
214
|
for (let i = 0; i < length; i++) {
|
|
214
215
|
const tabId = this.tabs.getTabId(tabValues[index]);
|
|
215
|
-
const el = document.getElementById(tabId);
|
|
216
|
+
const el = typeof document !== 'undefined' ? document.getElementById(tabId) : null;
|
|
216
217
|
if (!el?.hasAttribute('data-disabled')) return index;
|
|
217
218
|
index = ((index + direction) + length) % length;
|
|
218
219
|
}
|
|
@@ -220,6 +221,7 @@ export class TabsList {
|
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
private updateIndicator(): void {
|
|
224
|
+
if (typeof document === 'undefined') return;
|
|
223
225
|
const activeValue = this.tabs.value();
|
|
224
226
|
if (!activeValue) return;
|
|
225
227
|
|
|
@@ -1,31 +1,91 @@
|
|
|
1
1
|
import { cn } from '@/lib/utils';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AfterViewInit,
|
|
4
|
+
ChangeDetectionStrategy,
|
|
5
|
+
Component,
|
|
6
|
+
computed,
|
|
7
|
+
ElementRef,
|
|
8
|
+
forwardRef,
|
|
9
|
+
inject,
|
|
10
|
+
input,
|
|
11
|
+
signal,
|
|
12
|
+
} from '@angular/core';
|
|
13
|
+
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
3
14
|
|
|
4
15
|
/**
|
|
5
16
|
* Textarea component that applies shadcn textarea styles.
|
|
17
|
+
* Implements ControlValueAccessor for Angular Forms integration.
|
|
18
|
+
* Applied as an attribute on a native <textarea> element.
|
|
6
19
|
*
|
|
7
20
|
* @example
|
|
8
21
|
* <!-- Basic textarea -->
|
|
9
|
-
* <Textarea placeholder="
|
|
10
|
-
*
|
|
11
|
-
* <!-- With rows -->
|
|
12
|
-
* <Textarea rows="5" placeholder="Description"></Textarea>
|
|
22
|
+
* <textarea Textarea placeholder="Type your message here."></textarea>
|
|
13
23
|
*
|
|
14
24
|
* <!-- Disabled -->
|
|
15
|
-
* <Textarea disabled placeholder="Disabled"></
|
|
25
|
+
* <textarea Textarea disabled placeholder="Disabled"></textarea>
|
|
26
|
+
*
|
|
27
|
+
* <!-- Auto-resize -->
|
|
28
|
+
* <textarea Textarea [autoResize]="true" placeholder="Grows as you type..."></textarea>
|
|
29
|
+
*
|
|
30
|
+
* <!-- With reactive forms -->
|
|
31
|
+
* <textarea Textarea formControlName="message" placeholder="Enter message"></textarea>
|
|
16
32
|
*/
|
|
17
33
|
@Component({
|
|
18
|
-
selector: 'Textarea',
|
|
19
|
-
template:
|
|
34
|
+
selector: 'textarea[Textarea], Textarea',
|
|
35
|
+
template: '',
|
|
20
36
|
host: {
|
|
21
37
|
'[class]': 'computedClass()',
|
|
22
|
-
'
|
|
38
|
+
'[disabled]': 'isDisabled()',
|
|
39
|
+
'[id]': 'id()',
|
|
40
|
+
'[name]': 'name()',
|
|
41
|
+
'[placeholder]': 'placeholder()',
|
|
42
|
+
'[rows]': 'rows()',
|
|
43
|
+
'[attr.data-slot]': '"textarea"',
|
|
44
|
+
'[attr.data-auto-resize]': 'autoResize() || null',
|
|
45
|
+
'[attr.aria-invalid]': 'ariaInvalid() || null',
|
|
46
|
+
'[attr.aria-describedby]': 'ariaDescribedBy() || null',
|
|
47
|
+
'(input)': 'onInput($event)',
|
|
48
|
+
'(blur)': 'onTouched()',
|
|
23
49
|
},
|
|
50
|
+
providers: [
|
|
51
|
+
{
|
|
52
|
+
provide: NG_VALUE_ACCESSOR,
|
|
53
|
+
useExisting: forwardRef(() => Textarea),
|
|
54
|
+
multi: true,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
24
57
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
25
58
|
})
|
|
26
|
-
export class Textarea {
|
|
59
|
+
export class Textarea implements ControlValueAccessor, AfterViewInit {
|
|
60
|
+
/** Placeholder text */
|
|
61
|
+
readonly placeholder = input<string>('');
|
|
62
|
+
/** Textarea id attribute */
|
|
63
|
+
readonly id = input<string>('');
|
|
64
|
+
/** Textarea name attribute */
|
|
65
|
+
readonly name = input<string>('');
|
|
66
|
+
/** Number of visible text rows */
|
|
67
|
+
readonly rows = input<number>(3);
|
|
68
|
+
/** Whether the textarea is disabled */
|
|
69
|
+
readonly disabled = input(false, {
|
|
70
|
+
transform: (value: boolean | string) => value === '' || value === true || value === 'true',
|
|
71
|
+
});
|
|
72
|
+
/** Aria-invalid state for error display */
|
|
73
|
+
readonly ariaInvalid = input<boolean | undefined>(undefined, { alias: 'aria-invalid' });
|
|
74
|
+
/** Aria-describedby for accessibility */
|
|
75
|
+
readonly ariaDescribedBy = input<string | undefined>(undefined, { alias: 'aria-describedby' });
|
|
27
76
|
/** Additional CSS classes to apply */
|
|
28
77
|
readonly class = input<string>('');
|
|
78
|
+
/** When true, the textarea height adjusts automatically as the user types */
|
|
79
|
+
readonly autoResize = input<boolean>(false);
|
|
80
|
+
|
|
81
|
+
private readonly _elementRef = inject(ElementRef<HTMLTextAreaElement>);
|
|
82
|
+
|
|
83
|
+
/** Internal disabled state (set by ControlValueAccessor) */
|
|
84
|
+
protected readonly isDisabled = signal<boolean>(false);
|
|
85
|
+
|
|
86
|
+
/** ControlValueAccessor callbacks */
|
|
87
|
+
private onChange: (value: string) => void = () => {};
|
|
88
|
+
protected onTouched: () => void = () => {};
|
|
29
89
|
|
|
30
90
|
/** Computed class combining base styles and custom classes */
|
|
31
91
|
protected readonly computedClass = computed(() =>
|
|
@@ -33,7 +93,47 @@ export class Textarea {
|
|
|
33
93
|
'placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
|
34
94
|
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
|
35
95
|
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
|
96
|
+
'resize-none data-[auto-resize=true]:resize-none',
|
|
36
97
|
this.class(),
|
|
37
98
|
),
|
|
38
99
|
);
|
|
100
|
+
|
|
101
|
+
ngAfterViewInit(): void {
|
|
102
|
+
// Ensure element is available after hydration
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ControlValueAccessor implementation
|
|
106
|
+
writeValue(value: string): void {
|
|
107
|
+
const el = this._elementRef.nativeElement;
|
|
108
|
+
el.value = value ?? '';
|
|
109
|
+
if (this.autoResize()) {
|
|
110
|
+
el.style.height = 'auto';
|
|
111
|
+
el.style.height = el.scrollHeight + 'px';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
registerOnChange(fn: (value: string) => void): void {
|
|
115
|
+
this.onChange = fn;
|
|
116
|
+
}
|
|
117
|
+
registerOnTouched(fn: () => void): void {
|
|
118
|
+
this.onTouched = fn;
|
|
119
|
+
}
|
|
120
|
+
setDisabledState(isDisabled: boolean): void {
|
|
121
|
+
this.isDisabled.set(isDisabled);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Focus the textarea element */
|
|
125
|
+
focus(): void {
|
|
126
|
+
this._elementRef.nativeElement.focus();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Handle input events */
|
|
130
|
+
protected onInput(event: Event): void {
|
|
131
|
+
const target = event.target as HTMLTextAreaElement;
|
|
132
|
+
this.onChange(target.value);
|
|
133
|
+
if (this.autoResize()) {
|
|
134
|
+
const el = this._elementRef.nativeElement;
|
|
135
|
+
el.style.height = 'auto';
|
|
136
|
+
el.style.height = el.scrollHeight + 'px';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
39
139
|
}
|
|
@@ -178,7 +178,7 @@ export class ToastService {
|
|
|
178
178
|
this._toasts.update((toasts) => [...toasts, newToast]);
|
|
179
179
|
|
|
180
180
|
const duration = toast.duration ?? 4000;
|
|
181
|
-
if (duration > 0) {
|
|
181
|
+
if (duration > 0 && typeof window !== 'undefined') {
|
|
182
182
|
setTimeout(() => this.dismiss(id), duration);
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
forwardRef,
|
|
7
7
|
input,
|
|
8
8
|
model,
|
|
9
|
-
output,
|
|
10
9
|
signal,
|
|
11
10
|
} from '@angular/core';
|
|
12
11
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
@@ -120,9 +119,6 @@ export class Toggle implements ControlValueAccessor {
|
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
/** Emitted when pressed state changes */
|
|
124
|
-
readonly pressedChange = output<boolean>();
|
|
125
|
-
|
|
126
122
|
/** Whether the toggle is pressed/on */
|
|
127
123
|
readonly pressed = model<boolean>(false);
|
|
128
124
|
|
|
@@ -170,7 +166,6 @@ export class Toggle implements ControlValueAccessor {
|
|
|
170
166
|
this.pressed.set(newValue);
|
|
171
167
|
this.onChange(newValue);
|
|
172
168
|
this.onTouched();
|
|
173
|
-
this.pressedChange.emit(newValue);
|
|
174
169
|
}
|
|
175
170
|
}
|
|
176
171
|
// ControlValueAccessor implementation
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
inject,
|
|
9
9
|
input,
|
|
10
10
|
model,
|
|
11
|
-
output,
|
|
12
11
|
signal,
|
|
13
12
|
} from '@angular/core';
|
|
14
13
|
import type { ToggleVariants } from '../toggle/toggle-variants';
|
|
@@ -138,9 +137,6 @@ export type ToggleGroupProps = {
|
|
|
138
137
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
139
138
|
})
|
|
140
139
|
export class ToggleGroup {
|
|
141
|
-
/** Emitted when value changes */
|
|
142
|
-
readonly valueChange = output<string | string[]>();
|
|
143
|
-
|
|
144
140
|
/** The current selected value(s) */
|
|
145
141
|
readonly value = model<string | string[]>('');
|
|
146
142
|
|
|
@@ -205,7 +201,6 @@ export class ToggleGroup {
|
|
|
205
201
|
|
|
206
202
|
this.value.set(newValue);
|
|
207
203
|
this.context.value.set(newValue);
|
|
208
|
-
this.valueChange.emit(newValue);
|
|
209
204
|
},
|
|
210
205
|
isPressed: (itemValue: string) => {
|
|
211
206
|
const currentValue = this.context.value();
|