@shival99/z-ui 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/assets/css/animations.css +207 -0
  2. package/assets/css/base.css +76 -0
  3. package/assets/css/tailwind.css +53 -0
  4. package/assets/css/themes/gray.css +73 -0
  5. package/assets/css/themes/green.css +75 -0
  6. package/assets/css/themes/hospital.css +79 -0
  7. package/assets/css/themes/neutral.css +73 -0
  8. package/assets/css/themes/orange.css +73 -0
  9. package/assets/css/themes/slate.css +73 -0
  10. package/assets/css/themes/stone.css +73 -0
  11. package/assets/css/themes/violet.css +73 -0
  12. package/assets/css/themes/zinc.css +73 -0
  13. package/assets/images/avatar.svg +6 -0
  14. package/assets/images/logo.svg +6 -0
  15. package/fesm2022/shival99-z-ui-components-z-accordion.mjs +148 -0
  16. package/fesm2022/shival99-z-ui-components-z-accordion.mjs.map +1 -0
  17. package/fesm2022/shival99-z-ui-components-z-breadcrumb.mjs +74 -0
  18. package/fesm2022/shival99-z-ui-components-z-breadcrumb.mjs.map +1 -0
  19. package/fesm2022/shival99-z-ui-components-z-button.mjs +155 -0
  20. package/fesm2022/shival99-z-ui-components-z-button.mjs.map +1 -0
  21. package/fesm2022/shival99-z-ui-components-z-calendar.mjs +2335 -0
  22. package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -0
  23. package/fesm2022/shival99-z-ui-components-z-checkbox.mjs +240 -0
  24. package/fesm2022/shival99-z-ui-components-z-checkbox.mjs.map +1 -0
  25. package/fesm2022/shival99-z-ui-components-z-code.mjs +139 -0
  26. package/fesm2022/shival99-z-ui-components-z-code.mjs.map +1 -0
  27. package/fesm2022/shival99-z-ui-components-z-drawer.mjs +664 -0
  28. package/fesm2022/shival99-z-ui-components-z-drawer.mjs.map +1 -0
  29. package/fesm2022/shival99-z-ui-components-z-dropdown-menu.mjs +55 -0
  30. package/fesm2022/shival99-z-ui-components-z-dropdown-menu.mjs.map +1 -0
  31. package/fesm2022/shival99-z-ui-components-z-editor.mjs +411 -0
  32. package/fesm2022/shival99-z-ui-components-z-editor.mjs.map +1 -0
  33. package/fesm2022/shival99-z-ui-components-z-filter.mjs +794 -0
  34. package/fesm2022/shival99-z-ui-components-z-filter.mjs.map +1 -0
  35. package/fesm2022/shival99-z-ui-components-z-icon.mjs +451 -0
  36. package/fesm2022/shival99-z-ui-components-z-icon.mjs.map +1 -0
  37. package/fesm2022/shival99-z-ui-components-z-input.mjs +804 -0
  38. package/fesm2022/shival99-z-ui-components-z-input.mjs.map +1 -0
  39. package/fesm2022/shival99-z-ui-components-z-loading.mjs +105 -0
  40. package/fesm2022/shival99-z-ui-components-z-loading.mjs.map +1 -0
  41. package/fesm2022/shival99-z-ui-components-z-menu.mjs +351 -0
  42. package/fesm2022/shival99-z-ui-components-z-menu.mjs.map +1 -0
  43. package/fesm2022/shival99-z-ui-components-z-modal.mjs +722 -0
  44. package/fesm2022/shival99-z-ui-components-z-modal.mjs.map +1 -0
  45. package/fesm2022/shival99-z-ui-components-z-pagination.mjs +131 -0
  46. package/fesm2022/shival99-z-ui-components-z-pagination.mjs.map +1 -0
  47. package/fesm2022/shival99-z-ui-components-z-popover.mjs +917 -0
  48. package/fesm2022/shival99-z-ui-components-z-popover.mjs.map +1 -0
  49. package/fesm2022/shival99-z-ui-components-z-radio.mjs +154 -0
  50. package/fesm2022/shival99-z-ui-components-z-radio.mjs.map +1 -0
  51. package/fesm2022/shival99-z-ui-components-z-select.mjs +998 -0
  52. package/fesm2022/shival99-z-ui-components-z-select.mjs.map +1 -0
  53. package/fesm2022/shival99-z-ui-components-z-skeleton.mjs +139 -0
  54. package/fesm2022/shival99-z-ui-components-z-skeleton.mjs.map +1 -0
  55. package/fesm2022/shival99-z-ui-components-z-switch.mjs +127 -0
  56. package/fesm2022/shival99-z-ui-components-z-switch.mjs.map +1 -0
  57. package/fesm2022/shival99-z-ui-components-z-table.mjs +2628 -0
  58. package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -0
  59. package/fesm2022/shival99-z-ui-components-z-tabs.mjs +259 -0
  60. package/fesm2022/shival99-z-ui-components-z-tabs.mjs.map +1 -0
  61. package/fesm2022/shival99-z-ui-components-z-timeline.mjs +335 -0
  62. package/fesm2022/shival99-z-ui-components-z-timeline.mjs.map +1 -0
  63. package/fesm2022/shival99-z-ui-components-z-toast.mjs +93 -0
  64. package/fesm2022/shival99-z-ui-components-z-toast.mjs.map +1 -0
  65. package/fesm2022/shival99-z-ui-components-z-tooltip.mjs +660 -0
  66. package/fesm2022/shival99-z-ui-components-z-tooltip.mjs.map +1 -0
  67. package/fesm2022/shival99-z-ui-components-z-upload.mjs +504 -0
  68. package/fesm2022/shival99-z-ui-components-z-upload.mjs.map +1 -0
  69. package/fesm2022/shival99-z-ui-i18n.mjs +258 -0
  70. package/fesm2022/shival99-z-ui-i18n.mjs.map +1 -0
  71. package/fesm2022/shival99-z-ui-pipes.mjs +116 -0
  72. package/fesm2022/shival99-z-ui-pipes.mjs.map +1 -0
  73. package/fesm2022/shival99-z-ui-providers.mjs +203 -0
  74. package/fesm2022/shival99-z-ui-providers.mjs.map +1 -0
  75. package/fesm2022/shival99-z-ui-services.mjs +919 -0
  76. package/fesm2022/shival99-z-ui-services.mjs.map +1 -0
  77. package/fesm2022/shival99-z-ui-utils.mjs +591 -0
  78. package/fesm2022/shival99-z-ui-utils.mjs.map +1 -0
  79. package/fesm2022/z-ui.mjs +3 -19924
  80. package/fesm2022/z-ui.mjs.map +1 -1
  81. package/package.json +129 -1
  82. package/types/shival99-z-ui-components-z-accordion.d.ts +55 -0
  83. package/types/shival99-z-ui-components-z-breadcrumb.d.ts +36 -0
  84. package/types/shival99-z-ui-components-z-button.d.ts +41 -0
  85. package/types/shival99-z-ui-components-z-calendar.d.ts +300 -0
  86. package/types/shival99-z-ui-components-z-checkbox.d.ts +84 -0
  87. package/types/shival99-z-ui-components-z-code.d.ts +35 -0
  88. package/types/shival99-z-ui-components-z-drawer.d.ts +232 -0
  89. package/types/shival99-z-ui-components-z-dropdown-menu.d.ts +50 -0
  90. package/types/shival99-z-ui-components-z-editor.d.ts +115 -0
  91. package/types/shival99-z-ui-components-z-filter.d.ts +268 -0
  92. package/types/shival99-z-ui-components-z-icon.d.ts +291 -0
  93. package/types/shival99-z-ui-components-z-input.d.ts +188 -0
  94. package/types/shival99-z-ui-components-z-loading.d.ts +46 -0
  95. package/types/shival99-z-ui-components-z-menu.d.ts +116 -0
  96. package/types/shival99-z-ui-components-z-modal.d.ts +270 -0
  97. package/types/shival99-z-ui-components-z-pagination.d.ts +52 -0
  98. package/types/shival99-z-ui-components-z-popover.d.ts +134 -0
  99. package/types/shival99-z-ui-components-z-radio.d.ts +63 -0
  100. package/types/shival99-z-ui-components-z-select.d.ts +268 -0
  101. package/types/shival99-z-ui-components-z-skeleton.d.ts +55 -0
  102. package/types/shival99-z-ui-components-z-switch.d.ts +48 -0
  103. package/types/shival99-z-ui-components-z-table.d.ts +482 -0
  104. package/types/shival99-z-ui-components-z-tabs.d.ts +75 -0
  105. package/types/shival99-z-ui-components-z-timeline.d.ts +98 -0
  106. package/types/shival99-z-ui-components-z-toast.d.ts +61 -0
  107. package/types/shival99-z-ui-components-z-tooltip.d.ts +85 -0
  108. package/types/shival99-z-ui-components-z-upload.d.ts +136 -0
  109. package/types/shival99-z-ui-i18n.d.ts +50 -0
  110. package/types/shival99-z-ui-pipes.d.ts +36 -0
  111. package/types/shival99-z-ui-providers.d.ts +132 -0
  112. package/types/shival99-z-ui-services.d.ts +364 -0
  113. package/types/shival99-z-ui-utils.d.ts +145 -0
  114. package/types/z-ui.d.ts +3 -4977
