@yoozsoft/yoozsoft-ng 6.0.4 → 6.0.5

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 (45) hide show
  1. package/fesm2022/yoozsoft-yoozsoft-ng-autocomplete.mjs +142 -112
  2. package/fesm2022/yoozsoft-yoozsoft-ng-autocomplete.mjs.map +1 -1
  3. package/fesm2022/yoozsoft-yoozsoft-ng-dropdown.mjs +111 -92
  4. package/fesm2022/yoozsoft-yoozsoft-ng-dropdown.mjs.map +1 -1
  5. package/fesm2022/yoozsoft-yoozsoft-ng-file-upload.mjs +6 -3
  6. package/fesm2022/yoozsoft-yoozsoft-ng-file-upload.mjs.map +1 -1
  7. package/fesm2022/yoozsoft-yoozsoft-ng-navbar.mjs +2 -2
  8. package/fesm2022/yoozsoft-yoozsoft-ng-navbar.mjs.map +1 -1
  9. package/fesm2022/yoozsoft-yoozsoft-ng-progress.mjs +17 -13
  10. package/fesm2022/yoozsoft-yoozsoft-ng-progress.mjs.map +1 -1
  11. package/fesm2022/yoozsoft-yoozsoft-ng-select.mjs +6 -3
  12. package/fesm2022/yoozsoft-yoozsoft-ng-select.mjs.map +1 -1
  13. package/fesm2022/yoozsoft-yoozsoft-ng-table.mjs +316 -0
  14. package/fesm2022/yoozsoft-yoozsoft-ng-table.mjs.map +1 -0
  15. package/fesm2022/yoozsoft-yoozsoft-ng-tiff-viewer.mjs +104 -97
  16. package/fesm2022/yoozsoft-yoozsoft-ng-tiff-viewer.mjs.map +1 -1
  17. package/fesm2022/yoozsoft-yoozsoft-ng-toast.mjs +96 -48
  18. package/fesm2022/yoozsoft-yoozsoft-ng-toast.mjs.map +1 -1
  19. package/package.json +5 -1
  20. package/types/yoozsoft-yoozsoft-ng-autocomplete.d.ts +27 -24
  21. package/types/yoozsoft-yoozsoft-ng-dropdown.d.ts +35 -23
  22. package/types/yoozsoft-yoozsoft-ng-file-upload.d.ts +2 -1
  23. package/types/yoozsoft-yoozsoft-ng-navbar.d.ts +1 -0
  24. package/types/yoozsoft-yoozsoft-ng-progress.d.ts +9 -9
  25. package/types/yoozsoft-yoozsoft-ng-select.d.ts +2 -1
  26. package/types/yoozsoft-yoozsoft-ng-table.d.ts +124 -0
  27. package/types/yoozsoft-yoozsoft-ng-tiff-viewer.d.ts +35 -39
  28. package/types/yoozsoft-yoozsoft-ng-toast.d.ts +43 -33
  29. package/assets/js/UTIF.js-master/package.json +0 -24
  30. package/autocomplete/package.json +0 -4
  31. package/datepicker/package.json +0 -4
  32. package/directives/package.json +0 -4
  33. package/dropdown/package.json +0 -4
  34. package/file-upload/package.json +0 -4
  35. package/footer/package.json +0 -4
  36. package/loading/package.json +0 -4
  37. package/navbar/package.json +0 -4
  38. package/overlay/package.json +0 -4
  39. package/pagination/package.json +0 -4
  40. package/password-strength/package.json +0 -4
  41. package/progress/package.json +0 -4
  42. package/select/package.json +0 -4
  43. package/sidebar/package.json +0 -4
  44. package/tiff-viewer/package.json +0 -4
  45. package/toast/package.json +0 -4
@@ -1,22 +1,36 @@
1
1
  import * as i2 from '@angular/cdk/scrolling';
2
2
  import { ScrollingModule } from '@angular/cdk/scrolling';
3
- import { NgTemplateOutlet, NgClass } from '@angular/common';
3
+ import { isPlatformBrowser, NgTemplateOutlet, NgClass } from '@angular/common';
4
4
  import * as i0 from '@angular/core';
5
- import { EventEmitter, TemplateRef, forwardRef, HostListener, ViewChild, Output, Input, ContentChild, Component } from '@angular/core';
5
+ import { signal, EventEmitter, inject, PLATFORM_ID, DestroyRef, effect, TemplateRef, forwardRef, Output, Input, ContentChild, ViewChild, Component } from '@angular/core';
6
6
  import * as i1 from '@angular/forms';
7
7
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
8
- import { Subject, debounceTime, distinctUntilChanged, switchMap, catchError, of } from 'rxjs';
8
+ import { catchError, of } from 'rxjs';
9
9
 
