verben-ng-ui 0.0.1

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 (190) hide show
  1. package/README.md +24 -0
  2. package/ng-package.json +10 -0
  3. package/package.json +34 -0
  4. package/src/lib/Verbena-input/error.service.ts +23 -0
  5. package/src/lib/Verbena-input/verbena-input.component.css +59 -0
  6. package/src/lib/Verbena-input/verbena-input.component.html +64 -0
  7. package/src/lib/Verbena-input/verbena-input.component.spec.ts +25 -0
  8. package/src/lib/Verbena-input/verbena-input.component.ts +162 -0
  9. package/src/lib/Verbena-input/verbena-input.module.ts +12 -0
  10. package/src/lib/components/TemplateDirective.directive.ts +10 -0
  11. package/src/lib/components/card/card.component.css +20 -0
  12. package/src/lib/components/card/card.component.html +23 -0
  13. package/src/lib/components/card/card.component.spec.ts +23 -0
  14. package/src/lib/components/card/card.component.ts +33 -0
  15. package/src/lib/components/card/card.module.ts +10 -0
  16. package/src/lib/components/card-data-view/card-data-view-footer/card-data-view-footer.component.css +3 -0
  17. package/src/lib/components/card-data-view/card-data-view-footer/card-data-view-footer.component.html +1 -0
  18. package/src/lib/components/card-data-view/card-data-view-footer/card-data-view-footer.component.spec.ts +23 -0
  19. package/src/lib/components/card-data-view/card-data-view-footer/card-data-view-footer.component.ts +10 -0
  20. package/src/lib/components/card-data-view/card-data-view-header/card-data-view-header.component.css +7 -0
  21. package/src/lib/components/card-data-view/card-data-view-header/card-data-view-header.component.html +1 -0
  22. package/src/lib/components/card-data-view/card-data-view-header/card-data-view-header.component.spec.ts +23 -0
  23. package/src/lib/components/card-data-view/card-data-view-header/card-data-view-header.component.ts +10 -0
  24. package/src/lib/components/card-data-view/card-data-view.component.css +39 -0
  25. package/src/lib/components/card-data-view/card-data-view.component.html +29 -0
  26. package/src/lib/components/card-data-view/card-data-view.component.spec.ts +23 -0
  27. package/src/lib/components/card-data-view/card-data-view.component.ts +66 -0
  28. package/src/lib/components/card-data-view/card-data-view.module.ts +20 -0
  29. package/src/lib/components/card-data-view/card-data.ts +7 -0
  30. package/src/lib/components/card-data-view/left-card-data/left-card-data.component.css +15 -0
  31. package/src/lib/components/card-data-view/left-card-data/left-card-data.component.html +4 -0
  32. package/src/lib/components/card-data-view/left-card-data/left-card-data.component.spec.ts +23 -0
  33. package/src/lib/components/card-data-view/left-card-data/left-card-data.component.ts +19 -0
  34. package/src/lib/components/card-data-view/left-card-data-view/left-card-data-view.component.css +0 -0
  35. package/src/lib/components/card-data-view/left-card-data-view/left-card-data-view.component.html +4 -0
  36. package/src/lib/components/card-data-view/left-card-data-view/left-card-data-view.component.spec.ts +23 -0
  37. package/src/lib/components/card-data-view/left-card-data-view/left-card-data-view.component.ts +12 -0
  38. package/src/lib/components/card-data-view/right-card-data-view/right-card-data-view.component.css +3 -0
  39. package/src/lib/components/card-data-view/right-card-data-view/right-card-data-view.component.html +1 -0
  40. package/src/lib/components/card-data-view/right-card-data-view/right-card-data-view.component.spec.ts +23 -0
  41. package/src/lib/components/card-data-view/right-card-data-view/right-card-data-view.component.ts +10 -0
  42. package/src/lib/components/chip/ChipChangeEvent.ts +4 -0
  43. package/src/lib/components/chip/chip.component.css +94 -0
  44. package/src/lib/components/chip/chip.component.html +49 -0
  45. package/src/lib/components/chip/chip.component.spec.ts +23 -0
  46. package/src/lib/components/chip/chip.component.ts +209 -0
  47. package/src/lib/components/chip/chip.module.ts +24 -0
  48. package/src/lib/components/chip/documentation.md +26 -0
  49. package/src/lib/components/data-export/data-export.component.css +0 -0
  50. package/src/lib/components/data-export/data-export.component.html +90 -0
  51. package/src/lib/components/data-export/data-export.component.spec.ts +23 -0
  52. package/src/lib/components/data-export/data-export.component.ts +158 -0
  53. package/src/lib/components/data-export/data-export.module.ts +13 -0
  54. package/src/lib/components/data-export/data-export.service.spec.ts +16 -0
  55. package/src/lib/components/data-export/data-export.service.ts +152 -0
  56. package/src/lib/components/data-export/data-export.types.ts +21 -0
  57. package/src/lib/components/data-table/column.directive.spec.ts +8 -0
  58. package/src/lib/components/data-table/column.directive.ts +19 -0
  59. package/src/lib/components/data-table/data-table.component.css +0 -0
  60. package/src/lib/components/data-table/data-table.component.html +55 -0
  61. package/src/lib/components/data-table/data-table.component.spec.ts +21 -0
  62. package/src/lib/components/data-table/data-table.component.ts +336 -0
  63. package/src/lib/components/data-table/data-table.module.ts +11 -0
  64. package/src/lib/components/data-table/data-table.types.ts +13 -0
  65. package/src/lib/components/data-table/style.types.ts +55 -0
  66. package/src/lib/components/data-view/data-view-click-outside.directive.ts +44 -0
  67. package/src/lib/components/data-view/data-view.component.css +74 -0
  68. package/src/lib/components/data-view/data-view.component.html +161 -0
  69. package/src/lib/components/data-view/data-view.component.spec.ts +23 -0
  70. package/src/lib/components/data-view/data-view.component.ts +136 -0
  71. package/src/lib/components/data-view/data-view.module.ts +16 -0
  72. package/src/lib/components/date-picker/date-picker.component.css +65 -0
  73. package/src/lib/components/date-picker/date-picker.component.html +60 -0
  74. package/src/lib/components/date-picker/date-picker.component.specs.ts +23 -0
  75. package/src/lib/components/date-picker/date-picker.component.ts +143 -0
  76. package/src/lib/components/date-picker/date-picker.module.ts +12 -0
  77. package/src/lib/components/drop-down/DropdownChangeEvent.ts +4 -0
  78. package/src/lib/components/drop-down/DropdownLoadEvent.ts +19 -0
  79. package/src/lib/components/drop-down/DropdownMenuItem.ts +42 -0
  80. package/src/lib/components/drop-down/documentation.md +69 -0
  81. package/src/lib/components/drop-down/drop-down-item/drop-down-item.component.css +123 -0
  82. package/src/lib/components/drop-down/drop-down-item/drop-down-item.component.html +133 -0
  83. package/src/lib/components/drop-down/drop-down-item/drop-down-item.component.spec.ts +23 -0
  84. package/src/lib/components/drop-down/drop-down-item/drop-down-item.component.ts +88 -0
  85. package/src/lib/components/drop-down/drop-down.component.css +317 -0
  86. package/src/lib/components/drop-down/drop-down.component.html +177 -0
  87. package/src/lib/components/drop-down/drop-down.component.spec.ts +23 -0
  88. package/src/lib/components/drop-down/drop-down.component.ts +757 -0
  89. package/src/lib/components/drop-down/drop-down.module.ts +24 -0
  90. package/src/lib/components/image/image.component.css +12 -0
  91. package/src/lib/components/image/image.component.html +18 -0
  92. package/src/lib/components/image/image.component.spec.ts +23 -0
  93. package/src/lib/components/image/image.component.ts +45 -0
  94. package/src/lib/components/image/image.module.ts +11 -0
  95. package/src/lib/components/notification/notification.component.css +3 -0
  96. package/src/lib/components/notification/notification.component.html +19 -0
  97. package/src/lib/components/notification/notification.component.spec.ts +23 -0
  98. package/src/lib/components/notification/notification.component.ts +77 -0
  99. package/src/lib/components/notification/notification.module.ts +14 -0
  100. package/src/lib/components/shared.module.ts +9 -0
  101. package/src/lib/components/sort-table/sort-table.component.css +67 -0
  102. package/src/lib/components/sort-table/sort-table.component.html +164 -0
  103. package/src/lib/components/sort-table/sort-table.component.spec.ts +23 -0
  104. package/src/lib/components/sort-table/sort-table.component.ts +204 -0
  105. package/src/lib/components/sort-table/sort-table.module.ts +11 -0
  106. package/src/lib/components/svg/svg.component.css +0 -0
  107. package/src/lib/components/svg/svg.component.html +1 -0
  108. package/src/lib/components/svg/svg.component.spec.ts +23 -0
  109. package/src/lib/components/svg/svg.component.ts +64 -0
  110. package/src/lib/components/svg/svg.module.ts +10 -0
  111. package/src/lib/components/table-filter/table-filter.component.css +93 -0
  112. package/src/lib/components/table-filter/table-filter.component.html +293 -0
  113. package/src/lib/components/table-filter/table-filter.component.spec.ts +23 -0
  114. package/src/lib/components/table-filter/table-filter.component.ts +221 -0
  115. package/src/lib/components/table-filter/table-filter.module.ts +25 -0
  116. package/src/lib/components/tooltip/tooltip.component.css +8 -0
  117. package/src/lib/components/tooltip/tooltip.component.html +13 -0
  118. package/src/lib/components/tooltip/tooltip.component.spec.ts +23 -0
  119. package/src/lib/components/tooltip/tooltip.component.ts +84 -0
  120. package/src/lib/components/tooltip/tooltip.module.ts +11 -0
  121. package/src/lib/components/verben-mail/verben-mail.component.config.ts +11 -0
  122. package/src/lib/components/verben-mail/verben-mail.component.css +71 -0
  123. package/src/lib/components/verben-mail/verben-mail.component.html +139 -0
  124. package/src/lib/components/verben-mail/verben-mail.component.ts +151 -0
  125. package/src/lib/components/verben-mail/verben-mail.module.ts +18 -0
  126. package/src/lib/components/visible-column/visible-column.component.css +83 -0
  127. package/src/lib/components/visible-column/visible-column.component.html +77 -0
  128. package/src/lib/components/visible-column/visible-column.component.spec.ts +23 -0
  129. package/src/lib/components/visible-column/visible-column.component.ts +106 -0
  130. package/src/lib/components/visible-column/visible-column.module.ts +11 -0
  131. package/src/lib/config.ts +37 -0
  132. package/src/lib/control-options/control-options.directive.ts +129 -0
  133. package/src/lib/control-options/control-options.module.ts +10 -0
  134. package/src/lib/convert-to-integer/convert-to-integer.directive.ts +28 -0
  135. package/src/lib/convert-to-integer/convert-to-integer.module.ts +10 -0
  136. package/src/lib/convert-to-integer/public-api.ts +1 -0
  137. package/src/lib/convert-to-number/convert-to-number.directive.ts +22 -0
  138. package/src/lib/convert-to-number/convert-to-number.module.ts +8 -0
  139. package/src/lib/convert-to-number/public-api.ts +1 -0
  140. package/src/lib/email-validator/email-validator.directive.ts +24 -0
  141. package/src/lib/email-validator/email-validator.module.ts +14 -0
  142. package/src/lib/models/column-filter.ts +5 -0
  143. package/src/lib/models/data-filter.ts +8 -0
  144. package/src/lib/models/mail-model.ts +8 -0
  145. package/src/lib/models/sort-filter.ts +0 -0
  146. package/src/lib/models/table-filter.ts +34 -0
  147. package/src/lib/number-range/number-range.directive.ts +35 -0
  148. package/src/lib/number-range/number-range.module.ts +11 -0
  149. package/src/lib/phone-number/phone-number.directive.ts +22 -0
  150. package/src/lib/phone-number/phone-number.module.ts +10 -0
  151. package/src/lib/phone-number/public-api.ts +1 -0
  152. package/src/lib/required-input/required-input.directive.ts +18 -0
  153. package/src/lib/required-input/required-input.module.ts +8 -0
  154. package/src/lib/theme-switcher/theme-switcher.directive.spec.ts +8 -0
  155. package/src/lib/theme-switcher/theme-switcher.directive.ts +70 -0
  156. package/src/lib/theme-switcher/theme-switcher.module.ts +8 -0
  157. package/src/lib/validate/error-message.service.ts +104 -0
  158. package/src/lib/validate/validate.directive.ts +110 -0
  159. package/src/lib/validate/validate.module.ts +17 -0
  160. package/src/lib/validate-input/validate-input.directive.ts +8 -0
  161. package/src/lib/validate-input/validate-input.module.ts +8 -0
  162. package/src/lib/verben-ng-ui.component.spec.ts +23 -0
  163. package/src/lib/verben-ng-ui.component.ts +16 -0
  164. package/src/lib/verben-ng-ui.service.spec.ts +16 -0
  165. package/src/lib/verben-ng-ui.service.ts +9 -0
  166. package/src/lib/verbena-badge/verbena-badge.component.css +16 -0
  167. package/src/lib/verbena-badge/verbena-badge.component.html +13 -0
  168. package/src/lib/verbena-badge/verbena-badge.component.spec.ts +25 -0
  169. package/src/lib/verbena-badge/verbena-badge.component.ts +18 -0
  170. package/src/lib/verbena-badge/verbena-badge.module.ts +10 -0
  171. package/src/lib/verbena-button/verbena-button.component.css +20 -0
  172. package/src/lib/verbena-button/verbena-button.component.html +25 -0
  173. package/src/lib/verbena-button/verbena-button.component.spec.ts +25 -0
  174. package/src/lib/verbena-button/verbena-button.component.ts +92 -0
  175. package/src/lib/verbena-button/verbena-button.module.ts +11 -0
  176. package/src/lib/verbena-switch/verbena-switch.component.css +45 -0
  177. package/src/lib/verbena-switch/verbena-switch.component.html +8 -0
  178. package/src/lib/verbena-switch/verbena-switch.component.spec.ts +25 -0
  179. package/src/lib/verbena-switch/verbena-switch.component.ts +31 -0
  180. package/src/lib/verbena-switch/verbena-switch.module.ts +10 -0
  181. package/src/lib/verbena-textarea/verbena-textarea.component.css +0 -0
  182. package/src/lib/verbena-textarea/verbena-textarea.component.html +19 -0
  183. package/src/lib/verbena-textarea/verbena-textarea.component.spec.ts +33 -0
  184. package/src/lib/verbena-textarea/verbena-textarea.component.ts +44 -0
  185. package/src/lib/verbena-textarea/verbena-textarea.module.ts +11 -0
  186. package/src/public-api.ts +122 -0
  187. package/src/styles.css +103 -0
  188. package/tsconfig.lib.json +15 -0
  189. package/tsconfig.lib.prod.json +11 -0
  190. package/tsconfig.spec.json +15 -0