@@ -0,0 +1,998 @@
1
+ import { NgClass, NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, TemplateRef, Directive, Pipe, input, contentChild, output, viewChild, signal, Injector, DestroyRef, computed, effect, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+ import * as i1 from '@angular/forms';
6
+ import { NgControl, PristineChangeEvent, TouchedChangeEvent, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import { injectVirtualizer } from '@tanstack/angular-virtual';
8
+ import { Subject, debounceTime, merge, filter, isObservable, takeUntil, catchError, of, firstValueFrom } from 'rxjs';
9
+ import { cva } from 'class-variance-authority';
10
+ import { zRemoveVietnamese, zMergeClasses, zTransform, zGenerateId } from '@shival99/z-ui/utils';
11
+ import { ZHighlightPipe, ZSafeHtmlPipe } from '@shival99/z-ui/pipes';
12
+ import { ZButtonComponent } from '@shival99/z-ui/components/z-button';
13
+ import { ZIconComponent } from '@shival99/z-ui/components/z-icon';
14
+ import { ZLoadingComponent } from '@shival99/z-ui/components/z-loading';
15
+ import { ZPopoverDirective } from '@shival99/z-ui/components/z-popover';
16
+ import { ZTooltipDirective } from '@shival99/z-ui/components/z-tooltip';
17
+
18
+ class ZSelectOptionDirective {
19
+ templateRef = inject((TemplateRef));
20
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSelectOptionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
21
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: ZSelectOptionDirective, isStandalone: true, selector: "[z-select-option], [zSelectOption]", ngImport: i0 });
22
+ }
23
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSelectOptionDirective, decorators: [{
24
+ type: Directive,
25
+ args: [{
26
+ selector: '[z-select-option], [zSelectOption]',
27
+ standalone: true,
28
+ }]
29
+ }] });
30
+
31
+ class ZSelectSelectedDirective {
32
+ templateRef = inject((TemplateRef));
33
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSelectSelectedDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
34
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: ZSelectSelectedDirective, isStandalone: true, selector: "[z-select-selected], [zSelectSelected]", ngImport: i0 });
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSelectSelectedDirective, decorators: [{
37
+ type: Directive,
38
+ args: [{
39
+ selector: '[z-select-selected], [zSelectSelected]',
40
+ standalone: true,
41
+ }]
42
+ }] });
43
+
44
+ class ZIsSelectedPipe {
45
+ transform(option, value, isMultipleMode) {
46
+ if (isMultipleMode) {
47
+ const current = value ?? [];
48
+ return current.includes(option.value);
49
+ }
50
+ return value === option.value;
51
+ }
52
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZIsSelectedPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
53
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZIsSelectedPipe, isStandalone: true, name: "zIsSelected" });
54
+ }
55
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZIsSelectedPipe, decorators: [{
56
+ type: Pipe,
57
+ args: [{
58
+ name: 'zIsSelected',
59
+ standalone: true,
60
+ pure: true,
61
+ }]
62
+ }] });
63
+
64
+ const zSelectVariants = cva([
65
+ 'flex w-full items-center rounded-[6px] border border-input bg-white shadow-xs cursor-pointer',
66
+ 'transition-[border-color,box-shadow,background-color,color,opacity] duration-200',
67
+ 'dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
68
+ 'outline-none',
69
+ 'text-sm',
70
+ ], {
71
+ variants: {
72
+ zSize: {
73
+ sm: 'min-h-8 px-2 py-1 gap-2 text-sm',
74
+ default: 'min-h-9 px-2 py-1.5 gap-2 text-sm',
75
+ lg: 'min-h-10 px-2 py-2 gap-2.5 text-sm',
76
+ },
77
+ zStatus: {
78
+ default: 'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]',
79
+ open: 'border-ring ring-ring/50 ring-[3px]',
80
+ error: 'border-destructive! ring-destructive/20 ring-[3px] focus-within:border-destructive! focus-within:ring-destructive/30',
81
+ disabled: 'opacity-50 cursor-not-allowed bg-muted! text-muted-foreground',
82
+ readonly: 'cursor-default bg-muted/50 text-muted-foreground',
83
+ },
84
+ },
85
+ defaultVariants: {
86
+ zSize: 'default',
87
+ zStatus: 'default',
88
+ },
89
+ });
90
+ const zSelectTagVariants = cva('inline-flex items-center gap-1 rounded border px-1.5 py-0.5 text-xs font-medium transition-colors', {
91
+ variants: {
92
+ zSize: {
93
+ sm: 'text-[10px] px-1 py-0',
94
+ default: 'text-xs px-1.5 py-0.5',
95
+ lg: 'text-xs px-2 py-1',
96
+ },
97
+ },
98
+ defaultVariants: {
99
+ zSize: 'default',
100
+ },
101
+ });
102
+ const zSelectOptionVariants = cva('flex items-center gap-2 px-2 py-1.5 cursor-pointer transition-colors text-sm rounded-[4px] outline-none whitespace-nowrap', {
103
+ variants: {
104
+ selected: {
105
+ true: 'bg-primary/15 text-primary',
106
+ false: 'hover:bg-primary/10 hover:text-foreground',
107
+ },
108
+ disabled: {
109
+ true: 'opacity-50 cursor-not-allowed pointer-events-none',
110
+ false: '',
111
+ },
112
+ },
113
+ defaultVariants: {
114
+ selected: false,
115
+ disabled: false,
116
+ },
117
+ });
118
+
119
+ class ZOptionClassesPipe {
120
+ transform(isSelected, isDisabled) {
121
+ return zSelectOptionVariants({
122
+ selected: isSelected,
123
+ disabled: isDisabled ?? false,
124
+ });
125
+ }
126
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZOptionClassesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
127
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZOptionClassesPipe, isStandalone: true, name: "zOptionClasses" });
128
+ }
129
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZOptionClassesPipe, decorators: [{
130
+ type: Pipe,
131
+ args: [{
132
+ name: 'zOptionClasses',
133
+ standalone: true,
134
+ pure: true,
135
+ }]
136
+ }] });
137
+
138
+ const TAG_COLORS = [
139
+ {
140
+ bg: 'bg-blue-100 dark:bg-blue-950',
141
+ text: 'text-blue-700 dark:text-blue-300',
142
+ border: 'border-blue-300 dark:border-blue-800',
143
+ },
144
+ {
145
+ bg: 'bg-green-100 dark:bg-green-950',
146
+ text: 'text-green-700 dark:text-green-300',
147
+ border: 'border-green-300 dark:border-green-800',
148
+ },
149
+ {
150
+ bg: 'bg-purple-100 dark:bg-purple-950',
151
+ text: 'text-purple-700 dark:text-purple-300',
152
+ border: 'border-purple-300 dark:border-purple-800',
153
+ },
154
+ {
155
+ bg: 'bg-orange-100 dark:bg-orange-950',
156
+ text: 'text-orange-700 dark:text-orange-300',
157
+ border: 'border-orange-300 dark:border-orange-800',
158
+ },
159
+ {
160
+ bg: 'bg-pink-100 dark:bg-pink-950',
161
+ text: 'text-pink-700 dark:text-pink-300',
162
+ border: 'border-pink-300 dark:border-pink-800',
163
+ },
164
+ {
165
+ bg: 'bg-cyan-100 dark:bg-cyan-950',
166
+ text: 'text-cyan-700 dark:text-cyan-300',
167
+ border: 'border-cyan-300 dark:border-cyan-800',
168
+ },
169
+ {
170
+ bg: 'bg-yellow-100 dark:bg-yellow-950',
171
+ text: 'text-yellow-700 dark:text-yellow-300',
172
+ border: 'border-yellow-300 dark:border-yellow-800',
173
+ },
174
+ {
175
+ bg: 'bg-red-100 dark:bg-red-950',
176
+ text: 'text-red-700 dark:text-red-300',
177
+ border: 'border-red-300 dark:border-red-800',
178
+ },
179
+ {
180
+ bg: 'bg-indigo-100 dark:bg-indigo-950',
181
+ text: 'text-indigo-700 dark:text-indigo-300',
182
+ border: 'border-indigo-300 dark:border-indigo-800',
183
+ },
184
+ {
185
+ bg: 'bg-teal-100 dark:bg-teal-950',
186
+ text: 'text-teal-700 dark:text-teal-300',
187
+ border: 'border-teal-300 dark:border-teal-800',
188
+ },
189
+ ];
190
+
191
+ const colorCache = new Map();
192
+ function getTagColor(value) {
193
+ const key = String(value);
194
+ const cached = colorCache.get(key);
195
+ if (cached) {
196
+ return cached;
197
+ }
198
+ let hash = 0;
199
+ for (let i = 0; i < key.length; i++) {
200
+ hash = (hash << 5) - hash + key.charCodeAt(i);
201
+ hash = hash & hash;
202
+ }
203
+ const index = Math.abs(hash) % TAG_COLORS.length;
204
+ const color = TAG_COLORS[index];
205
+ colorCache.set(key, color);
206
+ return color;
207
+ }
208
+ function filterOptions(options, searchText) {
209
+ const query = searchText.trim();
210
+ if (!query) {
211
+ return options;
212
+ }
213
+ const normalizedQuery = zRemoveVietnamese(query.toLowerCase());
214
+ return options.filter(opt => {
215
+ const normalizedLabel = zRemoveVietnamese(opt.label.toLowerCase());
216
+ return normalizedLabel.includes(normalizedQuery);
217
+ });
218
+ }
219
+
220
+ class ZTagClassesPipe {
221
+ transform(option, size, isTagsMode) {
222
+ const baseClasses = zSelectTagVariants({ zSize: size });
223
+ if (isTagsMode) {
224
+ const color = getTagColor(option.value);
225
+ return zMergeClasses(baseClasses, color.bg, color.text, color.border);
226
+ }
227
+ return zMergeClasses(baseClasses, 'bg-muted text-foreground border-border');
228
+ }
229
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTagClassesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
230
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: ZTagClassesPipe, isStandalone: true, name: "zTagClasses" });
231
+ }
232
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTagClassesPipe, decorators: [{
233
+ type: Pipe,
234
+ args: [{
235
+ name: 'zTagClasses',
236
+ standalone: true,
237
+ pure: true,
238
+ }]
239
+ }] });
240
+
241
+ class ZSelectComponent {
242
+ class = input('', ...(ngDevMode ? [{ debugName: "class" }] : []));
243
+ zMode = input('single', ...(ngDevMode ? [{ debugName: "zMode" }] : []));
244
+ zSize = input('default', ...(ngDevMode ? [{ debugName: "zSize" }] : []));
245
+ zLabel = input('', ...(ngDevMode ? [{ debugName: "zLabel" }] : []));
246
+ zLabelClass = input('', ...(ngDevMode ? [{ debugName: "zLabelClass" }] : []));
247
+ zPlaceholder = input('Select...', ...(ngDevMode ? [{ debugName: "zPlaceholder" }] : []));
248
+ zRequired = input(false, { ...(ngDevMode ? { debugName: "zRequired" } : {}), transform: zTransform });
249
+ zDisabled = input(false, { ...(ngDevMode ? { debugName: "zDisabled" } : {}), transform: zTransform });
250
+ zReadonly = input(false, { ...(ngDevMode ? { debugName: "zReadonly" } : {}), transform: zTransform });
251
+ zLoading = input(false, { ...(ngDevMode ? { debugName: "zLoading" } : {}), transform: zTransform });
252
+ zPrefix = input('', ...(ngDevMode ? [{ debugName: "zPrefix" }] : []));
253
+ zAllowClear = input(false, { ...(ngDevMode ? { debugName: "zAllowClear" } : {}), transform: zTransform });
254
+ zWrap = input(true, { ...(ngDevMode ? { debugName: "zWrap" } : {}), transform: zTransform });
255
+ zShowSearch = input(true, { ...(ngDevMode ? { debugName: "zShowSearch" } : {}), transform: zTransform });
256
+ zPlaceholderSearch = input('Search...', ...(ngDevMode ? [{ debugName: "zPlaceholderSearch" }] : []));
257
+ zDebounce = input(300, ...(ngDevMode ? [{ debugName: "zDebounce" }] : []));
258
+ zNotFoundText = input('No results found', ...(ngDevMode ? [{ debugName: "zNotFoundText" }] : []));
259
+ zEmptyText = input('No data', ...(ngDevMode ? [{ debugName: "zEmptyText" }] : []));
260
+ zEmptyIcon = input('lucideInbox', ...(ngDevMode ? [{ debugName: "zEmptyIcon" }] : []));
261
+ zMaxTagCount = input(3, ...(ngDevMode ? [{ debugName: "zMaxTagCount" }] : []));
262
+ zDropdownMaxHeight = input(256, ...(ngDevMode ? [{ debugName: "zDropdownMaxHeight" }] : []));
263
+ zOptionHeight = input(32, ...(ngDevMode ? [{ debugName: "zOptionHeight" }] : []));
264
+ zVirtualScroll = input(false, { ...(ngDevMode ? { debugName: "zVirtualScroll" } : {}), transform: zTransform });
265
+ zShowAction = input(false, { ...(ngDevMode ? { debugName: "zShowAction" } : {}), transform: zTransform });
266
+ zOptions = input([], ...(ngDevMode ? [{ debugName: "zOptions" }] : []));
267
+ zKey = input('', ...(ngDevMode ? [{ debugName: "zKey" }] : []));
268
+ zSearchServer = input(false, { ...(ngDevMode ? { debugName: "zSearchServer" } : {}), transform: zTransform });
269
+ zLoadingMore = input(false, { ...(ngDevMode ? { debugName: "zLoadingMore" } : {}), transform: zTransform });
270
+ zEnableLoadMore = input(false, { ...(ngDevMode ? { debugName: "zEnableLoadMore" } : {}), transform: zTransform });
271
+ zScrollDistance = input(2, ...(ngDevMode ? [{ debugName: "zScrollDistance" }] : []));
272
+ zSelectedTemplate = input(null, ...(ngDevMode ? [{ debugName: "zSelectedTemplate" }] : []));
273
+ zOptionTemplate = input(null, ...(ngDevMode ? [{ debugName: "zOptionTemplate" }] : []));
274
+ zActionTemplate = input(null, ...(ngDevMode ? [{ debugName: "zActionTemplate" }] : []));
275
+ customSelectedDirective = contentChild((ZSelectSelectedDirective), ...(ngDevMode ? [{ debugName: "customSelectedDirective" }] : []));
276
+ customOptionDirective = contentChild((ZSelectOptionDirective), ...(ngDevMode ? [{ debugName: "customOptionDirective" }] : []));
277
+ zOnSearch = output();
278
+ zOnLoadMore = output();
279
+ zControl = output();
280
+ zAsyncValidators = input([], ...(ngDevMode ? [{ debugName: "zAsyncValidators" }] : []));
281
+ zAsyncDebounce = input(300, ...(ngDevMode ? [{ debugName: "zAsyncDebounce" }] : []));
282
+ zAsyncValidateOn = input('change', ...(ngDevMode ? [{ debugName: "zAsyncValidateOn" }] : []));
283
+ zValidators = input([], ...(ngDevMode ? [{ debugName: "zValidators" }] : []));
284
+ triggerRef = viewChild('triggerEl', ...(ngDevMode ? [{ debugName: "triggerRef" }] : []));
285
+ searchInputRef = viewChild('dropdownSearchInput', ...(ngDevMode ? [{ debugName: "searchInputRef" }] : []));
286
+ dropdownTpl = viewChild('dropdownTpl', ...(ngDevMode ? [{ debugName: "dropdownTpl" }] : []));
287
+ optionsContainerRef = viewChild('optionsContainer', ...(ngDevMode ? [{ debugName: "optionsContainerRef" }] : []));
288
+ virtualScrollRef = viewChild('virtualScrollElement', ...(ngDevMode ? [{ debugName: "virtualScrollRef" }] : []));
289
+ loadMoreSentinelRef = viewChild('loadMoreSentinel', ...(ngDevMode ? [{ debugName: "loadMoreSentinelRef" }] : []));
290
+ selectId = zGenerateId('z-select');
291
+ dropdownId = `${this.selectId}-dropdown`;
292
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
293
+ searchText = signal('', ...(ngDevMode ? [{ debugName: "searchText" }] : []));
294
+ dropdownWidth = signal(0, ...(ngDevMode ? [{ debugName: "dropdownWidth" }] : []));
295
+ hasScrollShadow = signal(false, ...(ngDevMode ? [{ debugName: "hasScrollShadow" }] : []));
296
+ _popoverControl = signal(null, ...(ngDevMode ? [{ debugName: "_popoverControl" }] : []));
297
+ _searchSubject = new Subject();
298
+ _loadMoreObserver = null;
299
+ _allKnownOptions = signal([], ...(ngDevMode ? [{ debugName: "_allKnownOptions" }] : []));
300
+ _optionGap = 3;
301
+ virtualizer = injectVirtualizer(() => ({
302
+ scrollElement: this.virtualScrollRef(),
303
+ count: this.filteredOptions().length,
304
+ estimateSize: () => this.zOptionHeight() + this._optionGap,
305
+ overscan: 5,
306
+ }));
307
+ _value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : []));
308
+ currentValue = this._value.asReadonly();
309
+ _disabled = signal(false, ...(ngDevMode ? [{ debugName: "_disabled" }] : []));
310
+ _touched = signal(false, ...(ngDevMode ? [{ debugName: "_touched" }] : []));
311
+ _dirty = signal(false, ...(ngDevMode ? [{ debugName: "_dirty" }] : []));
312
+ _formControl = signal(null, ...(ngDevMode ? [{ debugName: "_formControl" }] : []));
313
+ _formStateVersion = signal(0, ...(ngDevMode ? [{ debugName: "_formStateVersion" }] : []));
314
+ _injector = inject(Injector);
315
+ _destroyRef = inject(DestroyRef);
316
+ _onChange = () => void 0;
317
+ _onTouched = () => void 0;
318
+ _ngControl = null;
319
+ isValidating = signal(false, ...(ngDevMode ? [{ debugName: "isValidating" }] : []));
320
+ _asyncErrors = signal(new Map(), ...(ngDevMode ? [{ debugName: "_asyncErrors" }] : []));
321
+ _asyncCancelSubject = new Subject();
322
+ _asyncValidationSubject = new Subject();
323
+ _asyncValidationAbortController = null;
324
+ isDisabled = computed(() => this._disabled() || this.zDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
325
+ isReadonly = computed(() => this.zReadonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : []));
326
+ isInteractionDisabled = computed(() => this.isDisabled() || this.isReadonly() || this.zLoading(), ...(ngDevMode ? [{ debugName: "isInteractionDisabled" }] : []));
327
+ isMultipleMode = computed(() => {
328
+ const mode = this.zMode();
329
+ return mode === 'multiple' || mode === 'tags';
330
+ }, ...(ngDevMode ? [{ debugName: "isMultipleMode" }] : []));
331
+ isTagsMode = computed(() => this.zMode() === 'tags', ...(ngDevMode ? [{ debugName: "isTagsMode" }] : []));
332
+ effectiveSelectedTemplate = computed(() => {
333
+ const directive = this.customSelectedDirective();
334
+ if (directive) {
335
+ return directive.templateRef;
336
+ }
337
+ return this.zSelectedTemplate();
338
+ }, ...(ngDevMode ? [{ debugName: "effectiveSelectedTemplate" }] : []));
339
+ effectiveOptionTemplate = computed(() => {
340
+ const directive = this.customOptionDirective();
341
+ if (directive) {
342
+ return directive.templateRef;
343
+ }
344
+ return this.zOptionTemplate();
345
+ }, ...(ngDevMode ? [{ debugName: "effectiveOptionTemplate" }] : []));
346
+ shouldUseVirtualScroll = computed(() => {
347
+ if (this.zVirtualScroll()) {
348
+ return true;
349
+ }
350
+ if (this.zEnableLoadMore()) {
351
+ return false;
352
+ }
353
+ return this.zOptions().length > 50;
354
+ }, ...(ngDevMode ? [{ debugName: "shouldUseVirtualScroll" }] : []));
355
+ selectedOption = computed(() => {
356
+ if (this.isMultipleMode()) {
357
+ return null;
358
+ }
359
+ const value = this._value();
360
+ if (value === null || value === undefined) {
361
+ return null;
362
+ }
363
+ const options = this.zSearchServer() ? this._allKnownOptions() : this.zOptions();
364
+ return options.find(opt => opt.value === value) ?? null;
365
+ }, ...(ngDevMode ? [{ debugName: "selectedOption" }] : []));
366
+ selectedOptions = computed(() => {
367
+ if (!this.isMultipleMode()) {
368
+ return [];
369
+ }
370
+ const value = this._value();
371
+ if (!Array.isArray(value)) {
372
+ return [];
373
+ }
374
+ const options = this.zSearchServer() ? this._allKnownOptions() : this.zOptions();
375
+ return value
376
+ .map(v => options.find(opt => opt.value === v))
377
+ .filter((opt) => opt !== undefined);
378
+ }, ...(ngDevMode ? [{ debugName: "selectedOptions" }] : []));
379
+ displayedTags = computed(() => {
380
+ const selected = this.selectedOptions();
381
+ const max = this.zMaxTagCount();
382
+ return selected.slice(0, max);
383
+ }, ...(ngDevMode ? [{ debugName: "displayedTags" }] : []));
384
+ remainingCount = computed(() => {
385
+ const selected = this.selectedOptions();
386
+ const max = this.zMaxTagCount();
387
+ return Math.max(0, selected.length - max);
388
+ }, ...(ngDevMode ? [{ debugName: "remainingCount" }] : []));
389
+ hasValue = computed(() => {
390
+ const value = this._value();
391
+ if (Array.isArray(value)) {
392
+ return value.length > 0;
393
+ }
394
+ return value !== null && value !== undefined;
395
+ }, ...(ngDevMode ? [{ debugName: "hasValue" }] : []));
396
+ filteredOptions = computed(() => {
397
+ if (this.zSearchServer()) {
398
+ return this.zOptions();
399
+ }
400
+ return filterOptions(this.zOptions(), this.searchText());
401
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
402
+ isEmptyData = computed(() => {
403
+ if (this.zSearchServer() && this.searchText().trim()) {
404
+ return false;
405
+ }
406
+ return this.zOptions().length === 0;
407
+ }, ...(ngDevMode ? [{ debugName: "isEmptyData" }] : []));
408
+ isNoSearchResults = computed(() => {
409
+ if (this.zSearchServer() && this.searchText().trim()) {
410
+ return this.zOptions().length === 0;
411
+ }
412
+ const noFiltered = this.filteredOptions().length === 0;
413
+ const hasOptions = this.zOptions().length > 0;
414
+ const hasSearch = this.searchText().trim() !== '';
415
+ return noFiltered && hasOptions && hasSearch;
416
+ }, ...(ngDevMode ? [{ debugName: "isNoSearchResults" }] : []));
417
+ optionsMaxHeight = computed(() => {
418
+ const total = this.zDropdownMaxHeight();
419
+ const searchHeight = this.zShowSearch() ? 44 : 0;
420
+ const actionHeight = this.zShowAction() && this.isMultipleMode() ? 40 : 0;
421
+ return total - searchHeight - actionHeight;
422
+ }, ...(ngDevMode ? [{ debugName: "optionsMaxHeight" }] : []));
423
+ currentStatus = computed(() => {
424
+ if (this.isDisabled()) {
425
+ return 'disabled';
426
+ }
427
+ if (this.isReadonly()) {
428
+ return 'readonly';
429
+ }
430
+ if (this.isOpen()) {
431
+ return 'open';
432
+ }
433
+ if (this.hasError()) {
434
+ return 'error';
435
+ }
436
+ return 'default';
437
+ }, ...(ngDevMode ? [{ debugName: "currentStatus" }] : []));
438
+ _shouldShowValidation = computed(() => {
439
+ const control = this._formControl();
440
+ this._formStateVersion();
441
+ if (control) {
442
+ return control.dirty;
443
+ }
444
+ return this._dirty() || this._touched();
445
+ }, ...(ngDevMode ? [{ debugName: "_shouldShowValidation" }] : []));
446
+ hasError = computed(() => {
447
+ const asyncErrors = this._asyncErrors();
448
+ if (asyncErrors.size > 0 && this._shouldShowValidation()) {
449
+ return true;
450
+ }
451
+ const customErrors = this._getValidationErrors();
452
+ if (customErrors.length > 0) {
453
+ return true;
454
+ }
455
+ const control = this._formControl();
456
+ this._formStateVersion();
457
+ if (!control) {
458
+ return false;
459
+ }
460
+ return control.invalid && this._shouldShowValidation();
461
+ }, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
462
+ showError = computed(() => this.hasError(), ...(ngDevMode ? [{ debugName: "showError" }] : []));
463
+ errorMessage = computed(() => {
464
+ const asyncErrors = this._asyncErrors();
465
+ if (asyncErrors.size > 0) {
466
+ return asyncErrors.values().next().value;
467
+ }
468
+ const customErrors = this._getValidationErrors();
469
+ if (customErrors.length > 0) {
470
+ return customErrors[0];
471
+ }
472
+ const control = this._formControl();
473
+ const errors = control?.errors;
474
+ if (!errors) {
475
+ return '';
476
+ }
477
+ const validators = this.zValidators();
478
+ for (const validator of validators) {
479
+ if (errors[validator.error]) {
480
+ return validator.message;
481
+ }
482
+ }
483
+ if (errors['required']) {
484
+ return 'Vui lòng chọn giá trị';
485
+ }
486
+ return '';
487
+ }, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
488
+ selectClasses = computed(() => zMergeClasses(zSelectVariants({
489
+ zSize: this.zSize(),
490
+ zStatus: this.currentStatus(),
491
+ }),
492
+ // prettier-ignore
493
+ !this.zWrap() &&
494
+ this.isMultipleMode() && {
495
+ 'h-8': this.zSize() === 'sm',
496
+ 'h-9': this.zSize() === 'default',
497
+ 'h-10': this.zSize() === 'lg',
498
+ }, this.class()), ...(ngDevMode ? [{ debugName: "selectClasses" }] : []));
499
+ constructor() {
500
+ effect(() => {
501
+ if (!this.zSearchServer()) {
502
+ return;
503
+ }
504
+ const newOpts = this.zOptions();
505
+ const known = this._allKnownOptions();
506
+ const knownValues = new Set(known.map(o => o.value));
507
+ const toAdd = newOpts.filter(o => !knownValues.has(o.value));
508
+ if (toAdd.length > 0) {
509
+ this._allKnownOptions.set([...known, ...toAdd]);
510
+ }
511
+ });
512
+ effect(() => {
513
+ const isOpen = this.isOpen();
514
+ const useVirtual = this.shouldUseVirtualScroll();
515
+ if (!isOpen) {
516
+ return;
517
+ }
518
+ const getScrollElement = () => {
519
+ if (useVirtual) {
520
+ return this.virtualScrollRef()?.nativeElement ?? null;
521
+ }
522
+ return this.optionsContainerRef()?.nativeElement ?? null;
523
+ };
524
+ const handleScroll = () => {
525
+ const el = getScrollElement();
526
+ if (!el) {
527
+ return;
528
+ }
529
+ const isScrollable = el.scrollHeight > el.clientHeight;
530
+ this.hasScrollShadow.set(isScrollable);
531
+ };
532
+ requestAnimationFrame(() => {
533
+ requestAnimationFrame(() => {
534
+ handleScroll();
535
+ const el = getScrollElement();
536
+ if (el) {
537
+ el.addEventListener('scroll', handleScroll);
538
+ }
539
+ });
540
+ });
541
+ return () => {
542
+ const el = getScrollElement();
543
+ if (el) {
544
+ el.removeEventListener('scroll', handleScroll);
545
+ }
546
+ };
547
+ });
548
+ effect(() => {
549
+ const sentinel = this.loadMoreSentinelRef();
550
+ const isOpen = this.isOpen();
551
+ const optionsCount = this.filteredOptions().length;
552
+ if (!sentinel || !isOpen || optionsCount === 0) {
553
+ this._cleanupLoadMoreObserver();
554
+ return;
555
+ }
556
+ this._setupLoadMoreObserver();
557
+ });
558
+ }
559
+ ngOnInit() {
560
+ this._searchSubject.pipe(debounceTime(this.zDebounce()), takeUntilDestroyed(this._destroyRef)).subscribe(value => {
561
+ this.searchText.set(value);
562
+ this.zOnSearch.emit(value);
563
+ });
564
+ const asyncValidators = this.zAsyncValidators();
565
+ if (asyncValidators.length > 0) {
566
+ const debounceMs = this.zAsyncDebounce() || 300;
567
+ this._asyncValidationSubject
568
+ .pipe(debounceTime(debounceMs), takeUntilDestroyed(this._destroyRef))
569
+ .subscribe(value => {
570
+ void this._runAsyncValidation(value);
571
+ });
572
+ }
573
+ void Promise.resolve().then(() => {
574
+ try {
575
+ this._ngControl = this._injector.get(NgControl, null);
576
+ if (!this._ngControl?.control) {
577
+ return;
578
+ }
579
+ const { control } = this._ngControl;
580
+ this._formControl.set(control);
581
+ merge(control.statusChanges, control.valueChanges, control.events.pipe(filter(event => event instanceof PristineChangeEvent || event instanceof TouchedChangeEvent)))
582
+ .pipe(takeUntilDestroyed(this._destroyRef))
583
+ .subscribe(() => {
584
+ this._formStateVersion.update(v => v + 1);
585
+ });
586
+ }
587
+ catch {
588
+ this._ngControl = null;
589
+ }
590
+ });
591
+ setTimeout(() => this._emitControl());
592
+ }
593
+ writeValue(value) {
594
+ const fieldKey = this.zKey();
595
+ if (!fieldKey) {
596
+ this._value.set(value);
597
+ return;
598
+ }
599
+ if (value === null || value === undefined) {
600
+ this._value.set(value);
601
+ return;
602
+ }
603
+ if (Array.isArray(value)) {
604
+ const extractedValues = value.map(item => {
605
+ if (typeof item === 'object' && item !== null && fieldKey in item) {
606
+ return item[fieldKey];
607
+ }
608
+ return item;
609
+ });
610
+ this._value.set(extractedValues);
611
+ return;
612
+ }
613
+ if (typeof value === 'object' && value !== null && fieldKey in value) {
614
+ const extractedValue = value[fieldKey];
615
+ this._value.set(extractedValue);
616
+ return;
617
+ }
618
+ this._value.set(value);
619
+ }
620
+ registerOnChange(fn) {
621
+ this._onChange = fn;
622
+ }
623
+ registerOnTouched(fn) {
624
+ this._onTouched = fn;
625
+ }
626
+ setDisabledState(isDisabled) {
627
+ this._disabled.set(isDisabled);
628
+ }
629
+ onTriggerKeydown(event) {
630
+ if (this.isInteractionDisabled()) {
631
+ return;
632
+ }
633
+ if (event.key === 'Enter' || event.key === ' ') {
634
+ event.preventDefault();
635
+ this.toggleDropdown();
636
+ return;
637
+ }
638
+ if (event.key === 'Escape' && this.isOpen()) {
639
+ this._popoverControl()?.close();
640
+ }
641
+ }
642
+ onTriggerMousedown() {
643
+ const triggerEl = this.triggerRef()?.nativeElement;
644
+ if (triggerEl) {
645
+ this.dropdownWidth.set(triggerEl.getBoundingClientRect().width);
646
+ }
647
+ }
648
+ toggleDropdown() {
649
+ this.onTriggerMousedown();
650
+ this._popoverControl()?.toggle();
651
+ }
652
+ onPopoverControl(control) {
653
+ this._popoverControl.set(control);
654
+ }
655
+ onPopoverShow() {
656
+ this.isOpen.set(true);
657
+ this.searchText.set('');
658
+ this.zOnSearch.emit('');
659
+ if (this.zShowSearch()) {
660
+ setTimeout(() => {
661
+ this.searchInputRef()?.nativeElement.focus();
662
+ }, 100);
663
+ }
664
+ }
665
+ onPopoverHideStart() {
666
+ this.isOpen.set(false);
667
+ this.hasScrollShadow.set(false);
668
+ if (this._dirty()) {
669
+ this._touched.set(true);
670
+ }
671
+ this._onTouched();
672
+ }
673
+ onPopoverHideEnd() {
674
+ if (this.zSearchServer()) {
675
+ this.searchText.set('');
676
+ this.zOnSearch.emit('');
677
+ }
678
+ }
679
+ onSearchChange(value) {
680
+ this._searchSubject.next(value);
681
+ requestAnimationFrame(() => {
682
+ requestAnimationFrame(() => {
683
+ this.checkScrollShadow();
684
+ });
685
+ });
686
+ }
687
+ checkScrollShadow() {
688
+ const container = this.optionsContainerRef()?.nativeElement;
689
+ if (!container) {
690
+ this.hasScrollShadow.set(false);
691
+ return;
692
+ }
693
+ const isScrollable = container.scrollHeight > container.clientHeight;
694
+ this.hasScrollShadow.set(isScrollable);
695
+ }
696
+ _setupLoadMoreObserver() {
697
+ this._cleanupLoadMoreObserver();
698
+ const sentinel = this.loadMoreSentinelRef()?.nativeElement;
699
+ const container = this.optionsContainerRef()?.nativeElement;
700
+ if (!sentinel || !container) {
701
+ return;
702
+ }
703
+ this._loadMoreObserver = new IntersectionObserver(entries => {
704
+ const entry = entries[0];
705
+ if (entry?.isIntersecting && !this.zLoadingMore()) {
706
+ const currentCount = this.filteredOptions().length;
707
+ this.zOnLoadMore.emit({
708
+ currentCount,
709
+ scrollTop: container.scrollTop,
710
+ });
711
+ }
712
+ }, {
713
+ root: container,
714
+ rootMargin: '100px',
715
+ threshold: 0,
716
+ });
717
+ this._loadMoreObserver.observe(sentinel);
718
+ }
719
+ _cleanupLoadMoreObserver() {
720
+ if (this._loadMoreObserver) {
721
+ this._loadMoreObserver.disconnect();
722
+ this._loadMoreObserver = null;
723
+ }
724
+ }
725
+ selectOption(option, closeFn) {
726
+ if (option.disabled) {
727
+ return;
728
+ }
729
+ if (this.isMultipleMode()) {
730
+ const current = this._value() ?? [];
731
+ const isSelected = current.includes(option.value);
732
+ const newValue = isSelected ? current.filter(v => v !== option.value) : [...current, option.value];
733
+ this._value.set(newValue);
734
+ this._dirty.set(true);
735
+ this._onChange(newValue);
736
+ this._triggerAsyncValidation(newValue
737
+ .map(v => this.zOptions().find(o => o.value === v))
738
+ .filter((o) => o !== undefined));
739
+ return;
740
+ }
741
+ this._value.set(option.value);
742
+ this._dirty.set(true);
743
+ this._onChange(option.value);
744
+ this._triggerAsyncValidation(option);
745
+ this._popoverControl()?.close();
746
+ if (closeFn) {
747
+ closeFn();
748
+ }
749
+ }
750
+ removeOption(event, option) {
751
+ event.stopPropagation();
752
+ const current = this._value() ?? [];
753
+ const newValue = current.filter(v => v !== option.value);
754
+ this._value.set(newValue);
755
+ this._dirty.set(true);
756
+ this._onChange(newValue);
757
+ }
758
+ clearAll(event) {
759
+ event.stopPropagation();
760
+ if (this.isMultipleMode()) {
761
+ this._value.set([]);
762
+ this._dirty.set(true);
763
+ this._onChange([]);
764
+ return;
765
+ }
766
+ this._value.set(null);
767
+ this._dirty.set(true);
768
+ this._onChange(null);
769
+ }
770
+ selectAll() {
771
+ if (!this.isMultipleMode()) {
772
+ return;
773
+ }
774
+ const enabledOptions = this.zOptions().filter(opt => !opt.disabled);
775
+ const allValues = enabledOptions.map(opt => opt.value);
776
+ this._value.set(allValues);
777
+ this._dirty.set(true);
778
+ this._onChange(allValues);
779
+ }
780
+ removeAll() {
781
+ if (!this.isMultipleMode()) {
782
+ return;
783
+ }
784
+ this._value.set([]);
785
+ this._dirty.set(true);
786
+ this._onChange([]);
787
+ }
788
+ reset() {
789
+ if (this.isMultipleMode()) {
790
+ this._value.set([]);
791
+ this._onChange([]);
792
+ }
793
+ if (!this.isMultipleMode()) {
794
+ this._value.set(null);
795
+ this._onChange(null);
796
+ }
797
+ this._touched.set(false);
798
+ this._dirty.set(false);
799
+ this.searchText.set('');
800
+ if (!this._ngControl?.control) {
801
+ return;
802
+ }
803
+ this._ngControl.control.markAsPristine();
804
+ this._ngControl.control.markAsUntouched();
805
+ }
806
+ focus() {
807
+ this.triggerRef()?.nativeElement.focus();
808
+ }
809
+ blur() {
810
+ this.triggerRef()?.nativeElement.blur();
811
+ }
812
+ markAsTouched() {
813
+ this._touched.set(true);
814
+ this._onTouched();
815
+ }
816
+ markAsUntouched() {
817
+ this._touched.set(false);
818
+ }
819
+ markAsDirty() {
820
+ this._dirty.set(true);
821
+ }
822
+ markAsPristine() {
823
+ this._dirty.set(false);
824
+ }
825
+ validate() {
826
+ this._touched.set(true);
827
+ this._dirty.set(true);
828
+ this._onTouched();
829
+ if (this._ngControl?.control) {
830
+ this._ngControl.control.markAsDirty();
831
+ this._ngControl.control.markAsTouched();
832
+ }
833
+ }
834
+ _emitControl() {
835
+ this.zControl.emit({
836
+ validate: () => this.validate(),
837
+ reset: () => this.reset(),
838
+ focus: () => this.focus(),
839
+ blur: () => this.blur(),
840
+ markAsTouched: () => this.markAsTouched(),
841
+ markAsUntouched: () => this.markAsUntouched(),
842
+ markAsDirty: () => this.markAsDirty(),
843
+ markAsPristine: () => this.markAsPristine(),
844
+ hasError: this.hasError,
845
+ isValidating: this.isValidating,
846
+ isOpen: this.isOpen.asReadonly(),
847
+ value: this._value.asReadonly(),
848
+ selectedOption: this.selectedOption,
849
+ selectedOptions: this.selectedOptions,
850
+ errorMessage: this.errorMessage,
851
+ });
852
+ }
853
+ trackByValue = (_index, option) => {
854
+ const key = this.zKey();
855
+ if (key && key in option) {
856
+ return option[key];
857
+ }
858
+ return option.value;
859
+ };
860
+ _getValidationErrors() {
861
+ const validators = this.zValidators();
862
+ const errors = [];
863
+ if (!this._shouldShowValidation()) {
864
+ return errors;
865
+ }
866
+ const selectedValue = this.isMultipleMode() ? this.selectedOptions() : this.selectedOption();
867
+ const isRequired = this.zRequired() || this._formControl()?.errors?.['required'];
868
+ if (isRequired && !this.hasValue()) {
869
+ errors.push('Vui lòng chọn giá trị');
870
+ }
871
+ for (const validator of validators) {
872
+ if (!validator.validate(selectedValue)) {
873
+ errors.push(validator.message);
874
+ }
875
+ }
876
+ return errors;
877
+ }
878
+ async _runAsyncValidation(value) {
879
+ const asyncValidators = this.zAsyncValidators();
880
+ if (asyncValidators.length === 0) {
881
+ return;
882
+ }
883
+ if (value === null || value === undefined) {
884
+ this._asyncErrors.set(new Map());
885
+ this.isValidating.set(false);
886
+ return;
887
+ }
888
+ this._cancelAsyncValidation();
889
+ this._asyncValidationAbortController = new AbortController();
890
+ const { signal } = this._asyncValidationAbortController;
891
+ this.isValidating.set(true);
892
+ const errors = new Map();
893
+ try {
894
+ const validationPromises = asyncValidators.map(async (validator) => {
895
+ if (signal.aborted) {
896
+ return null;
897
+ }
898
+ try {
899
+ const result = validator.validate(value);
900
+ if (!isObservable(result)) {
901
+ const isValid = await result;
902
+ if (signal.aborted) {
903
+ return null;
904
+ }
905
+ return { validator, isValid };
906
+ }
907
+ const safeResult$ = result.pipe(takeUntil(this._asyncCancelSubject), catchError(err => {
908
+ console.error(`Async validation error for ${validator.error}:`, err);
909
+ return of(false);
910
+ }));
911
+ const isValid = await firstValueFrom(safeResult$);
912
+ if (signal.aborted) {
913
+ return null;
914
+ }
915
+ return { validator, isValid };
916
+ }
917
+ catch (error) {
918
+ if (!signal.aborted) {
919
+ console.error(`Async validation error for ${validator.error}:`, error);
920
+ return { validator, isValid: false };
921
+ }
922
+ return null;
923
+ }
924
+ });
925
+ const results = await Promise.all(validationPromises);
926
+ if (signal.aborted) {
927
+ return;
928
+ }
929
+ for (const result of results) {
930
+ if (result && !result.isValid) {
931
+ errors.set(result.validator.error, result.validator.message);
932
+ }
933
+ }
934
+ this._asyncErrors.set(errors);
935
+ }
936
+ catch (error) {
937
+ if (!signal.aborted) {
938
+ console.error('Async validation error:', error);
939
+ }
940
+ }
941
+ finally {
942
+ if (!signal.aborted) {
943
+ this.isValidating.set(false);
944
+ }
945
+ }
946
+ }
947
+ _cancelAsyncValidation() {
948
+ if (this._asyncValidationAbortController) {
949
+ this._asyncValidationAbortController.abort();
950
+ this._asyncValidationAbortController = null;
951
+ }
952
+ this._asyncCancelSubject.next();
953
+ }
954
+ _triggerAsyncValidation(value) {
955
+ if (this.zAsyncValidators().length > 0 && this.zAsyncValidateOn() === 'change') {
956
+ this._asyncValidationSubject.next(value);
957
+ }
958
+ }
959
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
960
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZSelectComponent, isStandalone: true, selector: "z-select", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, zMode: { classPropertyName: "zMode", publicName: "zMode", isSignal: true, isRequired: false, transformFunction: null }, zSize: { classPropertyName: "zSize", publicName: "zSize", isSignal: true, isRequired: false, transformFunction: null }, zLabel: { classPropertyName: "zLabel", publicName: "zLabel", isSignal: true, isRequired: false, transformFunction: null }, zLabelClass: { classPropertyName: "zLabelClass", publicName: "zLabelClass", isSignal: true, isRequired: false, transformFunction: null }, zPlaceholder: { classPropertyName: "zPlaceholder", publicName: "zPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, zRequired: { classPropertyName: "zRequired", publicName: "zRequired", isSignal: true, isRequired: false, transformFunction: null }, zDisabled: { classPropertyName: "zDisabled", publicName: "zDisabled", isSignal: true, isRequired: false, transformFunction: null }, zReadonly: { classPropertyName: "zReadonly", publicName: "zReadonly", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zPrefix: { classPropertyName: "zPrefix", publicName: "zPrefix", isSignal: true, isRequired: false, transformFunction: null }, zAllowClear: { classPropertyName: "zAllowClear", publicName: "zAllowClear", isSignal: true, isRequired: false, transformFunction: null }, zWrap: { classPropertyName: "zWrap", publicName: "zWrap", isSignal: true, isRequired: false, transformFunction: null }, zShowSearch: { classPropertyName: "zShowSearch", publicName: "zShowSearch", isSignal: true, isRequired: false, transformFunction: null }, zPlaceholderSearch: { classPropertyName: "zPlaceholderSearch", publicName: "zPlaceholderSearch", isSignal: true, isRequired: false, transformFunction: null }, zDebounce: { classPropertyName: "zDebounce", publicName: "zDebounce", isSignal: true, isRequired: false, transformFunction: null }, zNotFoundText: { classPropertyName: "zNotFoundText", publicName: "zNotFoundText", isSignal: true, isRequired: false, transformFunction: null }, zEmptyText: { classPropertyName: "zEmptyText", publicName: "zEmptyText", isSignal: true, isRequired: false, transformFunction: null }, zEmptyIcon: { classPropertyName: "zEmptyIcon", publicName: "zEmptyIcon", isSignal: true, isRequired: false, transformFunction: null }, zMaxTagCount: { classPropertyName: "zMaxTagCount", publicName: "zMaxTagCount", isSignal: true, isRequired: false, transformFunction: null }, zDropdownMaxHeight: { classPropertyName: "zDropdownMaxHeight", publicName: "zDropdownMaxHeight", isSignal: true, isRequired: false, transformFunction: null }, zOptionHeight: { classPropertyName: "zOptionHeight", publicName: "zOptionHeight", isSignal: true, isRequired: false, transformFunction: null }, zVirtualScroll: { classPropertyName: "zVirtualScroll", publicName: "zVirtualScroll", isSignal: true, isRequired: false, transformFunction: null }, zShowAction: { classPropertyName: "zShowAction", publicName: "zShowAction", isSignal: true, isRequired: false, transformFunction: null }, zOptions: { classPropertyName: "zOptions", publicName: "zOptions", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null }, zSearchServer: { classPropertyName: "zSearchServer", publicName: "zSearchServer", isSignal: true, isRequired: false, transformFunction: null }, zLoadingMore: { classPropertyName: "zLoadingMore", publicName: "zLoadingMore", isSignal: true, isRequired: false, transformFunction: null }, zEnableLoadMore: { classPropertyName: "zEnableLoadMore", publicName: "zEnableLoadMore", isSignal: true, isRequired: false, transformFunction: null }, zScrollDistance: { classPropertyName: "zScrollDistance", publicName: "zScrollDistance", isSignal: true, isRequired: false, transformFunction: null }, zSelectedTemplate: { classPropertyName: "zSelectedTemplate", publicName: "zSelectedTemplate", isSignal: true, isRequired: false, transformFunction: null }, zOptionTemplate: { classPropertyName: "zOptionTemplate", publicName: "zOptionTemplate", isSignal: true, isRequired: false, transformFunction: null }, zActionTemplate: { classPropertyName: "zActionTemplate", publicName: "zActionTemplate", isSignal: true, isRequired: false, transformFunction: null }, zAsyncValidators: { classPropertyName: "zAsyncValidators", publicName: "zAsyncValidators", isSignal: true, isRequired: false, transformFunction: null }, zAsyncDebounce: { classPropertyName: "zAsyncDebounce", publicName: "zAsyncDebounce", isSignal: true, isRequired: false, transformFunction: null }, zAsyncValidateOn: { classPropertyName: "zAsyncValidateOn", publicName: "zAsyncValidateOn", isSignal: true, isRequired: false, transformFunction: null }, zValidators: { classPropertyName: "zValidators", publicName: "zValidators", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zOnSearch: "zOnSearch", zOnLoadMore: "zOnLoadMore", zControl: "zControl" }, providers: [
961
+ {
962
+ provide: NG_VALUE_ACCESSOR,
963
+ useExisting: forwardRef(() => ZSelectComponent),
964
+ multi: true,
965
+ },
966
+ ], queries: [{ propertyName: "customSelectedDirective", first: true, predicate: (ZSelectSelectedDirective), descendants: true, isSignal: true }, { propertyName: "customOptionDirective", first: true, predicate: (ZSelectOptionDirective), descendants: true, isSignal: true }], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerEl"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["dropdownSearchInput"], descendants: true, isSignal: true }, { propertyName: "dropdownTpl", first: true, predicate: ["dropdownTpl"], descendants: true, isSignal: true }, { propertyName: "optionsContainerRef", first: true, predicate: ["optionsContainer"], descendants: true, isSignal: true }, { propertyName: "virtualScrollRef", first: true, predicate: ["virtualScrollElement"], descendants: true, isSignal: true }, { propertyName: "loadMoreSentinelRef", first: true, predicate: ["loadMoreSentinel"], descendants: true, isSignal: true }], exportAs: ["zSelect"], ngImport: i0, template: "<div class=\"z-select-wrapper relative flex w-full flex-col gap-2\">\n @if (zLabel()) {\n <label [for]=\"selectId\" class=\"text-xs leading-none font-medium\" [class]=\"zLabelClass()\">\n {{ zLabel() }}\n @if (zRequired()) {\n <span class=\"text-destructive! ml-0.5\">*</span>\n }\n </label>\n }\n\n <div\n #triggerEl\n [id]=\"selectId\"\n z-popover\n [zPopoverContent]=\"dropdownTpl\"\n [zOffset]=\"6\"\n [zDisabled]=\"isInteractionDisabled()\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n zClass=\"border-0 shadow-none bg-transparent p-0\"\n (zControl)=\"onPopoverControl($event)\"\n (zShow)=\"onPopoverShow()\"\n (zHideStart)=\"onPopoverHideStart()\"\n (zHide)=\"onPopoverHideEnd()\"\n class=\"z-select-trigger cursor-pointer\"\n [class]=\"selectClasses()\"\n [class.z-select-open]=\"isOpen()\"\n (mousedown)=\"onTriggerMousedown()\"\n (keydown)=\"onTriggerKeydown($event)\"\n tabindex=\"0\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-controls]=\"dropdownId\">\n @if (zPrefix()) {\n <z-icon [zType]=\"zPrefix() || 'lucideChevronDown'\" zSize=\"16\" class=\"text-muted-foreground shrink-0\" />\n }\n\n <div\n class=\"z-select-tags-wrapper flex min-w-0 flex-1 items-center gap-1\"\n [class.flex-wrap]=\"zWrap()\"\n [class.overflow-hidden]=\"!zWrap()\">\n @if (isMultipleMode()) {\n @if (selectedOptions().length === 0) {\n <span class=\"text-muted-foreground\">{{ zPlaceholder() }}</span>\n } @else {\n @for (opt of displayedTags(); track opt.value) {\n <span #tagEl [class]=\"opt | zTagClasses: zSize() : isTagsMode()\" class=\"z-select-tag\">\n @if (effectiveSelectedTemplate()) {\n <ng-container *ngTemplateOutlet=\"effectiveSelectedTemplate()!; context: { $implicit: opt }\" />\n } @else {\n <span\n class=\"max-w-25 truncate\"\n z-tooltip\n [zContent]=\"opt.label\"\n zPosition=\"top\"\n [zHideDelay]=\"0\"\n [zOffset]=\"6\"\n [zTriggerElement]=\"tagEl\">\n {{ opt.label }}\n </span>\n }\n @if (zAllowClear() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"-mr-0.5 flex size-3.5 items-center justify-center rounded-[3px] transition-colors hover:bg-black/10\"\n (click)=\"removeOption($event, opt)\"\n tabindex=\"-1\">\n <z-icon zType=\"lucideX\" zSize=\"10\" />\n </button>\n }\n </span>\n }\n @if (remainingCount() > 0) {\n <span class=\"z-select-remaining text-muted-foreground bg-muted rounded px-1.5 py-0.5 text-xs font-medium\">\n +{{ remainingCount() }}\n </span>\n }\n }\n } @else {\n @if (selectedOption()) {\n @if (effectiveSelectedTemplate()) {\n <ng-container *ngTemplateOutlet=\"effectiveSelectedTemplate()!; context: { $implicit: selectedOption() }\" />\n } @else {\n <span\n class=\"truncate\"\n z-tooltip\n [zContent]=\"selectedOption()!.label\"\n zPosition=\"top\"\n [zHideDelay]=\"0\"\n [zOffset]=\"10\">\n {{ selectedOption()!.label }}\n </span>\n }\n } @else {\n <span class=\"text-muted-foreground\">{{ zPlaceholder() }}</span>\n }\n }\n </div>\n\n @if (zLoading() || zLoadingMore()) {\n <z-icon zType=\"lucideLoaderCircle\" zSize=\"14\" class=\"text-muted-foreground shrink-0 animate-spin\" />\n }\n\n @if (!zLoading() && zAllowClear() && hasValue() && !isDisabled() && !isReadonly()) {\n <button\n type=\"button\"\n class=\"text-muted-foreground hover:text-foreground flex shrink-0 items-center justify-center transition-colors\"\n (click)=\"clearAll($event)\"\n tabindex=\"-1\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n </button>\n }\n\n @if (isValidating()) {\n <z-icon zType=\"lucideLoader2\" zSize=\"14\" class=\"text-muted-foreground shrink-0 animate-spin\" />\n }\n\n <z-icon\n zType=\"lucideChevronDown\"\n zSize=\"16\"\n class=\"text-muted-foreground shrink-0 transition-transform duration-200\"\n [class.rotate-180]=\"isOpen()\" />\n </div>\n\n @if (showError()) {\n <p class=\"text-destructive animate-in fade-in slide-in-from-top-1 m-0 text-xs duration-200\">\n {{ errorMessage() }}\n </p>\n }\n</div>\n\n<ng-template #dropdownTpl let-close=\"close\">\n <div\n [id]=\"dropdownId\"\n class=\"z-select-dropdown bg-popover border-border rounded-[6px] border shadow-lg\"\n [style.width.px]=\"dropdownWidth()\">\n @if (zShowSearch()) {\n <div class=\"border-border flex items-center border-b px-3\">\n <z-icon zType=\"lucideSearch\" zSize=\"16\" class=\"text-muted-foreground/50 mr-2 shrink-0\" />\n <input\n #dropdownSearchInput\n type=\"text\"\n class=\"placeholder:text-muted-foreground text-foreground flex h-10 w-full bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50\"\n [placeholder]=\"zPlaceholderSearch()\"\n [ngModel]=\"searchText()\"\n (ngModelChange)=\"onSearchChange($event)\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\" />\n </div>\n }\n\n @if (zLoading()) {\n <div class=\"flex flex-col items-center justify-center py-8\">\n <z-loading [zLoading]=\"true\" zSize=\"default\" />\n </div>\n } @else {\n @if (shouldUseVirtualScroll()) {\n <div\n #virtualScrollElement\n class=\"z-select-options z-select-virtual-scroll overflow-x-hidden overflow-y-auto overscroll-y-contain p-1\"\n [style.height.px]=\"optionsMaxHeight()\">\n <div class=\"z-select-virtual-inner relative\" [style.height.px]=\"virtualizer.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let opt = filteredOptions()[virtualItem.index];\n <div\n #optionEl\n class=\"z-select-option absolute right-0 left-0 min-w-0\"\n [ngClass]=\"opt | zIsSelected: currentValue() : isMultipleMode() | zOptionClasses: opt.disabled\"\n [style.height.px]=\"zOptionHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\"\n (click)=\"selectOption(opt, close)\">\n @if (effectiveOptionTemplate()) {\n <ng-container\n *ngTemplateOutlet=\"\n effectiveOptionTemplate()!;\n context: { $implicit: opt, selected: opt | zIsSelected: currentValue() : isMultipleMode() }\n \" />\n } @else {\n <span\n class=\"min-w-0 flex-1 truncate\"\n z-tooltip\n [zContent]=\"opt.label\"\n zPosition=\"top\"\n [zHideDelay]=\"0\"\n [zOffset]=\"5\"\n [zArrow]=\"false\"\n [zTriggerElement]=\"optionEl\"\n [innerHTML]=\"opt.label | zHighlight: searchText() | zSafeHtml\"></span>\n }\n @if (opt | zIsSelected: currentValue() : isMultipleMode()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-primary ml-auto shrink-0\" />\n }\n </div>\n }\n </div>\n @if (zLoadingMore()) {\n <div\n class=\"bg-popover/80 absolute right-0 bottom-0 left-0 flex items-center justify-center py-2\"\n [style.transform]=\"'translateY(' + virtualizer.getTotalSize() + 'px)'\">\n <z-loading zSize=\"xs\" [zLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #optionsContainer\n class=\"z-select-options flex flex-col gap-0.75 overflow-x-hidden overflow-y-auto overscroll-y-contain p-1\"\n [style.maxHeight.px]=\"optionsMaxHeight()\">\n @for (opt of filteredOptions(); track opt.value) {\n <div\n #optionEl2\n class=\"z-select-option relative min-w-0\"\n [ngClass]=\"opt | zIsSelected: currentValue() : isMultipleMode() | zOptionClasses: opt.disabled\"\n [style.minHeight.px]=\"zOptionHeight()\"\n (click)=\"selectOption(opt, close)\">\n @if (effectiveOptionTemplate()) {\n <ng-container\n *ngTemplateOutlet=\"\n effectiveOptionTemplate()!;\n context: { $implicit: opt, selected: opt | zIsSelected: currentValue() : isMultipleMode() }\n \" />\n } @else {\n <span\n class=\"min-w-0 flex-1 truncate\"\n z-tooltip\n [zContent]=\"opt.label\"\n zPosition=\"top\"\n [zArrow]=\"false\"\n [zOffset]=\"5\"\n [zHideDelay]=\"0\"\n [zTriggerElement]=\"optionEl2\"\n [innerHTML]=\"opt.label | zHighlight: searchText() | zSafeHtml\"></span>\n }\n @if (opt | zIsSelected: currentValue() : isMultipleMode()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-primary ml-auto shrink-0\" />\n }\n </div>\n }\n <div #loadMoreSentinel class=\"h-px w-full shrink-0\"></div>\n </div>\n }\n\n @if (filteredOptions().length === 0) {\n <div class=\"flex flex-col items-center justify-center py-8\">\n @if (isEmptyData()) {\n <z-icon [zType]=\"zEmptyIcon()\" zSize=\"40\" class=\"text-muted-foreground/50 mb-2\" />\n <span class=\"text-muted-foreground text-sm\">{{ zEmptyText() }}</span>\n } @else {\n <z-icon zType=\"lucideSearch\" zSize=\"40\" class=\"text-muted-foreground/50 mb-2\" />\n <span class=\"text-muted-foreground text-sm\">{{ zNotFoundText() }}</span>\n }\n </div>\n }\n\n @if (zLoadingMore()) {\n <div class=\"z-select-loading-more border-border/50 flex items-center justify-center border-t py-1.5\">\n <z-loading zSize=\"xs\" [zLoading]=\"true\" />\n </div>\n }\n }\n\n @if (zShowAction() && isMultipleMode()) {\n <div class=\"border-border flex items-center gap-2 border-t px-2 py-1.5\">\n @if (zActionTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionTemplate()!\" />\n } @else {\n <z-button zSize=\"xs\" zType=\"ghost-info\" class=\"flex-1\" (click)=\"selectAll()\" [zWave]=\"false\">\n <z-icon zType=\"lucideCheckCheck\" zSize=\"14\" />\n Select All\n </z-button>\n <z-button zSize=\"xs\" zType=\"ghost-error\" class=\"flex-1\" (click)=\"removeAll()\" [zWave]=\"false\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n Remove All\n </z-button>\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [".z-select-trigger{-webkit-user-select:none;user-select:none}.z-select-trigger:focus{outline:none}.z-select-trigger *{cursor:inherit}.z-select-tag{-webkit-user-select:none;user-select:none;animation:z-select-tag-enter .2s cubic-bezier(.4,0,.2,1);transition:transform .15s ease}.z-select-tag:hover{transform:translateY(-1px)}.z-select-tag:active{transform:scale(.95)}@keyframes z-select-tag-enter{0%{opacity:0;transform:scale(.8) translateY(4px)}to{opacity:1;transform:scale(1) translateY(0)}}.z-select-remaining{animation:z-select-remaining-enter .25s cubic-bezier(.4,0,.2,1)}@keyframes z-select-remaining-enter{0%{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.z-select-dropdown{animation:z-select-dropdown-enter .15s ease-out;contain:layout style;will-change:transform,opacity}@keyframes z-select-dropdown-enter{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.z-select-options{overflow-x:hidden!important}.z-select-virtual-scroll .z-select-virtual-inner{width:100%}.z-select-loading-more{animation:z-select-loading-enter .2s ease-out}@keyframes z-select-loading-enter{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl"], exportAs: ["zPopover"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "pipe", type: ZIsSelectedPipe, name: "zIsSelected" }, { kind: "pipe", type: ZOptionClassesPipe, name: "zOptionClasses" }, { kind: "pipe", type: ZTagClassesPipe, name: "zTagClasses" }, { kind: "pipe", type: ZHighlightPipe, name: "zHighlight" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
967
+ }
968
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZSelectComponent, decorators: [{
969
+ type: Component,
970
+ args: [{ selector: 'z-select', imports: [
971
+ FormsModule,
972
+ NgClass,
973
+ ZIconComponent,
974
+ ZPopoverDirective,
975
+ NgTemplateOutlet,
976
+ ZButtonComponent,
977
+ ZTooltipDirective,
978
+ ZIsSelectedPipe,
979
+ ZOptionClassesPipe,
980
+ ZTagClassesPipe,
981
+ ZLoadingComponent,
982
+ ZHighlightPipe,
983
+ ZSafeHtmlPipe,
984
+ ], standalone: true, providers: [
985
+ {
986
+ provide: NG_VALUE_ACCESSOR,
987
+ useExisting: forwardRef(() => ZSelectComponent),
988
+ multi: true,
989
+ },
990
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, exportAs: 'zSelect', template: "<div class=\"z-select-wrapper relative flex w-full flex-col gap-2\">\n @if (zLabel()) {\n <label [for]=\"selectId\" class=\"text-xs leading-none font-medium\" [class]=\"zLabelClass()\">\n {{ zLabel() }}\n @if (zRequired()) {\n <span class=\"text-destructive! ml-0.5\">*</span>\n }\n </label>\n }\n\n <div\n #triggerEl\n [id]=\"selectId\"\n z-popover\n [zPopoverContent]=\"dropdownTpl\"\n [zOffset]=\"6\"\n [zDisabled]=\"isInteractionDisabled()\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n zClass=\"border-0 shadow-none bg-transparent p-0\"\n (zControl)=\"onPopoverControl($event)\"\n (zShow)=\"onPopoverShow()\"\n (zHideStart)=\"onPopoverHideStart()\"\n (zHide)=\"onPopoverHideEnd()\"\n class=\"z-select-trigger cursor-pointer\"\n [class]=\"selectClasses()\"\n [class.z-select-open]=\"isOpen()\"\n (mousedown)=\"onTriggerMousedown()\"\n (keydown)=\"onTriggerKeydown($event)\"\n tabindex=\"0\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-controls]=\"dropdownId\">\n @if (zPrefix()) {\n <z-icon [zType]=\"zPrefix() || 'lucideChevronDown'\" zSize=\"16\" class=\"text-muted-foreground shrink-0\" />\n }\n\n <div\n class=\"z-select-tags-wrapper flex min-w-0 flex-1 items-center gap-1\"\n [class.flex-wrap]=\"zWrap()\"\n [class.overflow-hidden]=\"!zWrap()\">\n @if (isMultipleMode()) {\n @if (selectedOptions().length === 0) {\n <span class=\"text-muted-foreground\">{{ zPlaceholder() }}</span>\n } @else {\n @for (opt of displayedTags(); track opt.value) {\n <span #tagEl [class]=\"opt | zTagClasses: zSize() : isTagsMode()\" class=\"z-select-tag\">\n @if (effectiveSelectedTemplate()) {\n <ng-container *ngTemplateOutlet=\"effectiveSelectedTemplate()!; context: { $implicit: opt }\" />\n } @else {\n <span\n class=\"max-w-25 truncate\"\n z-tooltip\n [zContent]=\"opt.label\"\n zPosition=\"top\"\n [zHideDelay]=\"0\"\n [zOffset]=\"6\"\n [zTriggerElement]=\"tagEl\">\n {{ opt.label }}\n </span>\n }\n @if (zAllowClear() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"-mr-0.5 flex size-3.5 items-center justify-center rounded-[3px] transition-colors hover:bg-black/10\"\n (click)=\"removeOption($event, opt)\"\n tabindex=\"-1\">\n <z-icon zType=\"lucideX\" zSize=\"10\" />\n </button>\n }\n </span>\n }\n @if (remainingCount() > 0) {\n <span class=\"z-select-remaining text-muted-foreground bg-muted rounded px-1.5 py-0.5 text-xs font-medium\">\n +{{ remainingCount() }}\n </span>\n }\n }\n } @else {\n @if (selectedOption()) {\n @if (effectiveSelectedTemplate()) {\n <ng-container *ngTemplateOutlet=\"effectiveSelectedTemplate()!; context: { $implicit: selectedOption() }\" />\n } @else {\n <span\n class=\"truncate\"\n z-tooltip\n [zContent]=\"selectedOption()!.label\"\n zPosition=\"top\"\n [zHideDelay]=\"0\"\n [zOffset]=\"10\">\n {{ selectedOption()!.label }}\n </span>\n }\n } @else {\n <span class=\"text-muted-foreground\">{{ zPlaceholder() }}</span>\n }\n }\n </div>\n\n @if (zLoading() || zLoadingMore()) {\n <z-icon zType=\"lucideLoaderCircle\" zSize=\"14\" class=\"text-muted-foreground shrink-0 animate-spin\" />\n }\n\n @if (!zLoading() && zAllowClear() && hasValue() && !isDisabled() && !isReadonly()) {\n <button\n type=\"button\"\n class=\"text-muted-foreground hover:text-foreground flex shrink-0 items-center justify-center transition-colors\"\n (click)=\"clearAll($event)\"\n tabindex=\"-1\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n </button>\n }\n\n @if (isValidating()) {\n <z-icon zType=\"lucideLoader2\" zSize=\"14\" class=\"text-muted-foreground shrink-0 animate-spin\" />\n }\n\n <z-icon\n zType=\"lucideChevronDown\"\n zSize=\"16\"\n class=\"text-muted-foreground shrink-0 transition-transform duration-200\"\n [class.rotate-180]=\"isOpen()\" />\n </div>\n\n @if (showError()) {\n <p class=\"text-destructive animate-in fade-in slide-in-from-top-1 m-0 text-xs duration-200\">\n {{ errorMessage() }}\n </p>\n }\n</div>\n\n<ng-template #dropdownTpl let-close=\"close\">\n <div\n [id]=\"dropdownId\"\n class=\"z-select-dropdown bg-popover border-border rounded-[6px] border shadow-lg\"\n [style.width.px]=\"dropdownWidth()\">\n @if (zShowSearch()) {\n <div class=\"border-border flex items-center border-b px-3\">\n <z-icon zType=\"lucideSearch\" zSize=\"16\" class=\"text-muted-foreground/50 mr-2 shrink-0\" />\n <input\n #dropdownSearchInput\n type=\"text\"\n class=\"placeholder:text-muted-foreground text-foreground flex h-10 w-full bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50\"\n [placeholder]=\"zPlaceholderSearch()\"\n [ngModel]=\"searchText()\"\n (ngModelChange)=\"onSearchChange($event)\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n spellcheck=\"false\" />\n </div>\n }\n\n @if (zLoading()) {\n <div class=\"flex flex-col items-center justify-center py-8\">\n <z-loading [zLoading]=\"true\" zSize=\"default\" />\n </div>\n } @else {\n @if (shouldUseVirtualScroll()) {\n <div\n #virtualScrollElement\n class=\"z-select-options z-select-virtual-scroll overflow-x-hidden overflow-y-auto overscroll-y-contain p-1\"\n [style.height.px]=\"optionsMaxHeight()\">\n <div class=\"z-select-virtual-inner relative\" [style.height.px]=\"virtualizer.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let opt = filteredOptions()[virtualItem.index];\n <div\n #optionEl\n class=\"z-select-option absolute right-0 left-0 min-w-0\"\n [ngClass]=\"opt | zIsSelected: currentValue() : isMultipleMode() | zOptionClasses: opt.disabled\"\n [style.height.px]=\"zOptionHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\"\n (click)=\"selectOption(opt, close)\">\n @if (effectiveOptionTemplate()) {\n <ng-container\n *ngTemplateOutlet=\"\n effectiveOptionTemplate()!;\n context: { $implicit: opt, selected: opt | zIsSelected: currentValue() : isMultipleMode() }\n \" />\n } @else {\n <span\n class=\"min-w-0 flex-1 truncate\"\n z-tooltip\n [zContent]=\"opt.label\"\n zPosition=\"top\"\n [zHideDelay]=\"0\"\n [zOffset]=\"5\"\n [zArrow]=\"false\"\n [zTriggerElement]=\"optionEl\"\n [innerHTML]=\"opt.label | zHighlight: searchText() | zSafeHtml\"></span>\n }\n @if (opt | zIsSelected: currentValue() : isMultipleMode()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-primary ml-auto shrink-0\" />\n }\n </div>\n }\n </div>\n @if (zLoadingMore()) {\n <div\n class=\"bg-popover/80 absolute right-0 bottom-0 left-0 flex items-center justify-center py-2\"\n [style.transform]=\"'translateY(' + virtualizer.getTotalSize() + 'px)'\">\n <z-loading zSize=\"xs\" [zLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #optionsContainer\n class=\"z-select-options flex flex-col gap-0.75 overflow-x-hidden overflow-y-auto overscroll-y-contain p-1\"\n [style.maxHeight.px]=\"optionsMaxHeight()\">\n @for (opt of filteredOptions(); track opt.value) {\n <div\n #optionEl2\n class=\"z-select-option relative min-w-0\"\n [ngClass]=\"opt | zIsSelected: currentValue() : isMultipleMode() | zOptionClasses: opt.disabled\"\n [style.minHeight.px]=\"zOptionHeight()\"\n (click)=\"selectOption(opt, close)\">\n @if (effectiveOptionTemplate()) {\n <ng-container\n *ngTemplateOutlet=\"\n effectiveOptionTemplate()!;\n context: { $implicit: opt, selected: opt | zIsSelected: currentValue() : isMultipleMode() }\n \" />\n } @else {\n <span\n class=\"min-w-0 flex-1 truncate\"\n z-tooltip\n [zContent]=\"opt.label\"\n zPosition=\"top\"\n [zArrow]=\"false\"\n [zOffset]=\"5\"\n [zHideDelay]=\"0\"\n [zTriggerElement]=\"optionEl2\"\n [innerHTML]=\"opt.label | zHighlight: searchText() | zSafeHtml\"></span>\n }\n @if (opt | zIsSelected: currentValue() : isMultipleMode()) {\n <z-icon zType=\"lucideCheck\" zSize=\"14\" class=\"text-primary ml-auto shrink-0\" />\n }\n </div>\n }\n <div #loadMoreSentinel class=\"h-px w-full shrink-0\"></div>\n </div>\n }\n\n @if (filteredOptions().length === 0) {\n <div class=\"flex flex-col items-center justify-center py-8\">\n @if (isEmptyData()) {\n <z-icon [zType]=\"zEmptyIcon()\" zSize=\"40\" class=\"text-muted-foreground/50 mb-2\" />\n <span class=\"text-muted-foreground text-sm\">{{ zEmptyText() }}</span>\n } @else {\n <z-icon zType=\"lucideSearch\" zSize=\"40\" class=\"text-muted-foreground/50 mb-2\" />\n <span class=\"text-muted-foreground text-sm\">{{ zNotFoundText() }}</span>\n }\n </div>\n }\n\n @if (zLoadingMore()) {\n <div class=\"z-select-loading-more border-border/50 flex items-center justify-center border-t py-1.5\">\n <z-loading zSize=\"xs\" [zLoading]=\"true\" />\n </div>\n }\n }\n\n @if (zShowAction() && isMultipleMode()) {\n <div class=\"border-border flex items-center gap-2 border-t px-2 py-1.5\">\n @if (zActionTemplate()) {\n <ng-container *ngTemplateOutlet=\"zActionTemplate()!\" />\n } @else {\n <z-button zSize=\"xs\" zType=\"ghost-info\" class=\"flex-1\" (click)=\"selectAll()\" [zWave]=\"false\">\n <z-icon zType=\"lucideCheckCheck\" zSize=\"14\" />\n Select All\n </z-button>\n <z-button zSize=\"xs\" zType=\"ghost-error\" class=\"flex-1\" (click)=\"removeAll()\" [zWave]=\"false\">\n <z-icon zType=\"lucideX\" zSize=\"14\" />\n Remove All\n </z-button>\n }\n </div>\n }\n </div>\n</ng-template>\n", styles: [".z-select-trigger{-webkit-user-select:none;user-select:none}.z-select-trigger:focus{outline:none}.z-select-trigger *{cursor:inherit}.z-select-tag{-webkit-user-select:none;user-select:none;animation:z-select-tag-enter .2s cubic-bezier(.4,0,.2,1);transition:transform .15s ease}.z-select-tag:hover{transform:translateY(-1px)}.z-select-tag:active{transform:scale(.95)}@keyframes z-select-tag-enter{0%{opacity:0;transform:scale(.8) translateY(4px)}to{opacity:1;transform:scale(1) translateY(0)}}.z-select-remaining{animation:z-select-remaining-enter .25s cubic-bezier(.4,0,.2,1)}@keyframes z-select-remaining-enter{0%{opacity:0;transform:scale(.5)}to{opacity:1;transform:scale(1)}}.z-select-dropdown{animation:z-select-dropdown-enter .15s ease-out;contain:layout style;will-change:transform,opacity}@keyframes z-select-dropdown-enter{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.z-select-options{overflow-x:hidden!important}.z-select-virtual-scroll .z-select-virtual-inner{width:100%}.z-select-loading-more{animation:z-select-loading-enter .2s ease-out}@keyframes z-select-loading-enter{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
991
+ }], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], zMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMode", required: false }] }], zSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSize", required: false }] }], zLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLabel", required: false }] }], zLabelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLabelClass", required: false }] }], zPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "zPlaceholder", required: false }] }], zRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "zRequired", required: false }] }], zDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "zDisabled", required: false }] }], zReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "zReadonly", required: false }] }], zLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLoading", required: false }] }], zPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "zPrefix", required: false }] }], zAllowClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "zAllowClear", required: false }] }], zWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "zWrap", required: false }] }], zShowSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowSearch", required: false }] }], zPlaceholderSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "zPlaceholderSearch", required: false }] }], zDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "zDebounce", required: false }] }], zNotFoundText: [{ type: i0.Input, args: [{ isSignal: true, alias: "zNotFoundText", required: false }] }], zEmptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEmptyText", required: false }] }], zEmptyIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEmptyIcon", required: false }] }], zMaxTagCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "zMaxTagCount", required: false }] }], zDropdownMaxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "zDropdownMaxHeight", required: false }] }], zOptionHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "zOptionHeight", required: false }] }], zVirtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "zVirtualScroll", required: false }] }], zShowAction: [{ type: i0.Input, args: [{ isSignal: true, alias: "zShowAction", required: false }] }], zOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "zOptions", required: false }] }], zKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "zKey", required: false }] }], zSearchServer: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSearchServer", required: false }] }], zLoadingMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLoadingMore", required: false }] }], zEnableLoadMore: [{ type: i0.Input, args: [{ isSignal: true, alias: "zEnableLoadMore", required: false }] }], zScrollDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "zScrollDistance", required: false }] }], zSelectedTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zSelectedTemplate", required: false }] }], zOptionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zOptionTemplate", required: false }] }], zActionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "zActionTemplate", required: false }] }], customSelectedDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ZSelectSelectedDirective), { isSignal: true }] }], customOptionDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ZSelectOptionDirective), { isSignal: true }] }], zOnSearch: [{ type: i0.Output, args: ["zOnSearch"] }], zOnLoadMore: [{ type: i0.Output, args: ["zOnLoadMore"] }], zControl: [{ type: i0.Output, args: ["zControl"] }], zAsyncValidators: [{ type: i0.Input, args: [{ isSignal: true, alias: "zAsyncValidators", required: false }] }], zAsyncDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "zAsyncDebounce", required: false }] }], zAsyncValidateOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "zAsyncValidateOn", required: false }] }], zValidators: [{ type: i0.Input, args: [{ isSignal: true, alias: "zValidators", required: false }] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], searchInputRef: [{ type: i0.ViewChild, args: ['dropdownSearchInput', { isSignal: true }] }], dropdownTpl: [{ type: i0.ViewChild, args: ['dropdownTpl', { isSignal: true }] }], optionsContainerRef: [{ type: i0.ViewChild, args: ['optionsContainer', { isSignal: true }] }], virtualScrollRef: [{ type: i0.ViewChild, args: ['virtualScrollElement', { isSignal: true }] }], loadMoreSentinelRef: [{ type: i0.ViewChild, args: ['loadMoreSentinel', { isSignal: true }] }] } });
992
+
993
+ /**
994
+ * Generated bundle index. Do not edit.
995
+ */
996
+
997
+ export { TAG_COLORS, ZIsSelectedPipe, ZOptionClassesPipe, ZSelectComponent, ZSelectOptionDirective, ZSelectSelectedDirective, ZTagClassesPipe, filterOptions, getTagColor, zSelectOptionVariants, zSelectTagVariants, zSelectVariants };
998
+ //# sourceMappingURL=shival99-z-ui-components-z-select.mjs.map