nexheal-lib 0.0.2 → 0.0.3

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 (62) hide show
  1. package/.editorconfig +17 -0
  2. package/.vscode/extensions.json +4 -0
  3. package/.vscode/launch.json +20 -0
  4. package/.vscode/tasks.json +42 -0
  5. package/README.md +15 -19
  6. package/angular.json +36 -0
  7. package/package.json +47 -23
  8. package/projects/nexheal-lib/README.md +63 -0
  9. package/projects/nexheal-lib/ng-package.json +9 -0
  10. package/projects/nexheal-lib/package.json +12 -0
  11. package/projects/nexheal-lib/src/directives/clickoutside.directive.ts +34 -0
  12. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.html +52 -0
  13. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.scss +22 -0
  14. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.spec.ts +22 -0
  15. package/projects/nexheal-lib/src/lib/controls/autocomplete-control/autocomplete-control.component.ts +367 -0
  16. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.html +152 -0
  17. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.scss +194 -0
  18. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.spec.ts +22 -0
  19. package/projects/nexheal-lib/src/lib/controls/calendar-control/calendar-control.component.ts +759 -0
  20. package/projects/nexheal-lib/src/lib/controls/checkbox-control/checkbox-control.component.html +4 -0
  21. package/projects/nexheal-lib/src/lib/controls/checkbox-control/checkbox-control.component.spec.ts +22 -0
  22. package/projects/nexheal-lib/src/lib/controls/checkbox-control/checkbox-control.component.ts +94 -0
  23. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.html +61 -0
  24. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.scss +132 -0
  25. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.spec.ts +22 -0
  26. package/projects/nexheal-lib/src/lib/controls/input-control/input-control.component.ts +202 -0
  27. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.html +72 -0
  28. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.scss +90 -0
  29. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.spec.ts +22 -0
  30. package/projects/nexheal-lib/src/lib/controls/multiselect-control/multiselect-control.component.ts +482 -0
  31. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.html +53 -0
  32. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.scss +19 -0
  33. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.spec.ts +22 -0
  34. package/projects/nexheal-lib/src/lib/controls/select-control/select-control.component.ts +375 -0
  35. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.html +4 -0
  36. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.scss +53 -0
  37. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.spec.ts +22 -0
  38. package/projects/nexheal-lib/src/lib/controls/switch-control/switch-control.component.ts +93 -0
  39. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.html +88 -0
  40. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.scss +122 -0
  41. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.spec.ts +22 -0
  42. package/projects/nexheal-lib/src/lib/controls/text-editor/text-editor.component.ts +314 -0
  43. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.html +19 -0
  44. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.scss +15 -0
  45. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.spec.ts +22 -0
  46. package/projects/nexheal-lib/src/lib/controls/textarea-control/textarea-control.component.ts +83 -0
  47. package/projects/nexheal-lib/src/public-api.ts +13 -0
  48. package/projects/nexheal-lib/src/styles/nexheal.scss +1 -0
  49. package/projects/nexheal-lib/tsconfig.lib.json +18 -0
  50. package/projects/nexheal-lib/tsconfig.lib.prod.json +11 -0
  51. package/projects/nexheal-lib/tsconfig.spec.json +14 -0
  52. package/tsconfig.json +39 -0
  53. package/fesm2022/nexheal-lib.mjs +0 -2837
  54. package/fesm2022/nexheal-lib.mjs.map +0 -1
  55. package/index.d.ts +0 -498
  56. package/src/styles/fonts/icomoon.eot +0 -0
  57. package/src/styles/fonts/icomoon.svg +0 -46
  58. package/src/styles/fonts/icomoon.ttf +0 -0
  59. package/src/styles/fonts/icomoon.woff +0 -0
  60. package/src/styles/icon.css +0 -133
  61. package/src/styles/nexheal.scss +0 -2
  62. /package/{src → projects/nexheal-lib/src}/styles/_formcontrols.scss +0 -0
