@acpaas-ui/ngx-forms 6.1.9 → 6.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +42 -0
- package/LICENSE.md +21 -0
- package/dist/README.md +23 -0
- package/dist/esm2020/lib/datepicker/components/datepicker/datepicker.component.mjs +232 -0
- package/{fesm2015 → dist/fesm2015}/acpaas-ui-ngx-forms.mjs +7 -4
- package/dist/fesm2015/acpaas-ui-ngx-forms.mjs.map +1 -0
- package/{fesm2020 → dist/fesm2020}/acpaas-ui-ngx-forms.mjs +7 -4
- package/dist/fesm2020/acpaas-ui-ngx-forms.mjs.map +1 -0
- package/dist/package.json +44 -0
- package/karma.conf.js +38 -0
- package/ng-package.json +8 -0
- package/package.json +6 -27
- package/src/lib/auto-complete/README.md +148 -0
- package/src/lib/auto-complete/auto-complete.module.ts +15 -0
- package/src/lib/auto-complete/components/auto-complete/auto-complete.component.html +51 -0
- package/src/lib/auto-complete/components/auto-complete/auto-complete.component.scss +4 -0
- package/src/lib/auto-complete/components/auto-complete/auto-complete.component.spec.ts +378 -0
- package/src/lib/auto-complete/components/auto-complete/auto-complete.component.ts +281 -0
- package/src/lib/auto-complete/public-api.ts +2 -0
- package/src/lib/datepicker/README.md +110 -0
- package/src/lib/datepicker/components/datepicker/datepicker.component.html +47 -0
- package/src/lib/datepicker/components/datepicker/datepicker.component.scss +13 -0
- package/src/lib/datepicker/components/datepicker/datepicker.component.spec.ts +204 -0
- package/src/lib/datepicker/components/datepicker/datepicker.component.ts +251 -0
- package/src/lib/datepicker/datepicker.conf.ts +11 -0
- package/src/lib/datepicker/datepicker.module.ts +50 -0
- package/src/lib/datepicker/public-api.ts +8 -0
- package/src/lib/datepicker/types/datepicker.types.ts +8 -0
- package/src/lib/range-slider/README.md +56 -0
- package/src/lib/range-slider/components/range-slider/range-slider.component.html +46 -0
- package/src/lib/range-slider/components/range-slider/range-slider.component.scss +12 -0
- package/src/lib/range-slider/components/range-slider/range-slider.component.spec.ts +216 -0
- package/src/lib/range-slider/components/range-slider/range-slider.component.ts +301 -0
- package/src/lib/range-slider/public-api.ts +3 -0
- package/src/lib/range-slider/range-slider.module.ts +11 -0
- package/src/lib/range-slider/types/range-slider.types.ts +4 -0
- package/src/lib/search-filter/README.md +86 -0
- package/src/lib/search-filter/components/search-filter/search-filter.component.html +66 -0
- package/src/lib/search-filter/components/search-filter/search-filter.component.scss +23 -0
- package/src/lib/search-filter/components/search-filter/search-filter.component.spec.ts +264 -0
- package/src/lib/search-filter/components/search-filter/search-filter.component.ts +140 -0
- package/src/lib/search-filter/public-api.ts +3 -0
- package/src/lib/search-filter/search-filter.module.ts +13 -0
- package/src/lib/search-filter/types/search-filter.types.ts +4 -0
- package/src/lib/shared/services/search.service.spec.ts +78 -0
- package/src/lib/shared/services/search.service.ts +32 -0
- package/src/lib/shared/types/search.types.ts +6 -0
- package/src/lib/timepicker/README.md +84 -0
- package/src/lib/timepicker/classes/timepicker.validators.spec.ts +54 -0
- package/src/lib/timepicker/classes/timepicker.validators.ts +61 -0
- package/src/lib/timepicker/components/timepicker/timepicker.component.html +37 -0
- package/src/lib/timepicker/components/timepicker/timepicker.component.scss +3 -0
- package/src/lib/timepicker/components/timepicker/timepicker.component.spec.ts +161 -0
- package/src/lib/timepicker/components/timepicker/timepicker.component.ts +128 -0
- package/src/lib/timepicker/public-api.ts +4 -0
- package/src/lib/timepicker/timepicker.module.ts +13 -0
- package/src/lib/timepicker/types/timepicker.types.ts +5 -0
- package/src/lib/upload/README.md +283 -0
- package/src/lib/upload/classes/uploader.class.spec.ts +100 -0
- package/src/lib/upload/classes/uploader.class.ts +144 -0
- package/src/lib/upload/components/upload/upload.component.html +28 -0
- package/src/lib/upload/components/upload/upload.component.scss +3 -0
- package/src/lib/upload/components/upload/upload.component.spec.ts +117 -0
- package/src/lib/upload/components/upload/upload.component.ts +50 -0
- package/src/lib/upload/components/upload-input/upload-input.component.html +11 -0
- package/src/lib/upload/components/upload-input/upload-input.component.spec.ts +55 -0
- package/src/lib/upload/components/upload-input/upload-input.component.ts +35 -0
- package/src/lib/upload/components/upload-queue/upload-queue.component.html +16 -0
- package/src/lib/upload/components/upload-queue/upload-queue.component.spec.ts +99 -0
- package/src/lib/upload/components/upload-queue/upload-queue.component.ts +36 -0
- package/src/lib/upload/components/upload-zone/upload-zone.component.html +55 -0
- package/src/lib/upload/components/upload-zone/upload-zone.component.scss +3 -0
- package/src/lib/upload/components/upload-zone/upload-zone.component.spec.ts +144 -0
- package/src/lib/upload/components/upload-zone/upload-zone.component.ts +142 -0
- package/src/lib/upload/components/validation-list/validation-list.component.html +15 -0
- package/src/lib/upload/components/validation-list/validation-list.component.spec.ts +57 -0
- package/src/lib/upload/components/validation-list/validation-list.component.ts +29 -0
- package/src/lib/upload/public-api.ts +10 -0
- package/src/lib/upload/services/validation-messages.service.spec.ts +66 -0
- package/src/lib/upload/services/validation-messages.service.ts +27 -0
- package/src/lib/upload/types/upload.types.ts +20 -0
- package/src/lib/upload/upload.conf.ts +15 -0
- package/src/lib/upload/upload.module.ts +34 -0
- package/src/public-api.ts +6 -0
- package/src/test.ts +9 -0
- package/tsconfig.lib.json +26 -0
- package/tsconfig.spec.json +17 -0
- package/esm2020/lib/datepicker/components/datepicker/datepicker.component.mjs +0 -229
- package/fesm2015/acpaas-ui-ngx-forms.mjs.map +0 -1
- package/fesm2020/acpaas-ui-ngx-forms.mjs.map +0 -1
- /package/{esm2020 → dist/esm2020}/acpaas-ui-ngx-forms.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/auto-complete/auto-complete.module.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/auto-complete/components/auto-complete/auto-complete.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/auto-complete/public-api.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/datepicker/datepicker.conf.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/datepicker/datepicker.module.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/datepicker/public-api.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/datepicker/types/datepicker.types.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/range-slider/components/range-slider/range-slider.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/range-slider/public-api.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/range-slider/range-slider.module.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/range-slider/types/range-slider.types.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/search-filter/components/search-filter/search-filter.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/search-filter/public-api.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/search-filter/search-filter.module.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/search-filter/types/search-filter.types.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/shared/services/search.service.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/shared/types/search.types.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/timepicker/classes/timepicker.validators.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/timepicker/components/timepicker/timepicker.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/timepicker/public-api.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/timepicker/timepicker.module.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/timepicker/types/timepicker.types.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/classes/uploader.class.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/components/upload/upload.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/components/upload-input/upload-input.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/components/upload-queue/upload-queue.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/components/upload-zone/upload-zone.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/components/validation-list/validation-list.component.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/public-api.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/services/validation-messages.service.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/types/upload.types.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/upload.conf.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/lib/upload/upload.module.mjs +0 -0
- /package/{esm2020 → dist/esm2020}/public-api.mjs +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{lib → dist/lib}/auto-complete/auto-complete.module.d.ts +0 -0
- /package/{lib → dist/lib}/auto-complete/components/auto-complete/auto-complete.component.d.ts +0 -0
- /package/{lib → dist/lib}/auto-complete/public-api.d.ts +0 -0
- /package/{lib → dist/lib}/datepicker/components/datepicker/datepicker.component.d.ts +0 -0
- /package/{lib → dist/lib}/datepicker/datepicker.conf.d.ts +0 -0
- /package/{lib → dist/lib}/datepicker/datepicker.module.d.ts +0 -0
- /package/{lib → dist/lib}/datepicker/public-api.d.ts +0 -0
- /package/{lib → dist/lib}/datepicker/types/datepicker.types.d.ts +0 -0
- /package/{lib → dist/lib}/range-slider/components/range-slider/range-slider.component.d.ts +0 -0
- /package/{lib → dist/lib}/range-slider/public-api.d.ts +0 -0
- /package/{lib → dist/lib}/range-slider/range-slider.module.d.ts +0 -0
- /package/{lib → dist/lib}/range-slider/types/range-slider.types.d.ts +0 -0
- /package/{lib → dist/lib}/search-filter/components/search-filter/search-filter.component.d.ts +0 -0
- /package/{lib → dist/lib}/search-filter/public-api.d.ts +0 -0
- /package/{lib → dist/lib}/search-filter/search-filter.module.d.ts +0 -0
- /package/{lib → dist/lib}/search-filter/types/search-filter.types.d.ts +0 -0
- /package/{lib → dist/lib}/shared/services/search.service.d.ts +0 -0
- /package/{lib → dist/lib}/shared/types/search.types.d.ts +0 -0
- /package/{lib → dist/lib}/timepicker/classes/timepicker.validators.d.ts +0 -0
- /package/{lib → dist/lib}/timepicker/components/timepicker/timepicker.component.d.ts +0 -0
- /package/{lib → dist/lib}/timepicker/public-api.d.ts +0 -0
- /package/{lib → dist/lib}/timepicker/timepicker.module.d.ts +0 -0
- /package/{lib → dist/lib}/timepicker/types/timepicker.types.d.ts +0 -0
- /package/{lib → dist/lib}/upload/classes/uploader.class.d.ts +0 -0
- /package/{lib → dist/lib}/upload/components/upload/upload.component.d.ts +0 -0
- /package/{lib → dist/lib}/upload/components/upload-input/upload-input.component.d.ts +0 -0
- /package/{lib → dist/lib}/upload/components/upload-queue/upload-queue.component.d.ts +0 -0
- /package/{lib → dist/lib}/upload/components/upload-zone/upload-zone.component.d.ts +0 -0
- /package/{lib → dist/lib}/upload/components/validation-list/validation-list.component.d.ts +0 -0
- /package/{lib → dist/lib}/upload/public-api.d.ts +0 -0
- /package/{lib → dist/lib}/upload/services/validation-messages.service.d.ts +0 -0
- /package/{lib → dist/lib}/upload/types/upload.types.d.ts +0 -0
- /package/{lib → dist/lib}/upload/upload.conf.d.ts +0 -0
- /package/{lib → dist/lib}/upload/upload.module.d.ts +0 -0
- /package/{public-api.d.ts → dist/public-api.d.ts} +0 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ContentChild,
|
|
4
|
+
ElementRef,
|
|
5
|
+
EventEmitter,
|
|
6
|
+
forwardRef,
|
|
7
|
+
Input,
|
|
8
|
+
OnChanges,
|
|
9
|
+
OnInit,
|
|
10
|
+
Output,
|
|
11
|
+
SimpleChanges,
|
|
12
|
+
TemplateRef,
|
|
13
|
+
ViewChild,
|
|
14
|
+
} from '@angular/core';
|
|
15
|
+
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
16
|
+
import { get, isEqual } from 'lodash-es';
|
|
17
|
+
import { FlyoutDirective, FlyoutZoneDirective } from '@acpaas-ui/ngx-flyout';
|
|
18
|
+
import { SearchService } from '../../../shared/services/search.service';
|
|
19
|
+
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'aui-auto-complete',
|
|
22
|
+
styleUrls: ['./auto-complete.component.scss'],
|
|
23
|
+
templateUrl: './auto-complete.component.html',
|
|
24
|
+
providers: [
|
|
25
|
+
{
|
|
26
|
+
provide: NG_VALUE_ACCESSOR,
|
|
27
|
+
useExisting: forwardRef(() => AutoCompleteComponent), // eslint-disable-line
|
|
28
|
+
multi: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
})
|
|
32
|
+
export class AutoCompleteComponent implements ControlValueAccessor, OnInit, OnChanges {
|
|
33
|
+
@Input() id: string;
|
|
34
|
+
@Input() placeholder: string;
|
|
35
|
+
@Input() description: string;
|
|
36
|
+
@Input() inputLabel: string;
|
|
37
|
+
@Input() results: any[] = []; // The values for the selectable list
|
|
38
|
+
@Input() data: any[] = []; // The values to search in when remote search is disabled
|
|
39
|
+
@Input() remote = false; // Disable or enamble remote search
|
|
40
|
+
@Input() minCharacters = 0;
|
|
41
|
+
@Input() clearInvalid = false;
|
|
42
|
+
@Input() searchIncentiveText: string;
|
|
43
|
+
@Input() loadingText: string;
|
|
44
|
+
@Input() noResultsText: string;
|
|
45
|
+
@Input() showAllByDefault = false;
|
|
46
|
+
@Input() autoComplete = 'off';
|
|
47
|
+
|
|
48
|
+
// specify which label/value props to use
|
|
49
|
+
@Input() label: string;
|
|
50
|
+
@Input() value: string;
|
|
51
|
+
|
|
52
|
+
// Eventemitter for searchvalue (parent object should update the results with this param)
|
|
53
|
+
@Output() search: EventEmitter<string> = new EventEmitter();
|
|
54
|
+
// eslint-disable-next-line @angular-eslint/no-output-native
|
|
55
|
+
@Output() select: EventEmitter<any> = new EventEmitter();
|
|
56
|
+
|
|
57
|
+
@ViewChild(FlyoutDirective, { static: true }) flyout: FlyoutDirective;
|
|
58
|
+
@ViewChild(FlyoutZoneDirective, { static: true }) flyoutZone: FlyoutZoneDirective;
|
|
59
|
+
|
|
60
|
+
@ContentChild(TemplateRef, { static: true }) public template: TemplateRef<any>;
|
|
61
|
+
|
|
62
|
+
public query = '';
|
|
63
|
+
public index = -1; // index for active element in selectable list, by default -1 (so it starts in the input field)
|
|
64
|
+
public selectedItem: any = null; // keep a backup of the selectedItem
|
|
65
|
+
public searching = false; // track remote search state
|
|
66
|
+
public focused = false;
|
|
67
|
+
public isDisabled = false;
|
|
68
|
+
|
|
69
|
+
private remoteValue = false;
|
|
70
|
+
|
|
71
|
+
constructor(private ref: ElementRef, private searchService: SearchService) {}
|
|
72
|
+
|
|
73
|
+
public updateModel = (_: any) => {};
|
|
74
|
+
|
|
75
|
+
// CONTROL_VALUE_ACCESSOR interface
|
|
76
|
+
public writeValue(value = '') {
|
|
77
|
+
if (this.value) {
|
|
78
|
+
const selected = this.data.find((item: any) => item[this.value] === value);
|
|
79
|
+
|
|
80
|
+
if (selected) {
|
|
81
|
+
return (this.query = selected[this.label]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this.remote && !!value) {
|
|
85
|
+
this.remoteValue = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.query = value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// CONTROL_VALUE_ACCESSOR interface
|
|
93
|
+
public registerOnChange(fn) {
|
|
94
|
+
this.updateModel = fn;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// CONTROL_VALUE_ACCESSOR interface
|
|
98
|
+
public registerOnTouched() {}
|
|
99
|
+
|
|
100
|
+
public setDisabledState(isDisabled: boolean): void {
|
|
101
|
+
this.isDisabled = isDisabled;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public ngOnInit(): void {
|
|
105
|
+
if (Array.isArray(this.data) && this.data.length > 0 && !this.query && this.showAllByDefault) {
|
|
106
|
+
this.results = [...this.data];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// OnChanges interface
|
|
111
|
+
public ngOnChanges(changes: SimpleChanges): void {
|
|
112
|
+
if (!changes) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const newData = get(changes, 'data.currentValue', []);
|
|
117
|
+
if (!isEqual(newData, get(changes, 'data.previousValue', []))) {
|
|
118
|
+
if (this.remote) {
|
|
119
|
+
this.remoteSearch();
|
|
120
|
+
} else {
|
|
121
|
+
this.localSearch();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (changes.results && changes.results.currentValue) {
|
|
126
|
+
this.searching = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public propagateChange(item: any) {
|
|
131
|
+
this.query = item !== null ? (this.label ? item[this.label] : item) : '';
|
|
132
|
+
this.select.emit(item);
|
|
133
|
+
|
|
134
|
+
if (!item) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const key = this.value ? this.value : this.label ? this.label : null;
|
|
139
|
+
this.updateModel(key ? item[key] || '' : item);
|
|
140
|
+
this.selectedItem = item;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* triggers on input value change
|
|
145
|
+
*/
|
|
146
|
+
public doSearch(): void {
|
|
147
|
+
this.index = -1; // reset index
|
|
148
|
+
this.searching = true;
|
|
149
|
+
|
|
150
|
+
if (this.remote) {
|
|
151
|
+
this.search.emit(this.query); // ask for new remote data
|
|
152
|
+
} else {
|
|
153
|
+
this.localSearch();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.openFlyout(); // open the flyout when there is a change
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* triggers on selectable-list:select -> onClick event in selectable-list
|
|
161
|
+
*/
|
|
162
|
+
public onSelect(item: any): void {
|
|
163
|
+
this.propagateChange(item);
|
|
164
|
+
this.closeFlyout(); // Close the flyout manually
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public onFlyoutClosed(): void {
|
|
168
|
+
// there is only 1 result, select it
|
|
169
|
+
if (this.index >= 0 && this.results.length === 1) {
|
|
170
|
+
return this.onSelect(this.results[0]);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// there is no query nor selected item, clear the selected item
|
|
174
|
+
if (!this.query && this.index < 0) {
|
|
175
|
+
return this.onSelect(null);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// reset the query for an invalid query if clearInvalid is true
|
|
179
|
+
if (this.clearInvalid && this.query && !this.results.length && this.index < 0) {
|
|
180
|
+
this.query = this.selectedItem ? (this.label ? this.selectedItem[this.label] : this.selectedItem) : '';
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public onKeyArrowDown(): void {
|
|
185
|
+
if (this.index < this.results.length - 1) {
|
|
186
|
+
this.scrollList(1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.openFlyout();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public onKeyArrowUp(): void {
|
|
193
|
+
if (this.index >= 0) {
|
|
194
|
+
this.scrollList(-1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public onKeyEnter(event: Event): void {
|
|
199
|
+
event.preventDefault(); // Do not submit form when selecting an item.
|
|
200
|
+
|
|
201
|
+
const query = this.index >= 0 ? this.results[this.index] : this.query;
|
|
202
|
+
|
|
203
|
+
this.propagateChange(query);
|
|
204
|
+
this.closeFlyout();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public onKeyEscape(): void {
|
|
208
|
+
this.closeFlyout();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public onFocus(): void {
|
|
212
|
+
this.focused = true;
|
|
213
|
+
this.openFlyout();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public openFlyout(): void {
|
|
217
|
+
if (this.flyout) {
|
|
218
|
+
this.flyout.open();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public closeFlyout(): void {
|
|
223
|
+
if (this.flyout) {
|
|
224
|
+
this.flyout.close();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this.focused = false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public localSearch(): void {
|
|
231
|
+
this.results = this.searchService.search(this.data, {
|
|
232
|
+
minLength: this.minCharacters,
|
|
233
|
+
key: this.label,
|
|
234
|
+
query: this.query,
|
|
235
|
+
showAllByDefault: this.showAllByDefault,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (this.results.length === 1 && this.query === this.results[0][this.label]) {
|
|
239
|
+
this.index = 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.searching = false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public remoteSearch(): void {
|
|
246
|
+
if (!this.remoteValue || !this.data) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const selected = this.data.find((item: any) => {
|
|
251
|
+
if (this.value) {
|
|
252
|
+
return item[this.value] === this.query;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return item === this.query;
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (selected) {
|
|
259
|
+
this.query = this.label ? selected[this.label] : selected;
|
|
260
|
+
} else {
|
|
261
|
+
this.query = '';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
this.remoteValue = false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
public scrollList(factor: number): void {
|
|
268
|
+
this.index += factor;
|
|
269
|
+
|
|
270
|
+
if (!this.flyoutZone) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const liItems = this.flyoutZone.element.getElementsByTagName('li');
|
|
275
|
+
const liHeight = liItems[1] ? liItems[1].offsetHeight : liItems[0].offsetHeight;
|
|
276
|
+
const zoneHeight = this.flyoutZone.element.offsetHeight;
|
|
277
|
+
const offset = zoneHeight / liHeight / 2;
|
|
278
|
+
|
|
279
|
+
this.flyoutZone.element.scrollTop = this.index * liHeight - offset * liHeight;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @acpaas-ui/ngx-forms
|
|
2
|
+
|
|
3
|
+
The package creates a custom input component allowing the user to select a date either by input or by picking one in the calendar flyout.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { DatepickerModule } from '@acpaas-ui/ngx-forms';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Documentation
|
|
12
|
+
|
|
13
|
+
Visit our [documentation site](https://antwerp-ui.digipolis.be/) for full how-to docs and guidelines
|
|
14
|
+
|
|
15
|
+
### Flyout module
|
|
16
|
+
|
|
17
|
+
#### API
|
|
18
|
+
|
|
19
|
+
| Name | Default value | Description |
|
|
20
|
+
| -------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| `@Input() id: string;` | - | The id to use on the input field. |
|
|
22
|
+
| `@Input() name: string;` | - | The name to use on the input field. |
|
|
23
|
+
| `@Input() label: string;` | - | The label to use on the input field. |
|
|
24
|
+
| `@Input() description: string;` | - | The description to use on the input field. |
|
|
25
|
+
| `@Input() placeholder: string;` | `'dd/mm/yyyy'` | The placeholder to use on the input field. |
|
|
26
|
+
| `@Input() autocomplete: string;` | `'off'` | Turn the browsers autocompletion on or off. |
|
|
27
|
+
| `@Input() range: DateRange;` | - | A range of dates or weekdays to disable (see the [@acpaas-ui/js-date-utils](https://github.com/digipolisantwerp/antwerp-ui_js/blob/master/packages/date-utils/README.md) package for more info on date ranges). |
|
|
28
|
+
|
|
29
|
+
#### Example
|
|
30
|
+
|
|
31
|
+
You can provide custom month and weekday labels as wel as error labels via the `forChild` method.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { DatepickerModule } from '@acpaas-ui/ngx-forms';
|
|
35
|
+
|
|
36
|
+
@NgModule({
|
|
37
|
+
imports: [
|
|
38
|
+
DatepickerModule.forChild(
|
|
39
|
+
['Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag', 'Zondag'],
|
|
40
|
+
[
|
|
41
|
+
'Januari',
|
|
42
|
+
'Februari',
|
|
43
|
+
'Maart',
|
|
44
|
+
'April',
|
|
45
|
+
'Mei',
|
|
46
|
+
'Juni',
|
|
47
|
+
'Juli',
|
|
48
|
+
'Augustus',
|
|
49
|
+
'September',
|
|
50
|
+
'Oktober',
|
|
51
|
+
'November',
|
|
52
|
+
'December',
|
|
53
|
+
],
|
|
54
|
+
{
|
|
55
|
+
ERRORS_INVALID_DATE: 'Ongeldige datum.',
|
|
56
|
+
ERRORS_INVALID_RANGE: 'Deze datum kan niet gekozen worden.',
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
})
|
|
61
|
+
export class AppModule {}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
You can use the component in template driven, as well as reactive forms. The value is always an ISO string of the Date.
|
|
65
|
+
In the example below we use a reactive form.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { FormBuilder, FormGroup } from '@angular/forms';
|
|
69
|
+
|
|
70
|
+
import { DateRange } from '@acpaas-ui/ngx-utils';
|
|
71
|
+
|
|
72
|
+
constructor(private fb: FormBuilder) { }
|
|
73
|
+
|
|
74
|
+
public dateRange: DateRange = [5, 6];
|
|
75
|
+
public dateForm: FormGroup;
|
|
76
|
+
|
|
77
|
+
this.dateForm = this.fb.group({
|
|
78
|
+
inputDate: [''],
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<form [formGroup]="dateForm">
|
|
84
|
+
<div
|
|
85
|
+
class="a-input has-icon-right"
|
|
86
|
+
[ngClass]="{'has-error': dateForm.controls.inputDate.dirty && dateForm.controls.inputDate.invalid}"
|
|
87
|
+
>
|
|
88
|
+
<label class="a-input__label" for="input-datepicker">Pick a date</label>
|
|
89
|
+
<aui-datepicker
|
|
90
|
+
data-id="input-datepicker"
|
|
91
|
+
name="input-datepicker"
|
|
92
|
+
autocomplete="off"
|
|
93
|
+
label="Pick date"
|
|
94
|
+
description="Description"
|
|
95
|
+
placeholder="dd/mm/jjjj"
|
|
96
|
+
formControlName="inputDate"
|
|
97
|
+
[range]="dateRange"
|
|
98
|
+
>
|
|
99
|
+
</aui-datepicker>
|
|
100
|
+
<div *ngIf="dateForm.controls['inputDate'].errors">
|
|
101
|
+
<p *ngIf="dateForm.controls['inputDate'].errors.format">{{ dateForm.controls['inputDate'].errors.format }}</p>
|
|
102
|
+
<p *ngIf="dateForm.controls['inputDate'].errors.range">{{ dateForm.controls['inputDate'].errors.range }}</p>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</form>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Contributing
|
|
109
|
+
|
|
110
|
+
Visit our [Contribution Guidelines](../../../../../CONTRIBUTING.md) for more information on how to contribute.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<div aria-haspopup="grid" auiFlyout class="aui-datepicker a-input has-icon-right">
|
|
2
|
+
<label class="a-input__label" for="{{ id }}" *ngIf="label">{{ label }}</label>
|
|
3
|
+
<small class="a-input__description" *ngIf="description"> {{ description }}</small>
|
|
4
|
+
<div class="a-input__wrapper">
|
|
5
|
+
<input
|
|
6
|
+
(blur)="handleBlur($event)"
|
|
7
|
+
[attr.disabled]="isDisabled ? true : null"
|
|
8
|
+
[autocomplete]="autocomplete"
|
|
9
|
+
[formControl]="formControl"
|
|
10
|
+
id="{{ id }}"
|
|
11
|
+
label="Pick date"
|
|
12
|
+
description="Description"
|
|
13
|
+
name="{{ name }}"
|
|
14
|
+
placeholder="{{ placeholder }}"
|
|
15
|
+
type="text"
|
|
16
|
+
/>
|
|
17
|
+
<ng-container *ngIf="isDisabled">
|
|
18
|
+
<aui-icon
|
|
19
|
+
name="ai-calendar"
|
|
20
|
+
/>
|
|
21
|
+
</ng-container>
|
|
22
|
+
<ng-container *ngIf="!isDisabled">
|
|
23
|
+
<aui-icon
|
|
24
|
+
auiFlyoutAction
|
|
25
|
+
[openOnFocus]="false"
|
|
26
|
+
name="ai-calendar"
|
|
27
|
+
className="is-clickable"
|
|
28
|
+
role="button"
|
|
29
|
+
[attr.aria-label]="ariaOpenDatepickerLabel"
|
|
30
|
+
tabindex="0"
|
|
31
|
+
/>
|
|
32
|
+
</ng-container>
|
|
33
|
+
|
|
34
|
+
<ng-container *ngIf="!isDisabled">
|
|
35
|
+
<div auiFlyoutZone class="m-datepicker m-datepicker--fixed" role="dialog">
|
|
36
|
+
<aui-calendar
|
|
37
|
+
(selectDate)="selectDateFromCalendar($event)"
|
|
38
|
+
[range]="range"
|
|
39
|
+
[selectedDate]="selectedDate"
|
|
40
|
+
[interval]="interval"
|
|
41
|
+
[weekdayLabels]="weekdayLabels"
|
|
42
|
+
[monthLabels]="monthLabels"
|
|
43
|
+
></aui-calendar>
|
|
44
|
+
</div>
|
|
45
|
+
</ng-container>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { Component, OnInit } from '@angular/core';
|
|
3
|
+
import {
|
|
4
|
+
UntypedFormBuilder,
|
|
5
|
+
UntypedFormControl,
|
|
6
|
+
UntypedFormGroup,
|
|
7
|
+
FormsModule,
|
|
8
|
+
ReactiveFormsModule,
|
|
9
|
+
} from '@angular/forms';
|
|
10
|
+
import { DateRange, DateHelper } from '@acpaas-ui/ngx-utils';
|
|
11
|
+
import { CalendarModule } from '@acpaas-ui/ngx-calendar';
|
|
12
|
+
import { FlyoutModule } from '@acpaas-ui/ngx-flyout';
|
|
13
|
+
import { IconModule } from '@acpaas-ui/ngx-icon';
|
|
14
|
+
|
|
15
|
+
import { DATEPICKER_DEFAULT_ERROR_LABELS, DATEPICKER_ERROR_LABELS } from '../../datepicker.conf';
|
|
16
|
+
import { DatepickerComponent } from './datepicker.component';
|
|
17
|
+
|
|
18
|
+
@Component({
|
|
19
|
+
selector: 'aui-test-one',
|
|
20
|
+
template: `
|
|
21
|
+
<form #testForm="ngForm" (ngSubmit)="submit(testForm.value)">
|
|
22
|
+
<aui-datepicker
|
|
23
|
+
id="test"
|
|
24
|
+
name="test"
|
|
25
|
+
placeholder="dd/mm/jjjj"
|
|
26
|
+
autocomplete="off"
|
|
27
|
+
[range]="range"
|
|
28
|
+
[(ngModel)]="dateModel"
|
|
29
|
+
></aui-datepicker>
|
|
30
|
+
</form>
|
|
31
|
+
`,
|
|
32
|
+
})
|
|
33
|
+
export class TestComponent {
|
|
34
|
+
public range: DateRange;
|
|
35
|
+
public dateModel: Date;
|
|
36
|
+
|
|
37
|
+
submit(form) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Component({
|
|
41
|
+
selector: 'aui-test-two',
|
|
42
|
+
template: `
|
|
43
|
+
<form [formGroup]="testForm" (ngSubmit)="submit(testForm.value)">
|
|
44
|
+
<aui-datepicker
|
|
45
|
+
id="test"
|
|
46
|
+
name="test"
|
|
47
|
+
placeholder="dd/mm/jjjj"
|
|
48
|
+
autocomplete="off"
|
|
49
|
+
[range]="range"
|
|
50
|
+
formControlName="date"
|
|
51
|
+
></aui-datepicker>
|
|
52
|
+
</form>
|
|
53
|
+
`,
|
|
54
|
+
})
|
|
55
|
+
export class ReactiveTestComponent implements OnInit {
|
|
56
|
+
public range: DateRange;
|
|
57
|
+
public dateModel: Date;
|
|
58
|
+
public testForm: UntypedFormGroup;
|
|
59
|
+
|
|
60
|
+
constructor(private fb: UntypedFormBuilder) {}
|
|
61
|
+
|
|
62
|
+
public ngOnInit() {
|
|
63
|
+
this.testForm = this.fb.group({
|
|
64
|
+
date: '',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
submit(form) {}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe('The Datepicker Component', () => {
|
|
72
|
+
// waitForAsync beforeEach
|
|
73
|
+
beforeEach(waitForAsync(() => {
|
|
74
|
+
TestBed.configureTestingModule({
|
|
75
|
+
imports: [FormsModule, ReactiveFormsModule, FlyoutModule, IconModule, CalendarModule],
|
|
76
|
+
declarations: [TestComponent, ReactiveTestComponent, DatepickerComponent],
|
|
77
|
+
providers: [
|
|
78
|
+
{
|
|
79
|
+
provide: DATEPICKER_ERROR_LABELS,
|
|
80
|
+
useValue: DATEPICKER_DEFAULT_ERROR_LABELS,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
}).compileComponents();
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
// describe('Template driven', () => {
|
|
87
|
+
// let comp: TestComponent;
|
|
88
|
+
// let fixture: ComponentFixture<TestComponent>;
|
|
89
|
+
// let picker: DatepickerComponent;
|
|
90
|
+
|
|
91
|
+
// // synchronous beforeEach
|
|
92
|
+
// beforeEach(() => {
|
|
93
|
+
// fixture = TestBed.createComponent(TestComponent);
|
|
94
|
+
|
|
95
|
+
// comp = fixture.componentInstance;
|
|
96
|
+
|
|
97
|
+
// picker = fixture.debugElement.children[0].children[0].componentInstance;
|
|
98
|
+
// });
|
|
99
|
+
|
|
100
|
+
// it('should exist', () => {
|
|
101
|
+
// fixture.detectChanges();
|
|
102
|
+
// expect(picker).toBeDefined();
|
|
103
|
+
// });
|
|
104
|
+
// });
|
|
105
|
+
|
|
106
|
+
describe('Reactive', () => {
|
|
107
|
+
let comp: ReactiveTestComponent;
|
|
108
|
+
let fixture: ComponentFixture<ReactiveTestComponent>;
|
|
109
|
+
let picker: DatepickerComponent;
|
|
110
|
+
|
|
111
|
+
// synchronous beforeEach
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
fixture = TestBed.createComponent(ReactiveTestComponent);
|
|
114
|
+
|
|
115
|
+
comp = fixture.componentInstance;
|
|
116
|
+
|
|
117
|
+
picker = fixture.debugElement.children[0].children[0].componentInstance;
|
|
118
|
+
picker.ngOnInit();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('writeValue', () => {
|
|
122
|
+
let accessor;
|
|
123
|
+
|
|
124
|
+
beforeEach(() => {
|
|
125
|
+
accessor = {
|
|
126
|
+
update: () => {},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
spyOn(accessor, 'update');
|
|
130
|
+
|
|
131
|
+
picker.registerOnChange(accessor.update);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should update the model if the value is a valid date', () => {
|
|
135
|
+
const date = new Date('2018-01-10T00:00:00Z');
|
|
136
|
+
|
|
137
|
+
picker.writeValue(date.toISOString());
|
|
138
|
+
|
|
139
|
+
expect(picker.formControl.value).toBeTruthy();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('selectDateFromCalendar', () => {
|
|
144
|
+
beforeEach(() => {
|
|
145
|
+
spyOn(picker.formControl, 'setValue').and.stub();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should update the values', () => {
|
|
149
|
+
const date = new Date('2018-01-10T00:00:00+01:00');
|
|
150
|
+
picker.selectDateFromCalendar({
|
|
151
|
+
date,
|
|
152
|
+
complete: true,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(picker.formControl.setValue).toHaveBeenCalledWith('10/01/2018');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('validate', () => {
|
|
160
|
+
it('should return null if the control has no value', () => {
|
|
161
|
+
const ctrl = new UntypedFormControl();
|
|
162
|
+
|
|
163
|
+
expect(picker.validate(ctrl)).toEqual(null);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should return the invalid date error if the date is invalid', () => {
|
|
167
|
+
const ctrl = new UntypedFormControl(false);
|
|
168
|
+
|
|
169
|
+
expect(picker.validate(ctrl)).toEqual({
|
|
170
|
+
format: DATEPICKER_DEFAULT_ERROR_LABELS.ERRORS_INVALID_DATE,
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should return null if the date is valid and no range was set', () => {
|
|
175
|
+
const ctrl = new UntypedFormControl(new Date());
|
|
176
|
+
|
|
177
|
+
expect(picker.validate(ctrl)).toEqual(null);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should return null if the date is valid and outside of the set range', () => {
|
|
181
|
+
spyOn(picker.calendarService, 'getRangeForDate').and.callFake(() => []);
|
|
182
|
+
picker.range = [1];
|
|
183
|
+
|
|
184
|
+
const ctrl = new UntypedFormControl(new Date().toISOString());
|
|
185
|
+
|
|
186
|
+
expect(picker.validate(ctrl)).toEqual(null);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should return the invalid range error if the date was valid and in the set range', () => {
|
|
190
|
+
const testDateISO = new Date('2018-01-15T12:00:00Z').toISOString();
|
|
191
|
+
const parsedDate = DateHelper.parseDate(testDateISO);
|
|
192
|
+
const range = [parsedDate.getDate()];
|
|
193
|
+
spyOn(picker.calendarService, 'getRangeForDate').and.callFake(() => range);
|
|
194
|
+
picker.range = range;
|
|
195
|
+
|
|
196
|
+
const ctrl = new UntypedFormControl(testDateISO);
|
|
197
|
+
|
|
198
|
+
expect(picker.validate(ctrl)).toEqual({
|
|
199
|
+
range: DATEPICKER_DEFAULT_ERROR_LABELS.ERRORS_INVALID_RANGE,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|