@@ -0,0 +1,757 @@
1
+ import {
2
+ AfterContentInit,
3
+ Component,
4
+ ContentChildren,
5
+ ElementRef,
6
+ EventEmitter,
7
+ HostBinding,
8
+ HostListener,
9
+ Input,
10
+ OnInit,
11
+ Optional,
12
+ Output,
13
+ QueryList,
14
+ Self,
15
+ TemplateRef,
16
+ ViewChild,
17
+ } from '@angular/core';
18
+ import { CommonModule } from '@angular/common';
19
+ import { ControlValueAccessor, FormsModule, NgControl } from '@angular/forms';
20
+ import { DropdownMenuItem } from './DropdownMenuItem';
21
+ import { DropdownLoadEvent } from './DropdownLoadEvent';
22
+ import { TemplateDirective } from '../TemplateDirective.directive';
23
+ import { DropdownChangeEvent } from './DropdownChangeEvent';
24
+ import { DropDownItemComponent } from './drop-down-item/drop-down-item.component';
25
+ import { isEqual, cloneDeep } from 'lodash';
26
+ import { debounceTime, Subject } from 'rxjs';
27
+ import { SharedModule } from '../shared.module';
28
+ import { SvgModule } from '../svg/svg.module';
29
+ import { TooltipModule } from '../tooltip/tooltip.module';
30
+
31
+ export interface DropdownMenuItemWrapper extends DropdownMenuItem {
32
+ expanded: boolean;
33
+ items?: DropdownMenuItemWrapper[];
34
+ isLoading: boolean;
35
+ copy: DropdownMenuItemWrapper[];
36
+ loadTimes?: DropdownLoadEvent;
37
+ loadTimesCopy?: DropdownLoadEvent;
38
+ }
39
+
40
+ @Component({
41
+ selector: 'verben-drop-down',
42
+ standalone: true,
43
+ imports: [
44
+ CommonModule,
45
+ DropDownItemComponent,
46
+ SharedModule,
47
+ FormsModule,
48
+ SvgModule,
49
+ TooltipModule,
50
+ ],
51
+ templateUrl: './drop-down.component.html',
52
+ styleUrl: './drop-down.component.css',
53
+ })
54
+ export class DropDownComponent
55
+ implements ControlValueAccessor, OnInit, AfterContentInit
56
+ {
57
+ // INTERNAL
58
+ private _options: any[] = []; // Internal variable for options
59
+
60
+ // INPUTS
61
+ @Input()
62
+ set options(value: any[]) {
63
+ if (this.group) {
64
+ if (this.isDropdownMenuItemWrapperArray(value)) {
65
+ this._options = value;
66
+ } else {
67
+ this._options = this.convertToExpandable(value); // Set the internal options variable
68
+ }
69
+ } else {
70
+ this._options = value;
71
+ }
72
+ }
73
+
74
+ get options(): any[] {
75
+ return this._options; // Return the internal options variable
76
+ }
77
+
78
+ @Input() width: string = '12rem';
79
+ @Input() showHorizontalLine: boolean = true;
80
+ @Input() horizontalLineColor: string = 'rgba(255, 230, 129, 1)';
81
+ @Input() optionLabel?: string;
82
+ @Input() optionSubLabel?: string;
83
+ @Input() optionValue?: string;
84
+ @Input() placeholder?: string;
85
+ @Input() invalidMessage?: string;
86
+ @Input() errorPosition: string = '';
87
+ @Input() loadMoreCaption: string = 'See more';
88
+ @Input() display: string = 'default';
89
+ @Input() showClear: boolean = false;
90
+ @Input() lazyLoad: boolean = false;
91
+ @Input() styleClass: string = '';
92
+ @Input() group: boolean = false;
93
+ @Input() multiselect: boolean = false;
94
+ @Input() filter: boolean = false;
95
+ @Input() avoidDuplication: boolean = false;
96
+ @Input() filterBy?: string;
97
+ @Input() debounceTime: number = 500;
98
+ @Input() minChar: number = 0;
99
+ @Input() disabled: boolean = false;
100
+ @Input() required: boolean = false;
101
+ @Input() load?: (context: DropdownLoadEvent) => Promise<any[]>;
102
+ @Input() asyncLabel?: (context: any) => string | null;
103
+ @Input() search?: (data: any, context: DropdownLoadEvent) => Promise<any[]>;
104
+
105
+ // OUTPUTS
106
+ @Output() optionsChange: EventEmitter<any[]> = new EventEmitter();
107
+ @Output() onChange: EventEmitter<DropdownChangeEvent> = new EventEmitter();
108
+ @Output() onClick: EventEmitter<Event> = new EventEmitter();
109
+ @Output() onClear: EventEmitter<Event> = new EventEmitter();
110
+
111
+ onItemChange: (value: any) => void = () => {};
112
+ onTouched: () => void = () => {};
113
+ isInvalid: boolean = false;
114
+
115
+ //TEMPLATING
116
+ @ViewChild('dropdownContainer', { static: true })
117
+ dropdownContainer!: ElementRef;
118
+ @ContentChildren(TemplateDirective) templates!: QueryList<TemplateDirective>;
119
+
120
+ @HostBinding('class.focused') isFocused = false;
121
+ isInputFocused = false;
122
+
123
+ selectedItemTemplate: TemplateRef<any> | null = null;
124
+ itemTemplate: TemplateRef<any> | null = null; // Allow null for custom item template
125
+ groupTemplate: TemplateRef<any> | null = null;
126
+
127
+ //VARIABLES
128
+ isExpanded: boolean = false;
129
+ loadTimes: DropdownLoadEvent = new DropdownLoadEvent();
130
+ initialIncrement: boolean = false;
131
+ selectedOption: any;
132
+ selectedOptions: any[] = [];
133
+ selectedOptionLabel: any;
134
+ selectedOptionLabels: any[] = [];
135
+ isLoading: boolean = false;
136
+ optionsCopy: any[] = [];
137
+ loadTimesCopy: DropdownLoadEvent = new DropdownLoadEvent();
138
+ selectedContexts: any[] = [];
139
+ searchHistory: string[] = [];
140
+ searchTerm$ = new Subject<string>();
141
+ searchContext: string = '';
142
+ allowSelectAll: boolean = false;
143
+ selectedAll: boolean = false;
144
+
145
+ constructor(@Optional() @Self() private ngControl: NgControl) {
146
+ if (this.ngControl) {
147
+ this.ngControl.valueAccessor = this; // Assign this component as the value accessor
148
+ this.ngControl?.statusChanges?.subscribe((status) => {
149
+ this.isInvalid = this.ngControl.touched
150
+ ? status === 'INVALID' && this.ngControl.touched
151
+ : false;
152
+ });
153
+ }
154
+ }
155
+ ngOnInit(): void {
156
+ if (this.multiselect) {
157
+ if (!this.lazyLoad) {
158
+ if (!this.group) {
159
+ this.allowSelectAll = true;
160
+ } else {
161
+ let isAllowed = true;
162
+ for (let item of this.options) {
163
+ if (item.lazyLoad) {
164
+ isAllowed = false;
165
+ break;
166
+ }
167
+ }
168
+ this.allowSelectAll = isAllowed;
169
+ }
170
+ }
171
+ }
172
+ if (this.filter) {
173
+ this.optionsCopy = cloneDeep(this._options);
174
+ this.searchTerm$
175
+ .pipe(debounceTime(this.debounceTime)) // Adjust debounce time here (in ms)
176
+ .subscribe(async (searchTerm) => {
177
+ if (searchTerm.length < this.minChar) {
178
+ return;
179
+ }
180
+ if (this.selectedContexts.length > 0) {
181
+ const item = this.selectedContexts[
182
+ this.selectedContexts.length - 1
183
+ ] as DropdownMenuItemWrapper;
184
+ if (item.search && item.loadTimes) {
185
+ item.loadTimes.reset();
186
+ item.isLoading = true;
187
+ const result = await item.search(searchTerm, item.loadTimes);
188
+ item.isLoading = false;
189
+ if (this.searchContext.length > 0) {
190
+ item.loadTimes.increaseLoadTime();
191
+ item.items = this.convertToExpandable(result);
192
+ this.group
193
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
194
+ : this.optionsChange.emit(this.options);
195
+ }
196
+ }
197
+ } else {
198
+ if (this.search && this.loadTimes) {
199
+ this.isLoading = true;
200
+ this.loadTimes.reset();
201
+ const result = await this.search(searchTerm, this.loadTimes);
202
+ this.isLoading = false;
203
+ if (this.searchContext.length > 0) {
204
+ this.options = this.convertToExpandable(result);
205
+ this.group
206
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
207
+ : this.optionsChange.emit(this.options);
208
+ }
209
+ }
210
+ }
211
+ });
212
+ }
213
+ }
214
+ ngAfterContentInit(): void {
215
+ this.templates.forEach((templateDirective) => {
216
+ if (templateDirective.vTemplate === 'selected') {
217
+ this.selectedItemTemplate = templateDirective.template;
218
+ }
219
+ if (templateDirective.vTemplate === 'item') {
220
+ this.itemTemplate = templateDirective.template;
221
+ }
222
+ if (templateDirective.vTemplate === 'group') {
223
+ this.groupTemplate = templateDirective.template;
224
+ }
225
+ });
226
+ }
227
+
228
+ @HostListener('focus') onFocus() {
229
+ this.isFocused = true;
230
+ }
231
+
232
+ @HostListener('blur') onBlur() {
233
+ this.isFocused = false;
234
+ }
235
+
236
+ onSearchFocus() {
237
+ this.isInputFocused = true;
238
+ }
239
+
240
+ onSearchBlur() {
241
+ this.isInputFocused = false;
242
+ }
243
+
244
+ onInputFocus() {
245
+ this.isInputFocused = true;
246
+ }
247
+
248
+ onInputBlur() {
249
+ this.isInputFocused = false;
250
+ }
251
+
252
+ onSearch(event: any) {
253
+ if (this.disabled) {
254
+ return;
255
+ }
256
+ const searchTerm = event.target.value;
257
+ if (this.selectedContexts.length > 0) {
258
+ const item = this.selectedContexts[
259
+ this.selectedContexts.length - 1
260
+ ] as DropdownMenuItemWrapper;
261
+ if (searchTerm.trim().length == 0) {
262
+ item.items = cloneDeep(item.copy);
263
+ item.loadTimes = cloneDeep(item.loadTimesCopy);
264
+ this.group
265
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
266
+ : this.optionsChange.emit(this.options);
267
+ return;
268
+ }
269
+ if (item.lazyLoad) {
270
+ this.searchTerm$.next(searchTerm);
271
+ } else {
272
+ if (item.filterBy) {
273
+ if (!item.items) {
274
+ return;
275
+ }
276
+ item.items = item.items.filter(
277
+ (x) =>
278
+ typeof x.value[item.filterBy!] == 'string' &&
279
+ new RegExp(searchTerm, 'i').test(x.value[item.filterBy!])
280
+ );
281
+ this.group
282
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
283
+ : this.optionsChange.emit(this.options);
284
+ } else {
285
+ if (!item.items) {
286
+ return;
287
+ }
288
+ item.items = item.copy.filter(
289
+ (x) =>
290
+ typeof x.value == 'string' &&
291
+ new RegExp(searchTerm, 'i').test(x.value)
292
+ );
293
+ this.group
294
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
295
+ : this.optionsChange.emit(this.options);
296
+ }
297
+ }
298
+ } else {
299
+ if (searchTerm.trim().length == 0) {
300
+ this.options = cloneDeep(this.optionsCopy);
301
+ this.loadTimes = cloneDeep(this.loadTimesCopy);
302
+ this.group
303
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
304
+ : this.optionsChange.emit(this.options);
305
+ return;
306
+ }
307
+ if (this.lazyLoad) {
308
+ this.searchTerm$.next(searchTerm);
309
+ } else {
310
+ if (this.filterBy) {
311
+ this.options = this.optionsCopy.filter(
312
+ (x) =>
313
+ typeof x[this.filterBy!] == 'string' &&
314
+ new RegExp(searchTerm, 'i').test(x[this.filterBy!])
315
+ );
316
+ this.group
317
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
318
+ : this.optionsChange.emit(this.options);
319
+ } else {
320
+ this.options = this.optionsCopy.filter(
321
+ (x) => typeof x == 'string' && new RegExp(searchTerm, 'i').test(x)
322
+ );
323
+ this.group
324
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
325
+ : this.optionsChange.emit(this.options);
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ onDropdownClick(event: Event) {
332
+ if (this.disabled) {
333
+ return;
334
+ }
335
+ this.toggleDropdown();
336
+ this.onClick.emit(event);
337
+ }
338
+
339
+ toggleDropdown() {
340
+ this.isExpanded = !this.isExpanded;
341
+ }
342
+
343
+ hasSibling(siblings: DropdownMenuItemWrapper[]): number | null {
344
+ for (let i = 0; i < siblings.length; i++) {
345
+ const index = this.selectedContexts.indexOf(siblings[i]);
346
+ if (index > -1) {
347
+ return index;
348
+ }
349
+ }
350
+ return null;
351
+ }
352
+
353
+ async expandMenu(
354
+ item: DropdownMenuItemWrapper,
355
+ siblings: DropdownMenuItemWrapper[]
356
+ ) {
357
+ if (item.items) {
358
+ if (this.filter) {
359
+ for (let it of siblings.filter((x) => x !== item)) {
360
+ if (it.expanded) {
361
+ it.expanded = false;
362
+ it.loadTimes && it.loadTimes.reset();
363
+ }
364
+ }
365
+ }
366
+ item.expanded = !item.expanded;
367
+ if (item.expanded) {
368
+ if (this.filter) {
369
+ const siblingCheck = this.hasSibling(siblings);
370
+ if (this.selectedContexts.length > 0 && siblingCheck !== null) {
371
+ const contextsLength = this.selectedContexts.length;
372
+ this.selectedContexts.splice(
373
+ siblingCheck,
374
+ contextsLength - siblingCheck
375
+ );
376
+ }
377
+ this.selectedContexts.push(item);
378
+ if (siblingCheck == null) {
379
+ this.searchHistory.push(this.searchContext);
380
+ }
381
+ this.searchContext = '';
382
+ }
383
+ if (item.lazyLoad && item.loadMore && item.loadTimes) {
384
+ item.isLoading = true;
385
+ var result = await item.loadMore(item.loadTimes);
386
+ item.isLoading = false;
387
+ item.loadTimes.increaseLoadTime();
388
+ item.items = this.convertToExpandable(result);
389
+ if (this.filter) {
390
+ item.copy = cloneDeep(item.items);
391
+ item.loadTimesCopy = cloneDeep(item.loadTimes);
392
+ }
393
+ this.group
394
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
395
+ : this.optionsChange.emit(this.options);
396
+ }
397
+ } else {
398
+ if (this.filter) {
399
+ const index = this.selectedContexts.indexOf(item);
400
+ const contextsLength = this.selectedContexts.length;
401
+ if (index > -1) {
402
+ this.selectedContexts.splice(index, contextsLength - index);
403
+ }
404
+ }
405
+ if (item.loadTimes) {
406
+ item.loadTimes.reset();
407
+ if (this.filter) {
408
+ item.loadTimesCopy = cloneDeep(item.loadTimes);
409
+ }
410
+ }
411
+ if (this.filter) {
412
+ if (this.searchHistory.length > 0) {
413
+ this.searchContext =
414
+ this.searchHistory[this.searchHistory.length - 1];
415
+ this.searchHistory.splice(this.searchHistory.length - 1, 1);
416
+ }
417
+ }
418
+ }
419
+ }
420
+ }
421
+
422
+ async loadMoreMenuItems(item: DropdownMenuItemWrapper) {
423
+ if (item.loadMore && item.loadTimes && item.items) {
424
+ const searchContext = this.searchContext.trim();
425
+ item.isLoading = true;
426
+ var result =
427
+ searchContext.length > 0 && item.search
428
+ ? await item.search(searchContext, item.loadTimes)
429
+ : await item.loadMore(item.loadTimes);
430
+ item.isLoading = false;
431
+ item.loadTimes.increaseLoadTime();
432
+ const converted = this.convertToExpandable(result);
433
+ for (let res of converted) {
434
+ item.items.push(res);
435
+ }
436
+ if (this.filter) {
437
+ if (searchContext.length == 0 || !item.search) {
438
+ item.copy = cloneDeep(item.items);
439
+ item.loadTimesCopy = cloneDeep(item.loadTimes);
440
+ }
441
+ }
442
+ this.group
443
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
444
+ : this.optionsChange.emit(this.options);
445
+ }
446
+ }
447
+
448
+ async loadMore() {
449
+ if (this.load) {
450
+ if (this.options.length > 0 && !this.initialIncrement) {
451
+ this.loadTimes.increaseLoadTime();
452
+ if (this.filter) {
453
+ this.loadTimesCopy = cloneDeep(this.loadTimes);
454
+ }
455
+ this.initialIncrement = true;
456
+ }
457
+ const searchContext = this.searchContext.trim();
458
+ this.isLoading = true;
459
+ var result =
460
+ searchContext.length > 0 && this.search
461
+ ? await this.search(searchContext, this.loadTimes)
462
+ : await this.load(this.loadTimes);
463
+ this.isLoading = false;
464
+ if (this.group) {
465
+ result = this.convertToExpandable(result);
466
+ }
467
+ this.loadTimes.increaseLoadTime();
468
+ for (let item of result) {
469
+ this.options.push(item);
470
+ }
471
+ if (this.filter) {
472
+ if (searchContext.length == 0 || !this.search) {
473
+ this.optionsCopy = cloneDeep(this.options);
474
+ this.loadTimesCopy = cloneDeep(this.loadTimes);
475
+ }
476
+ }
477
+ this.group
478
+ ? this.optionsChange.emit(this.options as DropdownMenuItem[])
479
+ : this.optionsChange.emit(this.options);
480
+ }
481
+ }
482
+
483
+ convertToExpandable(items: DropdownMenuItem[]): DropdownMenuItemWrapper[] {
484
+ const wrapper: DropdownMenuItemWrapper[] = [];
485
+ for (let item of items) {
486
+ const convertedItem: DropdownMenuItemWrapper = {
487
+ ...item,
488
+ isLoading: false,
489
+ expanded: false,
490
+ loadTimes: item.items ? new DropdownLoadEvent() : undefined,
491
+ loadTimesCopy: item.items ? new DropdownLoadEvent() : undefined,
492
+ items: item.items ? this.convertToExpandable(item.items) : undefined,
493
+ copy: item.items ? cloneDeep(this.convertToExpandable(item.items)) : [],
494
+ };
495
+ wrapper.push(convertedItem);
496
+ }
497
+ return wrapper;
498
+ }
499
+
500
+ onSelectAll(event: Event) {
501
+ if (this.selectedAll) {
502
+ this.selectedOptions = [];
503
+ this.selectedOptionLabels = [];
504
+ this.onItemChange([]);
505
+ this.onTouched();
506
+ this.onChange.emit({ originalEvent: event, value: [] });
507
+ if (this.required) {
508
+ this.isInvalid = true;
509
+ }
510
+ return;
511
+ }
512
+ if (!this.group) {
513
+ this.selectedOptions = [];
514
+ this.selectedOptionLabels = [];
515
+ for (let option of this.options) {
516
+ const index = this.checkMultiselectValue(option);
517
+ if (index == null) {
518
+ this.selectedOptions.push(this.getValue(option));
519
+ this.selectedOptionLabels.push(this.getOptionLabel(option));
520
+ }
521
+ }
522
+ } else {
523
+ this.selectedOptions = [];
524
+ this.selectedOptionLabels = [];
525
+ for (let option of this.options) {
526
+ for (let item of option.items) {
527
+ const index = this.checkMultiselectValue(item);
528
+ if (index == null) {
529
+ this.selectedOptions.push(this.getValue(item));
530
+ this.selectedOptionLabels.push(this.getOptionLabel(option));
531
+ }
532
+ }
533
+ }
534
+ }
535
+ this.onItemChange(this.selectedOptions);
536
+ this.onTouched();
537
+ this.onChange.emit({ originalEvent: event, value: this.selectedOptions });
538
+ }
539
+
540
+ clearSelection(event: Event) {
541
+ if (!this.multiselect) {
542
+ this.selectedOption = null;
543
+ this.selectedOptionLabel = null;
544
+ this.onItemChange(null);
545
+ this.onTouched();
546
+ this.onChange.emit({ originalEvent: event, value: null });
547
+ } else {
548
+ this.selectedOptions = [];
549
+ this.selectedOptionLabels = [];
550
+ this.onItemChange([]);
551
+ this.onTouched();
552
+ this.onChange.emit({ originalEvent: event, value: [] });
553
+ }
554
+ if (this.required) {
555
+ this.isInvalid = true;
556
+ }
557
+ this.selectedAll = false;
558
+ this.onClear.emit(event);
559
+ }
560
+
561
+ onSelect(value: any, event: Event) {
562
+ if (!this.multiselect) {
563
+ this.selectedOption = this.getValue(value);
564
+ this.selectedOptionLabel = this.getOptionLabel(value);
565
+ this.onItemChange(this.getValue(value));
566
+ this.onTouched();
567
+ this.onChange.emit({ originalEvent: event, value: this.getValue(value) });
568
+ this.toggleDropdown();
569
+ } else {
570
+ const exists = this.checkMultiselectValue(value);
571
+ if (exists == null) {
572
+ const newSelectedOptions = [
573
+ ...this.selectedOptions,
574
+ this.getValue(value),
575
+ ];
576
+ const newSelectedOptionLabels = [
577
+ ...this.selectedOptionLabels,
578
+ this.getOptionLabel(value),
579
+ ];
580
+ this.selectedOptions = newSelectedOptions;
581
+ this.selectedOptionLabels = newSelectedOptionLabels;
582
+ this.onItemChange(this.selectedOptions);
583
+ this.onTouched();
584
+ this.onChange.emit({
585
+ originalEvent: event,
586
+ value: this.selectedOptions,
587
+ });
588
+ } else {
589
+ this.selectedAll = false;
590
+ const newSelectedOptions = [...this.selectedOptions];
591
+ newSelectedOptions.splice(exists, 1);
592
+ const newSelectedOptionLabels = [...this.selectedOptionLabels];
593
+ newSelectedOptionLabels.splice(exists, 1);
594
+ this.selectedOptions = newSelectedOptions;
595
+ this.selectedOptionLabels = newSelectedOptionLabels;
596
+ this.onItemChange(this.selectedOptions);
597
+ this.onTouched();
598
+ this.onChange.emit({
599
+ originalEvent: event,
600
+ value: this.selectedOptions,
601
+ });
602
+ }
603
+ }
604
+ }
605
+
606
+ onMultiselectItemClosed(index: number, event: Event) {
607
+ const newSelectedOptions = [...this.selectedOptions];
608
+ newSelectedOptions.splice(index, 1);
609
+ const newSelectedOptionLabels = [...this.selectedOptionLabels];
610
+ newSelectedOptionLabels.splice(index, 1);
611
+ this.selectedOptions = newSelectedOptions;
612
+ this.selectedOptionLabels = newSelectedOptionLabels;
613
+ this.onItemChange(this.selectedOptions);
614
+ this.onTouched();
615
+ this.onChange.emit({ originalEvent: event, value: this.selectedOptions });
616
+ }
617
+
618
+ checkMultiselectValue(value: any): number | null {
619
+ for (let i = 0; i < this.selectedOptions.length; i++) {
620
+ if (isEqual(this.selectedOptions[i], this.getValue(value))) {
621
+ return i;
622
+ }
623
+ }
624
+ return null;
625
+ }
626
+
627
+ writeValue(obj: any): void {
628
+ if (!this.multiselect) {
629
+ if (obj == null || obj == undefined) {
630
+ this.selectedOption = obj;
631
+ this.selectedOptionLabel = obj;
632
+ return;
633
+ }
634
+ this.selectedOption = null;
635
+ this.selectedOptionLabel = null;
636
+ if (!this.group && !this.lazyLoad) {
637
+ for (let option of this.options) {
638
+ if (isEqual(this.getValue(option), obj)) {
639
+ this.selectedOption = this.getValue(option);
640
+ this.selectedOptionLabel = this.getOptionLabel(option);
641
+ break;
642
+ }
643
+ }
644
+ this.onTouched();
645
+ this.onChange.emit({ value: this.selectedOption });
646
+ return;
647
+ }
648
+ this.selectedOption = obj;
649
+ this.selectedOptionLabel = this.asyncLabel
650
+ ? this.asyncLabel(obj)
651
+ : this.getOptionLabel(obj);
652
+ this.onTouched();
653
+ this.onChange.emit({ value: this.selectedOption });
654
+ } else {
655
+ if (!Array.isArray(obj)) {
656
+ this.selectedOptions = [];
657
+ this.selectedOptionLabels = [];
658
+ return;
659
+ }
660
+ this.selectedOptions = [];
661
+ this.selectedOptionLabels = [];
662
+ if (!this.group && !this.lazyLoad) {
663
+ for (let option of this.options) {
664
+ for (let object of obj) {
665
+ if (isEqual(this.getValue(option), object)) {
666
+ this.selectedOptions.push(this.getValue(option));
667
+ this.selectedOptionLabels.push(this.getOptionLabel(option));
668
+ break;
669
+ }
670
+ }
671
+ }
672
+ this.onTouched();
673
+ this.onChange.emit({ value: this.selectedOptions });
674
+ return;
675
+ }
676
+ this.selectedOptions = obj;
677
+ for (let object of obj) {
678
+ this.selectedOptionLabels.push(
679
+ this.asyncLabel
680
+ ? this.asyncLabel(object)
681
+ : this.getOptionLabel(object)
682
+ );
683
+ }
684
+ this.onTouched();
685
+ this.onChange.emit({ value: this.selectedOptions });
686
+ }
687
+ }
688
+
689
+ getOptionFromValue(value: any): any {
690
+ if (!this.optionValue) {
691
+ return this.group ? value.value : value;
692
+ }
693
+ const index = this.group
694
+ ? this.options.findIndex(
695
+ (x) =>
696
+ x.value[this.optionValue!] && x.value[this.optionValue!] == value
697
+ )
698
+ : this.options.findIndex(
699
+ (x) => x[this.optionValue!] && x[this.optionValue!] == value
700
+ );
701
+ if (index < 0) {
702
+ return this.group ? value.value : value;
703
+ }
704
+ return this.options[index];
705
+ }
706
+
707
+ registerOnChange(fn: any): void {
708
+ this.onItemChange = fn;
709
+ }
710
+ registerOnTouched(fn: any): void {
711
+ this.onTouched = fn;
712
+ }
713
+ setDisabledState?(isDisabled: boolean): void {
714
+ this.disabled = isDisabled;
715
+ }
716
+
717
+ getValue(item: any): any {
718
+ if (!this.optionValue) {
719
+ return this.group ? item.value : item;
720
+ }
721
+ return this.group ? item.value[this.optionValue] : item[this.optionValue];
722
+ }
723
+ getOptionLabel(item: any): any {
724
+ if (this.group) {
725
+ return item.label;
726
+ }
727
+ if (!this.optionLabel) {
728
+ return item;
729
+ }
730
+ return typeof item === 'string' ? item : item[this.optionLabel];
731
+ }
732
+
733
+ private isDropdownMenuItemWrapperArray(value: any[]): boolean {
734
+ return (
735
+ Array.isArray(value) &&
736
+ value.every((item) => this.isDropdownMenuItemWrapper(item))
737
+ );
738
+ }
739
+
740
+ // Type guard function to check if an object is of type DropdownMenuItem
741
+ private isDropdownMenuItemWrapper(item: any): boolean {
742
+ return 'expanded' in item;
743
+ }
744
+
745
+ @HostListener('document:click', ['$event'])
746
+ onClickOutside(event: Event) {
747
+ if (!this.isExpanded) {
748
+ return;
749
+ }
750
+ if (
751
+ !this.dropdownContainer.nativeElement.contains(event.target) &&
752
+ this.isExpanded
753
+ ) {
754
+ this.isExpanded = false;
755
+ }
756
+ }
757
+ }