@@ -1,2837 +0,0 @@
1
- import * as i1 from '@angular/common';
2
- import { CommonModule, DatePipe, DOCUMENT } from '@angular/common';
3
- import * as i0 from '@angular/core';
4
- import { EventEmitter, ViewChild, Input, Output, Component, forwardRef, HostListener, inject, ElementRef, DestroyRef, Directive } from '@angular/core';
5
- import * as i2 from '@angular/forms';
6
- import { FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
7
- import { Subscription, debounceTime, distinctUntilChanged, fromEvent, Subject } from 'rxjs';
8
- import { createPopper } from '@popperjs/core';
9
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
-
11
- class AutocompleteControl {
12
- subscription = new Subscription();
13
- title;
14
- required = false;
15
- placeholder = "";
16
- customClass;
17
- clearVal = true;
18
- field = "";
19
- error = false;
20
- errorMessage = "";
21
- autocomplete = "";
22
- inputLoader = false;
23
- isAddNewItem = false;
24
- optionDisplayProperty = "displayname";
25
- optionSelected = new EventEmitter();
26
- search = new EventEmitter();
27
- selectionCleared = new EventEmitter();
28
- addNewItemClicked = new EventEmitter();
29
- blurEvent = new EventEmitter();
30
- optionPatched = new EventEmitter();
31
- _disabled = false;
32
- _options = [];
33
- set options(value) {
34
- this._options = value || [];
35
- this.processValueBuffer();
36
- }
37
- get options() {
38
- return this._options;
39
- }
40
- get disabled() {
41
- return this._disabled;
42
- }
43
- set disabled(value) {
44
- this._disabled = value;
45
- if (this.inputControl) {
46
- if (value) {
47
- this.inputControl.disable();
48
- }
49
- else {
50
- this.inputControl.enable();
51
- }
52
- }
53
- }
54
- valueBuffer = null;
55
- popperInstance;
56
- preventDropdownReopen = false;
57
- preventClearOnBlur = false;
58
- onChange = () => { };
59
- onTouched = () => { };
60
- inputElement;
61
- dropdownElement;
62
- inputControl = new FormControl("");
63
- isDropdownOpen = false;
64
- hasFocus = false;
65
- selectedItems;
66
- filteredSuggestions = [];
67
- highlightedIndex = null;
68
- constructor() {
69
- this.inputControl.valueChanges
70
- .pipe(debounceTime(10), distinctUntilChanged())
71
- .subscribe((value) => {
72
- if (this.preventDropdownReopen) {
73
- this.preventDropdownReopen = false;
74
- return;
75
- }
76
- if (value && value.trim().length > 0) {
77
- const filterValue = value;
78
- this.filterSuggestions(filterValue);
79
- this.search.emit(filterValue);
80
- }
81
- else {
82
- this.isDropdownOpen = false;
83
- }
84
- });
85
- }
86
- ngOnInit() {
87
- this.inputControl.markAsPristine();
88
- }
89
- ngAfterViewInit() {
90
- this.createPopperInstance();
91
- }
92
- ngOnDestroy() {
93
- this.subscription.unsubscribe();
94
- if (this.popperInstance) {
95
- this.popperInstance.destroy();
96
- }
97
- }
98
- writeValue(value) {
99
- this.preventDropdownReopen = true;
100
- if (value == null) {
101
- // Clear everything if we get null
102
- this.valueBuffer = null;
103
- this.selectedItems = null;
104
- this.preventDropdownReopen = true;
105
- this.inputControl.setValue("", { emitEvent: false });
106
- }
107
- else {
108
- // Otherwise, treat as normal
109
- this.valueBuffer = value;
110
- this.processValueBuffer();
111
- }
112
- }
113
- processValueBuffer() {
114
- if (this.valueBuffer == null) {
115
- this.selectedItems = null;
116
- this.preventDropdownReopen = true;
117
- // this.inputControl.setValue("", { emitEvent: false });
118
- return;
119
- }
120
- if (this._options && this._options.length > 0) {
121
- const matchedSuggestion = this._options.find((s) => s.id === this.valueBuffer);
122
- if (matchedSuggestion) {
123
- this.preventDropdownReopen = true;
124
- this.inputControl.setValue(matchedSuggestion[this.optionDisplayProperty], { emitEvent: false });
125
- this.selectedItems = matchedSuggestion;
126
- this.onChange(matchedSuggestion.id);
127
- this.optionPatched.emit(matchedSuggestion);
128
- this.valueBuffer = null;
129
- return;
130
- }
131
- }
132
- this.inputControl.setValue("", { emitEvent: false });
133
- this.selectedItems = null;
134
- }
135
- registerOnChange(fn) {
136
- this.onChange = fn;
137
- }
138
- registerOnTouched(fn) {
139
- this.onTouched = fn;
140
- }
141
- setDisabledState(isDisabled) {
142
- if (isDisabled) {
143
- this.inputControl.disable();
144
- }
145
- else {
146
- this.inputControl.enable();
147
- }
148
- }
149
- filterSuggestions(value) {
150
- if (this.preventDropdownReopen || !this.hasFocus) {
151
- this.preventDropdownReopen = false;
152
- return;
153
- }
154
- if (value === "") {
155
- this.filteredSuggestions = [];
156
- this.isDropdownOpen = false;
157
- return;
158
- }
159
- const filterValue = value.toString().toLowerCase();
160
- this.filteredSuggestions = this.options.filter((suggestion) => suggestion[this.optionDisplayProperty]
161
- ?.toLowerCase()
162
- .includes(filterValue));
163
- this.highlightedIndex = this.filteredSuggestions.length > 0 ? 0 : null;
164
- this.isDropdownOpen = true;
165
- setTimeout(() => {
166
- this.createPopperInstance();
167
- }, 0);
168
- }
169
- selectSuggestion(suggestion) {
170
- this.preventDropdownReopen = true;
171
- this.inputControl.setValue(suggestion[this.optionDisplayProperty], {
172
- emitEvent: false,
173
- });
174
- this.onChange(suggestion.id);
175
- this.inputControl.markAsTouched();
176
- this.inputControl.updateValueAndValidity(); // Trigger validation update
177
- this.isDropdownOpen = false;
178
- if (this.selectedItems?.id !== suggestion.id) {
179
- this.selectedItems = suggestion;
180
- this.optionSelected.emit(suggestion);
181
- }
182
- }
183
- // events
184
- onFocus() {
185
- this.hasFocus = true;
186
- }
187
- onBlur() {
188
- this.blurEvent.emit();
189
- this.onTouched();
190
- // Delay onBlur handling so that mousedown and click can occur first.
191
- setTimeout(() => {
192
- this.hasFocus = false;
193
- this.isDropdownOpen = false;
194
- // If an option click is in progress, do not clear the input.
195
- if (!this.preventClearOnBlur) {
196
- // Optionally: check if the current value is valid against options.
197
- // If not found, clear the input.
198
- const currentValue = this.inputControl.value;
199
- const found = this._options.find((opt) => String(opt[this.optionDisplayProperty]).toLowerCase() ===
200
- String(currentValue).toLowerCase());
201
- if (!found) {
202
- this.inputControl.setValue("");
203
- this.selectedItems = null;
204
- this.onChange(null);
205
- }
206
- }
207
- // Reset the flag after handling blur.
208
- this.preventClearOnBlur = false;
209
- }, 300);
210
- }
211
- onKeyDown(event) {
212
- if (!this.isDropdownOpen) {
213
- return;
214
- }
215
- switch (event.key) {
216
- case "ArrowDown":
217
- this.highlightedIndex =
218
- this.highlightedIndex === null ||
219
- this.highlightedIndex === this.filteredSuggestions.length - 1
220
- ? 0
221
- : this.highlightedIndex + 1;
222
- event.preventDefault();
223
- this.scrollHighlightedItemIntoView();
224
- break;
225
- case "ArrowUp":
226
- this.highlightedIndex =
227
- this.highlightedIndex === null || this.highlightedIndex === 0
228
- ? this.filteredSuggestions.length - 1
229
- : this.highlightedIndex - 1;
230
- event.preventDefault();
231
- this.scrollHighlightedItemIntoView();
232
- break;
233
- case "Enter":
234
- if (this.highlightedIndex !== null &&
235
- this.filteredSuggestions.length > 0) {
236
- this.selectSuggestion(this.filteredSuggestions[this.highlightedIndex]);
237
- }
238
- else {
239
- this.isDropdownOpen = false;
240
- }
241
- event.preventDefault();
242
- break;
243
- case "Escape":
244
- this.isDropdownOpen = false;
245
- break;
246
- default:
247
- break;
248
- }
249
- }
250
- onOptionMouseDown() {
251
- this.preventClearOnBlur = true;
252
- }
253
- onMouseOver(index) {
254
- this.highlightedIndex = index;
255
- }
256
- // actions
257
- onAddNewItemClick() {
258
- this.addNewItemClicked.emit();
259
- }
260
- // popper
261
- createPopperInstance() {
262
- if (this.popperInstance) {
263
- this.popperInstance.destroy();
264
- }
265
- if (this.inputElement && this.dropdownElement) {
266
- this.popperInstance = createPopper(this.inputElement.nativeElement, this.dropdownElement.nativeElement, {
267
- placement: "bottom-start",
268
- modifiers: [
269
- {
270
- name: "offset",
271
- options: {
272
- offset: [0, 1],
273
- },
274
- },
275
- {
276
- name: "flip",
277
- options: {
278
- fallbackPlacements: ["top-start", "bottom-start"],
279
- },
280
- },
281
- ],
282
- });
283
- }
284
- }
285
- scrollHighlightedItemIntoView() {
286
- if (this.highlightedIndex !== null) {
287
- const highlightedItem = this.dropdownElement.nativeElement.querySelectorAll(".list-item")[this.highlightedIndex];
288
- if (highlightedItem) {
289
- highlightedItem.scrollIntoView({ block: "nearest" });
290
- }
291
- }
292
- }
293
- // clear
294
- resetInput() {
295
- this.inputControl.setValue("");
296
- this.selectionCleared.emit();
297
- this.selectedItems = null;
298
- this.valueBuffer = null;
299
- }
300
- removeItem(item) {
301
- this.selectedItems = this.selectedItems.filter((i) => i !== item);
302
- this.onChange(this.selectedItems);
303
- this.optionSelected.emit(this.selectedItems);
304
- }
305
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: AutocompleteControl, deps: [], target: i0.ɵɵFactoryTarget.Component });
306
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: AutocompleteControl, isStandalone: true, selector: "autocomplete-control", inputs: { title: "title", required: "required", placeholder: "placeholder", customClass: "customClass", clearVal: "clearVal", field: "field", error: "error", errorMessage: "errorMessage", autocomplete: "autocomplete", inputLoader: "inputLoader", isAddNewItem: "isAddNewItem", optionDisplayProperty: "optionDisplayProperty", options: "options", disabled: "disabled" }, outputs: { optionSelected: "optionSelected", search: "search", selectionCleared: "selectionCleared", addNewItemClicked: "addNewItemClicked", blurEvent: "blurEvent", optionPatched: "optionPatched" }, providers: [
307
- {
308
- provide: NG_VALUE_ACCESSOR,
309
- useExisting: AutocompleteControl,
310
- multi: true,
311
- },
312
- ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "dropdownElement", first: true, predicate: ["dropdownElement"], descendants: true }], ngImport: i0, template: "<div class=\"form-group auto-complete\" [ngClass]=\"customClass\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n <input #inputElement type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [formControl]=\"inputControl\"\r\n (blur)=\"onBlur()\" (keydown)=\"onKeyDown($event)\" (focus)=\"onFocus()\" [ngClass]=\"{'is-invalid': error}\"\r\n [attr.autocomplete]=\"autocomplete || null\" />\r\n\r\n <span class=\"focus-border\"></span>\r\n\r\n @if (!inputLoader && inputControl.value && clearVal && hasFocus) {\r\n <label class=\"clear\" (click)=\"resetInput()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n @if (isDropdownOpen) {\r\n <div #dropdownElement class=\"option-list\">\r\n @if (filteredSuggestions.length === 0) {\r\n <div class=\"no-results\">\r\n <div>No results found</div>\r\n @if (isAddNewItem) {\r\n <div (click)=\"onAddNewItemClick()\" class=\"btn-new\">\r\n Add New Item\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n @for (suggestion of filteredSuggestions; track suggestion[optionDisplayProperty]; let i = $index) {\r\n <div class=\"list-item\" [ngClass]=\"{\r\n 'active': suggestion === selectedItems,\r\n 'highlighted': highlightedIndex === i\r\n }\" (mousedown)=\"onOptionMouseDown()\" (click)=\"selectSuggestion(suggestion)\" (mouseover)=\"onMouseOver(i)\">\r\n @if (suggestion.countryCode) {\r\n <img src=\"https://flagcdn.com/w80/{{ suggestion.countryCode }}.png\" width=\"20\"\r\n alt=\"{{ suggestion.countryCode }} flag\" loading=\"lazy\" />\r\n }\r\n {{ suggestion[optionDisplayProperty] }}\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group.auto-complete .form-control{padding-right:unset}.form-group.auto-complete .clear{right:7px}.form-group.auto-complete .option-list .no-results{padding:10px;color:#9b9b9b;text-align:center}.form-group.auto-complete .option-list .no-results .btn-new{padding:5px 0;cursor:pointer;color:#0d6efd}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
313
- }
314
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: AutocompleteControl, decorators: [{
315
- type: Component,
316
- args: [{ selector: "autocomplete-control", standalone: true, imports: [CommonModule, ReactiveFormsModule], providers: [
317
- {
318
- provide: NG_VALUE_ACCESSOR,
319
- useExisting: AutocompleteControl,
320
- multi: true,
321
- },
322
- ], template: "<div class=\"form-group auto-complete\" [ngClass]=\"customClass\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n <input #inputElement type=\"text\" class=\"form-control\" [placeholder]=\"placeholder\" [formControl]=\"inputControl\"\r\n (blur)=\"onBlur()\" (keydown)=\"onKeyDown($event)\" (focus)=\"onFocus()\" [ngClass]=\"{'is-invalid': error}\"\r\n [attr.autocomplete]=\"autocomplete || null\" />\r\n\r\n <span class=\"focus-border\"></span>\r\n\r\n @if (!inputLoader && inputControl.value && clearVal && hasFocus) {\r\n <label class=\"clear\" (click)=\"resetInput()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n @if (isDropdownOpen) {\r\n <div #dropdownElement class=\"option-list\">\r\n @if (filteredSuggestions.length === 0) {\r\n <div class=\"no-results\">\r\n <div>No results found</div>\r\n @if (isAddNewItem) {\r\n <div (click)=\"onAddNewItemClick()\" class=\"btn-new\">\r\n Add New Item\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n @for (suggestion of filteredSuggestions; track suggestion[optionDisplayProperty]; let i = $index) {\r\n <div class=\"list-item\" [ngClass]=\"{\r\n 'active': suggestion === selectedItems,\r\n 'highlighted': highlightedIndex === i\r\n }\" (mousedown)=\"onOptionMouseDown()\" (click)=\"selectSuggestion(suggestion)\" (mouseover)=\"onMouseOver(i)\">\r\n @if (suggestion.countryCode) {\r\n <img src=\"https://flagcdn.com/w80/{{ suggestion.countryCode }}.png\" width=\"20\"\r\n alt=\"{{ suggestion.countryCode }} flag\" loading=\"lazy\" />\r\n }\r\n {{ suggestion[optionDisplayProperty] }}\r\n </div>\r\n }\r\n }\r\n </div>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group.auto-complete .form-control{padding-right:unset}.form-group.auto-complete .clear{right:7px}.form-group.auto-complete .option-list .no-results{padding:10px;color:#9b9b9b;text-align:center}.form-group.auto-complete .option-list .no-results .btn-new{padding:5px 0;cursor:pointer;color:#0d6efd}\n"] }]
323
- }], ctorParameters: () => [], propDecorators: { title: [{
324
- type: Input
325
- }], required: [{
326
- type: Input
327
- }], placeholder: [{
328
- type: Input
329
- }], customClass: [{
330
- type: Input
331
- }], clearVal: [{
332
- type: Input
333
- }], field: [{
334
- type: Input
335
- }], error: [{
336
- type: Input
337
- }], errorMessage: [{
338
- type: Input
339
- }], autocomplete: [{
340
- type: Input
341
- }], inputLoader: [{
342
- type: Input
343
- }], isAddNewItem: [{
344
- type: Input
345
- }], optionDisplayProperty: [{
346
- type: Input
347
- }], optionSelected: [{
348
- type: Output
349
- }], search: [{
350
- type: Output
351
- }], selectionCleared: [{
352
- type: Output
353
- }], addNewItemClicked: [{
354
- type: Output
355
- }], blurEvent: [{
356
- type: Output
357
- }], optionPatched: [{
358
- type: Output
359
- }], options: [{
360
- type: Input
361
- }], disabled: [{
362
- type: Input
363
- }], inputElement: [{
364
- type: ViewChild,
365
- args: ["inputElement"]
366
- }], dropdownElement: [{
367
- type: ViewChild,
368
- args: ["dropdownElement"]
369
- }] } });
370
-
371
- class CalendarControl {
372
- datePipe;
373
- title;
374
- required = false;
375
- customClass = "";
376
- clearVal = true;
377
- deFocus = true;
378
- error = false;
379
- errorMessage = "";
380
- inputLoader = false;
381
- hourFormat = "24";
382
- selectionMode = "single";
383
- timeOnly = false;
384
- dateFormat = "dd/MM/yyyy";
385
- placeholder = "dd-mm-yyyy";
386
- disabled = false;
387
- readonly = false;
388
- submitted = false;
389
- inputPlaceholder = false;
390
- closeVal = false;
391
- showTime = false;
392
- selectionCleared = new EventEmitter();
393
- blurEvent = new EventEmitter();
394
- dateSelected = new EventEmitter();
395
- _minDate;
396
- _maxDate;
397
- get minDate() {
398
- return this._minDate;
399
- }
400
- set minDate(value) {
401
- this._minDate = value;
402
- }
403
- get maxDate() {
404
- return this._maxDate;
405
- }
406
- set maxDate(value) {
407
- this._maxDate = value;
408
- }
409
- get meridian() {
410
- if (this.hourFormat !== '12')
411
- return 'AM';
412
- return this.selectedHour >= 12 ? 'PM' : 'AM';
413
- }
414
- static _currentlyOpen;
415
- onChangeFn = () => { };
416
- onTouchedFn = () => { };
417
- popperInstance = null;
418
- rootElement;
419
- inputEl;
420
- datePickerEl;
421
- isOpen = false;
422
- focus = false;
423
- displayMonth;
424
- displayYear;
425
- selectedDate = null;
426
- today = new Date();
427
- todayYear = this.today.getFullYear();
428
- todayMonth = this.today.getMonth();
429
- todayDate = this.today.getDate();
430
- selectedHour = this.today.getHours();
431
- selectedMinute = this.today.getMinutes();
432
- dayClassMap = {};
433
- inputVal = null; // Replace with the appropriate type and default value
434
- yearRangeSize = 15; // The year range view shows a chunk of years, for example 15 years at a time.
435
- currentView = "day";
436
- inputControl = new FormControl({ value: "", disabled: false });
437
- constructor(datePipe) {
438
- this.datePipe = datePipe;
439
- const today = new Date();
440
- this.displayMonth = today.getMonth();
441
- this.displayYear = today.getFullYear();
442
- }
443
- ngOnInit() {
444
- const now = new Date();
445
- this.displayYear = now.getFullYear();
446
- this.displayMonth = now.getMonth();
447
- }
448
- ngAfterViewChecked() {
449
- if (this.isOpen) {
450
- this.initializePopper();
451
- }
452
- }
453
- ngAfterViewInit() {
454
- if (this.isOpen) {
455
- this.initializePopper();
456
- }
457
- }
458
- ngOnDestroy() {
459
- this.destroyPopper();
460
- }
461
- writeValue(value) {
462
- // 1) Clear out if no value
463
- if (!value) {
464
- this.selectedDate = null;
465
- this.inputControl.setValue("", { emitEvent: false });
466
- return;
467
- }
468
- if (this.timeOnly) {
469
- let hours, mins;
470
- if (typeof value === "string") {
471
- // “HH:mm” or “hh:mm AM/PM”
472
- const m24 = value.match(/^(\d{1,2}):(\d{2})$/);
473
- const m12 = value.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
474
- if (m24) {
475
- hours = +m24[1];
476
- mins = +m24[2];
477
- }
478
- else if (m12) {
479
- let h = +m12[1];
480
- if (/PM/i.test(m12[3]) && h < 12)
481
- h += 12;
482
- if (/AM/i.test(m12[3]) && h === 12)
483
- h = 0;
484
- hours = h;
485
- mins = +m12[2];
486
- }
487
- else {
488
- // fallback parse
489
- const dt = new Date(value);
490
- hours = dt.getHours();
491
- mins = dt.getMinutes();
492
- }
493
- }
494
- else if (value instanceof Date) {
495
- hours = value.getHours();
496
- mins = value.getMinutes();
497
- }
498
- else {
499
- // maybe a timestamp or something else
500
- const dt = new Date(value);
501
- hours = dt.getHours();
502
- mins = dt.getMinutes();
503
- }
504
- // 3) Apply
505
- this.selectedHour = hours;
506
- this.selectedMinute = mins;
507
- // 4) Write back into the formControl & notify forms
508
- const out = this.formatOutputTimeOnly();
509
- this.inputControl.setValue(out, { emitEvent: false });
510
- this.onChangeFn(out);
511
- this.onTouchedFn();
512
- return;
513
- }
514
- // 2) Parse the incoming string (dd/MM/yyyy or dd/MM/yyyy HH:mm)
515
- const parsed = this.parseDate(value);
516
- this.selectedDate = parsed;
517
- if (this.showTime) {
518
- const raw = value;
519
- // split into date vs time
520
- let datePart = raw, timePart = null;
521
- const parts = raw.match(/^(.+?)\s+(.+)$/);
522
- if (parts) {
523
- datePart = parts[1];
524
- timePart = parts[2];
525
- }
526
- // parse the date
527
- const parsedDate = this.parseDate(datePart);
528
- if (!parsedDate) {
529
- return this.clearDate(new Event("manual"));
530
- }
531
- // parse the time portion (reuse your timeOnly logic)
532
- if (timePart) {
533
- let h, m;
534
- const m24 = timePart.match(/^(\d{1,2}):(\d{2})$/);
535
- const m12 = timePart.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
536
- if (m24) {
537
- h = +m24[1];
538
- m = +m24[2];
539
- }
540
- else if (m12) {
541
- h = +m12[1];
542
- m = +m12[2];
543
- if (/PM/i.test(m12[3]) && h < 12)
544
- h += 12;
545
- if (/AM/i.test(m12[3]) && h === 12)
546
- h = 0;
547
- }
548
- else {
549
- const dt = new Date(`1970-01-01T${timePart}`);
550
- h = dt.getHours();
551
- m = dt.getMinutes();
552
- }
553
- parsedDate.setHours(h, m);
554
- this.selectedHour = h;
555
- this.selectedMinute = m;
556
- }
557
- // update calendar view & control value
558
- this.selectedDate = parsedDate;
559
- this.displayMonth = parsedDate.getMonth();
560
- this.displayYear = parsedDate.getFullYear();
561
- const out = this.formatOutput(parsedDate);
562
- this.inputControl.setValue(out, { emitEvent: false });
563
- this.onChangeFn(out);
564
- this.onTouchedFn();
565
- return;
566
- }
567
- if (parsed) {
568
- // 3) Always update the calendar view
569
- this.displayMonth = parsed.getMonth();
570
- this.displayYear = parsed.getFullYear();
571
- // 4) ONLY if showTime is on, pull hours/minutes
572
- if (this.showTime) {
573
- this.selectedHour = parsed.getHours();
574
- this.selectedMinute = parsed.getMinutes();
575
- }
576
- // 5) Finally, set the input text via your formatter
577
- this.inputControl.setValue(this.formatOutput(parsed), {
578
- emitEvent: false,
579
- });
580
- }
581
- }
582
- registerOnChange(fn) {
583
- this.onChangeFn = fn;
584
- }
585
- registerOnTouched(fn) {
586
- this.onTouchedFn = fn;
587
- }
588
- setData(config) {
589
- if (config.hasOwnProperty("placeholder")) {
590
- this.placeholder = config.placeholder;
591
- }
592
- }
593
- // events
594
- onBlur() {
595
- this.onTouchedFn();
596
- const raw = this.inputControl.value;
597
- if (this.showTime && !this.timeOnly) {
598
- // split off the date vs. time
599
- const match = raw.match(/^(.+?)\s+(.+)$/);
600
- const datePart = match ? match[1] : raw;
601
- const timePart = match ? match[2] : "";
602
- // parse the date
603
- const pd = this.parseDate(datePart);
604
- if (!pd)
605
- return this.clearDate(new Event("manual"));
606
- // parse the time portion (reuse your timeOnly logic)
607
- let h, m;
608
- const m24 = timePart.match(/^(\d{1,2}):(\d{2})$/);
609
- const m12 = timePart.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
610
- if (m24) {
611
- h = +m24[1];
612
- m = +m24[2];
613
- }
614
- else if (m12) {
615
- h = +m12[1];
616
- m = +m12[2];
617
- if (/PM/i.test(m12[3]) && h < 12)
618
- h += 12;
619
- if (/AM/i.test(m12[3]) && h === 12)
620
- h = 0;
621
- }
622
- else {
623
- const tmp = new Date(`1970-01-01T${timePart}`);
624
- h = tmp.getHours();
625
- m = tmp.getMinutes();
626
- }
627
- // apply
628
- pd.setHours(h, m);
629
- this.selectedDate = pd;
630
- this.selectedHour = h;
631
- this.selectedMinute = m;
632
- // update calendar view
633
- this.displayMonth = pd.getMonth();
634
- this.displayYear = pd.getFullYear();
635
- // write back into the input & notify
636
- const out = this.formatOutput(pd);
637
- this.inputControl.setValue(out, { emitEvent: false });
638
- this.onChangeFn(out);
639
- this.dateSelected.emit(pd);
640
- this.blurEvent.emit();
641
- return;
642
- }
643
- if (this.timeOnly) {
644
- // try 24-hour HH:mm
645
- let m = raw.match(/^(\d{1,2}):(\d{2})$/);
646
- // or 12-hour h:mm AM/PM
647
- const mAmPm = !m && raw.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)$/i);
648
- if (m) {
649
- // 24h
650
- const h = +m[1], min = +m[2];
651
- if (h < 24 && min < 60) {
652
- this.selectedHour = h;
653
- this.selectedMinute = min;
654
- }
655
- else {
656
- return this.clearDate(new Event("manual"));
657
- }
658
- }
659
- else if (mAmPm) {
660
- // 12h
661
- let h = +mAmPm[1], min = +mAmPm[2];
662
- const ap = mAmPm[3].toUpperCase();
663
- if (h >= 1 && h <= 12 && min < 60) {
664
- if (ap === "PM" && h < 12)
665
- h += 12;
666
- if (ap === "AM" && h === 12)
667
- h = 0;
668
- this.selectedHour = h;
669
- this.selectedMinute = min;
670
- }
671
- else {
672
- return this.clearDate(new Event("manual"));
673
- }
674
- }
675
- else {
676
- // not a valid time string
677
- return this.clearDate(new Event("manual"));
678
- }
679
- const out = this.formatOutputTimeOnly();
680
- this.inputControl.setValue(out, { emitEvent: false });
681
- this.onChangeFn(out);
682
- this.dateSelected.emit(this.selectedDate);
683
- this.blurEvent.emit();
684
- return;
685
- }
686
- if (this.showTime) {
687
- this.blurEvent.emit();
688
- return;
689
- }
690
- const parsed = this.parseDate(raw);
691
- if (!parsed) {
692
- this.clearDate(new Event("manual"));
693
- }
694
- else {
695
- this.selectedDate = parsed;
696
- this.displayMonth = parsed.getMonth();
697
- this.displayYear = parsed.getFullYear();
698
- this.selectedHour = parsed.getHours();
699
- this.selectedMinute = parsed.getMinutes();
700
- const formatted = this.formatOutput(parsed);
701
- this.inputControl.setValue(formatted, { emitEvent: false });
702
- this.onChangeFn(formatted);
703
- }
704
- this.blurEvent.emit();
705
- }
706
- onFocus() {
707
- this.focus = true;
708
- }
709
- setDisabledState(isDisabled) {
710
- this.disabled = isDisabled;
711
- }
712
- onInputKeydown(event) {
713
- switch (event.key) {
714
- case "ArrowDown":
715
- event.preventDefault();
716
- this.toggleCalendar();
717
- break;
718
- case "Escape":
719
- this.isOpen = false;
720
- break;
721
- case " ":
722
- case "Spacebar":
723
- event.preventDefault();
724
- this.toggleCalendar();
725
- break;
726
- case "Tab":
727
- this.isOpen = false;
728
- break;
729
- }
730
- }
731
- // day
732
- selectDay(day) {
733
- if (!day)
734
- return;
735
- const picked = this.showTime
736
- ? this.buildDateWithTime(day)
737
- : new Date(this.displayYear, this.displayMonth, day);
738
- if (this.isDateDisabled(picked))
739
- return;
740
- this.selectedDate = picked;
741
- const out = this.formatOutput(picked);
742
- this.inputControl.setValue(out, { emitEvent: false });
743
- this.onChangeFn(out);
744
- this.onTouchedFn();
745
- this.dateSelected.emit(picked);
746
- this.isOpen = false;
747
- }
748
- get daysInMonth() {
749
- const firstDay = new Date(this.displayYear, this.displayMonth, 1).getDay();
750
- const totalDays = new Date(this.displayYear, this.displayMonth + 1, 0).getDate();
751
- const offset = firstDay;
752
- const daysArray = Array(offset).fill(null);
753
- this.dayClassMap = {}; // Reset
754
- for (let i = 1; i <= totalDays; i++) {
755
- const date = new Date(this.displayYear, this.displayMonth, i);
756
- this.dayClassMap[i] = {
757
- disabled: this.isDateDisabled(date),
758
- selected: this.selectedDate?.getFullYear() === this.displayYear &&
759
- this.selectedDate?.getMonth() === this.displayMonth &&
760
- this.selectedDate?.getDate() === i,
761
- today: this.todayYear === this.displayYear &&
762
- this.todayMonth === this.displayMonth &&
763
- this.todayDate === i,
764
- };
765
- daysArray.push(i);
766
- }
767
- return daysArray;
768
- }
769
- parseDate(val) {
770
- if (typeof val === "string") {
771
- const m = val.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
772
- if (m) {
773
- const d = +m[1], mo = +m[2] - 1, y = +m[3];
774
- return new Date(y, mo, d);
775
- }
776
- }
777
- // fallback
778
- const date = new Date(val);
779
- return isNaN(date.getTime()) ? null : date;
780
- }
781
- formatOutput(date) {
782
- const datePart = this.datePipe.transform(date, this.dateFormat);
783
- if (this.showTime) {
784
- const timeFormat = this.hourFormat === "12" ? "hh:mm a" : "HH:mm";
785
- const timePart = this.datePipe.transform(date, timeFormat);
786
- return `${datePart} ${timePart}`;
787
- }
788
- return datePart;
789
- }
790
- isSelectedDay(day) {
791
- if (!this.selectedDate || !day)
792
- return false;
793
- return (this.selectedDate.getFullYear() === this.displayYear &&
794
- this.selectedDate.getMonth() === this.displayMonth &&
795
- this.selectedDate.getDate() === day);
796
- }
797
- isDateDisabled(d) {
798
- if (this._minDate && d < new Date(this._minDate))
799
- return true;
800
- if (this._maxDate && d > new Date(this._maxDate))
801
- return true;
802
- return false;
803
- }
804
- isDayDisabled(day) {
805
- return (day !== null &&
806
- this.isDateDisabled(new Date(this.displayYear, this.displayMonth, day)));
807
- }
808
- // month
809
- goToMonthView() {
810
- this.currentView = "month";
811
- }
812
- months = [
813
- "Jan",
814
- "Feb",
815
- "Mar",
816
- "Apr",
817
- "May",
818
- "Jun",
819
- "Jul",
820
- "Aug",
821
- "Sep",
822
- "Oct",
823
- "Nov",
824
- "Dec",
825
- ];
826
- selectMonth(index) {
827
- this.displayMonth = index;
828
- this.currentView = "day";
829
- }
830
- get displayMonthName() {
831
- return this.months[this.displayMonth];
832
- }
833
- prevMonth() {
834
- if (this.displayMonth === 0) {
835
- this.displayMonth = 11;
836
- this.displayYear--;
837
- }
838
- else {
839
- this.displayMonth--;
840
- }
841
- }
842
- nextMonth() {
843
- if (this.displayMonth === 11) {
844
- this.displayMonth = 0;
845
- this.displayYear++;
846
- }
847
- else {
848
- this.displayMonth++;
849
- }
850
- }
851
- // year
852
- goToYearRangeView() {
853
- this.currentView = "yearRange";
854
- }
855
- get yearRange() {
856
- const startYear = this.getStartYearForRange();
857
- const years = [];
858
- for (let i = 0; i < this.yearRangeSize; i++) {
859
- years.push(startYear + i);
860
- }
861
- return years;
862
- }
863
- getStartYearForRange() {
864
- const remainder = this.displayYear % this.yearRangeSize;
865
- return this.displayYear - remainder;
866
- }
867
- selectYear(year) {
868
- this.displayYear = year;
869
- this.currentView = "month";
870
- }
871
- prevYearRange() {
872
- this.displayYear -= this.yearRangeSize;
873
- }
874
- nextYearRange() {
875
- this.displayYear += this.yearRangeSize;
876
- }
877
- // time picker
878
- formatOutputTimeOnly() {
879
- // you could choose 12h vs 24h based on hourFormat
880
- return this.datePipe.transform(new Date(0, 0, 0, this.selectedHour, this.selectedMinute), this.hourFormat === "12" ? "hh:mm a" : "HH:mm");
881
- }
882
- patchDateWithTime() {
883
- if (!this.selectedDate) {
884
- const now = new Date();
885
- this.selectedDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), this.selectedHour, this.selectedMinute);
886
- }
887
- else {
888
- this.selectedDate.setHours(this.selectedHour, this.selectedMinute);
889
- }
890
- const output = this.timeOnly
891
- ? this.formatOutputTimeOnly()
892
- : this.formatOutput(this.selectedDate);
893
- this.inputControl.setValue(output, { emitEvent: false });
894
- this.onChangeFn(output);
895
- this.onTouchedFn();
896
- this.dateSelected.emit(this.selectedDate);
897
- }
898
- setMeridian(value) {
899
- if (this.meridian === value)
900
- return;
901
- if (value === "AM" && this.selectedHour >= 12) {
902
- this.selectedHour -= 12;
903
- }
904
- else if (value === "PM" && this.selectedHour < 12) {
905
- this.selectedHour += 12;
906
- }
907
- this.patchDateWithTime();
908
- }
909
- buildDateWithTime(day) {
910
- return new Date(this.displayYear, this.displayMonth, day, this.selectedHour, this.selectedMinute);
911
- }
912
- incrementHour() {
913
- this.selectedHour = (this.selectedHour + 1) % 24;
914
- if (this.showTime || this.timeOnly)
915
- this.patchDateWithTime();
916
- }
917
- decrementHour() {
918
- this.selectedHour = (this.selectedHour + 23) % 24;
919
- if (this.showTime || this.timeOnly)
920
- this.patchDateWithTime();
921
- }
922
- incrementMinute() {
923
- this.selectedMinute++;
924
- if (this.selectedMinute >= 60) {
925
- this.selectedMinute = 0;
926
- this.selectedHour = (this.selectedHour + 1) % 24;
927
- }
928
- if (this.showTime || this.timeOnly) {
929
- this.patchDateWithTime();
930
- }
931
- }
932
- decrementMinute() {
933
- this.selectedMinute--;
934
- if (this.selectedMinute < 0) {
935
- this.selectedMinute = 59;
936
- // go back an hour, wrapping 0 → 23
937
- this.selectedHour = (this.selectedHour + 23) % 24;
938
- }
939
- if (this.showTime || this.timeOnly) {
940
- this.patchDateWithTime();
941
- }
942
- }
943
- // popper
944
- initializePopper() {
945
- if (!this.popperInstance &&
946
- this.inputEl?.nativeElement &&
947
- this.datePickerEl?.nativeElement) {
948
- this.popperInstance = createPopper(this.inputEl.nativeElement, this.datePickerEl.nativeElement, {
949
- placement: "bottom-start",
950
- modifiers: [
951
- { name: "offset", options: { offset: [0, 1] } },
952
- { name: "preventOverflow", options: { boundary: "viewport" } },
953
- { name: "flip", options: { fallbackPlacements: ["top-start"] } },
954
- ],
955
- });
956
- this.popperInstance.update();
957
- }
958
- }
959
- destroyPopper() {
960
- if (this.popperInstance) {
961
- this.popperInstance.destroy();
962
- this.popperInstance = null;
963
- }
964
- }
965
- // toggle, open, close and clear
966
- toggleCalendar() {
967
- if (this.disabled || this.readonly)
968
- return;
969
- // if another calendar is open, close it
970
- if (!this.isOpen &&
971
- CalendarControl._currentlyOpen &&
972
- CalendarControl._currentlyOpen !== this) {
973
- CalendarControl._currentlyOpen.closeCalendar();
974
- }
975
- // flip open/close
976
- this.isOpen = !this.isOpen;
977
- if (this.isOpen) {
978
- // mark this one as current
979
- CalendarControl._currentlyOpen = this;
980
- // reset the month/year view
981
- this.currentView = "day";
982
- if (this.selectedDate) {
983
- this.displayMonth = this.selectedDate.getMonth();
984
- this.displayYear = this.selectedDate.getFullYear();
985
- }
986
- else {
987
- const now = new Date();
988
- this.displayMonth = now.getMonth();
989
- this.displayYear = now.getFullYear();
990
- }
991
- // destroy any old popper
992
- if (this.popperInstance) {
993
- this.popperInstance.destroy();
994
- this.popperInstance = null;
995
- }
996
- if (this.inputEl && this.datePickerEl) {
997
- this.popperInstance = createPopper(this.inputEl.nativeElement, this.datePickerEl.nativeElement, {
998
- placement: "bottom-start",
999
- modifiers: [
1000
- { name: "offset", options: { offset: [0, 1] } },
1001
- { name: "preventOverflow", options: { boundary: "viewport" } },
1002
- { name: "flip", options: { fallbackPlacements: ["top-start"] } },
1003
- ],
1004
- });
1005
- this.popperInstance.update();
1006
- }
1007
- }
1008
- else {
1009
- this.destroyPopper();
1010
- }
1011
- }
1012
- openCalendar() {
1013
- if (this.disabled || this.readonly || this.isOpen)
1014
- return;
1015
- this.toggleCalendar();
1016
- }
1017
- clickOutside(event) {
1018
- if (!this.rootElement.nativeElement.contains(event.target) && this.isOpen) {
1019
- this.isOpen = false;
1020
- }
1021
- }
1022
- closeCalendar() {
1023
- this.destroyPopper();
1024
- this.isOpen = false;
1025
- if (CalendarControl._currentlyOpen === this) {
1026
- CalendarControl._currentlyOpen = undefined;
1027
- }
1028
- }
1029
- clearDate(event) {
1030
- this.selectionCleared.emit();
1031
- event.stopPropagation();
1032
- this.selectedDate = null;
1033
- this.selectedHour = this.today.getHours();
1034
- this.selectedMinute = this.today.getMinutes();
1035
- this.inputControl.setValue("", { emitEvent: false });
1036
- this.onChangeFn(null);
1037
- this.onTouchedFn();
1038
- this.dateSelected.emit(null);
1039
- }
1040
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: CalendarControl, deps: [{ token: i1.DatePipe }], target: i0.ɵɵFactoryTarget.Component });
1041
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: CalendarControl, isStandalone: true, selector: "calendar-control", inputs: { title: "title", required: "required", customClass: "customClass", clearVal: "clearVal", deFocus: "deFocus", error: "error", errorMessage: "errorMessage", inputLoader: "inputLoader", hourFormat: "hourFormat", selectionMode: "selectionMode", timeOnly: "timeOnly", dateFormat: "dateFormat", placeholder: "placeholder", disabled: "disabled", readonly: "readonly", submitted: "submitted", inputPlaceholder: "inputPlaceholder", closeVal: ["close-val", "closeVal"], showTime: "showTime", minDate: "minDate", maxDate: "maxDate" }, outputs: { selectionCleared: "selectionCleared", blurEvent: "blurEvent", dateSelected: "dateSelected" }, host: { listeners: { "document:click": "clickOutside($event)" } }, providers: [
1042
- DatePipe,
1043
- {
1044
- provide: NG_VALUE_ACCESSOR,
1045
- useExisting: forwardRef(() => CalendarControl),
1046
- multi: true,
1047
- },
1048
- ], viewQueries: [{ propertyName: "rootElement", first: true, predicate: ["root"], descendants: true, static: true }, { propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, static: true }, { propertyName: "datePickerEl", first: true, predicate: ["datePicker"], descendants: true }], ngImport: i0, template: "<div class=\"form-group calendar\" [ngClass]=\"customClass\">\r\n <label class=\"inp-label\" [ngClass]=\"{'required' : required}\" *ngIf=\"title\">{{ title }}</label>\r\n <div class=\"form-group calendar\" #root (click)=\"openCalendar()\" >\r\n <input type=\"text\" #inputEl [placeholder]=\"(inputPlaceholder && placeholder) ? placeholder : ''\"\r\n [formControl]=\"inputControl\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': error }\" (blur)=\"onBlur()\"\r\n (focus)=\"onFocus()\" (click)=\"openCalendar(); $event.stopPropagation()\" (keydown)=\"onInputKeydown($event)\" />\r\n\r\n <span class=\"focus-border\" *ngIf=\"deFocus\"></span>\r\n <span class=\"calendar-icon\">\r\n <i class=\"he\" [ngClass]=\"!timeOnly ? 'he-calendar-blank' : 'he-clock'\"></i>\r\n </span>\r\n <label class=\"clear\" *ngIf=\"!inputLoader && (selectedDate && !disabled) && clearVal\" (click)=\"clearDate($event)\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n <label *ngIf=\"inputLoader\" class=\"loader input-loader\"></label>\r\n <div *ngIf=\"error\" class=\"val-msg\">{{ errorMessage }}</div>\r\n\r\n <div class=\"datepicker-group\" #datePicker *ngIf=\"isOpen\" (click)=\"$event.stopPropagation()\">\r\n \r\n <!-- time picker -->\r\n <ng-container *ngIf=\"timeOnly\">\r\n <div class=\"time-picker\">\r\n <div class=\"time-select\">\r\n <button (click)=\"incrementHour()\"><i class=\"he he-chevron-up\"></i></button>\r\n <ng-container *ngIf=\"hourFormat === '12'; else show24\">\r\n <div class=\"time-value\">\r\n {{ ((selectedHour % 12) || 12) | number:'2.0' }}\r\n </div>\r\n </ng-container>\r\n <ng-template #show24>\r\n <div class=\"time-value\">\r\n {{ selectedHour | number:'2.0' }}\r\n </div>\r\n </ng-template>\r\n\r\n <button (click)=\"decrementHour()\"><i class=\"he he-chevron-down\"></i></button>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-select\">\r\n <button (click)=\"incrementMinute()\"><i class=\"he he-chevron-up\"></i></button>\r\n <div class=\"time-value\">{{ selectedMinute | number:'2.0' }}</div>\r\n <button (click)=\"decrementMinute()\"><i class=\"he he-chevron-down\"></i></button>\r\n </div>\r\n <div class=\"ampm-toggle\" *ngIf=\"hourFormat === '12'\">\r\n <button type=\"button\" [class.active]=\"meridian === 'AM'\" (click)=\"setMeridian('AM')\">AM</button>\r\n <button type=\"button\" [class.active]=\"meridian === 'PM'\" (click)=\"setMeridian('PM')\">PM</button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- day view -->\r\n <ng-container *ngIf=\"!timeOnly && currentView === 'day'\">\r\n <div class=\"header\">\r\n <button class=\"calendar-arrow\" (click)=\"prevMonth()\"><i class=\"he he-chevron-left\"></i></button>\r\n <div class=\"title\" (click)=\"goToMonthView()\">\r\n <div>{{ displayMonthName }}</div>\r\n <div>{{ displayYear }}</div>\r\n </div>\r\n <button class=\"calendar-arrow\" (click)=\"nextMonth()\"><i class=\"he he-chevron-right\"></i></button>\r\n </div>\r\n <div class=\"week-header\">\r\n <div>Sun</div>\r\n <div>Mon</div>\r\n <div>Tue</div>\r\n <div>Wed</div>\r\n <div>Thu</div>\r\n <div>Fri</div>\r\n <div>Sat</div>\r\n </div>\r\n <div class=\"days-grid\">\r\n <div class=\"day-cell\" *ngFor=\"let day of daysInMonth\" (click)=\"selectDay(day)\"\r\n [class.disabled]=\"day !== null && dayClassMap[day].disabled\"\r\n [class.selected]=\"day !== null && dayClassMap[day].selected\" [class.today]=\"\r\n day !== null &&\r\n displayYear === todayYear &&\r\n displayMonth === todayMonth &&\r\n day === todayDate\r\n \">\r\n {{day ? day : ''}}\r\n </div>\r\n </div>\r\n <ng-container *ngIf=\"showTime\">\r\n <div class=\"time-picker\">\r\n <div class=\"time-select\">\r\n <button type=\"button\" (click)=\"incrementHour()\">\r\n <i class=\"he he-chevron-up\"></i>\r\n </button>\r\n <ng-container *ngIf=\"hourFormat === '12'; else show24\">\r\n <div class=\"time-value\">\r\n {{\r\n selectedHour % 12 === 0\r\n ? 12\r\n : selectedHour % 12\r\n | number:'2.0' }}\r\n </div>\r\n </ng-container>\r\n <ng-template #show24>\r\n <div class=\"time-value\">{{ selectedHour | number:'2.0' }}</div>\r\n </ng-template>\r\n <button type=\"button\" (click)=\"decrementHour()\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </button>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-select\">\r\n <button type=\"button\" (click)=\"incrementMinute()\">\r\n <i class=\"he he-chevron-up\"></i>\r\n </button>\r\n <div class=\"time-value\">\r\n {{ selectedMinute < 10 ? '0' +selectedMinute : selectedMinute }} </div>\r\n <button type=\"button\" (click)=\"decrementMinute()\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </button>\r\n </div>\r\n <div class=\"ampm-toggle\" *ngIf=\"hourFormat === '12'\">\r\n <button type=\"button\" [class.active]=\"meridian === 'AM'\" (click)=\"setMeridian('AM')\">AM</button>\r\n <button type=\"button\" [class.active]=\"meridian === 'PM'\" (click)=\"setMeridian('PM')\">PM</button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- month view -->\r\n <ng-container *ngIf=\"!timeOnly && currentView === 'month'\">\r\n <div class=\"header\">\r\n <button class=\"calendar-arrow\" (click)=\"displayYear = displayYear - 1\"><i\r\n class=\"he he-chevron-left\"></i></button>\r\n <div class=\"title\" (click)=\"goToYearRangeView()\">{{ displayYear }}</div>\r\n <button class=\"calendar-arrow\" (click)=\"displayYear = displayYear + 1\"><i\r\n class=\"he he-chevron-right\"></i></button>\r\n </div>\r\n <div class=\"month-grid\">\r\n <div class=\"month-cell\" *ngFor=\"let m of months; index as i\" (click)=\"selectMonth(i)\">{{ m }}\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- year range view -->\r\n <ng-container *ngIf=\"!timeOnly && currentView === 'yearRange'\">\r\n <div class=\"header\">\r\n <button class=\"calendar-arrow\" (click)=\"prevYearRange()\"><i class=\"he he-chevron-left\"></i></button>\r\n <div class=\"title\">{{ yearRange[0] }} ~ {{ yearRange[yearRangeSize-1] }}</div>\r\n <button class=\"calendar-arrow\" (click)=\"nextYearRange()\"><i class=\"he he-chevron-right\"></i></button>\r\n </div>\r\n <div class=\"year-grid\">\r\n <div class=\"year-cell\" *ngFor=\"let y of yearRange\" (click)=\"selectYear(y)\">{{ y }}</div>\r\n </div>\r\n </ng-container>\r\n\r\n </div>\r\n </div>\r\n</div>", styles: [".form-group.calendar{position:relative}.form-group.calendar .clear{top:9px;right:34px}.form-group.calendar .datepicker-group{left:0;width:100%;z-index:1000;padding:.5rem;color:#495057;min-width:240px;border-radius:3px;background:#fff;box-shadow:0 2px 4px -1px #0003,0 4px 5px #00000024,0 1px 10px #0000001f}.form-group.calendar .datepicker-group .header{display:flex;font-weight:600;padding:5px;align-items:center;justify-content:space-between;border-bottom:1px solid #dee2e6}.form-group.calendar .datepicker-group .header .title{gap:8px;display:flex;font-size:15px;cursor:pointer;font-weight:600}.form-group.calendar .datepicker-group .header .calendar-arrow{width:28px;height:28px;border:none;line-height:1;cursor:pointer;background:none}.form-group.calendar .datepicker-group .header .calendar-arrow i{font-size:14px}.form-group.calendar .datepicker-group .header .calendar-arrow:hover{background:#f0f0f0}.form-group.calendar .datepicker-group .week-header{display:grid;padding:8px 0;font-size:13px;font-weight:700;text-align:center;grid-template-columns:repeat(7,1fr)}.form-group.calendar .datepicker-group .days-grid,.form-group.calendar .datepicker-group .month-grid,.form-group.calendar .datepicker-group .year-grid{gap:3px;padding:5px;display:grid;grid-template-columns:repeat(7,1fr)}.form-group.calendar .datepicker-group .days-grid .day-cell,.form-group.calendar .datepicker-group .days-grid .month-cell,.form-group.calendar .datepicker-group .days-grid .year-cell,.form-group.calendar .datepicker-group .month-grid .day-cell,.form-group.calendar .datepicker-group .month-grid .month-cell,.form-group.calendar .datepicker-group .month-grid .year-cell,.form-group.calendar .datepicker-group .year-grid .day-cell,.form-group.calendar .datepicker-group .year-grid .month-cell,.form-group.calendar .datepicker-group .year-grid .year-cell{display:flex;font-size:14px;cursor:pointer;align-items:center;justify-content:center}.form-group.calendar .datepicker-group .days-grid .day-cell.today,.form-group.calendar .datepicker-group .days-grid .month-cell.today,.form-group.calendar .datepicker-group .days-grid .year-cell.today,.form-group.calendar .datepicker-group .month-grid .day-cell.today,.form-group.calendar .datepicker-group .month-grid .month-cell.today,.form-group.calendar .datepicker-group .month-grid .year-cell.today,.form-group.calendar .datepicker-group .year-grid .day-cell.today,.form-group.calendar .datepicker-group .year-grid .month-cell.today,.form-group.calendar .datepicker-group .year-grid .year-cell.today{color:#37c0b3;font-weight:700;border:1px solid #37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.selected,.form-group.calendar .datepicker-group .days-grid .day-cell:hover,.form-group.calendar .datepicker-group .days-grid .month-cell.selected,.form-group.calendar .datepicker-group .days-grid .month-cell:hover,.form-group.calendar .datepicker-group .days-grid .year-cell.selected,.form-group.calendar .datepicker-group .days-grid .year-cell:hover,.form-group.calendar .datepicker-group .month-grid .day-cell.selected,.form-group.calendar .datepicker-group .month-grid .day-cell:hover,.form-group.calendar .datepicker-group .month-grid .month-cell.selected,.form-group.calendar .datepicker-group .month-grid .month-cell:hover,.form-group.calendar .datepicker-group .month-grid .year-cell.selected,.form-group.calendar .datepicker-group .month-grid .year-cell:hover,.form-group.calendar .datepicker-group .year-grid .day-cell.selected,.form-group.calendar .datepicker-group .year-grid .day-cell:hover,.form-group.calendar .datepicker-group .year-grid .month-cell.selected,.form-group.calendar .datepicker-group .year-grid .month-cell:hover,.form-group.calendar .datepicker-group .year-grid .year-cell.selected,.form-group.calendar .datepicker-group .year-grid .year-cell:hover{color:#fff;background:#37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled{color:#e0e0e0;background:none;-webkit-user-select:none;user-select:none;cursor:not-allowed}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled.today,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled.today,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled.today,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled.today,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled.today,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled.today,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled.today,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled.today,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled.today{color:#37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled.today:hover,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled.today:hover,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled.today:hover,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled.today:hover,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled.today:hover,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled.today:hover,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled.today:hover,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled.today:hover,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled.today:hover{color:#fff;background:#37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled:hover,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled:hover,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled:hover,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled:hover,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled:hover,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled:hover,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled:hover,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled:hover,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled:hover{color:#e0e0e0;background:transparent}.form-group.calendar .datepicker-group .days-grid .day-cell,.form-group.calendar .datepicker-group .month-grid .day-cell,.form-group.calendar .datepicker-group .year-grid .day-cell{padding:5px 2px;border-radius:50%}.form-group.calendar .datepicker-group .days-grid .month-cell,.form-group.calendar .datepicker-group .days-grid .year-cell,.form-group.calendar .datepicker-group .month-grid .month-cell,.form-group.calendar .datepicker-group .month-grid .year-cell,.form-group.calendar .datepicker-group .year-grid .month-cell,.form-group.calendar .datepicker-group .year-grid .year-cell{padding:8px 2px;border-radius:3px}.form-group.calendar .datepicker-group .month-grid{grid-template-columns:repeat(3,1fr)}.form-group.calendar .datepicker-group .year-grid{grid-template-columns:repeat(4,1fr)}.form-group.calendar .datepicker-group .time-picker{display:flex;-webkit-user-select:none;user-select:none;align-items:center;justify-content:center}.form-group.calendar .datepicker-group .time-picker .ampm-toggle{gap:6px;display:flex;margin-left:1rem;flex-direction:column;justify-content:center}.form-group.calendar .datepicker-group .time-picker .ampm-toggle button{cursor:pointer;padding:3px 8px;font-size:.75rem;background:#fff;border:1px solid #cccccc}.form-group.calendar .datepicker-group .time-picker .ampm-toggle button.active{color:#fff;border-color:#37c0b3;background-color:#37c0b3}.form-group.calendar .datepicker-group .time-picker .time-select{display:flex;margin:0 .75rem;align-items:center;flex-direction:column}.form-group.calendar .datepicker-group .time-picker .time-select button{border:none;cursor:pointer;padding:4px 8px;background:transparent}.form-group.calendar .datepicker-group .time-picker .time-select button i{font-size:1rem}.form-group.calendar .datepicker-group .time-picker .time-select button:hover{background:#f0f0f0}.form-group.calendar .datepicker-group .time-picker .time-select .time-value{width:2rem;font-size:1rem;text-align:center;margin:2px 3px 4px}.form-group.calendar .datepicker-group .time-picker .time-separator{line-height:1;font-size:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] });
1049
- }
1050
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: CalendarControl, decorators: [{
1051
- type: Component,
1052
- args: [{ selector: "calendar-control", standalone: true, imports: [
1053
- CommonModule,
1054
- ReactiveFormsModule,
1055
- FormsModule
1056
- ], providers: [
1057
- DatePipe,
1058
- {
1059
- provide: NG_VALUE_ACCESSOR,
1060
- useExisting: forwardRef(() => CalendarControl),
1061
- multi: true,
1062
- },
1063
- ], template: "<div class=\"form-group calendar\" [ngClass]=\"customClass\">\r\n <label class=\"inp-label\" [ngClass]=\"{'required' : required}\" *ngIf=\"title\">{{ title }}</label>\r\n <div class=\"form-group calendar\" #root (click)=\"openCalendar()\" >\r\n <input type=\"text\" #inputEl [placeholder]=\"(inputPlaceholder && placeholder) ? placeholder : ''\"\r\n [formControl]=\"inputControl\" class=\"form-control\" [ngClass]=\"{ 'is-invalid': error }\" (blur)=\"onBlur()\"\r\n (focus)=\"onFocus()\" (click)=\"openCalendar(); $event.stopPropagation()\" (keydown)=\"onInputKeydown($event)\" />\r\n\r\n <span class=\"focus-border\" *ngIf=\"deFocus\"></span>\r\n <span class=\"calendar-icon\">\r\n <i class=\"he\" [ngClass]=\"!timeOnly ? 'he-calendar-blank' : 'he-clock'\"></i>\r\n </span>\r\n <label class=\"clear\" *ngIf=\"!inputLoader && (selectedDate && !disabled) && clearVal\" (click)=\"clearDate($event)\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n <label *ngIf=\"inputLoader\" class=\"loader input-loader\"></label>\r\n <div *ngIf=\"error\" class=\"val-msg\">{{ errorMessage }}</div>\r\n\r\n <div class=\"datepicker-group\" #datePicker *ngIf=\"isOpen\" (click)=\"$event.stopPropagation()\">\r\n \r\n <!-- time picker -->\r\n <ng-container *ngIf=\"timeOnly\">\r\n <div class=\"time-picker\">\r\n <div class=\"time-select\">\r\n <button (click)=\"incrementHour()\"><i class=\"he he-chevron-up\"></i></button>\r\n <ng-container *ngIf=\"hourFormat === '12'; else show24\">\r\n <div class=\"time-value\">\r\n {{ ((selectedHour % 12) || 12) | number:'2.0' }}\r\n </div>\r\n </ng-container>\r\n <ng-template #show24>\r\n <div class=\"time-value\">\r\n {{ selectedHour | number:'2.0' }}\r\n </div>\r\n </ng-template>\r\n\r\n <button (click)=\"decrementHour()\"><i class=\"he he-chevron-down\"></i></button>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-select\">\r\n <button (click)=\"incrementMinute()\"><i class=\"he he-chevron-up\"></i></button>\r\n <div class=\"time-value\">{{ selectedMinute | number:'2.0' }}</div>\r\n <button (click)=\"decrementMinute()\"><i class=\"he he-chevron-down\"></i></button>\r\n </div>\r\n <div class=\"ampm-toggle\" *ngIf=\"hourFormat === '12'\">\r\n <button type=\"button\" [class.active]=\"meridian === 'AM'\" (click)=\"setMeridian('AM')\">AM</button>\r\n <button type=\"button\" [class.active]=\"meridian === 'PM'\" (click)=\"setMeridian('PM')\">PM</button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- day view -->\r\n <ng-container *ngIf=\"!timeOnly && currentView === 'day'\">\r\n <div class=\"header\">\r\n <button class=\"calendar-arrow\" (click)=\"prevMonth()\"><i class=\"he he-chevron-left\"></i></button>\r\n <div class=\"title\" (click)=\"goToMonthView()\">\r\n <div>{{ displayMonthName }}</div>\r\n <div>{{ displayYear }}</div>\r\n </div>\r\n <button class=\"calendar-arrow\" (click)=\"nextMonth()\"><i class=\"he he-chevron-right\"></i></button>\r\n </div>\r\n <div class=\"week-header\">\r\n <div>Sun</div>\r\n <div>Mon</div>\r\n <div>Tue</div>\r\n <div>Wed</div>\r\n <div>Thu</div>\r\n <div>Fri</div>\r\n <div>Sat</div>\r\n </div>\r\n <div class=\"days-grid\">\r\n <div class=\"day-cell\" *ngFor=\"let day of daysInMonth\" (click)=\"selectDay(day)\"\r\n [class.disabled]=\"day !== null && dayClassMap[day].disabled\"\r\n [class.selected]=\"day !== null && dayClassMap[day].selected\" [class.today]=\"\r\n day !== null &&\r\n displayYear === todayYear &&\r\n displayMonth === todayMonth &&\r\n day === todayDate\r\n \">\r\n {{day ? day : ''}}\r\n </div>\r\n </div>\r\n <ng-container *ngIf=\"showTime\">\r\n <div class=\"time-picker\">\r\n <div class=\"time-select\">\r\n <button type=\"button\" (click)=\"incrementHour()\">\r\n <i class=\"he he-chevron-up\"></i>\r\n </button>\r\n <ng-container *ngIf=\"hourFormat === '12'; else show24\">\r\n <div class=\"time-value\">\r\n {{\r\n selectedHour % 12 === 0\r\n ? 12\r\n : selectedHour % 12\r\n | number:'2.0' }}\r\n </div>\r\n </ng-container>\r\n <ng-template #show24>\r\n <div class=\"time-value\">{{ selectedHour | number:'2.0' }}</div>\r\n </ng-template>\r\n <button type=\"button\" (click)=\"decrementHour()\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </button>\r\n </div>\r\n <span class=\"time-separator\">:</span>\r\n <div class=\"time-select\">\r\n <button type=\"button\" (click)=\"incrementMinute()\">\r\n <i class=\"he he-chevron-up\"></i>\r\n </button>\r\n <div class=\"time-value\">\r\n {{ selectedMinute < 10 ? '0' +selectedMinute : selectedMinute }} </div>\r\n <button type=\"button\" (click)=\"decrementMinute()\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </button>\r\n </div>\r\n <div class=\"ampm-toggle\" *ngIf=\"hourFormat === '12'\">\r\n <button type=\"button\" [class.active]=\"meridian === 'AM'\" (click)=\"setMeridian('AM')\">AM</button>\r\n <button type=\"button\" [class.active]=\"meridian === 'PM'\" (click)=\"setMeridian('PM')\">PM</button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <!-- month view -->\r\n <ng-container *ngIf=\"!timeOnly && currentView === 'month'\">\r\n <div class=\"header\">\r\n <button class=\"calendar-arrow\" (click)=\"displayYear = displayYear - 1\"><i\r\n class=\"he he-chevron-left\"></i></button>\r\n <div class=\"title\" (click)=\"goToYearRangeView()\">{{ displayYear }}</div>\r\n <button class=\"calendar-arrow\" (click)=\"displayYear = displayYear + 1\"><i\r\n class=\"he he-chevron-right\"></i></button>\r\n </div>\r\n <div class=\"month-grid\">\r\n <div class=\"month-cell\" *ngFor=\"let m of months; index as i\" (click)=\"selectMonth(i)\">{{ m }}\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- year range view -->\r\n <ng-container *ngIf=\"!timeOnly && currentView === 'yearRange'\">\r\n <div class=\"header\">\r\n <button class=\"calendar-arrow\" (click)=\"prevYearRange()\"><i class=\"he he-chevron-left\"></i></button>\r\n <div class=\"title\">{{ yearRange[0] }} ~ {{ yearRange[yearRangeSize-1] }}</div>\r\n <button class=\"calendar-arrow\" (click)=\"nextYearRange()\"><i class=\"he he-chevron-right\"></i></button>\r\n </div>\r\n <div class=\"year-grid\">\r\n <div class=\"year-cell\" *ngFor=\"let y of yearRange\" (click)=\"selectYear(y)\">{{ y }}</div>\r\n </div>\r\n </ng-container>\r\n\r\n </div>\r\n </div>\r\n</div>", styles: [".form-group.calendar{position:relative}.form-group.calendar .clear{top:9px;right:34px}.form-group.calendar .datepicker-group{left:0;width:100%;z-index:1000;padding:.5rem;color:#495057;min-width:240px;border-radius:3px;background:#fff;box-shadow:0 2px 4px -1px #0003,0 4px 5px #00000024,0 1px 10px #0000001f}.form-group.calendar .datepicker-group .header{display:flex;font-weight:600;padding:5px;align-items:center;justify-content:space-between;border-bottom:1px solid #dee2e6}.form-group.calendar .datepicker-group .header .title{gap:8px;display:flex;font-size:15px;cursor:pointer;font-weight:600}.form-group.calendar .datepicker-group .header .calendar-arrow{width:28px;height:28px;border:none;line-height:1;cursor:pointer;background:none}.form-group.calendar .datepicker-group .header .calendar-arrow i{font-size:14px}.form-group.calendar .datepicker-group .header .calendar-arrow:hover{background:#f0f0f0}.form-group.calendar .datepicker-group .week-header{display:grid;padding:8px 0;font-size:13px;font-weight:700;text-align:center;grid-template-columns:repeat(7,1fr)}.form-group.calendar .datepicker-group .days-grid,.form-group.calendar .datepicker-group .month-grid,.form-group.calendar .datepicker-group .year-grid{gap:3px;padding:5px;display:grid;grid-template-columns:repeat(7,1fr)}.form-group.calendar .datepicker-group .days-grid .day-cell,.form-group.calendar .datepicker-group .days-grid .month-cell,.form-group.calendar .datepicker-group .days-grid .year-cell,.form-group.calendar .datepicker-group .month-grid .day-cell,.form-group.calendar .datepicker-group .month-grid .month-cell,.form-group.calendar .datepicker-group .month-grid .year-cell,.form-group.calendar .datepicker-group .year-grid .day-cell,.form-group.calendar .datepicker-group .year-grid .month-cell,.form-group.calendar .datepicker-group .year-grid .year-cell{display:flex;font-size:14px;cursor:pointer;align-items:center;justify-content:center}.form-group.calendar .datepicker-group .days-grid .day-cell.today,.form-group.calendar .datepicker-group .days-grid .month-cell.today,.form-group.calendar .datepicker-group .days-grid .year-cell.today,.form-group.calendar .datepicker-group .month-grid .day-cell.today,.form-group.calendar .datepicker-group .month-grid .month-cell.today,.form-group.calendar .datepicker-group .month-grid .year-cell.today,.form-group.calendar .datepicker-group .year-grid .day-cell.today,.form-group.calendar .datepicker-group .year-grid .month-cell.today,.form-group.calendar .datepicker-group .year-grid .year-cell.today{color:#37c0b3;font-weight:700;border:1px solid #37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.selected,.form-group.calendar .datepicker-group .days-grid .day-cell:hover,.form-group.calendar .datepicker-group .days-grid .month-cell.selected,.form-group.calendar .datepicker-group .days-grid .month-cell:hover,.form-group.calendar .datepicker-group .days-grid .year-cell.selected,.form-group.calendar .datepicker-group .days-grid .year-cell:hover,.form-group.calendar .datepicker-group .month-grid .day-cell.selected,.form-group.calendar .datepicker-group .month-grid .day-cell:hover,.form-group.calendar .datepicker-group .month-grid .month-cell.selected,.form-group.calendar .datepicker-group .month-grid .month-cell:hover,.form-group.calendar .datepicker-group .month-grid .year-cell.selected,.form-group.calendar .datepicker-group .month-grid .year-cell:hover,.form-group.calendar .datepicker-group .year-grid .day-cell.selected,.form-group.calendar .datepicker-group .year-grid .day-cell:hover,.form-group.calendar .datepicker-group .year-grid .month-cell.selected,.form-group.calendar .datepicker-group .year-grid .month-cell:hover,.form-group.calendar .datepicker-group .year-grid .year-cell.selected,.form-group.calendar .datepicker-group .year-grid .year-cell:hover{color:#fff;background:#37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled{color:#e0e0e0;background:none;-webkit-user-select:none;user-select:none;cursor:not-allowed}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled.today,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled.today,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled.today,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled.today,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled.today,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled.today,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled.today,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled.today,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled.today{color:#37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled.today:hover,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled.today:hover,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled.today:hover,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled.today:hover,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled.today:hover,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled.today:hover,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled.today:hover,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled.today:hover,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled.today:hover{color:#fff;background:#37c0b3}.form-group.calendar .datepicker-group .days-grid .day-cell.disabled:hover,.form-group.calendar .datepicker-group .days-grid .month-cell.disabled:hover,.form-group.calendar .datepicker-group .days-grid .year-cell.disabled:hover,.form-group.calendar .datepicker-group .month-grid .day-cell.disabled:hover,.form-group.calendar .datepicker-group .month-grid .month-cell.disabled:hover,.form-group.calendar .datepicker-group .month-grid .year-cell.disabled:hover,.form-group.calendar .datepicker-group .year-grid .day-cell.disabled:hover,.form-group.calendar .datepicker-group .year-grid .month-cell.disabled:hover,.form-group.calendar .datepicker-group .year-grid .year-cell.disabled:hover{color:#e0e0e0;background:transparent}.form-group.calendar .datepicker-group .days-grid .day-cell,.form-group.calendar .datepicker-group .month-grid .day-cell,.form-group.calendar .datepicker-group .year-grid .day-cell{padding:5px 2px;border-radius:50%}.form-group.calendar .datepicker-group .days-grid .month-cell,.form-group.calendar .datepicker-group .days-grid .year-cell,.form-group.calendar .datepicker-group .month-grid .month-cell,.form-group.calendar .datepicker-group .month-grid .year-cell,.form-group.calendar .datepicker-group .year-grid .month-cell,.form-group.calendar .datepicker-group .year-grid .year-cell{padding:8px 2px;border-radius:3px}.form-group.calendar .datepicker-group .month-grid{grid-template-columns:repeat(3,1fr)}.form-group.calendar .datepicker-group .year-grid{grid-template-columns:repeat(4,1fr)}.form-group.calendar .datepicker-group .time-picker{display:flex;-webkit-user-select:none;user-select:none;align-items:center;justify-content:center}.form-group.calendar .datepicker-group .time-picker .ampm-toggle{gap:6px;display:flex;margin-left:1rem;flex-direction:column;justify-content:center}.form-group.calendar .datepicker-group .time-picker .ampm-toggle button{cursor:pointer;padding:3px 8px;font-size:.75rem;background:#fff;border:1px solid #cccccc}.form-group.calendar .datepicker-group .time-picker .ampm-toggle button.active{color:#fff;border-color:#37c0b3;background-color:#37c0b3}.form-group.calendar .datepicker-group .time-picker .time-select{display:flex;margin:0 .75rem;align-items:center;flex-direction:column}.form-group.calendar .datepicker-group .time-picker .time-select button{border:none;cursor:pointer;padding:4px 8px;background:transparent}.form-group.calendar .datepicker-group .time-picker .time-select button i{font-size:1rem}.form-group.calendar .datepicker-group .time-picker .time-select button:hover{background:#f0f0f0}.form-group.calendar .datepicker-group .time-picker .time-select .time-value{width:2rem;font-size:1rem;text-align:center;margin:2px 3px 4px}.form-group.calendar .datepicker-group .time-picker .time-separator{line-height:1;font-size:1rem}\n"] }]
1064
- }], ctorParameters: () => [{ type: i1.DatePipe }], propDecorators: { title: [{
1065
- type: Input
1066
- }], required: [{
1067
- type: Input
1068
- }], customClass: [{
1069
- type: Input
1070
- }], clearVal: [{
1071
- type: Input
1072
- }], deFocus: [{
1073
- type: Input
1074
- }], error: [{
1075
- type: Input
1076
- }], errorMessage: [{
1077
- type: Input
1078
- }], inputLoader: [{
1079
- type: Input
1080
- }], hourFormat: [{
1081
- type: Input
1082
- }], selectionMode: [{
1083
- type: Input
1084
- }], timeOnly: [{
1085
- type: Input
1086
- }], dateFormat: [{
1087
- type: Input
1088
- }], placeholder: [{
1089
- type: Input
1090
- }], disabled: [{
1091
- type: Input
1092
- }], readonly: [{
1093
- type: Input
1094
- }], submitted: [{
1095
- type: Input
1096
- }], inputPlaceholder: [{
1097
- type: Input
1098
- }], closeVal: [{
1099
- type: Input,
1100
- args: ["close-val"]
1101
- }], showTime: [{
1102
- type: Input
1103
- }], selectionCleared: [{
1104
- type: Output
1105
- }], blurEvent: [{
1106
- type: Output
1107
- }], dateSelected: [{
1108
- type: Output
1109
- }], minDate: [{
1110
- type: Input
1111
- }], maxDate: [{
1112
- type: Input
1113
- }], rootElement: [{
1114
- type: ViewChild,
1115
- args: ["root", { static: true }]
1116
- }], inputEl: [{
1117
- type: ViewChild,
1118
- args: ["inputEl", { static: true }]
1119
- }], datePickerEl: [{
1120
- type: ViewChild,
1121
- args: ["datePicker", { static: false }]
1122
- }], clickOutside: [{
1123
- type: HostListener,
1124
- args: ["document:click", ["$event"]]
1125
- }] } });
1126
-
1127
- class CheckboxControl {
1128
- _renderer;
1129
- _elementRef;
1130
- title;
1131
- customClass;
1132
- type = 'boolean';
1133
- set disabled(value) { this._disabled = value; this.setDisabledState(value); }
1134
- checkboxChange = new EventEmitter();
1135
- blurEvent = new EventEmitter();
1136
- checkboxContainer;
1137
- _disabled = false;
1138
- checkboxControl = new FormControl({ value: false, disabled: this._disabled });
1139
- onChange = () => { };
1140
- onTouched = () => { };
1141
- constructor(_renderer, _elementRef) {
1142
- this._renderer = _renderer;
1143
- this._elementRef = _elementRef;
1144
- this.checkboxControl.valueChanges.subscribe(value => {
1145
- const converted = this.convertType(value);
1146
- this.onChange(converted);
1147
- this.onTouched();
1148
- this.checkboxChange.emit(converted);
1149
- });
1150
- }
1151
- writeValue(value) {
1152
- if (value !== undefined && value !== null) {
1153
- this.checkboxControl.setValue(this.convertType(value), { emitEvent: false });
1154
- }
1155
- }
1156
- registerOnChange(fn) {
1157
- this.onChange = fn;
1158
- }
1159
- registerOnTouched(fn) {
1160
- this.onTouched = fn;
1161
- }
1162
- convertType(value) {
1163
- switch (this.type) {
1164
- case 'number':
1165
- return Number(value == 2 ? 0 : value == 0 ? 2 : value);
1166
- case 'string':
1167
- return String(value);
1168
- default:
1169
- return !!value;
1170
- }
1171
- }
1172
- // events
1173
- onFocus(event) {
1174
- const targetElement = event.target;
1175
- if (targetElement.matches(':focus-visible')) {
1176
- this.checkboxContainer.nativeElement.classList.add('focused');
1177
- }
1178
- }
1179
- onBlur() {
1180
- this.blurEvent.emit();
1181
- this.checkboxContainer.nativeElement.classList.remove('focused');
1182
- }
1183
- setDisabledState(isDisabled) {
1184
- this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
1185
- if (isDisabled) {
1186
- this.checkboxControl.disable();
1187
- }
1188
- else {
1189
- this.checkboxControl.enable();
1190
- }
1191
- }
1192
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: CheckboxControl, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1193
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: CheckboxControl, isStandalone: true, selector: "checkbox-control", inputs: { title: "title", customClass: "customClass", type: "type", disabled: "disabled" }, outputs: { checkboxChange: "checkboxChange", blurEvent: "blurEvent" }, providers: [
1194
- {
1195
- provide: NG_VALUE_ACCESSOR,
1196
- useExisting: forwardRef(() => CheckboxControl),
1197
- multi: true,
1198
- },
1199
- ], viewQueries: [{ propertyName: "checkboxContainer", first: true, predicate: ["checkboxContainer"], descendants: true }], ngImport: i0, template: "<label class=\"checkbox-container\" [ngClass]=\"customClass\" #checkboxContainer>\r\n <input type=\"checkbox\" [formControl]=\"checkboxControl\" (focus)=\"onFocus($event)\" (blur)=\"onBlur()\" />\r\n <span class=\"checkmark\"></span>{{ title }}\r\n</label>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }] });
1200
- }
1201
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: CheckboxControl, decorators: [{
1202
- type: Component,
1203
- args: [{ selector: 'checkbox-control', standalone: true, imports: [
1204
- CommonModule,
1205
- ReactiveFormsModule,
1206
- FormsModule
1207
- ], providers: [
1208
- {
1209
- provide: NG_VALUE_ACCESSOR,
1210
- useExisting: forwardRef(() => CheckboxControl),
1211
- multi: true,
1212
- },
1213
- ], template: "<label class=\"checkbox-container\" [ngClass]=\"customClass\" #checkboxContainer>\r\n <input type=\"checkbox\" [formControl]=\"checkboxControl\" (focus)=\"onFocus($event)\" (blur)=\"onBlur()\" />\r\n <span class=\"checkmark\"></span>{{ title }}\r\n</label>" }]
1214
- }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }], propDecorators: { title: [{
1215
- type: Input
1216
- }], customClass: [{
1217
- type: Input
1218
- }], type: [{
1219
- type: Input
1220
- }], disabled: [{
1221
- type: Input
1222
- }], checkboxChange: [{
1223
- type: Output
1224
- }], blurEvent: [{
1225
- type: Output
1226
- }], checkboxContainer: [{
1227
- type: ViewChild,
1228
- args: ['checkboxContainer']
1229
- }] } });
1230
-
1231
- class InputControl {
1232
- type = "text";
1233
- title;
1234
- placeholder = "";
1235
- customClass;
1236
- clearVal = true;
1237
- required = false;
1238
- deFocus = true;
1239
- error = false;
1240
- errorMessage = "";
1241
- autocomplete = '';
1242
- readonly = false;
1243
- search = false;
1244
- price = false;
1245
- maxlength;
1246
- ngModel;
1247
- advSearch = false;
1248
- inputLoader = false;
1249
- useReactiveForms = true;
1250
- blurEvent = new EventEmitter();
1251
- selectionCleared = new EventEmitter();
1252
- inputChange = new EventEmitter();
1253
- ngModelChange = new EventEmitter();
1254
- searchClicked = new EventEmitter();
1255
- advSearchClicked = new EventEmitter();
1256
- _disabled = false;
1257
- set disabled(isDisabled) {
1258
- this._disabled = isDisabled;
1259
- this.setDisabledState(isDisabled);
1260
- }
1261
- get disabled() {
1262
- return this._disabled;
1263
- }
1264
- valueChangesSubscription;
1265
- onChange = () => { };
1266
- onTouched = () => { };
1267
- isFocused = false;
1268
- showPassword = false;
1269
- inputControl = new FormControl("");
1270
- ngOnInit() {
1271
- if (this.useReactiveForms) {
1272
- this.valueChangesSubscription = this.inputControl.valueChanges.subscribe((value) => {
1273
- this.onChange(value);
1274
- this.inputChange.emit(value ?? "");
1275
- });
1276
- }
1277
- }
1278
- ngOnDestroy() {
1279
- if (this.valueChangesSubscription) {
1280
- this.valueChangesSubscription.unsubscribe();
1281
- }
1282
- }
1283
- // input type
1284
- getInputType() {
1285
- return this.type === "password" && this.showPassword ? "text" : this.type;
1286
- }
1287
- writeValue(value) {
1288
- if (this.useReactiveForms) {
1289
- if (value !== undefined) {
1290
- this.inputControl.setValue(value, {
1291
- emitEvent: false,
1292
- });
1293
- if (value != '') {
1294
- this.onChange(value);
1295
- }
1296
- }
1297
- if (this.inputControl.disabled !== this.disabled) {
1298
- this.setDisabledState(this.disabled);
1299
- }
1300
- }
1301
- else {
1302
- this.ngModel = value;
1303
- }
1304
- }
1305
- registerOnChange(fn) {
1306
- this.onChange = fn;
1307
- }
1308
- registerOnTouched(fn) {
1309
- this.onTouched = fn;
1310
- }
1311
- // events
1312
- onBlur() {
1313
- this.blurEvent.emit();
1314
- setTimeout(() => {
1315
- this.isFocused = false;
1316
- this.onTouched();
1317
- if (!this.useReactiveForms) {
1318
- this.ngModelChange.emit(this.ngModel);
1319
- }
1320
- }, 200);
1321
- }
1322
- onFocus() {
1323
- this.isFocused = true;
1324
- }
1325
- onKeyPress(event) {
1326
- let allowedKeys = [];
1327
- switch (this.type) {
1328
- case "number":
1329
- allowedKeys = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
1330
- break;
1331
- case "decimal":
1332
- allowedKeys = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."];
1333
- break;
1334
- default:
1335
- break;
1336
- }
1337
- if (this.search && event.key == "Enter") {
1338
- this.onSearchClick();
1339
- }
1340
- if ((this.type == "number" || this.type == "decimal") &&
1341
- !allowedKeys.includes(event.key)) {
1342
- event.preventDefault();
1343
- return;
1344
- }
1345
- if (this.maxlength != null && (this.type == "number" || this.type == "decimal")) {
1346
- const currentValue = this.inputControl.value ?? "";
1347
- if (currentValue.length >= this.maxlength) {
1348
- event.preventDefault();
1349
- return;
1350
- }
1351
- }
1352
- }
1353
- setDisabledState(isDisabled) {
1354
- this._disabled = isDisabled;
1355
- if (this._disabled && this.useReactiveForms) {
1356
- this.inputControl.disable({
1357
- emitEvent: false,
1358
- });
1359
- }
1360
- else if (this.useReactiveForms) {
1361
- this.inputControl.enable({
1362
- emitEvent: false,
1363
- });
1364
- }
1365
- }
1366
- // actions
1367
- passwordVisible() {
1368
- this.showPassword = !this.showPassword;
1369
- }
1370
- onSearchClick() {
1371
- this.searchClicked.emit();
1372
- }
1373
- onAdvSearchClick() {
1374
- this.advSearchClicked.emit();
1375
- }
1376
- // clear
1377
- showClearButton() {
1378
- return (this.isFocused &&
1379
- this.clearVal &&
1380
- this.inputControl.value &&
1381
- this.type !== "password");
1382
- }
1383
- resetInput() {
1384
- this.selectionCleared.emit();
1385
- if (this.useReactiveForms) {
1386
- this.inputControl.setValue("");
1387
- this.inputControl.markAsPristine({ onlySelf: true });
1388
- }
1389
- else {
1390
- this.ngModel = "";
1391
- this.ngModelChange.emit(this.ngModel);
1392
- }
1393
- }
1394
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: InputControl, deps: [], target: i0.ɵɵFactoryTarget.Component });
1395
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: InputControl, isStandalone: true, selector: "input-control", inputs: { type: "type", title: "title", placeholder: "placeholder", customClass: "customClass", clearVal: "clearVal", required: "required", deFocus: "deFocus", error: "error", errorMessage: "errorMessage", autocomplete: "autocomplete", readonly: "readonly", search: "search", price: "price", maxlength: "maxlength", ngModel: "ngModel", advSearch: "advSearch", inputLoader: "inputLoader", useReactiveForms: "useReactiveForms", disabled: "disabled" }, outputs: { blurEvent: "blurEvent", selectionCleared: "selectionCleared", inputChange: "inputChange", ngModelChange: "ngModelChange", searchClicked: "searchClicked", advSearchClicked: "advSearchClicked" }, providers: [
1396
- {
1397
- provide: NG_VALUE_ACCESSOR,
1398
- useExisting: forwardRef(() => InputControl),
1399
- multi: true,
1400
- },
1401
- ], ngImport: i0, template: "<div class=\"form-group input\" [ngClass]=\"customClass\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n @if (useReactiveForms) {\r\n <input [type]=\"getInputType()\" class=\"form-control\" [placeholder]=\"placeholder\" [formControl]=\"inputControl\"\r\n [ngClass]=\"{ 'is-invalid': error }\" (focus)=\"onFocus()\" (blur)=\"onBlur()\" [readonly]=\"readonly\"\r\n (keypress)=\"onKeyPress($event)\" [attr.autocomplete]=\"autocomplete || null\" />\r\n } @else {\r\n <input [type]=\"getInputType()\" class=\"form-control\" [placeholder]=\"placeholder\" [(ngModel)]=\"ngModel\"\r\n [ngClass]=\"{ 'is-invalid': error }\" (focus)=\"onFocus()\" (blur)=\"onBlur()\" [readonly]=\"readonly\"\r\n (keypress)=\"onKeyPress($event)\" />\r\n }\r\n @if(deFocus){\r\n <span class=\"focus-border\"></span>\r\n }\r\n @if (!inputLoader && showClearButton()) {\r\n <label class=\"clear\" (click)=\"resetInput()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n\r\n @if (price) {\r\n <label class=\"ls-price\">\r\n <i class=\"he he-rupee\"></i>\r\n </label>\r\n }\r\n\r\n @if (type === 'search') {\r\n <label class=\"ls-search\">\r\n <i class=\"he he-search\"></i>\r\n </label>\r\n }\r\n\r\n @if (advSearch) {\r\n <label class=\"rs-search\" (click)=\"onAdvSearchClick()\">\r\n <i class=\"he he-search-adv\"></i>\r\n </label>\r\n }\r\n\r\n @if (search) {\r\n <label class=\"rs-search\" (click)=\"onSearchClick()\">\r\n <i class=\"he he-search\"></i>\r\n </label>\r\n }\r\n\r\n @if (type === 'password' && !disabled) {\r\n <label class=\"toggle-eye\" (click)=\"passwordVisible()\">\r\n <i class=\"he\" [ngClass]=\"showPassword ? 'he-eye-off' : 'he-eye'\"></i>\r\n </label>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group .toggle-eye,.form-group ::ng-deep .toggle-eye{top:28px;right:10px;padding:5px;line-height:1;position:absolute}.form-group .toggle-eye .he,.form-group ::ng-deep .toggle-eye .he{font-size:18px}.form-group .toggle-eye .rb,.form-group ::ng-deep .toggle-eye .rb{font-size:17px;color:#5f6554}.form-group.no-label .toggle-eye{top:9px}.form-group.group-sm .toggle-eye{top:5px}.form-group.left-input-search .ls-search{top:9px;left:10px;position:absolute}.form-group.left-input-search .ls-search .he{font-size:1em;color:#807a7a;font-weight:600}.form-group.left-input-search .form-control{text-indent:1.25rem}.form-group.right-input-search .rs-search{top:27px;right:7px;padding:5px;width:30px;height:30px;display:grid;cursor:pointer;position:absolute;border-radius:5px;place-items:center}.form-group.right-input-search .rs-search i{font-size:16px;color:#807a7a}.form-group.right-input-search .rs-search:hover{background:#ff7f5d1a}.form-group.right-input-search .rs-search:hover i{color:#ff7f5d}.form-group.right-input-search .rs-search+.rs-search{right:37px}.form-group.right-input-search .clear{right:2.5rem}.form-group.right-input-search .form-control{padding-right:4rem}.form-group.right-input-search.no-label .rs-search{top:7px}.form-group.right-input-search.no-label .clear{top:10px}.form-group.right-input-search.group-sm.no-label .rs-search{top:4px;width:27px;height:27px}.form-group.right-input-search.group-sm.no-label .rs-search i{font-size:14px}.form-group.right-input-search.group-sm.no-label .clear{top:6px;right:2.25rem}.form-group.right-input-search.double-search .form-control{padding-right:6rem}.form-group.right-input-search.double-search .clear{right:4.5rem}.form-group.price-input .form-control{text-indent:1rem}.form-group.price-input .ls-price{top:21px;padding:8px;display:flex;color:#2ba599;position:absolute;align-items:center;height:calc(var(--input-height) - 2px)}.form-group.price-input .ls-price i{top:1px;color:inherit;cursor:default;font-size:12px}.form-group .clear{z-index:4;cursor:pointer;background-color:transparent}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
1402
- }
1403
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: InputControl, decorators: [{
1404
- type: Component,
1405
- args: [{ selector: "input-control", standalone: true, imports: [
1406
- CommonModule,
1407
- ReactiveFormsModule,
1408
- FormsModule
1409
- ], providers: [
1410
- {
1411
- provide: NG_VALUE_ACCESSOR,
1412
- useExisting: forwardRef(() => InputControl),
1413
- multi: true,
1414
- },
1415
- ], template: "<div class=\"form-group input\" [ngClass]=\"customClass\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n @if (useReactiveForms) {\r\n <input [type]=\"getInputType()\" class=\"form-control\" [placeholder]=\"placeholder\" [formControl]=\"inputControl\"\r\n [ngClass]=\"{ 'is-invalid': error }\" (focus)=\"onFocus()\" (blur)=\"onBlur()\" [readonly]=\"readonly\"\r\n (keypress)=\"onKeyPress($event)\" [attr.autocomplete]=\"autocomplete || null\" />\r\n } @else {\r\n <input [type]=\"getInputType()\" class=\"form-control\" [placeholder]=\"placeholder\" [(ngModel)]=\"ngModel\"\r\n [ngClass]=\"{ 'is-invalid': error }\" (focus)=\"onFocus()\" (blur)=\"onBlur()\" [readonly]=\"readonly\"\r\n (keypress)=\"onKeyPress($event)\" />\r\n }\r\n @if(deFocus){\r\n <span class=\"focus-border\"></span>\r\n }\r\n @if (!inputLoader && showClearButton()) {\r\n <label class=\"clear\" (click)=\"resetInput()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n\r\n @if (price) {\r\n <label class=\"ls-price\">\r\n <i class=\"he he-rupee\"></i>\r\n </label>\r\n }\r\n\r\n @if (type === 'search') {\r\n <label class=\"ls-search\">\r\n <i class=\"he he-search\"></i>\r\n </label>\r\n }\r\n\r\n @if (advSearch) {\r\n <label class=\"rs-search\" (click)=\"onAdvSearchClick()\">\r\n <i class=\"he he-search-adv\"></i>\r\n </label>\r\n }\r\n\r\n @if (search) {\r\n <label class=\"rs-search\" (click)=\"onSearchClick()\">\r\n <i class=\"he he-search\"></i>\r\n </label>\r\n }\r\n\r\n @if (type === 'password' && !disabled) {\r\n <label class=\"toggle-eye\" (click)=\"passwordVisible()\">\r\n <i class=\"he\" [ngClass]=\"showPassword ? 'he-eye-off' : 'he-eye'\"></i>\r\n </label>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group .toggle-eye,.form-group ::ng-deep .toggle-eye{top:28px;right:10px;padding:5px;line-height:1;position:absolute}.form-group .toggle-eye .he,.form-group ::ng-deep .toggle-eye .he{font-size:18px}.form-group .toggle-eye .rb,.form-group ::ng-deep .toggle-eye .rb{font-size:17px;color:#5f6554}.form-group.no-label .toggle-eye{top:9px}.form-group.group-sm .toggle-eye{top:5px}.form-group.left-input-search .ls-search{top:9px;left:10px;position:absolute}.form-group.left-input-search .ls-search .he{font-size:1em;color:#807a7a;font-weight:600}.form-group.left-input-search .form-control{text-indent:1.25rem}.form-group.right-input-search .rs-search{top:27px;right:7px;padding:5px;width:30px;height:30px;display:grid;cursor:pointer;position:absolute;border-radius:5px;place-items:center}.form-group.right-input-search .rs-search i{font-size:16px;color:#807a7a}.form-group.right-input-search .rs-search:hover{background:#ff7f5d1a}.form-group.right-input-search .rs-search:hover i{color:#ff7f5d}.form-group.right-input-search .rs-search+.rs-search{right:37px}.form-group.right-input-search .clear{right:2.5rem}.form-group.right-input-search .form-control{padding-right:4rem}.form-group.right-input-search.no-label .rs-search{top:7px}.form-group.right-input-search.no-label .clear{top:10px}.form-group.right-input-search.group-sm.no-label .rs-search{top:4px;width:27px;height:27px}.form-group.right-input-search.group-sm.no-label .rs-search i{font-size:14px}.form-group.right-input-search.group-sm.no-label .clear{top:6px;right:2.25rem}.form-group.right-input-search.double-search .form-control{padding-right:6rem}.form-group.right-input-search.double-search .clear{right:4.5rem}.form-group.price-input .form-control{text-indent:1rem}.form-group.price-input .ls-price{top:21px;padding:8px;display:flex;color:#2ba599;position:absolute;align-items:center;height:calc(var(--input-height) - 2px)}.form-group.price-input .ls-price i{top:1px;color:inherit;cursor:default;font-size:12px}.form-group .clear{z-index:4;cursor:pointer;background-color:transparent}\n"] }]
1416
- }], propDecorators: { type: [{
1417
- type: Input
1418
- }], title: [{
1419
- type: Input
1420
- }], placeholder: [{
1421
- type: Input
1422
- }], customClass: [{
1423
- type: Input
1424
- }], clearVal: [{
1425
- type: Input
1426
- }], required: [{
1427
- type: Input
1428
- }], deFocus: [{
1429
- type: Input
1430
- }], error: [{
1431
- type: Input
1432
- }], errorMessage: [{
1433
- type: Input
1434
- }], autocomplete: [{
1435
- type: Input
1436
- }], readonly: [{
1437
- type: Input
1438
- }], search: [{
1439
- type: Input
1440
- }], price: [{
1441
- type: Input
1442
- }], maxlength: [{
1443
- type: Input
1444
- }], ngModel: [{
1445
- type: Input
1446
- }], advSearch: [{
1447
- type: Input
1448
- }], inputLoader: [{
1449
- type: Input
1450
- }], useReactiveForms: [{
1451
- type: Input
1452
- }], blurEvent: [{
1453
- type: Output
1454
- }], selectionCleared: [{
1455
- type: Output
1456
- }], inputChange: [{
1457
- type: Output
1458
- }], ngModelChange: [{
1459
- type: Output
1460
- }], searchClicked: [{
1461
- type: Output
1462
- }], advSearchClicked: [{
1463
- type: Output
1464
- }], disabled: [{
1465
- type: Input
1466
- }] } });
1467
-
1468
- class ClickOutsideDirective {
1469
- clickOutside = new EventEmitter();
1470
- host = inject(ElementRef);
1471
- document = inject(DOCUMENT);
1472
- destroyRef = inject(DestroyRef);
1473
- constructor() {
1474
- fromEvent(this.document, 'click')
1475
- .pipe(takeUntilDestroyed(this.destroyRef))
1476
- .subscribe((event) => {
1477
- const clickedInside = this.host.nativeElement.contains(event.target);
1478
- if (!clickedInside) {
1479
- this.clickOutside.emit(event);
1480
- }
1481
- });
1482
- }
1483
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: ClickOutsideDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1484
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.3", type: ClickOutsideDirective, isStandalone: true, selector: "[clickOutside]", outputs: { clickOutside: "clickOutside" }, ngImport: i0 });
1485
- }
1486
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: ClickOutsideDirective, decorators: [{
1487
- type: Directive,
1488
- args: [{
1489
- selector: '[clickOutside]',
1490
- standalone: true,
1491
- }]
1492
- }], ctorParameters: () => [], propDecorators: { clickOutside: [{
1493
- type: Output
1494
- }] } });
1495
-
1496
- class MultiselectControl {
1497
- cdRef;
1498
- subscription = new Subscription();
1499
- title;
1500
- selectedItems = [];
1501
- required = false;
1502
- placeholder;
1503
- customClass;
1504
- clearVal = true;
1505
- deFocus = true;
1506
- optionValueProperty = "id";
1507
- inputLoader = false;
1508
- error = false;
1509
- errorMessage = "";
1510
- autocomplete = "";
1511
- showSearch = false;
1512
- optionDisplayProperty = "displayname";
1513
- optionsSelected = new EventEmitter();
1514
- selectionCleared = new EventEmitter();
1515
- blurEvent = new EventEmitter();
1516
- inputControl = new FormControl({ value: "", disabled: false });
1517
- _options = [];
1518
- set options(arr) {
1519
- this._options = Array.isArray(arr) ? arr : [];
1520
- this.filteredOptions = [...this._options];
1521
- // If we previously received an array of primitive IDs, attempt to map them now that options have arrived
1522
- if (this._isPrimitivePatch && this._pendingPrimitiveValues.length > 0) {
1523
- this._mapPrimitivesToObjects();
1524
- }
1525
- this._recomputeSelectAllState();
1526
- }
1527
- get options() {
1528
- return this._options;
1529
- }
1530
- _disabled = false;
1531
- get disabled() {
1532
- return this._disabled;
1533
- }
1534
- set disabled(value) {
1535
- this._disabled = value;
1536
- if (this.inputControl) {
1537
- if (value) {
1538
- this.inputControl.disable({ emitEvent: false });
1539
- }
1540
- else {
1541
- this.inputControl.enable({ emitEvent: false });
1542
- }
1543
- }
1544
- }
1545
- _isPrimitivePatch = false;
1546
- _pendingPrimitiveValues = [];
1547
- onChange = () => { };
1548
- onTouched = () => { };
1549
- dropdown;
1550
- input;
1551
- filteredOptions = [];
1552
- isDropdownOpen = false;
1553
- selectAllChecked = false;
1554
- keySearch = "";
1555
- onSearch = new Subject();
1556
- selectedItemList = "";
1557
- highlightedIndex = null;
1558
- dropdownInitialized = false;
1559
- popperInstance;
1560
- constructor(cdRef) {
1561
- this.cdRef = cdRef;
1562
- }
1563
- ngOnInit() {
1564
- // Subscribe to search‐term changes, debounce, then filter
1565
- this.subscription = this.onSearch
1566
- .pipe(debounceTime(300), distinctUntilChanged())
1567
- .subscribe((searchText) => {
1568
- this._applyFilter(searchText);
1569
- this._recomputeSelectAllState();
1570
- });
1571
- // Initialize filteredOptions to match all options
1572
- this.filteredOptions = [...this.options];
1573
- }
1574
- ngAfterViewChecked() {
1575
- if (this.isDropdownOpen && !this.dropdownInitialized) {
1576
- this._createPopperInstance();
1577
- this.dropdownInitialized = true;
1578
- }
1579
- }
1580
- ngOnDestroy() {
1581
- this.subscription.unsubscribe();
1582
- if (this.popperInstance) {
1583
- this.popperInstance.destroy();
1584
- }
1585
- }
1586
- ngOnChanges(changes) {
1587
- if (changes["options"]) {
1588
- // When the parent replaces “options,” we reapply any filtering and “select all” logic
1589
- this.filteredOptions = [...this.options];
1590
- this._recomputeSelectAllState();
1591
- }
1592
- }
1593
- writeValue(value) {
1594
- // If parent gave us something that is not an array, clear selection
1595
- if (!Array.isArray(value)) {
1596
- this._isPrimitivePatch = false;
1597
- this._pendingPrimitiveValues = [];
1598
- this.selectedItems = [];
1599
- this.selectedItemList = "";
1600
- this.selectAllChecked = false;
1601
- return;
1602
- }
1603
- // CASE A: array of primitive IDs, e.g. [12,17]
1604
- if (value.length > 0 &&
1605
- (typeof value[0] === "string" || typeof value[0] === "number")) {
1606
- this._isPrimitivePatch = true;
1607
- this._pendingPrimitiveValues = [...value];
1608
- this._mapPrimitivesToObjects();
1609
- this._recomputeSelectAllState();
1610
- return;
1611
- }
1612
- // CASE B: array of full objects (with `id` or whatever `optionValueProperty` is)
1613
- if (value.length > 0 &&
1614
- typeof value[0] === "object" &&
1615
- value[0][this.optionValueProperty] != null) {
1616
- // Convert to an array of IDs, then treat exactly like CASE A
1617
- const idArray = value.map((obj) => obj[this.optionValueProperty]);
1618
- this._isPrimitivePatch = true;
1619
- this._pendingPrimitiveValues = idArray;
1620
- this._mapPrimitivesToObjects();
1621
- this._recomputeSelectAllState();
1622
- return;
1623
- }
1624
- // CASE C: maybe parent passed the exact same object references that already exist in `options`
1625
- // In that case, we can just use them directly.
1626
- this._isPrimitivePatch = false;
1627
- this._pendingPrimitiveValues = [];
1628
- this.selectedItems = [...value];
1629
- this.selectedItemList = this.selectedItems
1630
- .map((item) => item[this.optionDisplayProperty])
1631
- .join(", ");
1632
- this.onChange(this.selectedItems);
1633
- this._recomputeSelectAllState();
1634
- }
1635
- _mapPrimitivesToObjects() {
1636
- if (!this._isPrimitivePatch ||
1637
- this._pendingPrimitiveValues.length === 0 ||
1638
- this._options.length === 0) {
1639
- return;
1640
- }
1641
- // Find matching option‐objects whose ID property is in the pending list
1642
- const matched = this._options.filter((opt) => this._pendingPrimitiveValues.includes(opt[this.optionValueProperty]));
1643
- this.selectedItems = matched;
1644
- this.selectedItemList = matched
1645
- .map((item) => item[this.optionDisplayProperty])
1646
- .join(", ");
1647
- // Notify “onChange” with the full‐object selection
1648
- this.onChange(this.selectedItems);
1649
- this._isPrimitivePatch = false;
1650
- this._pendingPrimitiveValues = [];
1651
- }
1652
- registerOnChange(fn) {
1653
- this.onChange = fn;
1654
- }
1655
- registerOnTouched(fn) {
1656
- this.onTouched = fn;
1657
- }
1658
- // events
1659
- isSelected(option) {
1660
- return (Array.isArray(this.selectedItems) && this.selectedItems.includes(option));
1661
- }
1662
- onBlur(event) {
1663
- this.blurEvent.emit();
1664
- // const target = event.relatedTarget as HTMLElement;
1665
- // if (
1666
- // target &&
1667
- // (target.closest(".option-list") || target.closest(".list-filter"))
1668
- // ) {
1669
- // return;
1670
- // }
1671
- this.onTouched();
1672
- // setTimeout(() => {
1673
- // this.isDropdownOpen = false;
1674
- // }, 100);
1675
- }
1676
- setDisabledState(isDisabled) {
1677
- this.disabled = isDisabled;
1678
- if (isDisabled) {
1679
- this.inputControl.disable({ emitEvent: false });
1680
- }
1681
- else {
1682
- this.inputControl.enable({ emitEvent: false });
1683
- }
1684
- }
1685
- // search box
1686
- handleInput(event) {
1687
- const txt = event.target.value;
1688
- this.onSearch.next(txt);
1689
- }
1690
- _applyFilter(value) {
1691
- const lower = value.toLowerCase();
1692
- this.filteredOptions = this._options
1693
- .filter((opt) => opt[this.optionDisplayProperty]?.toLowerCase().includes(lower))
1694
- .sort((a, b) => {
1695
- const aIndex = a[this.optionDisplayProperty]
1696
- .toLowerCase()
1697
- .indexOf(lower);
1698
- const bIndex = b[this.optionDisplayProperty]
1699
- .toLowerCase()
1700
- .indexOf(lower);
1701
- return aIndex - bIndex;
1702
- });
1703
- // If an item was previously selected but is no longer in filteredOptions, remove it temporarily
1704
- this.selectedItems = this.selectedItems.filter((item) => this.filteredOptions.includes(item));
1705
- this.selectedItemList = this.selectedItems
1706
- .map((item) => item[this.optionDisplayProperty])
1707
- .join(", ");
1708
- this.onChange(this.selectedItems);
1709
- this.optionsSelected.emit(this.selectedItems);
1710
- if (this.filteredOptions.length > 0) {
1711
- this.highlightedIndex = this._options.indexOf(this.filteredOptions[0]);
1712
- }
1713
- else {
1714
- this.highlightedIndex = null;
1715
- }
1716
- this._recomputeSelectAllState();
1717
- this.cdRef.detectChanges();
1718
- }
1719
- // checkbox events
1720
- _recomputeSelectAllState() {
1721
- if (!this.filteredOptions || this.filteredOptions.length === 0) {
1722
- this.selectAllChecked = false;
1723
- return;
1724
- }
1725
- this.selectAllChecked = this.filteredOptions.every((opt) => this.selectedItems.includes(opt));
1726
- }
1727
- onSelectAllChange(event) {
1728
- const input = event.target;
1729
- this.selectAllChecked = input.checked;
1730
- this._toggleSelectAll();
1731
- }
1732
- _toggleSelectAll() {
1733
- if (this.selectAllChecked) {
1734
- // Add all currently filtered options to selection
1735
- this.selectedItems = Array.from(new Set([...this.selectedItems, ...this.filteredOptions]));
1736
- }
1737
- else {
1738
- // Remove all filtered options from selection
1739
- this.selectedItems = this.selectedItems.filter((item) => !this.filteredOptions.includes(item));
1740
- }
1741
- this.selectedItemList = this.selectedItems
1742
- .map((item) => item[this.optionDisplayProperty])
1743
- .join(", ");
1744
- this.onChange(this.selectedItems);
1745
- this.optionsSelected.emit(this.selectedItems);
1746
- }
1747
- removeSelectedOption(option) {
1748
- this.selectedItems = this.selectedItems.filter((item) => item !== option);
1749
- this.selectedItemList = this.selectedItems
1750
- .map((item) => item[this.optionDisplayProperty])
1751
- .join(", ");
1752
- this.onChange(this.selectedItems);
1753
- this.onTouched();
1754
- this.optionsSelected.emit(this.selectedItems);
1755
- }
1756
- toggleCheckbox(option) {
1757
- if (this.isSelected(option)) {
1758
- this.selectedItems = this.selectedItems.filter((item) => item !== option);
1759
- }
1760
- else {
1761
- this.selectedItems = [...this.selectedItems, option];
1762
- }
1763
- this.selectedItemList = this.selectedItems
1764
- .map((item) => item[this.optionDisplayProperty])
1765
- .join(", ");
1766
- // Update “select all” checkbox
1767
- this._recomputeSelectAllState();
1768
- // Notify form that selection changed
1769
- this.onChange(this.selectedItems);
1770
- this.onTouched();
1771
- this.optionsSelected.emit(this.selectedItems);
1772
- }
1773
- // Keyboard navigation inside the dropdown
1774
- onKeyDown(event) {
1775
- if (this.isDropdownOpen) {
1776
- switch (event.key) {
1777
- case "ArrowDown":
1778
- this._highlightNext();
1779
- event.preventDefault();
1780
- break;
1781
- case "ArrowUp":
1782
- this._highlightPrevious();
1783
- event.preventDefault();
1784
- break;
1785
- case "Enter":
1786
- if (this.highlightedIndex !== null &&
1787
- this.highlightedIndex < this.filteredOptions.length) {
1788
- this.toggleCheckbox(this.filteredOptions[this.highlightedIndex]);
1789
- }
1790
- event.preventDefault();
1791
- break;
1792
- case "Escape":
1793
- this.isDropdownOpen = false;
1794
- break;
1795
- case "Tab":
1796
- this.isDropdownOpen = false;
1797
- break;
1798
- }
1799
- }
1800
- else if (event.key === " ") {
1801
- this.toggleDropdown();
1802
- event.preventDefault();
1803
- }
1804
- }
1805
- handleWindowKeydown(event) {
1806
- if (event.key === "Tab") {
1807
- this.isDropdownOpen = false;
1808
- }
1809
- }
1810
- _highlightNext() {
1811
- if (this.highlightedIndex === null ||
1812
- this.highlightedIndex === this.filteredOptions.length - 1) {
1813
- this.highlightedIndex = 0;
1814
- }
1815
- else {
1816
- this.highlightedIndex++;
1817
- }
1818
- this._scrollToHighlighted();
1819
- }
1820
- _highlightPrevious() {
1821
- if (this.highlightedIndex === null || this.highlightedIndex === 0) {
1822
- this.highlightedIndex = this.filteredOptions.length - 1;
1823
- }
1824
- else {
1825
- this.highlightedIndex--;
1826
- }
1827
- this._scrollToHighlighted();
1828
- }
1829
- _scrollToHighlighted() {
1830
- setTimeout(() => {
1831
- const container = document.querySelector(".option-list");
1832
- if (!container)
1833
- return;
1834
- const highlighted = container.querySelector(".highlighted");
1835
- if (highlighted) {
1836
- highlighted.scrollIntoView({ block: "nearest" });
1837
- }
1838
- }, 0);
1839
- }
1840
- // popper
1841
- _createPopperInstance() {
1842
- if (!this.dropdown || !this.input)
1843
- return;
1844
- this.popperInstance = createPopper(this.input.nativeElement, this.dropdown.nativeElement, {
1845
- placement: "bottom-start",
1846
- modifiers: [
1847
- {
1848
- name: "offset",
1849
- options: {
1850
- offset: [0, 1],
1851
- },
1852
- },
1853
- {
1854
- name: "flip",
1855
- options: {
1856
- fallbackPlacements: ["top-start", "bottom-start"],
1857
- },
1858
- },
1859
- ],
1860
- });
1861
- }
1862
- // toggle, reset, close and clear
1863
- toggleDropdown() {
1864
- if (this.inputControl.disabled)
1865
- return;
1866
- this.isDropdownOpen = !this.isDropdownOpen;
1867
- this.dropdownInitialized = false;
1868
- if (this.isDropdownOpen) {
1869
- this.filteredOptions = [...this.options];
1870
- this.highlightedIndex = null;
1871
- }
1872
- }
1873
- selectListOutsideClick() {
1874
- this.isDropdownOpen = false;
1875
- }
1876
- resetSelection() {
1877
- this.selectedItems = [];
1878
- this.selectedItemList = "";
1879
- this.onChange([]);
1880
- this.onTouched();
1881
- this.isDropdownOpen = false;
1882
- this.selectAllChecked = false;
1883
- this.selectionCleared.emit();
1884
- }
1885
- onCloseDropdown() {
1886
- this.isDropdownOpen = false;
1887
- this._recomputeSelectAllState();
1888
- }
1889
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: MultiselectControl, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1890
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: MultiselectControl, isStandalone: true, selector: "multiselect-control", inputs: { title: "title", selectedItems: "selectedItems", required: "required", placeholder: "placeholder", customClass: "customClass", clearVal: "clearVal", deFocus: "deFocus", optionValueProperty: "optionValueProperty", inputLoader: "inputLoader", error: "error", errorMessage: "errorMessage", autocomplete: "autocomplete", showSearch: "showSearch", optionDisplayProperty: "optionDisplayProperty", options: "options", disabled: "disabled" }, outputs: { optionsSelected: "optionsSelected", selectionCleared: "selectionCleared", blurEvent: "blurEvent" }, host: { listeners: { "window:keydown": "handleWindowKeydown($event)" } }, providers: [
1891
- {
1892
- provide: NG_VALUE_ACCESSOR,
1893
- useExisting: forwardRef(() => MultiselectControl),
1894
- multi: true,
1895
- },
1896
- ], viewQueries: [{ propertyName: "dropdown", first: true, predicate: ["dropdown"], descendants: true }, { propertyName: "input", first: true, predicate: ["input"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"form-group multi-select\" [ngClass]=\"customClass\" (clickOutside)=\"selectListOutsideClick()\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n <input #input type=\"text\" class=\"form-control\" placeholder=\"{{ placeholder }}\" [formControl]=\"inputControl\"\r\n [ngClass]=\"{'is-invalid': error}\" (blur)=\"onBlur($event)\" (click)=\"toggleDropdown()\" readonly\r\n [value]=\"selectedItemList\" (keydown)=\"onKeyDown($event)\" [attr.autocomplete]=\"autocomplete || null\" />\r\n\r\n @if (deFocus) {\r\n <span class=\"focus-border\"></span>\r\n }\r\n\r\n @if (!inputLoader && isDropdownOpen && clearVal && selectedItems.length > 0) {\r\n <label class=\"clear\" (click)=\"resetSelection()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n\r\n @if (!inputLoader) {\r\n <label class=\"arrow\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </label> }\r\n\r\n @if (isDropdownOpen) {\r\n <div #dropdown class=\"option-list\">\r\n @if (showSearch && filteredOptions.length > 5) {\r\n <div class=\"list-filter\">\r\n <div>\r\n <label class=\"checkbox-container select-all\">\r\n <input type=\"checkbox\" [checked]=\"selectAllChecked\" (change)=\"onSelectAllChange($event)\" tabindex=\"-1\" />\r\n <span class=\"checkmark\"></span>\r\n </label>\r\n </div>\r\n <div class=\"form-group\">\r\n <input type=\"text\" class=\"form-control\" placeholder=\"Search...\" (input)=\"handleInput($event) \"\r\n (keydown)=\"onKeyDown($event)\" />\r\n </div>\r\n <div class=\"ms-close\">\r\n <button type=\"button\" class=\"icon-button\">\r\n <a (click)=\"onCloseDropdown()\"><i class=\"he he-close\"></i></a>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"list-wrap\">\r\n @for (option of filteredOptions; track option[optionDisplayProperty]; let i = $index) {\r\n <div class=\"list-item\" (change)=\"toggleCheckbox(option)\" [ngClass]=\"{ 'highlighted': i === highlightedIndex }\">\r\n <label class=\"checkbox-container multi-select\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" tabindex=\"-1\" />\r\n <span class=\"checkmark\"></span>\r\n {{ option[optionDisplayProperty] }}\r\n </label>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (filteredOptions.length === 0) {\r\n <div class=\"no-results\">No results found</div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group.multi-select .option-list .list-filter{gap:10px;display:flex;padding:9px 10px;align-items:center;border-bottom:1px solid #efefef}.form-group.multi-select .option-list .list-filter .checkbox-container.select-all{top:-10px;margin-bottom:0;padding-left:22px}.form-group.multi-select .option-list .list-filter .form-group{margin-bottom:0;width:calc(100% - 70px)}.form-group.multi-select .option-list .list-filter .form-group .form-control{height:34px;border-radius:5px;background:#fff;padding:.275rem .5rem;border:1px solid #cccccc!important}.form-group.multi-select .option-list .list-filter .form-group .form-control::placeholder{font-size:.925em}.form-group.multi-select .option-list .list-filter .ms-close .icon-button{background:#f0f0f0}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a{width:28px;height:28px;display:grid;cursor:pointer;position:relative;border-radius:5px;place-items:center;text-decoration:none;border:1px solid #efefef;box-shadow:0 0 3px #ebebeb}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a i{font-size:14px}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a:hover{background:#ff7f5d1a}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a:hover i{color:#ff7f5d}.form-group.multi-select .option-list .list-wrap{overflow:auto;max-height:180px}.form-group.multi-select .option-list .list-item{padding:0}.form-group.multi-select .option-list .list-item .checkbox-container.multi-select{width:100%;font-size:1em;margin-bottom:0;font-weight:400;color:#584e4e;letter-spacing:.015rem;padding:10px 10px 10px 40px}.form-group.multi-select .option-list .list-item .checkbox-container.multi-select .checkmark{top:9px;left:10px}.form-group.multi-select .option-list .no-results{padding:10px;color:#9b9b9b;text-align:center}.form-group.multi-select .clear{right:30px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", outputs: ["clickOutside"] }] });
1897
- }
1898
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: MultiselectControl, decorators: [{
1899
- type: Component,
1900
- args: [{ selector: "multiselect-control", standalone: true, imports: [
1901
- CommonModule,
1902
- ReactiveFormsModule,
1903
- ClickOutsideDirective
1904
- ], providers: [
1905
- {
1906
- provide: NG_VALUE_ACCESSOR,
1907
- useExisting: forwardRef(() => MultiselectControl),
1908
- multi: true,
1909
- },
1910
- ], template: "<div class=\"form-group multi-select\" [ngClass]=\"customClass\" (clickOutside)=\"selectListOutsideClick()\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n <input #input type=\"text\" class=\"form-control\" placeholder=\"{{ placeholder }}\" [formControl]=\"inputControl\"\r\n [ngClass]=\"{'is-invalid': error}\" (blur)=\"onBlur($event)\" (click)=\"toggleDropdown()\" readonly\r\n [value]=\"selectedItemList\" (keydown)=\"onKeyDown($event)\" [attr.autocomplete]=\"autocomplete || null\" />\r\n\r\n @if (deFocus) {\r\n <span class=\"focus-border\"></span>\r\n }\r\n\r\n @if (!inputLoader && isDropdownOpen && clearVal && selectedItems.length > 0) {\r\n <label class=\"clear\" (click)=\"resetSelection()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n\r\n @if (!inputLoader) {\r\n <label class=\"arrow\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </label> }\r\n\r\n @if (isDropdownOpen) {\r\n <div #dropdown class=\"option-list\">\r\n @if (showSearch && filteredOptions.length > 5) {\r\n <div class=\"list-filter\">\r\n <div>\r\n <label class=\"checkbox-container select-all\">\r\n <input type=\"checkbox\" [checked]=\"selectAllChecked\" (change)=\"onSelectAllChange($event)\" tabindex=\"-1\" />\r\n <span class=\"checkmark\"></span>\r\n </label>\r\n </div>\r\n <div class=\"form-group\">\r\n <input type=\"text\" class=\"form-control\" placeholder=\"Search...\" (input)=\"handleInput($event) \"\r\n (keydown)=\"onKeyDown($event)\" />\r\n </div>\r\n <div class=\"ms-close\">\r\n <button type=\"button\" class=\"icon-button\">\r\n <a (click)=\"onCloseDropdown()\"><i class=\"he he-close\"></i></a>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n\r\n <div class=\"list-wrap\">\r\n @for (option of filteredOptions; track option[optionDisplayProperty]; let i = $index) {\r\n <div class=\"list-item\" (change)=\"toggleCheckbox(option)\" [ngClass]=\"{ 'highlighted': i === highlightedIndex }\">\r\n <label class=\"checkbox-container multi-select\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" tabindex=\"-1\" />\r\n <span class=\"checkmark\"></span>\r\n {{ option[optionDisplayProperty] }}\r\n </label>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (filteredOptions.length === 0) {\r\n <div class=\"no-results\">No results found</div>\r\n }\r\n </div>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group.multi-select .option-list .list-filter{gap:10px;display:flex;padding:9px 10px;align-items:center;border-bottom:1px solid #efefef}.form-group.multi-select .option-list .list-filter .checkbox-container.select-all{top:-10px;margin-bottom:0;padding-left:22px}.form-group.multi-select .option-list .list-filter .form-group{margin-bottom:0;width:calc(100% - 70px)}.form-group.multi-select .option-list .list-filter .form-group .form-control{height:34px;border-radius:5px;background:#fff;padding:.275rem .5rem;border:1px solid #cccccc!important}.form-group.multi-select .option-list .list-filter .form-group .form-control::placeholder{font-size:.925em}.form-group.multi-select .option-list .list-filter .ms-close .icon-button{background:#f0f0f0}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a{width:28px;height:28px;display:grid;cursor:pointer;position:relative;border-radius:5px;place-items:center;text-decoration:none;border:1px solid #efefef;box-shadow:0 0 3px #ebebeb}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a i{font-size:14px}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a:hover{background:#ff7f5d1a}.form-group.multi-select .option-list .list-filter .ms-close .icon-button a:hover i{color:#ff7f5d}.form-group.multi-select .option-list .list-wrap{overflow:auto;max-height:180px}.form-group.multi-select .option-list .list-item{padding:0}.form-group.multi-select .option-list .list-item .checkbox-container.multi-select{width:100%;font-size:1em;margin-bottom:0;font-weight:400;color:#584e4e;letter-spacing:.015rem;padding:10px 10px 10px 40px}.form-group.multi-select .option-list .list-item .checkbox-container.multi-select .checkmark{top:9px;left:10px}.form-group.multi-select .option-list .no-results{padding:10px;color:#9b9b9b;text-align:center}.form-group.multi-select .clear{right:30px}\n"] }]
1911
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { title: [{
1912
- type: Input
1913
- }], selectedItems: [{
1914
- type: Input
1915
- }], required: [{
1916
- type: Input
1917
- }], placeholder: [{
1918
- type: Input
1919
- }], customClass: [{
1920
- type: Input
1921
- }], clearVal: [{
1922
- type: Input
1923
- }], deFocus: [{
1924
- type: Input
1925
- }], optionValueProperty: [{
1926
- type: Input
1927
- }], inputLoader: [{
1928
- type: Input
1929
- }], error: [{
1930
- type: Input
1931
- }], errorMessage: [{
1932
- type: Input
1933
- }], autocomplete: [{
1934
- type: Input
1935
- }], showSearch: [{
1936
- type: Input
1937
- }], optionDisplayProperty: [{
1938
- type: Input
1939
- }], optionsSelected: [{
1940
- type: Output
1941
- }], selectionCleared: [{
1942
- type: Output
1943
- }], blurEvent: [{
1944
- type: Output
1945
- }], options: [{
1946
- type: Input
1947
- }], disabled: [{
1948
- type: Input
1949
- }], dropdown: [{
1950
- type: ViewChild,
1951
- args: ["dropdown", { static: false }]
1952
- }], input: [{
1953
- type: ViewChild,
1954
- args: ["input", { static: false }]
1955
- }], handleWindowKeydown: [{
1956
- type: HostListener,
1957
- args: ["window:keydown", ["$event"]]
1958
- }] } });
1959
-
1960
- class SelectControl {
1961
- subscription = new Subscription();
1962
- optionDisplayProperty = "displayname";
1963
- required = false;
1964
- placeholder;
1965
- customClass;
1966
- clearVal = true;
1967
- deFocus = true;
1968
- error = false;
1969
- errorMessage = "";
1970
- autocomplete = "";
1971
- inputLoader = false;
1972
- isAddNewItem = false;
1973
- optionSelected = new EventEmitter();
1974
- selectionCleared = new EventEmitter();
1975
- addNewItemClicked = new EventEmitter();
1976
- blurEvent = new EventEmitter();
1977
- optionPatched = new EventEmitter();
1978
- title;
1979
- _options = [];
1980
- set options(opts) {
1981
- this._options = opts;
1982
- this.filteredOptions = [...this._options];
1983
- this.highlightSelectedItem();
1984
- this.processValueBuffer();
1985
- }
1986
- get options() {
1987
- return this._options;
1988
- }
1989
- get disabled() {
1990
- return this._disabled;
1991
- }
1992
- set disabled(value) {
1993
- this._disabled = value;
1994
- if (this.inputControl) {
1995
- if (value) {
1996
- this.inputControl.disable({ emitEvent: false });
1997
- }
1998
- else {
1999
- this.inputControl.enable({ emitEvent: false });
2000
- }
2001
- }
2002
- }
2003
- dropdown;
2004
- input;
2005
- filteredOptions = [];
2006
- keySearch = "";
2007
- selectedItem = null;
2008
- onSearch = new Subject();
2009
- inputControl = new FormControl({ value: "", disabled: false });
2010
- isDropdownOpen = false;
2011
- highlightedIndex = null;
2012
- onChange = () => { };
2013
- onTouched = () => { };
2014
- valueBuffer = null;
2015
- _disabled = false;
2016
- dropdownInitialized = false;
2017
- popperInstance;
2018
- ngOnInit() {
2019
- this.subscription = this.onSearch
2020
- .pipe(debounceTime(300), distinctUntilChanged())
2021
- .subscribe((searchText) => {
2022
- this.focusOption(searchText);
2023
- this.keySearch = "";
2024
- });
2025
- this.filteredOptions = this._options;
2026
- // Sync the input control value with the selected item
2027
- this.inputControl.valueChanges.subscribe((value) => {
2028
- this.onChange(value);
2029
- });
2030
- }
2031
- ngAfterViewChecked() {
2032
- if (this.isDropdownOpen && !this.dropdownInitialized) {
2033
- this.createPopperInstance();
2034
- this.dropdownInitialized = true;
2035
- }
2036
- }
2037
- ngOnChanges(changes) {
2038
- if (changes["options"]) {
2039
- this.filteredOptions = [...this._options];
2040
- this.highlightSelectedItem();
2041
- }
2042
- if (changes["disabled"]) {
2043
- this.disabled = changes["disabled"].currentValue;
2044
- }
2045
- }
2046
- ngOnDestroy() {
2047
- this.subscription.unsubscribe();
2048
- }
2049
- writeValue(value) {
2050
- if (this.inputControl.disabled !== this.disabled) {
2051
- this.setDisabledState(this.disabled);
2052
- }
2053
- this.valueBuffer = value;
2054
- this.processValueBuffer();
2055
- }
2056
- processValueBuffer() {
2057
- if (this.valueBuffer !== null &&
2058
- this._options &&
2059
- this._options.length > 0) {
2060
- // Try to match selected item by ID
2061
- const matchedItem = this._options.find((option) => option.id === this.valueBuffer);
2062
- if (matchedItem) {
2063
- this.selectedItem = matchedItem;
2064
- const displayValue = matchedItem[this.optionDisplayProperty];
2065
- this.inputControl.setValue(displayValue, { emitEvent: false });
2066
- this.filteredOptions = [...this._options];
2067
- this.onTouched();
2068
- this.inputControl.markAsDirty();
2069
- this.optionPatched.emit(this.selectedItem);
2070
- }
2071
- else {
2072
- // If not found, don't clear the field automatically
2073
- // You may choose to keep it or clear it. Here we clear it.
2074
- this.selectedItem = null;
2075
- this.inputControl.setValue("", { emitEvent: false });
2076
- this.highlightedIndex = null;
2077
- }
2078
- }
2079
- else if (this.valueBuffer === null) {
2080
- this.selectedItem = null;
2081
- this.inputControl.setValue("", { emitEvent: false });
2082
- this.highlightedIndex = null;
2083
- }
2084
- }
2085
- registerOnChange(fn) {
2086
- this.onChange = fn;
2087
- }
2088
- registerOnTouched(fn) {
2089
- this.onTouched = fn;
2090
- }
2091
- // events
2092
- onBlur() {
2093
- this.blurEvent.emit();
2094
- this.onTouched();
2095
- setTimeout(() => {
2096
- this.isDropdownOpen = false;
2097
- }, 300);
2098
- }
2099
- onKeyDown(event) {
2100
- const isAlphabetOrNumberKey = /^[a-zA-Z0-9]$/.test(event.key);
2101
- if (this.isDropdownOpen) {
2102
- switch (event.key) {
2103
- case "ArrowDown":
2104
- this.highlightNext();
2105
- event.preventDefault();
2106
- break;
2107
- case "ArrowUp":
2108
- this.highlightPrevious();
2109
- event.preventDefault();
2110
- break;
2111
- case "Enter":
2112
- if (this.highlightedIndex !== null) {
2113
- this.selectOption(this.filteredOptions[this.highlightedIndex]);
2114
- }
2115
- event.preventDefault();
2116
- break;
2117
- case "Escape":
2118
- this.isDropdownOpen = false;
2119
- break;
2120
- default:
2121
- if (isAlphabetOrNumberKey) {
2122
- this.keySearch += event.key.toString();
2123
- if (this.keySearch !== "") {
2124
- this.onSearch.next(this.keySearch);
2125
- }
2126
- }
2127
- break;
2128
- }
2129
- }
2130
- else if (event.key === " ") {
2131
- this.toggleDropdown();
2132
- event.preventDefault();
2133
- }
2134
- }
2135
- setDisabledState(isDisabled) {
2136
- this.disabled = isDisabled;
2137
- if (this.disabled) {
2138
- this.inputControl.disable({ emitEvent: false });
2139
- }
2140
- else {
2141
- this.inputControl.enable({ emitEvent: false });
2142
- }
2143
- }
2144
- selectOption(option) {
2145
- if (this.selectedItem !== option) {
2146
- this.selectedItem = option;
2147
- this.isDropdownOpen = false;
2148
- const displayValue = this.selectedItem[this.optionDisplayProperty];
2149
- this.inputControl.setValue(displayValue, { emitEvent: false });
2150
- this.onChange(this.selectedItem.id);
2151
- this.onTouched();
2152
- this.inputControl.markAsDirty();
2153
- this.optionSelected.emit(this.selectedItem);
2154
- }
2155
- }
2156
- // highlight
2157
- highlightSelectedItem() {
2158
- const selectedIndex = this.filteredOptions.findIndex((option) => option === this.selectedItem);
2159
- if (selectedIndex !== -1) {
2160
- this.highlightedIndex = selectedIndex;
2161
- this.scrollToHighlighted();
2162
- }
2163
- else {
2164
- this.highlightedIndex = null;
2165
- }
2166
- }
2167
- highlightNext() {
2168
- if (this.highlightedIndex === null ||
2169
- this.highlightedIndex === this.filteredOptions.length - 1) {
2170
- this.highlightedIndex = 0;
2171
- }
2172
- else {
2173
- this.highlightedIndex++;
2174
- }
2175
- this.scrollToHighlighted();
2176
- }
2177
- highlightPrevious() {
2178
- if (this.highlightedIndex === null || this.highlightedIndex === 0) {
2179
- this.highlightedIndex = this.filteredOptions.length - 1;
2180
- }
2181
- else {
2182
- this.highlightedIndex--;
2183
- }
2184
- this.scrollToHighlighted();
2185
- }
2186
- focusOption(value) {
2187
- const filterValue = value.toLowerCase();
2188
- let foundIndex = this._options.findIndex((option) => option[this.optionDisplayProperty]?.toLowerCase() === filterValue);
2189
- if (foundIndex === -1) {
2190
- foundIndex = this._options.findIndex((option) => option[this.optionDisplayProperty]
2191
- ?.toLowerCase()
2192
- .startsWith(filterValue));
2193
- }
2194
- if (foundIndex === -1) {
2195
- foundIndex = this._options.findIndex((option) => option[this.optionDisplayProperty]?.toLowerCase().includes(filterValue));
2196
- }
2197
- if (foundIndex !== -1) {
2198
- this.highlightedIndex = foundIndex;
2199
- this.scrollToHighlighted();
2200
- }
2201
- else {
2202
- this.highlightedIndex = null;
2203
- }
2204
- }
2205
- scrollToHighlighted() {
2206
- setTimeout(() => {
2207
- const container = document.querySelector(".option-list");
2208
- if (container) {
2209
- const highlighted = container.querySelector(".highlighted");
2210
- if (highlighted) {
2211
- highlighted.scrollIntoView({ block: "nearest" });
2212
- }
2213
- }
2214
- }, 0);
2215
- }
2216
- // actions
2217
- onAddNewItemClick() {
2218
- this.addNewItemClicked.emit();
2219
- }
2220
- // popper
2221
- createPopperInstance() {
2222
- if (this.dropdown) {
2223
- this.popperInstance = createPopper(this.input.nativeElement, this.dropdown.nativeElement, {
2224
- placement: "bottom-start",
2225
- modifiers: [
2226
- {
2227
- name: "offset",
2228
- options: {
2229
- offset: [0, 1],
2230
- },
2231
- },
2232
- {
2233
- name: "flip",
2234
- options: {
2235
- fallbackPlacements: ["top-start", "bottom-start"],
2236
- },
2237
- },
2238
- ],
2239
- });
2240
- }
2241
- }
2242
- // toggle, open, hide, reset and clear
2243
- toggleDropdown() {
2244
- if (!this.inputControl.disabled) {
2245
- this.isDropdownOpen = !this.isDropdownOpen;
2246
- this.dropdownInitialized = false;
2247
- if (this.isDropdownOpen && this.selectedItem) {
2248
- this.highlightSelectedItem();
2249
- }
2250
- }
2251
- }
2252
- openDropdown(event) {
2253
- if (event.ctrlKey && event.key === "Enter") {
2254
- this.toggleDropdown();
2255
- }
2256
- }
2257
- selectListOutsideClick(e) {
2258
- this.isDropdownOpen = false;
2259
- }
2260
- hideDropdown() {
2261
- setTimeout(() => {
2262
- this.isDropdownOpen = false;
2263
- }, 200);
2264
- }
2265
- resetSelection() {
2266
- this.selectionCleared.emit();
2267
- this.selectedItem = null;
2268
- this.inputControl.setValue("", { emitEvent: false });
2269
- this.onChange(null);
2270
- this.onTouched();
2271
- this.valueBuffer = null;
2272
- this.isDropdownOpen = false;
2273
- }
2274
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SelectControl, deps: [], target: i0.ɵɵFactoryTarget.Component });
2275
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: SelectControl, isStandalone: true, selector: "select-control", inputs: { optionDisplayProperty: "optionDisplayProperty", required: "required", placeholder: "placeholder", customClass: "customClass", clearVal: "clearVal", deFocus: "deFocus", error: "error", errorMessage: "errorMessage", autocomplete: "autocomplete", inputLoader: "inputLoader", isAddNewItem: "isAddNewItem", title: "title", options: "options", disabled: "disabled" }, outputs: { optionSelected: "optionSelected", selectionCleared: "selectionCleared", addNewItemClicked: "addNewItemClicked", blurEvent: "blurEvent", optionPatched: "optionPatched" }, providers: [
2276
- {
2277
- provide: NG_VALUE_ACCESSOR,
2278
- useExisting: forwardRef(() => SelectControl),
2279
- multi: true,
2280
- },
2281
- ], viewQueries: [{ propertyName: "dropdown", first: true, predicate: ["dropdown"], descendants: true }, { propertyName: "input", first: true, predicate: ["input"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"form-group select\" [ngClass]=\"customClass\" (clickOutside)=\"selectListOutsideClick($event)\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n <input #input type=\"text\" class=\"form-control\" [placeholder]=\"placeholder ? placeholder : ''\"\r\n [formControl]=\"inputControl\" [ngClass]=\"{ 'is-invalid': error }\" (blur)=\"onBlur()\" (click)=\"toggleDropdown()\"\r\n (keydown)=\"onKeyDown($event)\" readonly [attr.autocomplete]=\"autocomplete || null\" />\r\n\r\n @if (deFocus) {\r\n <span class=\"focus-border\"></span>\r\n }\r\n\r\n @if (!inputLoader && isDropdownOpen && selectedItem && clearVal) {\r\n <label class=\"clear\" (click)=\"resetSelection()\">\r\n <i class=\"he he-close\"></i>\r\n </label> }\r\n @if (!inputLoader) {\r\n <label class=\"arrow\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </label>\r\n }\r\n\r\n @if (isDropdownOpen) {\r\n <div #dropdown class=\"option-list\">\r\n <div class=\"no-results\" *ngIf=\"filteredOptions.length === 0\">\r\n <div>No results found</div>\r\n <div *ngIf=\"isAddNewItem\" (click)=\"onAddNewItemClick()\" class=\"btn-new\">Add New Item</div>\r\n </div>\r\n\r\n @for (option of filteredOptions; track option[optionDisplayProperty]; let idx = $index) {\r\n <div class=\"list-item\" [ngClass]=\"{\r\n 'active': option === selectedItem,\r\n 'highlighted': idx === highlightedIndex\r\n }\" (click)=\"selectOption(option)\">\r\n @if (option.countryCode) {\r\n <img src=\"https://flagcdn.com/w80/{{ option.countryCode }}.png\" width=\"20\" alt=\"{{ option.countryCode }} flag\" loading=\"lazy\" />\r\n }\r\n {{ option[optionDisplayProperty] }}\r\n </div>\r\n }\r\n\r\n </div>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group.select .option-list .no-results{padding:10px;color:#9b9b9b;text-align:center}.form-group.select .option-list .no-results .btn-new{padding:5px 0;cursor:pointer;color:#0d6efd}.form-group.select .clear{right:30px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", outputs: ["clickOutside"] }] });
2282
- }
2283
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SelectControl, decorators: [{
2284
- type: Component,
2285
- args: [{ selector: "select-control", standalone: true, imports: [
2286
- CommonModule,
2287
- ReactiveFormsModule,
2288
- ClickOutsideDirective
2289
- ], providers: [
2290
- {
2291
- provide: NG_VALUE_ACCESSOR,
2292
- useExisting: forwardRef(() => SelectControl),
2293
- multi: true,
2294
- },
2295
- ], template: "<div class=\"form-group select\" [ngClass]=\"customClass\" (clickOutside)=\"selectListOutsideClick($event)\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{ 'required': required }\">{{ title }}</label>\r\n }\r\n\r\n <input #input type=\"text\" class=\"form-control\" [placeholder]=\"placeholder ? placeholder : ''\"\r\n [formControl]=\"inputControl\" [ngClass]=\"{ 'is-invalid': error }\" (blur)=\"onBlur()\" (click)=\"toggleDropdown()\"\r\n (keydown)=\"onKeyDown($event)\" readonly [attr.autocomplete]=\"autocomplete || null\" />\r\n\r\n @if (deFocus) {\r\n <span class=\"focus-border\"></span>\r\n }\r\n\r\n @if (!inputLoader && isDropdownOpen && selectedItem && clearVal) {\r\n <label class=\"clear\" (click)=\"resetSelection()\">\r\n <i class=\"he he-close\"></i>\r\n </label> }\r\n @if (!inputLoader) {\r\n <label class=\"arrow\">\r\n <i class=\"he he-chevron-down\"></i>\r\n </label>\r\n }\r\n\r\n @if (isDropdownOpen) {\r\n <div #dropdown class=\"option-list\">\r\n <div class=\"no-results\" *ngIf=\"filteredOptions.length === 0\">\r\n <div>No results found</div>\r\n <div *ngIf=\"isAddNewItem\" (click)=\"onAddNewItemClick()\" class=\"btn-new\">Add New Item</div>\r\n </div>\r\n\r\n @for (option of filteredOptions; track option[optionDisplayProperty]; let idx = $index) {\r\n <div class=\"list-item\" [ngClass]=\"{\r\n 'active': option === selectedItem,\r\n 'highlighted': idx === highlightedIndex\r\n }\" (click)=\"selectOption(option)\">\r\n @if (option.countryCode) {\r\n <img src=\"https://flagcdn.com/w80/{{ option.countryCode }}.png\" width=\"20\" alt=\"{{ option.countryCode }} flag\" loading=\"lazy\" />\r\n }\r\n {{ option[optionDisplayProperty] }}\r\n </div>\r\n }\r\n\r\n </div>\r\n }\r\n\r\n @if (inputLoader) {\r\n <label class=\"loader input-loader\"></label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group.select .option-list .no-results{padding:10px;color:#9b9b9b;text-align:center}.form-group.select .option-list .no-results .btn-new{padding:5px 0;cursor:pointer;color:#0d6efd}.form-group.select .clear{right:30px}\n"] }]
2296
- }], propDecorators: { optionDisplayProperty: [{
2297
- type: Input
2298
- }], required: [{
2299
- type: Input
2300
- }], placeholder: [{
2301
- type: Input
2302
- }], customClass: [{
2303
- type: Input
2304
- }], clearVal: [{
2305
- type: Input
2306
- }], deFocus: [{
2307
- type: Input
2308
- }], error: [{
2309
- type: Input
2310
- }], errorMessage: [{
2311
- type: Input
2312
- }], autocomplete: [{
2313
- type: Input
2314
- }], inputLoader: [{
2315
- type: Input
2316
- }], isAddNewItem: [{
2317
- type: Input
2318
- }], optionSelected: [{
2319
- type: Output
2320
- }], selectionCleared: [{
2321
- type: Output
2322
- }], addNewItemClicked: [{
2323
- type: Output
2324
- }], blurEvent: [{
2325
- type: Output
2326
- }], optionPatched: [{
2327
- type: Output
2328
- }], title: [{
2329
- type: Input
2330
- }], options: [{
2331
- type: Input
2332
- }], disabled: [{
2333
- type: Input
2334
- }], dropdown: [{
2335
- type: ViewChild,
2336
- args: ["dropdown", { static: false }]
2337
- }], input: [{
2338
- type: ViewChild,
2339
- args: ["input", { static: false }]
2340
- }] } });
2341
-
2342
- class SwitchControl {
2343
- _renderer;
2344
- _elementRef;
2345
- title;
2346
- customClass;
2347
- set disabled(value) {
2348
- this._disabled = value;
2349
- this.setDisabledState(value);
2350
- }
2351
- set checked(value) {
2352
- this._checked = value;
2353
- this.switchControl.setValue(value, { emitEvent: false }); // set without emitting
2354
- }
2355
- switchChange = new EventEmitter();
2356
- blurEvent = new EventEmitter();
2357
- switchContainer;
2358
- get checked() {
2359
- return this._checked;
2360
- }
2361
- _checked = false;
2362
- _disabled = false;
2363
- switchControl = new FormControl({ value: false, disabled: this._disabled });
2364
- onChange = () => { };
2365
- onTouched = () => { };
2366
- constructor(_renderer, _elementRef) {
2367
- this._renderer = _renderer;
2368
- this._elementRef = _elementRef;
2369
- this.switchControl.valueChanges.subscribe(value => {
2370
- this.onChange(value ?? false);
2371
- this.onTouched();
2372
- this.switchChange.emit(value ?? false);
2373
- });
2374
- }
2375
- writeValue(value) {
2376
- if (value !== undefined) {
2377
- this.switchControl.setValue(value, { emitEvent: false });
2378
- }
2379
- }
2380
- registerOnChange(fn) {
2381
- this.onChange = fn;
2382
- }
2383
- registerOnTouched(fn) {
2384
- this.onTouched = fn;
2385
- }
2386
- // events
2387
- onFocus() {
2388
- this.switchContainer.nativeElement.classList.add('focused');
2389
- }
2390
- onBlur() {
2391
- this.blurEvent.emit();
2392
- this.onTouched();
2393
- this.switchContainer.nativeElement.classList.remove('focused');
2394
- }
2395
- setDisabledState(isDisabled) {
2396
- this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
2397
- if (isDisabled) {
2398
- this.switchControl.disable();
2399
- }
2400
- else {
2401
- this.switchControl.enable();
2402
- }
2403
- }
2404
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SwitchControl, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2405
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: SwitchControl, isStandalone: true, selector: "switch-control", inputs: { title: "title", customClass: "customClass", disabled: "disabled", checked: "checked" }, outputs: { switchChange: "switchChange", blurEvent: "blurEvent" }, providers: [
2406
- {
2407
- provide: NG_VALUE_ACCESSOR,
2408
- useExisting: forwardRef(() => SwitchControl),
2409
- multi: true,
2410
- },
2411
- ], viewQueries: [{ propertyName: "switchContainer", first: true, predicate: ["switchContainer"], descendants: true }], ngImport: i0, template: "<label class=\"switch-container\" [ngClass]=\"customClass\" #switchContainer>\r\n <input type=\"checkbox\" [formControl]=\"switchControl\" (focus)=\"onFocus()\" (blur)=\"onBlur()\">\r\n <span class=\"switch-slider\"></span>\r\n</label>", styles: [".switch-container{top:4px;width:32px;height:14px;position:relative;display:inline-block}.switch-container .switch-slider{inset:0;cursor:pointer;transition:.4s;position:absolute;border-radius:34px;background-color:#ccc}.switch-container .switch-slider:before{left:0;content:\"\";width:18px;bottom:-2px;height:18px;transition:.4s;position:absolute;border-radius:50%;border:1px solid #cccccc;background-color:#fff}.switch-container input{width:0;height:0;opacity:0}.switch-container input:checked+.switch-slider{background-color:#12ceb7}.switch-container input:checked+.switch-slider:before{transform:translate(14px);border:1px solid #12ceb7}.switch-container input:focus+.switch-slider{box-shadow:0 0 1px #12ceb7}.switch-container.square .switch-slider{border-radius:0}.switch-container.square .switch-slider:before{border-radius:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2412
- }
2413
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SwitchControl, decorators: [{
2414
- type: Component,
2415
- args: [{ selector: 'switch-control', standalone: true, imports: [
2416
- CommonModule,
2417
- ReactiveFormsModule
2418
- ], providers: [
2419
- {
2420
- provide: NG_VALUE_ACCESSOR,
2421
- useExisting: forwardRef(() => SwitchControl),
2422
- multi: true,
2423
- },
2424
- ], template: "<label class=\"switch-container\" [ngClass]=\"customClass\" #switchContainer>\r\n <input type=\"checkbox\" [formControl]=\"switchControl\" (focus)=\"onFocus()\" (blur)=\"onBlur()\">\r\n <span class=\"switch-slider\"></span>\r\n</label>", styles: [".switch-container{top:4px;width:32px;height:14px;position:relative;display:inline-block}.switch-container .switch-slider{inset:0;cursor:pointer;transition:.4s;position:absolute;border-radius:34px;background-color:#ccc}.switch-container .switch-slider:before{left:0;content:\"\";width:18px;bottom:-2px;height:18px;transition:.4s;position:absolute;border-radius:50%;border:1px solid #cccccc;background-color:#fff}.switch-container input{width:0;height:0;opacity:0}.switch-container input:checked+.switch-slider{background-color:#12ceb7}.switch-container input:checked+.switch-slider:before{transform:translate(14px);border:1px solid #12ceb7}.switch-container input:focus+.switch-slider{box-shadow:0 0 1px #12ceb7}.switch-container.square .switch-slider{border-radius:0}.switch-container.square .switch-slider:before{border-radius:0}\n"] }]
2425
- }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }], propDecorators: { title: [{
2426
- type: Input
2427
- }], customClass: [{
2428
- type: Input
2429
- }], disabled: [{
2430
- type: Input
2431
- }], checked: [{
2432
- type: Input
2433
- }], switchChange: [{
2434
- type: Output
2435
- }], blurEvent: [{
2436
- type: Output
2437
- }], switchContainer: [{
2438
- type: ViewChild,
2439
- args: ['switchContainer']
2440
- }] } });
2441
-
2442
- class TextEditor {
2443
- header = true;
2444
- media = true;
2445
- link = true;
2446
- placeholder = 'Type here...';
2447
- editorRef;
2448
- onChange = () => { };
2449
- onTouch = () => { };
2450
- undoStack = [];
2451
- redoStack = [];
2452
- ngOnInit() { }
2453
- ngAfterViewInit() {
2454
- this.saveState();
2455
- }
2456
- writeValue(value) {
2457
- this.editorRef.nativeElement.innerHTML = value || '';
2458
- this.saveState();
2459
- }
2460
- registerOnChange(fn) {
2461
- this.onChange = fn;
2462
- }
2463
- registerOnTouched(fn) {
2464
- this.onTouch = fn;
2465
- }
2466
- onInput() {
2467
- const html = this.editorRef.nativeElement.innerHTML;
2468
- this.onChange(html);
2469
- this.saveState();
2470
- }
2471
- // events
2472
- onTouched() {
2473
- this.onTouch();
2474
- }
2475
- setDisabledState(isDisabled) {
2476
- this.editorRef.nativeElement.contentEditable = (!isDisabled).toString();
2477
- }
2478
- // ---- Selection & Range Utilities ----
2479
- getSelectionRange() {
2480
- const selection = window.getSelection();
2481
- if (selection && selection.rangeCount > 0) {
2482
- return selection.getRangeAt(0);
2483
- }
2484
- return null;
2485
- }
2486
- wrapSelection(htmlTag, style) {
2487
- const range = this.getSelectionRange();
2488
- if (!range || range.collapsed)
2489
- return; // no selection
2490
- const selectedText = range.extractContents();
2491
- const el = document.createElement(htmlTag);
2492
- if (style) {
2493
- el.setAttribute('style', style);
2494
- }
2495
- el.appendChild(selectedText);
2496
- range.insertNode(el);
2497
- this.mergeAdjacentSimilarElements(el);
2498
- this.onInput();
2499
- }
2500
- // Merges adjacent spans with the same style to avoid clutter
2501
- mergeAdjacentSimilarElements(el) {
2502
- const parent = el.parentElement;
2503
- if (!parent)
2504
- return;
2505
- const siblings = Array.from(parent.childNodes);
2506
- for (let i = 0; i < siblings.length - 1; i++) {
2507
- const current = siblings[i];
2508
- const next = siblings[i + 1];
2509
- if (current.nodeType === 1 && next?.nodeType === 1) {
2510
- if (current.tagName === next.tagName && current.getAttribute('style') === next.getAttribute('style')) {
2511
- // merge them
2512
- while (next.firstChild) {
2513
- current.appendChild(next.firstChild);
2514
- }
2515
- next.remove();
2516
- }
2517
- }
2518
- }
2519
- }
2520
- // ---- Inline Formatting ----
2521
- // applyInlineStyle() toggles inline styles by wrapping selection in a styled span
2522
- applyInlineStyle(styleType) {
2523
- let style = '';
2524
- if (styleType === 'bold')
2525
- style = 'font-weight:bold;';
2526
- else if (styleType === 'italic')
2527
- style = 'font-style:italic;';
2528
- else if (styleType === 'underline')
2529
- style = 'text-decoration:underline;';
2530
- this.wrapSelection('span', style);
2531
- }
2532
- applyColor(event) {
2533
- const input = event.target;
2534
- const color = input.value;
2535
- // Now `color` is a string and `input` is never null
2536
- this.wrapSelection('span', `color:${color};`);
2537
- }
2538
- applyHighlight(event) {
2539
- const input = event.target;
2540
- const color = input.value;
2541
- this.wrapSelection('span', `background-color:${color};`);
2542
- }
2543
- // ---- Block Formatting (Headings, Paragraph) ----
2544
- // Replaces the parent block element containing selection with a new block type
2545
- applyBlockFormat(blockTag) {
2546
- const range = this.getSelectionRange();
2547
- if (!range)
2548
- return;
2549
- // Find the nearest block element
2550
- let block = this.findBlockAncestor(range.commonAncestorContainer);
2551
- if (!block) {
2552
- // If no block found, wrap the selection in a block
2553
- const wrapper = document.createElement(blockTag);
2554
- wrapper.appendChild(range.extractContents());
2555
- range.insertNode(wrapper);
2556
- }
2557
- else {
2558
- // Replace the block with a new block type
2559
- const newBlock = document.createElement(blockTag);
2560
- // Move children
2561
- while (block.firstChild) {
2562
- newBlock.appendChild(block.firstChild);
2563
- }
2564
- block.replaceWith(newBlock);
2565
- }
2566
- this.onInput();
2567
- }
2568
- findBlockAncestor(node) {
2569
- while (node && node !== this.editorRef.nativeElement) {
2570
- if (this.isBlockElement(node)) {
2571
- return node;
2572
- }
2573
- node = node.parentNode;
2574
- }
2575
- return null;
2576
- }
2577
- isBlockElement(node) {
2578
- if (node.nodeType !== 1)
2579
- return false;
2580
- const display = window.getComputedStyle(node).display;
2581
- return display === 'block' || display === 'list-item';
2582
- }
2583
- // ---- Lists ----
2584
- // Convert selected lines into a list
2585
- applyList(listType) {
2586
- const range = this.getSelectionRange();
2587
- if (!range)
2588
- return;
2589
- const commonBlock = this.findBlockAncestor(range.commonAncestorContainer);
2590
- // Get the text in the selection
2591
- const content = range.extractContents();
2592
- const lines = this.splitContentByLine(content);
2593
- const listEl = document.createElement(listType);
2594
- for (const line of lines) {
2595
- const li = document.createElement('li');
2596
- li.appendChild(line);
2597
- listEl.appendChild(li);
2598
- }
2599
- if (commonBlock) {
2600
- // Insert the list right where the block was or at the selection
2601
- range.insertNode(listEl);
2602
- }
2603
- else {
2604
- // If no common block, just insert at current position
2605
- range.insertNode(listEl);
2606
- }
2607
- this.onInput();
2608
- }
2609
- splitContentByLine(fragment) {
2610
- // A very naive approach: split by <br> or block elements.
2611
- // For advanced logic, detect line breaks more thoroughly.
2612
- const lines = [];
2613
- let currentFrag = document.createDocumentFragment();
2614
- Array.from(fragment.childNodes).forEach((node) => {
2615
- if (node.nodeName === 'BR' || this.isBlockElement(node)) {
2616
- // This node signifies a new line
2617
- lines.push(currentFrag);
2618
- currentFrag = document.createDocumentFragment();
2619
- if (this.isBlockElement(node)) {
2620
- while (node.firstChild) {
2621
- currentFrag.appendChild(node.firstChild);
2622
- }
2623
- lines.push(currentFrag);
2624
- currentFrag = document.createDocumentFragment();
2625
- }
2626
- }
2627
- else {
2628
- currentFrag.appendChild(node);
2629
- }
2630
- });
2631
- if (currentFrag.childNodes.length > 0) {
2632
- lines.push(currentFrag);
2633
- }
2634
- return lines;
2635
- }
2636
- // ---- Links ----
2637
- createLink() {
2638
- const url = prompt('Enter URL', 'https://');
2639
- if (!url)
2640
- return;
2641
- const range = this.getSelectionRange();
2642
- if (!range || range.collapsed)
2643
- return;
2644
- const selectedContent = range.extractContents();
2645
- const a = document.createElement('a');
2646
- a.href = url;
2647
- a.appendChild(selectedContent);
2648
- range.insertNode(a);
2649
- this.onInput();
2650
- }
2651
- // ---- Images ----
2652
- insertImage() {
2653
- const url = prompt('Enter image URL:');
2654
- if (!url)
2655
- return;
2656
- const range = this.getSelectionRange();
2657
- if (!range)
2658
- return;
2659
- const img = document.createElement('img');
2660
- img.src = url;
2661
- range.insertNode(img);
2662
- this.onInput();
2663
- }
2664
- // ---- Alignment ----
2665
- setAlignment(alignment) {
2666
- // Find the block and set text-align
2667
- const range = this.getSelectionRange();
2668
- if (!range)
2669
- return;
2670
- const block = this.findBlockAncestor(range.commonAncestorContainer);
2671
- if (block) {
2672
- block.style.textAlign = alignment;
2673
- this.onInput();
2674
- }
2675
- }
2676
- // ---- Clear Formatting ----
2677
- clearFormatting() {
2678
- const html = this.editorRef.nativeElement.innerText;
2679
- // Convert innerText to a plain <p> block for simplicity
2680
- this.editorRef.nativeElement.innerHTML = `<p>${html}</p>`;
2681
- this.onInput();
2682
- }
2683
- // ---- Undo/Redo ----
2684
- saveState() {
2685
- const currentHtml = this.editorRef.nativeElement.innerHTML;
2686
- if (this.undoStack.length === 0 || this.undoStack[this.undoStack.length - 1].html !== currentHtml) {
2687
- this.undoStack.push({ html: currentHtml });
2688
- this.redoStack = []; // clear redo on new input
2689
- }
2690
- }
2691
- // clear options
2692
- undo() {
2693
- if (this.undoStack.length > 1) {
2694
- const current = this.undoStack.pop();
2695
- this.redoStack.push(current);
2696
- const previous = this.undoStack[this.undoStack.length - 1];
2697
- this.editorRef.nativeElement.innerHTML = previous.html;
2698
- this.onChange(previous.html);
2699
- }
2700
- }
2701
- redo() {
2702
- if (this.redoStack.length > 0) {
2703
- const state = this.redoStack.pop();
2704
- this.undoStack.push(state);
2705
- this.editorRef.nativeElement.innerHTML = state.html;
2706
- this.onChange(state.html);
2707
- }
2708
- }
2709
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: TextEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
2710
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: TextEditor, isStandalone: true, selector: "app-text-editor", inputs: { header: "header", media: "media", link: "link", placeholder: "placeholder" }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true }], ngImport: i0, template: "<div class=\"text-editor\">\r\n <div class=\"toolbar\">\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"applyInlineStyle('bold')\" tooltip=\"Bold\">\r\n <i class=\"he he-bold\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyInlineStyle('italic')\" tooltip=\"italic\">\r\n <i class=\"he he-italics\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyInlineStyle('underline')\" tooltip=\"Underline\">\r\n <i class=\"he he-underline\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\" *ngIf=\"header\">\r\n <button type=\"button\" (click)=\"applyBlockFormat('h1')\" tooltip=\"Header 1\">\r\n <i class=\"he he-heading-1\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyBlockFormat('h2')\" tooltip=\"Header 2\">\r\n <i class=\"he he-heading-2\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items more-gap\">\r\n <div class=\"input-wrap\" tooltip=\"Text Color\">\r\n <i class=\"he he-text-drop\"></i>\r\n <input type=\"color\" (change)=\"applyColor($event)\" />\r\n </div>\r\n <div class=\"input-wrap\" tooltip=\"Background Color\">\r\n <i class=\"he he-background-drop\"></i>\r\n <input type=\"color\" (change)=\"applyHighlight($event)\" />\r\n </div>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"setAlignment('left')\" tooltip=\"Justify Left\">\r\n <i class=\"he he-left-align\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"setAlignment('center')\" tooltip=\"Justify Center\">\r\n <i class=\"he he-center-align\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"setAlignment('right')\" tooltip=\"Justify Right\">\r\n <i class=\"he he-right-align\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"setAlignment('justify')\" tooltip=\"Justify Full\">\r\n <i class=\"he he-justify\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" tooltip=\"Indent\">\r\n <i class=\"he he-text-indent-left\"></i>\r\n </button>\r\n <button type=\"button\" tooltip=\"Outdent\">\r\n <i class=\"he he-text-indent-right\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\" *ngIf=\"media\">\r\n <button type=\"button\" (click)=\"applyList('ul')\" tooltip=\"Unordered list\">\r\n <i class=\"he he-unordered-list\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyList('ol')\" tooltip=\"Ordered list\">\r\n <i class=\"he he-ordered-list\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\" *ngIf=\"link\">\r\n <button type=\"button\" (click)=\"createLink()\" tooltip=\"Link\">\r\n <i class=\"he he-link\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"insertImage()\" tooltip=\"Image\">\r\n <i class=\"he he-image\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"undo()\" tooltip=\"Undo\">\r\n <i class=\"he he-undo\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"redo()\" tooltip=\"Redo\">\r\n <i class=\"he he-redo\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"clearFormatting()\" tooltip=\"Clear Formatting\">\r\n <i class=\"he he-text-clear-format\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div #editor class=\"editor-content\" contenteditable=\"true\" (input)=\"onInput()\" (blur)=\"onTouched()\"\r\n [attr.data-placeholder]=\"placeholder\">\r\n </div>\r\n</div>", styles: [".text-editor{border:1px solid #ccc}.text-editor .toolbar{gap:.75rem;display:flex;flex-wrap:wrap;background:#f8f8f8;padding:.75rem;border-bottom:1px solid #cccccc}.text-editor .toolbar .toolbar-items{height:18px;display:flex;flex-wrap:wrap;align-items:center;padding-right:.75rem;border-right:1px solid #969090}.text-editor .toolbar .toolbar-items button,.text-editor .toolbar .toolbar-items .input-wrap{cursor:pointer;min-width:25px;min-height:20px}.text-editor .toolbar .toolbar-items button:hover i,.text-editor .toolbar .toolbar-items .input-wrap:hover i{color:#000}.text-editor .toolbar .toolbar-items button{border:0;display:grid;place-items:center;background:transparent}.text-editor .toolbar .toolbar-items button:hover{background:#eee}.text-editor .toolbar .toolbar-items .input-wrap{gap:2px;display:flex;cursor:pointer;align-items:center;flex-direction:column;justify-content:center}.text-editor .toolbar .toolbar-items .input-wrap input[type=color]{width:80%;padding:0;height:2px;border:none;cursor:inherit}.text-editor .toolbar .toolbar-items i{font-size:13px;color:#3c4148}.text-editor .toolbar .toolbar-items i.he-bold{font-weight:600}.text-editor .toolbar .toolbar-items i.he-underline{font-size:14px}.text-editor .toolbar .toolbar-items i.he-text-drop{font-size:12px}.text-editor .toolbar .toolbar-items i.he-background-drop{top:-1px;font-size:12px}.text-editor .toolbar .toolbar-items i.he-heading-1,.text-editor .toolbar .toolbar-items i.he-link,.text-editor .toolbar .toolbar-items i.he-left-align,.text-editor .toolbar .toolbar-items i.he-center-align,.text-editor .toolbar .toolbar-items i.he-right-align,.text-editor .toolbar .toolbar-items i.he-justify{font-size:16px}.text-editor .toolbar .toolbar-items i.he-text-indent-left,.text-editor .toolbar .toolbar-items i.he-text-indent-right{font-size:17px}.text-editor .toolbar .toolbar-items i.he-heading-2{top:1px;font-size:16px}.text-editor .toolbar .toolbar-items i.he-unordered-list,.text-editor .toolbar .toolbar-items i.he-ordered-list{top:-1px;font-size:20px}.text-editor .toolbar .toolbar-items i.he-image{font-size:15px;font-weight:600}.text-editor .toolbar .toolbar-items i.he-redo,.text-editor .toolbar .toolbar-items i.he-undo{top:-1px;font-size:14px}.text-editor .toolbar .toolbar-items.more-gap{gap:5px}.text-editor .toolbar .toolbar-items:last-child{border-right:0}.text-editor .editor-content{outline:none;min-height:200px;position:relative;background:#fff;padding:.75rem}.text-editor .editor-content[data-placeholder]:empty:before{content:attr(data-placeholder);color:#aaa;pointer-events:none;position:absolute;left:.75rem;top:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2711
- }
2712
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: TextEditor, decorators: [{
2713
- type: Component,
2714
- args: [{ selector: 'app-text-editor', imports: [CommonModule,
2715
- ], template: "<div class=\"text-editor\">\r\n <div class=\"toolbar\">\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"applyInlineStyle('bold')\" tooltip=\"Bold\">\r\n <i class=\"he he-bold\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyInlineStyle('italic')\" tooltip=\"italic\">\r\n <i class=\"he he-italics\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyInlineStyle('underline')\" tooltip=\"Underline\">\r\n <i class=\"he he-underline\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\" *ngIf=\"header\">\r\n <button type=\"button\" (click)=\"applyBlockFormat('h1')\" tooltip=\"Header 1\">\r\n <i class=\"he he-heading-1\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyBlockFormat('h2')\" tooltip=\"Header 2\">\r\n <i class=\"he he-heading-2\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items more-gap\">\r\n <div class=\"input-wrap\" tooltip=\"Text Color\">\r\n <i class=\"he he-text-drop\"></i>\r\n <input type=\"color\" (change)=\"applyColor($event)\" />\r\n </div>\r\n <div class=\"input-wrap\" tooltip=\"Background Color\">\r\n <i class=\"he he-background-drop\"></i>\r\n <input type=\"color\" (change)=\"applyHighlight($event)\" />\r\n </div>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"setAlignment('left')\" tooltip=\"Justify Left\">\r\n <i class=\"he he-left-align\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"setAlignment('center')\" tooltip=\"Justify Center\">\r\n <i class=\"he he-center-align\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"setAlignment('right')\" tooltip=\"Justify Right\">\r\n <i class=\"he he-right-align\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"setAlignment('justify')\" tooltip=\"Justify Full\">\r\n <i class=\"he he-justify\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" tooltip=\"Indent\">\r\n <i class=\"he he-text-indent-left\"></i>\r\n </button>\r\n <button type=\"button\" tooltip=\"Outdent\">\r\n <i class=\"he he-text-indent-right\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\" *ngIf=\"media\">\r\n <button type=\"button\" (click)=\"applyList('ul')\" tooltip=\"Unordered list\">\r\n <i class=\"he he-unordered-list\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"applyList('ol')\" tooltip=\"Ordered list\">\r\n <i class=\"he he-ordered-list\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\" *ngIf=\"link\">\r\n <button type=\"button\" (click)=\"createLink()\" tooltip=\"Link\">\r\n <i class=\"he he-link\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"insertImage()\" tooltip=\"Image\">\r\n <i class=\"he he-image\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"undo()\" tooltip=\"Undo\">\r\n <i class=\"he he-undo\"></i>\r\n </button>\r\n <button type=\"button\" (click)=\"redo()\" tooltip=\"Redo\">\r\n <i class=\"he he-redo\"></i>\r\n </button>\r\n </div>\r\n <div class=\"toolbar-items\">\r\n <button type=\"button\" (click)=\"clearFormatting()\" tooltip=\"Clear Formatting\">\r\n <i class=\"he he-text-clear-format\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div #editor class=\"editor-content\" contenteditable=\"true\" (input)=\"onInput()\" (blur)=\"onTouched()\"\r\n [attr.data-placeholder]=\"placeholder\">\r\n </div>\r\n</div>", styles: [".text-editor{border:1px solid #ccc}.text-editor .toolbar{gap:.75rem;display:flex;flex-wrap:wrap;background:#f8f8f8;padding:.75rem;border-bottom:1px solid #cccccc}.text-editor .toolbar .toolbar-items{height:18px;display:flex;flex-wrap:wrap;align-items:center;padding-right:.75rem;border-right:1px solid #969090}.text-editor .toolbar .toolbar-items button,.text-editor .toolbar .toolbar-items .input-wrap{cursor:pointer;min-width:25px;min-height:20px}.text-editor .toolbar .toolbar-items button:hover i,.text-editor .toolbar .toolbar-items .input-wrap:hover i{color:#000}.text-editor .toolbar .toolbar-items button{border:0;display:grid;place-items:center;background:transparent}.text-editor .toolbar .toolbar-items button:hover{background:#eee}.text-editor .toolbar .toolbar-items .input-wrap{gap:2px;display:flex;cursor:pointer;align-items:center;flex-direction:column;justify-content:center}.text-editor .toolbar .toolbar-items .input-wrap input[type=color]{width:80%;padding:0;height:2px;border:none;cursor:inherit}.text-editor .toolbar .toolbar-items i{font-size:13px;color:#3c4148}.text-editor .toolbar .toolbar-items i.he-bold{font-weight:600}.text-editor .toolbar .toolbar-items i.he-underline{font-size:14px}.text-editor .toolbar .toolbar-items i.he-text-drop{font-size:12px}.text-editor .toolbar .toolbar-items i.he-background-drop{top:-1px;font-size:12px}.text-editor .toolbar .toolbar-items i.he-heading-1,.text-editor .toolbar .toolbar-items i.he-link,.text-editor .toolbar .toolbar-items i.he-left-align,.text-editor .toolbar .toolbar-items i.he-center-align,.text-editor .toolbar .toolbar-items i.he-right-align,.text-editor .toolbar .toolbar-items i.he-justify{font-size:16px}.text-editor .toolbar .toolbar-items i.he-text-indent-left,.text-editor .toolbar .toolbar-items i.he-text-indent-right{font-size:17px}.text-editor .toolbar .toolbar-items i.he-heading-2{top:1px;font-size:16px}.text-editor .toolbar .toolbar-items i.he-unordered-list,.text-editor .toolbar .toolbar-items i.he-ordered-list{top:-1px;font-size:20px}.text-editor .toolbar .toolbar-items i.he-image{font-size:15px;font-weight:600}.text-editor .toolbar .toolbar-items i.he-redo,.text-editor .toolbar .toolbar-items i.he-undo{top:-1px;font-size:14px}.text-editor .toolbar .toolbar-items.more-gap{gap:5px}.text-editor .toolbar .toolbar-items:last-child{border-right:0}.text-editor .editor-content{outline:none;min-height:200px;position:relative;background:#fff;padding:.75rem}.text-editor .editor-content[data-placeholder]:empty:before{content:attr(data-placeholder);color:#aaa;pointer-events:none;position:absolute;left:.75rem;top:.75rem}\n"] }]
2716
- }], propDecorators: { header: [{
2717
- type: Input
2718
- }], media: [{
2719
- type: Input
2720
- }], link: [{
2721
- type: Input
2722
- }], placeholder: [{
2723
- type: Input
2724
- }], editorRef: [{
2725
- type: ViewChild,
2726
- args: ['editor']
2727
- }] } });
2728
-
2729
- class TextareaControl {
2730
- title;
2731
- placeholder = '';
2732
- customClass;
2733
- clearVal = true;
2734
- disabled = false;
2735
- required = false;
2736
- error = false; // New input for error state
2737
- errorMessage = ''; // New input for error message
2738
- textareaChange = new EventEmitter();
2739
- selectionCleared = new EventEmitter();
2740
- blurEvent = new EventEmitter();
2741
- onChange = () => { };
2742
- onTouched = () => { };
2743
- textareaControl = new FormControl('');
2744
- constructor() {
2745
- this.textareaControl.valueChanges.subscribe(value => {
2746
- this.onChange(value);
2747
- this.onTouched();
2748
- this.textareaChange.emit(value ?? '');
2749
- });
2750
- }
2751
- writeValue(value) {
2752
- if (value !== undefined) {
2753
- this.textareaControl.setValue(value, { emitEvent: false });
2754
- }
2755
- if (this.textareaControl.disabled !== this.disabled) {
2756
- this.setDisabledState(this.disabled);
2757
- }
2758
- }
2759
- registerOnChange(fn) {
2760
- this.onChange = fn;
2761
- }
2762
- registerOnTouched(fn) {
2763
- this.onTouched = fn;
2764
- }
2765
- // events
2766
- onBlur() {
2767
- this.blurEvent.emit();
2768
- this.onTouched();
2769
- }
2770
- setDisabledState(isDisabled) {
2771
- if (this.disabled) {
2772
- this.textareaControl.disable({ emitEvent: false });
2773
- }
2774
- else {
2775
- this.textareaControl.enable({ emitEvent: false });
2776
- }
2777
- }
2778
- // clear
2779
- resetTextarea() {
2780
- this.textareaControl.setValue('');
2781
- this.selectionCleared.emit();
2782
- }
2783
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: TextareaControl, deps: [], target: i0.ɵɵFactoryTarget.Component });
2784
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: TextareaControl, isStandalone: true, selector: "textarea-control", inputs: { title: "title", placeholder: "placeholder", customClass: "customClass", clearVal: "clearVal", disabled: "disabled", required: "required", error: "error", errorMessage: "errorMessage" }, outputs: { textareaChange: "textareaChange", selectionCleared: "selectionCleared", blurEvent: "blurEvent" }, providers: [
2785
- {
2786
- provide: NG_VALUE_ACCESSOR,
2787
- useExisting: forwardRef(() => TextareaControl),
2788
- multi: true,
2789
- },
2790
- ], ngImport: i0, template: "<div class=\"form-group textarea\" [ngClass]=\"customClass\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{'required' : required}\">{{ title }}</label>\r\n }\r\n <textarea class=\"form-control\" [placeholder]=\"placeholder\" [formControl]=\"textareaControl\"\r\n [ngClass]=\"{ 'is-invalid': error }\" (blur)=\"onBlur()\"></textarea>\r\n\r\n <span class=\"focus-border\"></span>\r\n\r\n @if (textareaControl.value && clearVal) {\r\n <label class=\"clear\" (click)=\"resetTextarea()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group textarea{resize:none;min-height:100px}.form-group textarea.resize{resize:auto}.form-group textarea::-webkit-scrollbar-thumb{border-radius:10px}.form-group textarea::-webkit-scrollbar-track{background:#e8eaf1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
2791
- }
2792
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: TextareaControl, decorators: [{
2793
- type: Component,
2794
- args: [{ selector: 'textarea-control', standalone: true, imports: [
2795
- CommonModule,
2796
- ReactiveFormsModule
2797
- ], providers: [
2798
- {
2799
- provide: NG_VALUE_ACCESSOR,
2800
- useExisting: forwardRef(() => TextareaControl),
2801
- multi: true,
2802
- },
2803
- ], template: "<div class=\"form-group textarea\" [ngClass]=\"customClass\">\r\n @if (title) {\r\n <label class=\"inp-label\" [ngClass]=\"{'required' : required}\">{{ title }}</label>\r\n }\r\n <textarea class=\"form-control\" [placeholder]=\"placeholder\" [formControl]=\"textareaControl\"\r\n [ngClass]=\"{ 'is-invalid': error }\" (blur)=\"onBlur()\"></textarea>\r\n\r\n <span class=\"focus-border\"></span>\r\n\r\n @if (textareaControl.value && clearVal) {\r\n <label class=\"clear\" (click)=\"resetTextarea()\">\r\n <i class=\"he he-close\"></i>\r\n </label>\r\n }\r\n\r\n @if (error) {\r\n <div class=\"val-msg\">{{ errorMessage }}</div>\r\n }\r\n</div>", styles: [".form-group textarea{resize:none;min-height:100px}.form-group textarea.resize{resize:auto}.form-group textarea::-webkit-scrollbar-thumb{border-radius:10px}.form-group textarea::-webkit-scrollbar-track{background:#e8eaf1}\n"] }]
2804
- }], ctorParameters: () => [], propDecorators: { title: [{
2805
- type: Input
2806
- }], placeholder: [{
2807
- type: Input
2808
- }], customClass: [{
2809
- type: Input
2810
- }], clearVal: [{
2811
- type: Input
2812
- }], disabled: [{
2813
- type: Input
2814
- }], required: [{
2815
- type: Input
2816
- }], error: [{
2817
- type: Input
2818
- }], errorMessage: [{
2819
- type: Input
2820
- }], textareaChange: [{
2821
- type: Output
2822
- }], selectionCleared: [{
2823
- type: Output
2824
- }], blurEvent: [{
2825
- type: Output
2826
- }] } });
2827
-
2828
- /*
2829
- * Public API Surface of heal-lib
2830
- */
2831
-
2832
- /**
2833
- * Generated bundle index. Do not edit.
2834
- */
2835
-
2836
- export { AutocompleteControl, CalendarControl, CheckboxControl, InputControl, MultiselectControl, SelectControl, SwitchControl, TextEditor, TextareaControl };
2837
- //# sourceMappingURL=nexheal-lib.mjs.map