10
10
  class YsAutocompleteComponent {
11
11
  elRef;
12
- searchTerm = '';
13
- filteredOptions = [];
14
- selectedOptions = [];
15
- showDropdown = false;
16
- activeIndex = -1;
17
- error = null;
12
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
13
+ filteredOptions = signal([], ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
14
+ selectedOptions = signal([], ...(ngDevMode ? [{ debugName: "selectedOptions" }] : []));
15
+ showDropdown = signal(false, ...(ngDevMode ? [{ debugName: "showDropdown" }] : []));
16
+ activeIndex = signal(-1, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
17
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
18
+ inputRef;
19
+ _loading = signal(false, ...(ngDevMode ? [{ debugName: "_loading" }] : []));
20
+ set loading(value) { this._loading.set(!!value); }
21
+ get loading() { return this._loading(); }
22
+ loadingMessage = 'Loading...';
18
23
  /** Array or async function */
19
- options;
24
+ _options = [];
25
+ set options(value) {
26
+ this._options = value;
27
+ if (Array.isArray(value)) {
28
+ this.filteredOptions.set(value);
29
+ }
30
+ }
31
+ get options() {
32
+ return this._options;
33
+ }
20
34
  /** Label display field */
21
35
  optionLabel = 'label';
22
36
  /** placeholder */
@@ -31,8 +45,10 @@ class YsAutocompleteComponent {
31
45
  styleClass;
32
46
  /** Badge style class on Multiple choice mode */
33
47
  badgeStyleClass = 'text-bg-light';
34
- loading = false;
35
- loadingMessage = 'Loading...';
48
+ removeIconClass = 'fa fa-times';
49
+ searchIconClass = 'fa fa-search';
50
+ spinnerIconClass = 'fa fa-spinner fa-spin';
51
+ multipleCheckIconClass = 'fa fa-check';
36
52
  /** No results found message */
37
53
  noResultMessage = 'No results found.';
38
54
  /** Infinite scroll feature enabled */
@@ -42,50 +58,55 @@ class YsAutocompleteComponent {
42
58
  // [virtualScrollOptions]="options"
43
59
  /** Event to request more items */
44
60
  loadMore = new EventEmitter();
45
- /** Output selection */
46
- selected = new EventEmitter();
47
- /** Output selection after removed item */
48
- removed = new EventEmitter();
49
- search$ = new Subject();
61
+ /** Return selected options */
62
+ selectionChange = new EventEmitter();
50
63
  _onChange = () => { };
51
64
  _onTouched = () => { };
52
- inputRef;
53
65
  get isArrayItems() { return Array.isArray(this.options); }
54
66
  get isFunctionItems() { return typeof this.options === 'function'; }
55
67
  constructor(elRef) {
56
68
  this.elRef = elRef;
57
- }
58
- ngOnInit() {
59
- if (Array.isArray(this.options)) {
60
- this.filteredOptions = this.options;
69
+ const platformId = inject(PLATFORM_ID);
70
+ if (isPlatformBrowser(platformId)) {
71
+ const handler = (event) => {
72
+ if (!this.elRef.nativeElement.contains(event.target)) {
73
+ this.showDropdown.set(false);
74
+ }
75
+ };
76
+ document.addEventListener('click', handler);
77
+ inject(DestroyRef).onDestroy(() => {
78
+ document.removeEventListener('click', handler);
79
+ });
61
80
  }
62
- /** Only in async function mode */
63
- if (typeof this.options === 'function') {
64
- const fetchFn = this.options;
65
- this.search$
66
- .pipe(debounceTime(300), distinctUntilChanged(), switchMap((term) => {
67
- this.loading = true;
68
- this.error = null;
69
- return fetchFn(term).pipe(catchError((err) => {
70
- this.error = 'Error receiving data!';
81
+ effect(() => {
82
+ const term = this.searchTerm();
83
+ if (typeof this._options === 'function') {
84
+ this._loading.set(true);
85
+ this.error.set(null);
86
+ this._options(term).pipe(catchError(err => {
87
+ this.error.set('Error receiving data!');
71
88
  console.error(err);
72
89
  return of([]);
73
- }));
74
- }))
75
- .subscribe((results) => {
76
- this.filteredOptions = results;
77
- this.loading = false;
78
- this.showDropdown = true;
79
- });
80
- }
90
+ })).subscribe(results => {
91
+ this.filteredOptions.set(results);
92
+ this._loading.set(false);
93
+ if (results.length > 0) {
94
+ this.showDropdown.set(true); // فقط اگر نتایج داریم
95
+ }
96
+ else {
97
+ this.showDropdown.set(false); // یا همون default
98
+ }
99
+ });
100
+ }
101
+ });
81
102
  }
82
103
  /** For the initial value of FormControl */
83
104
  writeValue(value) {
84
105
  if (this.multiple) {
85
- this.selectedOptions = value ? [...value] : [];
106
+ this.selectedOptions.set(value ? [...value] : []);
86
107
  }
87
108
  else {
88
- this.searchTerm = value ? this.getLabel(value) : '';
109
+ this.searchTerm.set(value ? this.getLabel(value) : '');
89
110
  }
90
111
  }
91
112
  registerOnChange(fn) {
@@ -100,56 +121,67 @@ class YsAutocompleteComponent {
100
121
  this.inputRef.nativeElement.disabled = isDisabled;
101
122
  }
102
123
  }
103
- onSearchChange() {
104
- const term = this.searchTerm.trim();
105
- if (Array.isArray(this.options)) {
124
+ onSearchChange(term) {
125
+ this.searchTerm.set(term.trim());
126
+ if (Array.isArray(this._options)) {
106
127
  const lower = term.toLowerCase();
107
- this.filteredOptions = this.options.filter((item) => this.getLabel(item).toLowerCase().includes(lower));
108
- this.showDropdown = this.filteredOptions.length >= 0;
109
- }
110
- else if (typeof this.options === 'function') {
111
- this.search$.next(term);
128
+ this.filteredOptions.set(this._options.filter(item => this.getLabel(item).toLowerCase().includes(lower)));
129
+ this.showDropdown.set(true);
112
130
  }
113
131
  if (!this.multiple && !term) {
114
132
  this.clearSelection();
115
133
  }
116
134
  }
117
135
  getLabel(item) {
118
- if (!item)
136
+ if (item === null || item === undefined)
119
137
  return '';
120
- if (typeof item === 'string')
121
- return item;
122
- if (item && this.optionLabel && item[this.optionLabel]) {
123
- return item[this.optionLabel];
138
+ // string / number
139
+ if (typeof item === 'string' || typeof item === 'number') {
140
+ return String(item);
141
+ }
142
+ // object
143
+ if (this.optionLabel && item[this.optionLabel] != null) {
144
+ return String(item[this.optionLabel]);
124
145
  }
125
146
  return '';
126
147
  }
148
+ isEqual(a, b) {
149
+ if (typeof a !== 'object' && typeof b !== 'object') {
150
+ return a === b;
151
+ }
152
+ return this.getLabel(a) === this.getLabel(b);
153
+ }
127
154
  onSelect(item) {
128
155
  if (this.multiple) {
129
- const exists = this.selectedOptions.some((x) => this.getLabel(x) === this.getLabel(item));
130
- if (!exists) {
131
- this.selectedOptions.push(item);
132
- this._onChange(this.selectedOptions);
133
- this._onTouched();
134
- this.selected.emit(this.selectedOptions);
135
- }
136
- this.searchTerm = '';
137
- this.showDropdown = true;
138
- this.onSearchChange();
156
+ this.selectedOptions.update(list => list.some(x => this.isEqual(x, item))
157
+ ? list.filter(x => !this.isEqual(x, item)) // ✅ حذف
158
+ : [...list, item] // ✅ اضافه
159
+ );
160
+ const value = this.selectedOptions();
161
+ this._onChange(value);
162
+ this._onTouched();
163
+ this.selectionChange.emit(value);
164
+ this.searchTerm.set('');
165
+ this.showDropdown.set(true);
166
+ this.onSearchChange(this.searchTerm());
139
167
  }
140
168
  else {
141
- this.searchTerm = this.getLabel(item);
169
+ this.searchTerm.set(this.getLabel(item));
142
170
  this._onChange(item);
143
171
  this._onTouched();
144
- this.selected.emit(item);
145
- this.showDropdown = false;
172
+ this.selectionChange.emit(item);
173
+ this.showDropdown.set(false);
146
174
  }
147
- this.activeIndex = -1;
175
+ this.activeIndex.set(-1);
148
176
  }
149
177
  removeItem(item) {
150
- this.selectedOptions = this.selectedOptions.filter((x) => this.getLabel(x) !== this.getLabel(item));
151
- this._onChange(this.selectedOptions);
152
- this.removed.emit(this.selectedOptions);
178
+ const prev = this.selectedOptions();
179
+ this.selectedOptions.update(list => list.filter(x => this.getLabel(x) !== this.getLabel(item)));
180
+ if (prev !== this.selectedOptions()) {
181
+ const value = this.selectedOptions();
182
+ this._onChange(value);
183
+ this.selectionChange.emit(value);
184
+ }
153
185
  }
154
186
  onScroll(event) {
155
187
  if (!this.infiniteScroll || this.loading)
@@ -158,63 +190,61 @@ class YsAutocompleteComponent {
158
190
  const nearBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 10;
159
191
  if (nearBottom) {
160
192
  // Request more items
161
- this.loadMore.emit(this.searchTerm);
193
+ this.loadMore.emit(this.searchTerm());
162
194
  }
163
195
  }
164
196
  clearSelection() {
165
197
  if (this.multiple) {
166
- this.selectedOptions = [];
167
- this._onChange(this.selectedOptions);
168
- this.removed.emit(this.selectedOptions);
198
+ this.selectedOptions.set([]);
199
+ const value = this.selectedOptions();
200
+ this._onChange(value);
201
+ this.selectionChange.emit(value);
169
202
  }
170
203
  else {
171
- this.searchTerm = '';
204
+ this.searchTerm.set('');
172
205
  this._onChange(null);
173
- this.selected.emit(null);
206
+ this.selectionChange.emit(null);
174
207
  }
175
- this.showDropdown = false;
208
+ // this.showDropdown.set(false);
176
209
  }
177
- /** Keyboard control */
178
210
  handleKeyboard(event) {
179
- if (!this.showDropdown || this.filteredOptions.length === 0)
211
+ const list = this.filteredOptions();
212
+ if (!this.showDropdown() || list.length === 0)
180
213
  return;
181
214
  switch (event.key) {
182
215
  case 'ArrowDown':
183
- this.activeIndex = (this.activeIndex + 1) % this.filteredOptions.length;
216
+ // برو به آیتم بعدی
217
+ this.activeIndex.update(i => (i + 1) % list.length);
184
218
  event.preventDefault();
185
219
  break;
186
220
  case 'ArrowUp':
187
- this.activeIndex =
188
- (this.activeIndex - 1 + this.filteredOptions.length) %
189
- this.filteredOptions.length;
221
+ // برو به آیتم قبلی
222
+ this.activeIndex.update(i => (i - 1 + list.length) % list.length);
190
223
  event.preventDefault();
191
224
  break;
192
225
  case 'Enter':
193
- if (this.activeIndex >= 0) {
194
- this.onSelect(this.filteredOptions[this.activeIndex]);
195
- event.preventDefault();
226
+ const index = this.activeIndex();
227
+ if (index >= 0 && index < list.length) {
228
+ this.onSelect(list[index]);
196
229
  }
230
+ event.preventDefault();
197
231
  break;
198
232
  case 'Escape':
199
- this.showDropdown = false;
233
+ this.showDropdown.set(false);
200
234
  break;
201
235
  }
202
236
  }
203
- /** Close by clicking outside */
204
- onClickOutside(event) {
205
- const clickedInside = this.elRef.nativeElement.contains(event.target);
206
- if (!clickedInside) {
207
- this.showDropdown = false;
208
- }
237
+ isSelected(item) {
238
+ return this.selectedOptions().some(x => this.isEqual(x, item));
209
239
  }
210
240
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: YsAutocompleteComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
211
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: YsAutocompleteComponent, isStandalone: true, selector: "ys-autocomplete", inputs: { options: "options", optionLabel: "optionLabel", placeholder: "placeholder", multiple: "multiple", showClear: "showClear", disabled: "disabled", styleClass: "styleClass", badgeStyleClass: "badgeStyleClass", loading: "loading", loadingMessage: "loadingMessage", noResultMessage: "noResultMessage", infiniteScroll: "infiniteScroll", virtualScroll: "virtualScroll", virtualScrollItemSize: "virtualScrollItemSize" }, outputs: { loadMore: "loadMore", selected: "selected", removed: "removed" }, host: { listeners: { "keydown": "handleKeyboard($event)", "document:click": "onClickOutside($event)" } }, providers: [
241
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: YsAutocompleteComponent, isStandalone: true, selector: "ys-autocomplete", inputs: { loading: "loading", loadingMessage: "loadingMessage", options: "options", optionLabel: "optionLabel", placeholder: "placeholder", multiple: "multiple", showClear: "showClear", disabled: "disabled", styleClass: "styleClass", badgeStyleClass: "badgeStyleClass", removeIconClass: "removeIconClass", searchIconClass: "searchIconClass", spinnerIconClass: "spinnerIconClass", multipleCheckIconClass: "multipleCheckIconClass", noResultMessage: "noResultMessage", infiniteScroll: "infiniteScroll", virtualScroll: "virtualScroll", virtualScrollItemSize: "virtualScrollItemSize" }, outputs: { loadMore: "loadMore", selectionChange: "selectionChange" }, providers: [
212
242
  {
213
243
  provide: NG_VALUE_ACCESSOR,
214
244
  useExisting: forwardRef(() => YsAutocompleteComponent),
215
245
  multi: true,
216
246
  },
217
- ], queries: [{ propertyName: "itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputRef"], descendants: true }], ngImport: i0, template: "<div class=\"ys-autocomplete position-relative\">\r\n <div class=\"input-group flex-wrap\">\r\n @if (multiple && selectedOptions.length > 0) {\r\n <div class=\"d-flex flex-wrap\">\r\n @for (item of selectedOptions; track item) {\r\n <span class=\"badge me-1 mb-1 p-2 d-flex align-items-center\" [ngClass]=\"badgeStyleClass\">\r\n {{ getLabel(item) }}\r\n <i class=\"fa fa-times ms-2 cursor-pointer\" (click)=\"removeItem(item)\"></i>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"input-group w-100\">\r\n <input #inputRef type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [(ngModel)]=\"searchTerm\"\r\n (input)=\"onSearchChange()\" (focus)=\"showDropdown = true\" [ngClass]=\"styleClass\" />\r\n\r\n @if (showClear) {\r\n @if ((multiple && selectedOptions.length > 0) || (!multiple && searchTerm)) {\r\n <button class=\"btn btn-outline-secondary btn-clear\" type=\"button\" id=\"btn-clear\" (click)=\"clearSelection()\"\r\n [disabled]=\"disabled\">\r\n <span class=\"fa fa-times\"></span>\r\n </button>\r\n }\r\n }\r\n <span class=\"input-group-text\" (click)=\"showDropdown = !showDropdown\">\r\n <i class=\"fa fa-search\"></i>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (showDropdown) {\r\n <div class=\"dropdown-container position-absolute w-100 shadow-sm bg-white border rounded\">\r\n @if (loading) {\r\n <div class=\"list-group-item text-center\">\r\n <i class=\"fa fa-spinner fa-spin me-2\"></i>{{ loadingMessage }}\r\n </div>\r\n }\r\n @else if (error) {\r\n <div class=\"list-group-item text-danger text-center\">{{ error }}</div>\r\n }\r\n @else if (filteredOptions.length === 0) {\r\n <div class=\"list-group-item text-muted text-center\">\r\n {{ noResultMessage }}\r\n </div>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A Virtual Scroll \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u0628\u0632\u0631\u06AF -->\r\n @else if (virtualScroll && filteredOptions.length > 100) {\r\n <cdk-virtual-scroll-viewport [itemSize]=\"virtualScrollItemSize\" class=\"list-group w-100 overflow-y-auto\">\r\n <ul class=\"list-group w-100\">\r\n @for (item of filteredOptions; track item; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex\"\r\n (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </cdk-virtual-scroll-viewport>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A \u0645\u0639\u0645\u0648\u0644\u06CC \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u06A9\u0648\u0686\u06A9 -->\r\n @else {\r\n <ul class=\"list-group w-100 overflow-y-auto\" [class.scrollable]=\"infiniteScroll\" (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions; track item; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex\"\r\n (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n }\r\n\r\n</div>", styles: [".ys-autocomplete .dropdown-container,.ys-autocomplete .list-group{z-index:1000;max-height:200px}.ys-autocomplete cdk-virtual-scroll-viewport{height:200px;display:block}.ys-autocomplete .btn-clear:hover{background-color:var(--bs-btn-hover-bg)}.ys-autocomplete .badge{font-size:.9rem;cursor:default;font-weight:var(--bs-body-font-weight)}.ys-autocomplete .badge i{cursor:pointer;font-size:.8rem}.ys-autocomplete .badge:hover{font-weight:var(--bs-badge-font-weight)}\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: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "component", type: i2.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }] });
247
+ ], queries: [{ propertyName: "itemTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputRef"], descendants: true }], ngImport: i0, template: "<div class=\"ys-autocomplete position-relative\">\r\n <div class=\"input-group flex-wrap\">\r\n @if (multiple && selectedOptions().length > 0) {\r\n <div class=\"d-flex flex-wrap\">\r\n @for (item of selectedOptions(); track $index) {\r\n <span class=\"badge me-1 mb-1 p-2 d-flex align-items-center\" [ngClass]=\"badgeStyleClass\">\r\n {{ getLabel(item) }}\r\n <i class=\"{{removeIconClass}} ms-2 cursor-pointer\" (click)=\"removeItem(item)\"></i>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"input-group w-100\">\r\n <input #inputRef type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [ngModel]=\"searchTerm()\"\r\n (ngModelChange)=\"onSearchChange($event)\" (keydown)=\"handleKeyboard($event)\"\r\n (focus)=\"showDropdown.set(true)\" [ngClass]=\"styleClass\" />\r\n\r\n @if (showClear) {\r\n @if ((multiple && selectedOptions().length > 0) || (!multiple && searchTerm())) {\r\n <button class=\"btn btn-outline-secondary btn-clear\" type=\"button\" id=\"btn-clear\" (click)=\"clearSelection()\"\r\n [disabled]=\"disabled\">\r\n <span class=\"{{removeIconClass}}\"></span>\r\n </button>\r\n }\r\n }\r\n <span class=\"input-group-text\" (click)=\"showDropdown.set( !showDropdown())\">\r\n <i class=\"{{searchIconClass}}\"></i>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (showDropdown()) {\r\n <div class=\"dropdown-container position-absolute w-100 shadow-sm bg-white border rounded\">\r\n @if (loading) {\r\n <div class=\"list-group-item text-center\">\r\n <i class=\"{{spinnerIconClass}} me-2\"></i>{{ loadingMessage }}\r\n </div>\r\n }\r\n @else if (error()) {\r\n <div class=\"list-group-item text-danger text-center\">{{ error() }}</div>\r\n }\r\n @else if (filteredOptions().length === 0) {\r\n <div class=\"list-group-item text-muted text-center\">\r\n {{ noResultMessage }}\r\n </div>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A Virtual Scroll \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u0628\u0632\u0631\u06AF -->\r\n @else if (virtualScroll && filteredOptions().length > 100) {\r\n <cdk-virtual-scroll-viewport [itemSize]=\"virtualScrollItemSize\" class=\"list-group w-100 overflow-y-auto\">\r\n <ul class=\"list-group w-100\">\r\n @for (item of filteredOptions(); track $index; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex()\"\r\n [class.selected]=\"isSelected(item)\" (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n @if(multiple){\r\n <i class=\"{{multipleCheckIconClass}} me-1 text-success\" [class.invisible]=\"!isSelected(item)\">\r\n </i>\r\n }\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </cdk-virtual-scroll-viewport>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A \u0645\u0639\u0645\u0648\u0644\u06CC \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u06A9\u0648\u0686\u06A9 -->\r\n @else {\r\n <ul class=\"list-group w-100 overflow-y-auto\" [class.scrollable]=\"infiniteScroll\" (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track $index; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex()\"\r\n [class.selected]=\"isSelected(item)\" (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n @if(multiple){\r\n <i class=\"{{multipleCheckIconClass}} me-1 text-success\" [class.invisible]=\"!isSelected(item)\">\r\n </i>\r\n }\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n }\r\n\r\n</div>", styles: [".ys-autocomplete .dropdown-container,.ys-autocomplete .list-group{z-index:1000;max-height:200px}.ys-autocomplete cdk-virtual-scroll-viewport{height:200px;display:block}.ys-autocomplete .list-group-item-action{font-size:13px;font-weight:500;line-height:22px;text-transform:uppercase}.ys-autocomplete .list-group-item-action.selected{color:var(--bs-list-group-action-hover-color);background-color:var(--bs-list-group-action-hover-bg)}.ys-autocomplete .btn-clear:hover{background-color:var(--bs-btn-hover-bg)}.ys-autocomplete .badge{font-size:.9rem;cursor:default;font-weight:var(--bs-body-font-weight)}.ys-autocomplete .badge i{cursor:pointer;font-size:.8rem}.ys-autocomplete .badge:hover{font-weight:var(--bs-badge-font-weight)}\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: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i2.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "component", type: i2.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }] });
218
248
  }
219
249
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: YsAutocompleteComponent, decorators: [{
220
250
  type: Component,
@@ -224,8 +254,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
224
254
  useExisting: forwardRef(() => YsAutocompleteComponent),
225
255
  multi: true,
226
256
  },
227
- ], template: "<div class=\"ys-autocomplete position-relative\">\r\n <div class=\"input-group flex-wrap\">\r\n @if (multiple && selectedOptions.length > 0) {\r\n <div class=\"d-flex flex-wrap\">\r\n @for (item of selectedOptions; track item) {\r\n <span class=\"badge me-1 mb-1 p-2 d-flex align-items-center\" [ngClass]=\"badgeStyleClass\">\r\n {{ getLabel(item) }}\r\n <i class=\"fa fa-times ms-2 cursor-pointer\" (click)=\"removeItem(item)\"></i>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"input-group w-100\">\r\n <input #inputRef type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [(ngModel)]=\"searchTerm\"\r\n (input)=\"onSearchChange()\" (focus)=\"showDropdown = true\" [ngClass]=\"styleClass\" />\r\n\r\n @if (showClear) {\r\n @if ((multiple && selectedOptions.length > 0) || (!multiple && searchTerm)) {\r\n <button class=\"btn btn-outline-secondary btn-clear\" type=\"button\" id=\"btn-clear\" (click)=\"clearSelection()\"\r\n [disabled]=\"disabled\">\r\n <span class=\"fa fa-times\"></span>\r\n </button>\r\n }\r\n }\r\n <span class=\"input-group-text\" (click)=\"showDropdown = !showDropdown\">\r\n <i class=\"fa fa-search\"></i>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (showDropdown) {\r\n <div class=\"dropdown-container position-absolute w-100 shadow-sm bg-white border rounded\">\r\n @if (loading) {\r\n <div class=\"list-group-item text-center\">\r\n <i class=\"fa fa-spinner fa-spin me-2\"></i>{{ loadingMessage }}\r\n </div>\r\n }\r\n @else if (error) {\r\n <div class=\"list-group-item text-danger text-center\">{{ error }}</div>\r\n }\r\n @else if (filteredOptions.length === 0) {\r\n <div class=\"list-group-item text-muted text-center\">\r\n {{ noResultMessage }}\r\n </div>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A Virtual Scroll \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u0628\u0632\u0631\u06AF -->\r\n @else if (virtualScroll && filteredOptions.length > 100) {\r\n <cdk-virtual-scroll-viewport [itemSize]=\"virtualScrollItemSize\" class=\"list-group w-100 overflow-y-auto\">\r\n <ul class=\"list-group w-100\">\r\n @for (item of filteredOptions; track item; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex\"\r\n (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </cdk-virtual-scroll-viewport>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A \u0645\u0639\u0645\u0648\u0644\u06CC \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u06A9\u0648\u0686\u06A9 -->\r\n @else {\r\n <ul class=\"list-group w-100 overflow-y-auto\" [class.scrollable]=\"infiniteScroll\" (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions; track item; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex\"\r\n (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n }\r\n\r\n</div>", styles: [".ys-autocomplete .dropdown-container,.ys-autocomplete .list-group{z-index:1000;max-height:200px}.ys-autocomplete cdk-virtual-scroll-viewport{height:200px;display:block}.ys-autocomplete .btn-clear:hover{background-color:var(--bs-btn-hover-bg)}.ys-autocomplete .badge{font-size:.9rem;cursor:default;font-weight:var(--bs-body-font-weight)}.ys-autocomplete .badge i{cursor:pointer;font-size:.8rem}.ys-autocomplete .badge:hover{font-weight:var(--bs-badge-font-weight)}\n"] }]
228
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { options: [{
257
+ ], template: "<div class=\"ys-autocomplete position-relative\">\r\n <div class=\"input-group flex-wrap\">\r\n @if (multiple && selectedOptions().length > 0) {\r\n <div class=\"d-flex flex-wrap\">\r\n @for (item of selectedOptions(); track $index) {\r\n <span class=\"badge me-1 mb-1 p-2 d-flex align-items-center\" [ngClass]=\"badgeStyleClass\">\r\n {{ getLabel(item) }}\r\n <i class=\"{{removeIconClass}} ms-2 cursor-pointer\" (click)=\"removeItem(item)\"></i>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"input-group w-100\">\r\n <input #inputRef type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [ngModel]=\"searchTerm()\"\r\n (ngModelChange)=\"onSearchChange($event)\" (keydown)=\"handleKeyboard($event)\"\r\n (focus)=\"showDropdown.set(true)\" [ngClass]=\"styleClass\" />\r\n\r\n @if (showClear) {\r\n @if ((multiple && selectedOptions().length > 0) || (!multiple && searchTerm())) {\r\n <button class=\"btn btn-outline-secondary btn-clear\" type=\"button\" id=\"btn-clear\" (click)=\"clearSelection()\"\r\n [disabled]=\"disabled\">\r\n <span class=\"{{removeIconClass}}\"></span>\r\n </button>\r\n }\r\n }\r\n <span class=\"input-group-text\" (click)=\"showDropdown.set( !showDropdown())\">\r\n <i class=\"{{searchIconClass}}\"></i>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (showDropdown()) {\r\n <div class=\"dropdown-container position-absolute w-100 shadow-sm bg-white border rounded\">\r\n @if (loading) {\r\n <div class=\"list-group-item text-center\">\r\n <i class=\"{{spinnerIconClass}} me-2\"></i>{{ loadingMessage }}\r\n </div>\r\n }\r\n @else if (error()) {\r\n <div class=\"list-group-item text-danger text-center\">{{ error() }}</div>\r\n }\r\n @else if (filteredOptions().length === 0) {\r\n <div class=\"list-group-item text-muted text-center\">\r\n {{ noResultMessage }}\r\n </div>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A Virtual Scroll \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u0628\u0632\u0631\u06AF -->\r\n @else if (virtualScroll && filteredOptions().length > 100) {\r\n <cdk-virtual-scroll-viewport [itemSize]=\"virtualScrollItemSize\" class=\"list-group w-100 overflow-y-auto\">\r\n <ul class=\"list-group w-100\">\r\n @for (item of filteredOptions(); track $index; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex()\"\r\n [class.selected]=\"isSelected(item)\" (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n @if(multiple){\r\n <i class=\"{{multipleCheckIconClass}} me-1 text-success\" [class.invisible]=\"!isSelected(item)\">\r\n </i>\r\n }\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </cdk-virtual-scroll-viewport>\r\n }\r\n <!-- \u2705 \u062D\u0627\u0644\u062A \u0645\u0639\u0645\u0648\u0644\u06CC \u0628\u0631\u0627\u06CC \u0644\u06CC\u0633\u062A\u200C\u0647\u0627\u06CC \u06A9\u0648\u0686\u06A9 -->\r\n @else {\r\n <ul class=\"list-group w-100 overflow-y-auto\" [class.scrollable]=\"infiniteScroll\" (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track $index; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex()\"\r\n [class.selected]=\"isSelected(item)\" (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n @if(multiple){\r\n <i class=\"{{multipleCheckIconClass}} me-1 text-success\" [class.invisible]=\"!isSelected(item)\">\r\n </i>\r\n }\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n }\r\n\r\n</div>", styles: [".ys-autocomplete .dropdown-container,.ys-autocomplete .list-group{z-index:1000;max-height:200px}.ys-autocomplete cdk-virtual-scroll-viewport{height:200px;display:block}.ys-autocomplete .list-group-item-action{font-size:13px;font-weight:500;line-height:22px;text-transform:uppercase}.ys-autocomplete .list-group-item-action.selected{color:var(--bs-list-group-action-hover-color);background-color:var(--bs-list-group-action-hover-bg)}.ys-autocomplete .btn-clear:hover{background-color:var(--bs-btn-hover-bg)}.ys-autocomplete .badge{font-size:.9rem;cursor:default;font-weight:var(--bs-body-font-weight)}.ys-autocomplete .badge i{cursor:pointer;font-size:.8rem}.ys-autocomplete .badge:hover{font-weight:var(--bs-badge-font-weight)}\n"] }]
258
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { inputRef: [{
259
+ type: ViewChild,
260
+ args: ['inputRef']
261
+ }], loading: [{
262
+ type: Input
263
+ }], loadingMessage: [{
264
+ type: Input
265
+ }], options: [{
229
266
  type: Input
230
267
  }], optionLabel: [{
231
268
  type: Input
@@ -244,9 +281,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
244
281
  type: Input
245
282
  }], badgeStyleClass: [{
246
283
  type: Input
247
- }], loading: [{
284
+ }], removeIconClass: [{
248
285
  type: Input
249
- }], loadingMessage: [{
286
+ }], searchIconClass: [{
287
+ type: Input
288
+ }], spinnerIconClass: [{
289
+ type: Input
290
+ }], multipleCheckIconClass: [{
250
291
  type: Input
251
292
  }], noResultMessage: [{
252
293
  type: Input
@@ -258,19 +299,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
258
299
  type: Input
259
300
  }], loadMore: [{
260
301
  type: Output
261
- }], selected: [{
262
- type: Output
263
- }], removed: [{
302
+ }], selectionChange: [{
264
303
  type: Output
265
- }], inputRef: [{
266
- type: ViewChild,
267
- args: ['inputRef']
268
- }], handleKeyboard: [{
269
- type: HostListener,
270
- args: ['keydown', ['$event']]
271
- }], onClickOutside: [{
272
- type: HostListener,
273
- args: ['document:click', ['$event']]
274
304
  }] } });
275
305
 
276
306
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"yoozsoft-yoozsoft-ng-autocomplete.mjs","sources":["../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/src/ys-autocomplete/ys-autocomplete.component.ts","../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/src/ys-autocomplete/ys-autocomplete.component.html","../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/public-api.ts","../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/yoozsoft-yoozsoft-ng-autocomplete.ts"],"sourcesContent":["import { ScrollingModule } from '@angular/cdk/scrolling';\r\nimport { NgClass, NgTemplateOutlet } from '@angular/common';\r\nimport { Component, ContentChild, ElementRef, EventEmitter, forwardRef, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core';\r\nimport { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { catchError, debounceTime, distinctUntilChanged, Observable, of, Subject, switchMap } from 'rxjs';\r\n\r\n@Component({\r\n selector: 'ys-autocomplete',\r\n imports: [FormsModule, NgTemplateOutlet, NgClass, ScrollingModule],\r\n templateUrl: './ys-autocomplete.component.html',\r\n styleUrl: './ys-autocomplete.component.scss',\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => YsAutocompleteComponent),\r\n multi: true,\r\n },\r\n ],\r\n})\r\nexport class YsAutocompleteComponent implements ControlValueAccessor {\r\n\r\n searchTerm = '';\r\n filteredOptions: any[] = [];\r\n selectedOptions: any[] = [];\r\n showDropdown = false;\r\n activeIndex = -1;\r\n error: string | null = null;\r\n\r\n /** Array or async function */\r\n @Input() options?: any[] | ((term: string) => Observable<any[]>);\r\n\r\n /** Label display field */\r\n @Input() optionLabel = 'label';\r\n\r\n /** placeholder */\r\n @Input() placeholder: string = '';\r\n\r\n /** Multiple choice mode */\r\n @Input() multiple = false;\r\n\r\n /** Button to clear selection */\r\n @Input() showClear = false;\r\n\r\n @Input() disabled: boolean = false;\r\n\r\n /** Custom template */\r\n @ContentChild(TemplateRef) itemTemplate?: TemplateRef<any>;\r\n\r\n @Input() styleClass?: string;\r\n /** Badge style class on Multiple choice mode */\r\n @Input() badgeStyleClass: string = 'text-bg-light';\r\n @Input() loading: boolean = false;\r\n @Input() loadingMessage: string = 'Loading...';\r\n /** No results found message */\r\n @Input() noResultMessage: string = 'No results found.';\r\n\r\n /** Infinite scroll feature enabled */\r\n @Input() infiniteScroll = false;\r\n\r\n @Input() virtualScroll: boolean = true;\r\n @Input() virtualScrollItemSize: number = 36;\r\n // [virtualScrollOptions]=\"options\"\r\n\r\n /** Event to request more items */\r\n @Output() loadMore = new EventEmitter<string>();\r\n\r\n /** Output selection */\r\n @Output() selected = new EventEmitter<any>();\r\n /** Output selection after removed item */\r\n @Output() removed = new EventEmitter<any>();\r\n\r\n private search$ = new Subject<string>();\r\n private _onChange: (value: any) => void = () => { };\r\n private _onTouched: () => void = () => { };\r\n\r\n @ViewChild('inputRef') inputRef!: ElementRef<HTMLInputElement>;\r\n\r\n get isArrayItems(): boolean { return Array.isArray(this.options); }\r\n get isFunctionItems(): boolean { return typeof this.options === 'function'; }\r\n\r\n constructor(private elRef: ElementRef) { }\r\n\r\n ngOnInit() {\r\n if (Array.isArray(this.options)) {\r\n this.filteredOptions = this.options;\r\n }\r\n\r\n /** Only in async function mode */\r\n if (typeof this.options === 'function') {\r\n const fetchFn = this.options as (term: string) => Observable<any[]>;\r\n this.search$\r\n .pipe(\r\n debounceTime(300),\r\n distinctUntilChanged(),\r\n switchMap((term) => {\r\n this.loading = true;\r\n this.error = null;\r\n return fetchFn(term).pipe(\r\n catchError((err) => {\r\n this.error = 'Error receiving data!';\r\n console.error(err);\r\n return of([]);\r\n })\r\n );\r\n })\r\n )\r\n .subscribe((results) => {\r\n this.filteredOptions = results;\r\n this.loading = false;\r\n this.showDropdown = true;\r\n });\r\n }\r\n\r\n }\r\n\r\n /** For the initial value of FormControl */\r\n writeValue(value: any): void {\r\n if (this.multiple) {\r\n this.selectedOptions = value ? [...value] : [];\r\n } else {\r\n this.searchTerm = value ? this.getLabel(value) : '';\r\n }\r\n }\r\n\r\n registerOnChange(fn: any): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: any): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n setDisabledState?(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n if (this.inputRef) {\r\n this.inputRef.nativeElement.disabled = isDisabled;\r\n }\r\n }\r\n\r\n onSearchChange() {\r\n const term = this.searchTerm.trim();\r\n\r\n if (Array.isArray(this.options)) {\r\n const lower = term.toLowerCase();\r\n this.filteredOptions = this.options.filter((item) =>\r\n this.getLabel(item).toLowerCase().includes(lower)\r\n );\r\n this.showDropdown = this.filteredOptions.length >= 0;\r\n } else if (typeof this.options === 'function') {\r\n this.search$.next(term);\r\n }\r\n\r\n if (!this.multiple && !term) {\r\n this.clearSelection();\r\n }\r\n }\r\n\r\n getLabel(item: any): string {\r\n if (!item) return '';\r\n if (typeof item === 'string') return item;\r\n if (item && this.optionLabel && item[this.optionLabel]) {\r\n return item[this.optionLabel];\r\n }\r\n return '';\r\n }\r\n\r\n onSelect(item: any) {\r\n if (this.multiple) {\r\n const exists = this.selectedOptions.some(\r\n (x) => this.getLabel(x) === this.getLabel(item)\r\n );\r\n if (!exists) {\r\n this.selectedOptions.push(item);\r\n this._onChange(this.selectedOptions);\r\n this._onTouched();\r\n this.selected.emit(this.selectedOptions);\r\n }\r\n this.searchTerm = '';\r\n this.showDropdown = true;\r\n this.onSearchChange();\r\n } else {\r\n this.searchTerm = this.getLabel(item);\r\n this._onChange(item);\r\n this._onTouched();\r\n this.selected.emit(item);\r\n this.showDropdown = false;\r\n }\r\n\r\n this.activeIndex = -1;\r\n }\r\n\r\n removeItem(item: any) {\r\n this.selectedOptions = this.selectedOptions.filter(\r\n (x) => this.getLabel(x) !== this.getLabel(item)\r\n );\r\n this._onChange(this.selectedOptions);\r\n this.removed.emit(this.selectedOptions);\r\n }\r\n\r\n onScroll(event: Event) {\r\n if (!this.infiniteScroll || this.loading) return;\r\n\r\n const target = event.target as HTMLElement;\r\n const nearBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 10;\r\n\r\n if (nearBottom) {\r\n // Request more items\r\n this.loadMore.emit(this.searchTerm);\r\n }\r\n }\r\n\r\n clearSelection() {\r\n if (this.multiple) {\r\n this.selectedOptions = [];\r\n this._onChange(this.selectedOptions);\r\n this.removed.emit(this.selectedOptions);\r\n } else {\r\n this.searchTerm = '';\r\n this._onChange(null);\r\n this.selected.emit(null);\r\n }\r\n\r\n this.showDropdown = false;\r\n }\r\n\r\n /** Keyboard control */\r\n @HostListener('keydown', ['$event'])\r\n handleKeyboard(event: KeyboardEvent) {\r\n if (!this.showDropdown || this.filteredOptions.length === 0) return;\r\n\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n this.activeIndex = (this.activeIndex + 1) % this.filteredOptions.length;\r\n event.preventDefault();\r\n break;\r\n case 'ArrowUp':\r\n this.activeIndex =\r\n (this.activeIndex - 1 + this.filteredOptions.length) %\r\n this.filteredOptions.length;\r\n event.preventDefault();\r\n break;\r\n case 'Enter':\r\n if (this.activeIndex >= 0) {\r\n this.onSelect(this.filteredOptions[this.activeIndex]);\r\n event.preventDefault();\r\n }\r\n break;\r\n case 'Escape':\r\n this.showDropdown = false;\r\n break;\r\n }\r\n }\r\n\r\n /** Close by clicking outside */\r\n @HostListener('document:click', ['$event'])\r\n onClickOutside(event: MouseEvent) {\r\n const clickedInside = this.elRef.nativeElement.contains(event.target);\r\n if (!clickedInside) {\r\n this.showDropdown = false;\r\n }\r\n }\r\n\r\n}\r\n","<div class=\"ys-autocomplete position-relative\">\r\n <div class=\"input-group flex-wrap\">\r\n @if (multiple && selectedOptions.length > 0) {\r\n <div class=\"d-flex flex-wrap\">\r\n @for (item of selectedOptions; track item) {\r\n <span class=\"badge me-1 mb-1 p-2 d-flex align-items-center\" [ngClass]=\"badgeStyleClass\">\r\n {{ getLabel(item) }}\r\n <i class=\"fa fa-times ms-2 cursor-pointer\" (click)=\"removeItem(item)\"></i>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"input-group w-100\">\r\n <input #inputRef type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [(ngModel)]=\"searchTerm\"\r\n (input)=\"onSearchChange()\" (focus)=\"showDropdown = true\" [ngClass]=\"styleClass\" />\r\n\r\n @if (showClear) {\r\n @if ((multiple && selectedOptions.length > 0) || (!multiple && searchTerm)) {\r\n <button class=\"btn btn-outline-secondary btn-clear\" type=\"button\" id=\"btn-clear\" (click)=\"clearSelection()\"\r\n [disabled]=\"disabled\">\r\n <span class=\"fa fa-times\"></span>\r\n </button>\r\n }\r\n }\r\n <span class=\"input-group-text\" (click)=\"showDropdown = !showDropdown\">\r\n <i class=\"fa fa-search\"></i>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (showDropdown) {\r\n <div class=\"dropdown-container position-absolute w-100 shadow-sm bg-white border rounded\">\r\n @if (loading) {\r\n <div class=\"list-group-item text-center\">\r\n <i class=\"fa fa-spinner fa-spin me-2\"></i>{{ loadingMessage }}\r\n </div>\r\n }\r\n @else if (error) {\r\n <div class=\"list-group-item text-danger text-center\">{{ error }}</div>\r\n }\r\n @else if (filteredOptions.length === 0) {\r\n <div class=\"list-group-item text-muted text-center\">\r\n {{ noResultMessage }}\r\n </div>\r\n }\r\n <!-- ✅ حالت Virtual Scroll برای لیست‌های بزرگ -->\r\n @else if (virtualScroll && filteredOptions.length > 100) {\r\n <cdk-virtual-scroll-viewport [itemSize]=\"virtualScrollItemSize\" class=\"list-group w-100 overflow-y-auto\">\r\n <ul class=\"list-group w-100\">\r\n @for (item of filteredOptions; track item; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex\"\r\n (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </cdk-virtual-scroll-viewport>\r\n }\r\n <!-- ✅ حالت معمولی برای لیست‌های کوچک -->\r\n @else {\r\n <ul class=\"list-group w-100 overflow-y-auto\" [class.scrollable]=\"infiniteScroll\" (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions; track item; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex\"\r\n (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n }\r\n\r\n</div>","/*\r\n * Public API Surface of ys-autocomplete\r\n */\r\n\r\nexport * from './src/ys-autocomplete/ys-autocomplete.component';\r\n\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;MAmBa,uBAAuB,CAAA;AA6Dd,IAAA,KAAA;IA3DpB,UAAU,GAAG,EAAE;IACf,eAAe,GAAU,EAAE;IAC3B,eAAe,GAAU,EAAE;IAC3B,YAAY,GAAG,KAAK;IACpB,WAAW,GAAG,CAAC,CAAC;IAChB,KAAK,GAAkB,IAAI;;AAGlB,IAAA,OAAO;;IAGP,WAAW,GAAG,OAAO;;IAGrB,WAAW,GAAW,EAAE;;IAGxB,QAAQ,GAAG,KAAK;;IAGhB,SAAS,GAAG,KAAK;IAEjB,QAAQ,GAAY,KAAK;;AAGP,IAAA,YAAY;AAE9B,IAAA,UAAU;;IAEV,eAAe,GAAW,eAAe;IACzC,OAAO,GAAY,KAAK;IACxB,cAAc,GAAW,YAAY;;IAErC,eAAe,GAAW,mBAAmB;;IAG7C,cAAc,GAAG,KAAK;IAEtB,aAAa,GAAY,IAAI;IAC7B,qBAAqB,GAAW,EAAE;;;AAIjC,IAAA,QAAQ,GAAG,IAAI,YAAY,EAAU;;AAGrC,IAAA,QAAQ,GAAG,IAAI,YAAY,EAAO;;AAElC,IAAA,OAAO,GAAG,IAAI,YAAY,EAAO;AAEnC,IAAA,OAAO,GAAG,IAAI,OAAO,EAAU;AAC/B,IAAA,SAAS,GAAyB,MAAK,EAAG,CAAC;AAC3C,IAAA,UAAU,GAAe,MAAK,EAAG,CAAC;AAEnB,IAAA,QAAQ;AAE/B,IAAA,IAAI,YAAY,GAAA,EAAc,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,IAAI,eAAe,GAAA,EAAc,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC;AAE5E,IAAA,WAAA,CAAoB,KAAiB,EAAA;QAAjB,IAAA,CAAA,KAAK,GAAL,KAAK;IAAgB;IAEzC,QAAQ,GAAA;QACN,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;AAC/B,YAAA,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,OAAO;QACrC;;AAGA,QAAA,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE;AACtC,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,OAA8C;AACnE,YAAA,IAAI,CAAC;AACF,iBAAA,IAAI,CACH,YAAY,CAAC,GAAG,CAAC,EACjB,oBAAoB,EAAE,EACtB,SAAS,CAAC,CAAC,IAAI,KAAI;AACjB,gBAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,gBAAA,IAAI,CAAC,KAAK,GAAG,IAAI;AACjB,gBAAA,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CACvB,UAAU,CAAC,CAAC,GAAG,KAAI;AACjB,oBAAA,IAAI,CAAC,KAAK,GAAG,uBAAuB;AACpC,oBAAA,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;AAClB,oBAAA,OAAO,EAAE,CAAC,EAAE,CAAC;gBACf,CAAC,CAAC,CACH;AACH,YAAA,CAAC,CAAC;AAEH,iBAAA,SAAS,CAAC,CAAC,OAAO,KAAI;AACrB,gBAAA,IAAI,CAAC,eAAe,GAAG,OAAO;AAC9B,gBAAA,IAAI,CAAC,OAAO,GAAG,KAAK;AACpB,gBAAA,IAAI,CAAC,YAAY,GAAG,IAAI;AAC1B,YAAA,CAAC,CAAC;QACN;IAEF;;AAGA,IAAA,UAAU,CAAC,KAAU,EAAA;AACnB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AACjB,YAAA,IAAI,CAAC,eAAe,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE;QAChD;aAAO;AACL,YAAA,IAAI,CAAC,UAAU,GAAG,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE;QACrD;IACF;AAEA,IAAA,gBAAgB,CAAC,EAAO,EAAA;AACtB,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE;IACrB;AAEA,IAAA,iBAAiB,CAAC,EAAO,EAAA;AACvB,QAAA,IAAI,CAAC,UAAU,GAAG,EAAE;IACtB;AAEA,IAAA,gBAAgB,CAAE,UAAmB,EAAA;AACnC,QAAA,IAAI,CAAC,QAAQ,GAAG,UAAU;AAC1B,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;YACjB,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,GAAG,UAAU;QACnD;IACF;IAEA,cAAc,GAAA;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE;QAEnC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;AAC/B,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE;AAChC,YAAA,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,KAC9C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAClD;YACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,IAAI,CAAC;QACtD;AAAO,aAAA,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE;AAC7C,YAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QACzB;QAEA,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE;YAC3B,IAAI,CAAC,cAAc,EAAE;QACvB;IACF;AAEA,IAAA,QAAQ,CAAC,IAAS,EAAA;AAChB,QAAA,IAAI,CAAC,IAAI;AAAE,YAAA,OAAO,EAAE;QACpB,IAAI,OAAO,IAAI,KAAK,QAAQ;AAAE,YAAA,OAAO,IAAI;AACzC,QAAA,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;AACtD,YAAA,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;QAC/B;AACA,QAAA,OAAO,EAAE;IACX;AAEA,IAAA,QAAQ,CAAC,IAAS,EAAA;AAChB,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;YACjB,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CACtC,CAAC,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAChD;YACD,IAAI,CAAC,MAAM,EAAE;AACX,gBAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;AAC/B,gBAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC;gBACpC,IAAI,CAAC,UAAU,EAAE;gBACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;YAC1C;AACA,YAAA,IAAI,CAAC,UAAU,GAAG,EAAE;AACpB,YAAA,IAAI,CAAC,YAAY,GAAG,IAAI;YACxB,IAAI,CAAC,cAAc,EAAE;QACvB;aAAO;YACL,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;AACrC,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YACpB,IAAI,CAAC,UAAU,EAAE;AACjB,YAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;AACxB,YAAA,IAAI,CAAC,YAAY,GAAG,KAAK;QAC3B;AAEA,QAAA,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;IACvB;AAEA,IAAA,UAAU,CAAC,IAAS,EAAA;AAClB,QAAA,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAChD,CAAC,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAChD;AACD,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;IACzC;AAEA,IAAA,QAAQ,CAAC,KAAY,EAAA;AACnB,QAAA,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO;YAAE;AAE1C,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;AAC1C,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,YAAY,GAAG,EAAE;QAErF,IAAI,UAAU,EAAE;;YAEd,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;QACrC;IACF;IAEA,cAAc,GAAA;AACZ,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AACjB,YAAA,IAAI,CAAC,eAAe,GAAG,EAAE;AACzB,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC;YACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;QACzC;aAAO;AACL,YAAA,IAAI,CAAC,UAAU,GAAG,EAAE;AACpB,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;AACpB,YAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B;AAEA,QAAA,IAAI,CAAC,YAAY,GAAG,KAAK;IAC3B;;AAIA,IAAA,cAAc,CAAC,KAAoB,EAAA;QACjC,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC;YAAE;AAE7D,QAAA,QAAQ,KAAK,CAAC,GAAG;AACf,YAAA,KAAK,WAAW;AACd,gBAAA,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM;gBACvE,KAAK,CAAC,cAAc,EAAE;gBACtB;AACF,YAAA,KAAK,SAAS;AACZ,gBAAA,IAAI,CAAC,WAAW;oBACd,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM;AACnD,wBAAA,IAAI,CAAC,eAAe,CAAC,MAAM;gBAC7B,KAAK,CAAC,cAAc,EAAE;gBACtB;AACF,YAAA,KAAK,OAAO;AACV,gBAAA,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE;AACzB,oBAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;oBACrD,KAAK,CAAC,cAAc,EAAE;gBACxB;gBACA;AACF,YAAA,KAAK,QAAQ;AACX,gBAAA,IAAI,CAAC,YAAY,GAAG,KAAK;gBACzB;;IAEN;;AAIA,IAAA,cAAc,CAAC,KAAiB,EAAA;AAC9B,QAAA,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;QACrE,IAAI,CAAC,aAAa,EAAE;AAClB,YAAA,IAAI,CAAC,YAAY,GAAG,KAAK;QAC3B;IACF;uGAjPW,uBAAuB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,UAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,WAAA,EAAA,aAAA,EAAA,WAAA,EAAA,aAAA,EAAA,QAAA,EAAA,UAAA,EAAA,SAAA,EAAA,WAAA,EAAA,QAAA,EAAA,UAAA,EAAA,UAAA,EAAA,YAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,SAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,gBAAA,EAAA,aAAA,EAAA,eAAA,EAAA,qBAAA,EAAA,uBAAA,EAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,UAAA,EAAA,OAAA,EAAA,SAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,SAAA,EAAA,wBAAA,EAAA,gBAAA,EAAA,wBAAA,EAAA,EAAA,EAAA,SAAA,EARvB;AACT,YAAA;AACE,gBAAA,OAAO,EAAE,iBAAiB;AAC1B,gBAAA,WAAW,EAAE,UAAU,CAAC,MAAM,uBAAuB,CAAC;AACtD,gBAAA,KAAK,EAAE,IAAI;AACZ,aAAA;SACF,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,cAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EA6Ba,WAAW,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,UAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,UAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC9C3B,o9HAiFM,EAAA,MAAA,EAAA,CAAA,mdAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDzEM,WAAW,+mBAAE,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,OAAO,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,eAAe,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,yBAAA,EAAA,QAAA,EAAA,uCAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,aAAA,EAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,wBAAA,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,CAAA,aAAA,EAAA,YAAA,CAAA,EAAA,OAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;2FAWtD,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAbnC,SAAS;+BACE,iBAAiB,EAAA,OAAA,EAClB,CAAC,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,CAAC,EAAA,SAAA,EAGvD;AACT,wBAAA;AACE,4BAAA,OAAO,EAAE,iBAAiB;AAC1B,4BAAA,WAAW,EAAE,UAAU,CAAC,6BAA6B,CAAC;AACtD,4BAAA,KAAK,EAAE,IAAI;AACZ,yBAAA;AACF,qBAAA,EAAA,QAAA,EAAA,o9HAAA,EAAA,MAAA,EAAA,CAAA,mdAAA,CAAA,EAAA;;sBAYA;;sBAGA;;sBAGA;;sBAGA;;sBAGA;;sBAEA;;sBAGA,YAAY;uBAAC,WAAW;;sBAExB;;sBAEA;;sBACA;;sBACA;;sBAEA;;sBAGA;;sBAEA;;sBACA;;sBAIA;;sBAGA;;sBAEA;;sBAMA,SAAS;uBAAC,UAAU;;sBAuJpB,YAAY;uBAAC,SAAS,EAAE,CAAC,QAAQ,CAAC;;sBA4BlC,YAAY;uBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;;AE9P5C;;AAEG;;ACFH;;AAEG;;;;"}
1
+ {"version":3,"file":"yoozsoft-yoozsoft-ng-autocomplete.mjs","sources":["../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/src/ys-autocomplete/ys-autocomplete.component.ts","../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/src/ys-autocomplete/ys-autocomplete.component.html","../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/public-api.ts","../../../../projects/yoozsoft/yoozsoft-ng/autocomplete/yoozsoft-yoozsoft-ng-autocomplete.ts"],"sourcesContent":["import { ScrollingModule } from '@angular/cdk/scrolling';\r\nimport { isPlatformBrowser, NgClass, NgTemplateOutlet } from '@angular/common';\r\nimport { Component, ContentChild, DestroyRef, effect, ElementRef, EventEmitter, forwardRef, inject, Input, Output, PLATFORM_ID, signal, TemplateRef, ViewChild } from '@angular/core';\r\nimport { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';\r\nimport { catchError, Observable, of } from 'rxjs';\r\n\r\n@Component({\r\n selector: 'ys-autocomplete',\r\n imports: [FormsModule, NgTemplateOutlet, NgClass, ScrollingModule],\r\n templateUrl: './ys-autocomplete.component.html',\r\n styleUrl: './ys-autocomplete.component.scss',\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => YsAutocompleteComponent),\r\n multi: true,\r\n },\r\n ],\r\n})\r\nexport class YsAutocompleteComponent implements ControlValueAccessor {\r\n\r\n searchTerm = signal('');\r\n filteredOptions = signal<any[]>([]);\r\n selectedOptions = signal<any[]>([]);\r\n showDropdown = signal(false);\r\n activeIndex = signal(-1);\r\n error = signal<string | null>(null);\r\n\r\n @ViewChild('inputRef') inputRef!: ElementRef<HTMLInputElement>;\r\n\r\n private _loading = signal(false);\r\n @Input()\r\n set loading(value: boolean) { this._loading.set(!!value); }\r\n get loading() { return this._loading(); }\r\n\r\n @Input() loadingMessage: string = 'Loading...';\r\n\r\n /** Array or async function */\r\n private _options: any[] | ((term: string) => Observable<any[]>) = [];\r\n\r\n @Input()\r\n set options(value: any[] | ((term: string) => Observable<any[]>)) {\r\n this._options = value;\r\n\r\n if (Array.isArray(value)) {\r\n this.filteredOptions.set(value);\r\n }\r\n\r\n }\r\n\r\n get options() {\r\n return this._options;\r\n }\r\n\r\n /** Label display field */\r\n @Input() optionLabel = 'label';\r\n\r\n /** placeholder */\r\n @Input() placeholder: string = '';\r\n\r\n /** Multiple choice mode */\r\n @Input() multiple = false;\r\n\r\n /** Button to clear selection */\r\n @Input() showClear = false;\r\n\r\n @Input() disabled: boolean = false;\r\n\r\n /** Custom template */\r\n @ContentChild(TemplateRef) itemTemplate?: TemplateRef<any>;\r\n\r\n @Input() styleClass?: string;\r\n /** Badge style class on Multiple choice mode */\r\n @Input() badgeStyleClass: string = 'text-bg-light';\r\n @Input() removeIconClass: string = 'fa fa-times';\r\n @Input() searchIconClass: string = 'fa fa-search';\r\n @Input() spinnerIconClass: string = 'fa fa-spinner fa-spin';\r\n @Input() multipleCheckIconClass: string = 'fa fa-check';\r\n\r\n /** No results found message */\r\n @Input() noResultMessage: string = 'No results found.';\r\n\r\n /** Infinite scroll feature enabled */\r\n @Input() infiniteScroll = false;\r\n\r\n @Input() virtualScroll: boolean = true;\r\n @Input() virtualScrollItemSize: number = 36;\r\n // [virtualScrollOptions]=\"options\"\r\n\r\n /** Event to request more items */\r\n @Output() loadMore = new EventEmitter<string>();\r\n\r\n /** Return selected options */\r\n @Output() selectionChange = new EventEmitter<any>();\r\n\r\n private _onChange: (value: any) => void = () => { };\r\n private _onTouched: () => void = () => { };\r\n\r\n get isArrayItems(): boolean { return Array.isArray(this.options); }\r\n get isFunctionItems(): boolean { return typeof this.options === 'function'; }\r\n\r\n constructor(private elRef: ElementRef) {\r\n\r\n const platformId = inject(PLATFORM_ID);\r\n\r\n if (isPlatformBrowser(platformId)) {\r\n const handler = (event: MouseEvent) => {\r\n if (!this.elRef.nativeElement.contains(event.target)) {\r\n this.showDropdown.set(false);\r\n }\r\n };\r\n\r\n document.addEventListener('click', handler);\r\n\r\n inject(DestroyRef).onDestroy(() => {\r\n document.removeEventListener('click', handler);\r\n });\r\n }\r\n\r\n effect(() => {\r\n const term = this.searchTerm();\r\n\r\n if (typeof this._options === 'function') {\r\n this._loading.set(true);\r\n this.error.set(null);\r\n\r\n this._options(term).pipe(\r\n catchError(err => {\r\n this.error.set('Error receiving data!');\r\n console.error(err);\r\n return of([]);\r\n })\r\n ).subscribe(results => {\r\n this.filteredOptions.set(results);\r\n this._loading.set(false);\r\n\r\n if (results.length > 0) {\r\n this.showDropdown.set(true); // فقط اگر نتایج داریم\r\n } else {\r\n this.showDropdown.set(false); // یا همون default\r\n }\r\n\r\n });\r\n }\r\n });\r\n\r\n }\r\n\r\n /** For the initial value of FormControl */\r\n writeValue(value: any): void {\r\n if (this.multiple) {\r\n this.selectedOptions.set(value ? [...value] : []);\r\n } else {\r\n this.searchTerm.set(value ? this.getLabel(value) : '');\r\n }\r\n }\r\n\r\n registerOnChange(fn: any): void {\r\n this._onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: any): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n setDisabledState?(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n if (this.inputRef) {\r\n this.inputRef.nativeElement.disabled = isDisabled;\r\n }\r\n }\r\n\r\n onSearchChange(term: string) {\r\n this.searchTerm.set(term.trim());\r\n\r\n if (Array.isArray(this._options)) {\r\n const lower = term.toLowerCase();\r\n this.filteredOptions.set(\r\n this._options.filter(item =>\r\n this.getLabel(item).toLowerCase().includes(lower)\r\n )\r\n );\r\n this.showDropdown.set(true);\r\n }\r\n\r\n if (!this.multiple && !term) {\r\n this.clearSelection();\r\n }\r\n }\r\n\r\n getLabel(item: any): string {\r\n if (item === null || item === undefined) return '';\r\n\r\n // string / number\r\n if (typeof item === 'string' || typeof item === 'number') {\r\n return String(item);\r\n }\r\n\r\n // object\r\n if (this.optionLabel && item[this.optionLabel] != null) {\r\n return String(item[this.optionLabel]);\r\n }\r\n\r\n return '';\r\n }\r\n\r\n private isEqual(a: any, b: any): boolean {\r\n if (typeof a !== 'object' && typeof b !== 'object') {\r\n return a === b;\r\n }\r\n return this.getLabel(a) === this.getLabel(b);\r\n }\r\n\r\n onSelect(item: any) {\r\n if (this.multiple) {\r\n this.selectedOptions.update(list =>\r\n list.some(x => this.isEqual(x, item))\r\n ? list.filter(x => !this.isEqual(x, item)) // ✅ حذف\r\n : [...list, item] // ✅ اضافه\r\n );\r\n\r\n const value = this.selectedOptions();\r\n this._onChange(value);\r\n this._onTouched();\r\n this.selectionChange.emit(value);\r\n\r\n this.searchTerm.set('');\r\n this.showDropdown.set(true);\r\n this.onSearchChange(this.searchTerm());\r\n\r\n } else {\r\n this.searchTerm.set(this.getLabel(item));\r\n this._onChange(item);\r\n this._onTouched();\r\n this.selectionChange.emit(item);\r\n this.showDropdown.set(false);\r\n }\r\n\r\n this.activeIndex.set(-1);\r\n }\r\n\r\n\r\n removeItem(item: any) {\r\n const prev = this.selectedOptions();\r\n\r\n this.selectedOptions.update(list =>\r\n list.filter(x => this.getLabel(x) !== this.getLabel(item))\r\n );\r\n\r\n if (prev !== this.selectedOptions()) {\r\n const value = this.selectedOptions();\r\n this._onChange(value);\r\n this.selectionChange.emit(value);\r\n }\r\n }\r\n\r\n onScroll(event: Event) {\r\n if (!this.infiniteScroll || this.loading) return;\r\n\r\n const target = event.target as HTMLElement;\r\n const nearBottom = target.scrollHeight - target.scrollTop <= target.clientHeight + 10;\r\n\r\n if (nearBottom) {\r\n // Request more items\r\n this.loadMore.emit(this.searchTerm());\r\n }\r\n }\r\n\r\n clearSelection() {\r\n if (this.multiple) {\r\n this.selectedOptions.set([]);\r\n const value = this.selectedOptions();\r\n this._onChange(value);\r\n this.selectionChange.emit(value);\r\n } else {\r\n this.searchTerm.set('');\r\n this._onChange(null);\r\n this.selectionChange.emit(null);\r\n }\r\n\r\n // this.showDropdown.set(false);\r\n }\r\n\r\n handleKeyboard(event: KeyboardEvent) {\r\n const list = this.filteredOptions();\r\n if (!this.showDropdown() || list.length === 0) return;\r\n\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n // برو به آیتم بعدی\r\n this.activeIndex.update(i => (i + 1) % list.length);\r\n event.preventDefault();\r\n break;\r\n case 'ArrowUp':\r\n // برو به آیتم قبلی\r\n this.activeIndex.update(i => (i - 1 + list.length) % list.length);\r\n event.preventDefault();\r\n break;\r\n case 'Enter':\r\n const index = this.activeIndex();\r\n if (index >= 0 && index < list.length) {\r\n this.onSelect(list[index]);\r\n }\r\n event.preventDefault();\r\n break;\r\n case 'Escape':\r\n this.showDropdown.set(false);\r\n break;\r\n }\r\n }\r\n\r\n isSelected(item: any): boolean {\r\n return this.selectedOptions().some(x => this.isEqual(x, item));\r\n }\r\n\r\n}\r\n","<div class=\"ys-autocomplete position-relative\">\r\n <div class=\"input-group flex-wrap\">\r\n @if (multiple && selectedOptions().length > 0) {\r\n <div class=\"d-flex flex-wrap\">\r\n @for (item of selectedOptions(); track $index) {\r\n <span class=\"badge me-1 mb-1 p-2 d-flex align-items-center\" [ngClass]=\"badgeStyleClass\">\r\n {{ getLabel(item) }}\r\n <i class=\"{{removeIconClass}} ms-2 cursor-pointer\" (click)=\"removeItem(item)\"></i>\r\n </span>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"input-group w-100\">\r\n <input #inputRef type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [ngModel]=\"searchTerm()\"\r\n (ngModelChange)=\"onSearchChange($event)\" (keydown)=\"handleKeyboard($event)\"\r\n (focus)=\"showDropdown.set(true)\" [ngClass]=\"styleClass\" />\r\n\r\n @if (showClear) {\r\n @if ((multiple && selectedOptions().length > 0) || (!multiple && searchTerm())) {\r\n <button class=\"btn btn-outline-secondary btn-clear\" type=\"button\" id=\"btn-clear\" (click)=\"clearSelection()\"\r\n [disabled]=\"disabled\">\r\n <span class=\"{{removeIconClass}}\"></span>\r\n </button>\r\n }\r\n }\r\n <span class=\"input-group-text\" (click)=\"showDropdown.set( !showDropdown())\">\r\n <i class=\"{{searchIconClass}}\"></i>\r\n </span>\r\n </div>\r\n </div>\r\n\r\n @if (showDropdown()) {\r\n <div class=\"dropdown-container position-absolute w-100 shadow-sm bg-white border rounded\">\r\n @if (loading) {\r\n <div class=\"list-group-item text-center\">\r\n <i class=\"{{spinnerIconClass}} me-2\"></i>{{ loadingMessage }}\r\n </div>\r\n }\r\n @else if (error()) {\r\n <div class=\"list-group-item text-danger text-center\">{{ error() }}</div>\r\n }\r\n @else if (filteredOptions().length === 0) {\r\n <div class=\"list-group-item text-muted text-center\">\r\n {{ noResultMessage }}\r\n </div>\r\n }\r\n <!-- ✅ حالت Virtual Scroll برای لیست‌های بزرگ -->\r\n @else if (virtualScroll && filteredOptions().length > 100) {\r\n <cdk-virtual-scroll-viewport [itemSize]=\"virtualScrollItemSize\" class=\"list-group w-100 overflow-y-auto\">\r\n <ul class=\"list-group w-100\">\r\n @for (item of filteredOptions(); track $index; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex()\"\r\n [class.selected]=\"isSelected(item)\" (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n @if(multiple){\r\n <i class=\"{{multipleCheckIconClass}} me-1 text-success\" [class.invisible]=\"!isSelected(item)\">\r\n </i>\r\n }\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </cdk-virtual-scroll-viewport>\r\n }\r\n <!-- ✅ حالت معمولی برای لیست‌های کوچک -->\r\n @else {\r\n <ul class=\"list-group w-100 overflow-y-auto\" [class.scrollable]=\"infiniteScroll\" (scroll)=\"onScroll($event)\">\r\n @for (item of filteredOptions(); track $index; let i = $index) {\r\n <li class=\"list-group-item list-group-item-action\" [class.active]=\"i === activeIndex()\"\r\n [class.selected]=\"isSelected(item)\" (click)=\"onSelect(item)\">\r\n @if (itemTemplate) {\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: item }\"></ng-container>\r\n } @else {\r\n @if(multiple){\r\n <i class=\"{{multipleCheckIconClass}} me-1 text-success\" [class.invisible]=\"!isSelected(item)\">\r\n </i>\r\n }\r\n {{ getLabel(item) }}\r\n }\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n }\r\n\r\n</div>","/*\r\n * Public API Surface of ys-autocomplete\r\n */\r\n\r\nexport * from './src/ys-autocomplete/ys-autocomplete.component';\r\n\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}