@klippa/ngx-enhancy-forms 2.3.1 → 5.0.0

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 (39) hide show
  1. package/bundles/klippa-ngx-enhancy-forms.umd.js +960 -469
  2. package/bundles/klippa-ngx-enhancy-forms.umd.js.map +1 -1
  3. package/bundles/klippa-ngx-enhancy-forms.umd.min.js +3 -2
  4. package/bundles/klippa-ngx-enhancy-forms.umd.min.js.map +1 -1
  5. package/esm2015/lib/elements/date-picker/date-picker.component.js +92 -0
  6. package/esm2015/lib/elements/date-time-picker/date-time-picker.component.js +311 -0
  7. package/esm2015/lib/elements/email/email-input.component.js +2 -2
  8. package/esm2015/lib/elements/password-field/password-field.component.js +6 -4
  9. package/esm2015/lib/elements/select/select-footer/select-footer.component.js +15 -0
  10. package/esm2015/lib/elements/select/select.component.js +30 -9
  11. package/esm2015/lib/elements/sortable-items/sortable-items.component.js +46 -4
  12. package/esm2015/lib/elements/text-input/text-input.component.js +7 -4
  13. package/esm2015/lib/elements/value-accessor-base/multiple-value-accessor-base.component.js +63 -0
  14. package/esm2015/lib/elements/value-accessor-base/value-accessor-base.component.js +3 -3
  15. package/esm2015/lib/form/form-element/form-element.component.js +13 -15
  16. package/esm2015/lib/form/form.component.js +64 -18
  17. package/esm2015/lib/ngx-enhancy-forms.module.js +36 -25
  18. package/esm2015/lib/util/values.js +8 -17
  19. package/esm2015/public-api.js +5 -2
  20. package/fesm2015/klippa-ngx-enhancy-forms.js +556 -90
  21. package/fesm2015/klippa-ngx-enhancy-forms.js.map +1 -1
  22. package/klippa-ngx-enhancy-forms.metadata.json +1 -1
  23. package/lib/elements/date-picker/date-picker.component.d.ts +23 -0
  24. package/lib/elements/date-time-picker/date-time-picker.component.d.ts +59 -0
  25. package/lib/elements/email/email-input.component.d.ts +1 -1
  26. package/lib/elements/password-field/password-field.component.d.ts +2 -1
  27. package/lib/elements/select/select-footer/select-footer.component.d.ts +4 -0
  28. package/lib/elements/select/select.component.d.ts +12 -4
  29. package/lib/elements/sortable-items/sortable-items.component.d.ts +12 -2
  30. package/lib/elements/text-input/text-input.component.d.ts +2 -0
  31. package/lib/elements/value-accessor-base/multiple-value-accessor-base.component.d.ts +11 -0
  32. package/lib/elements/value-accessor-base/value-accessor-base.component.d.ts +2 -1
  33. package/lib/form/form-element/form-element.component.d.ts +5 -5
  34. package/lib/form/form.component.d.ts +11 -7
  35. package/lib/util/values.d.ts +2 -4
  36. package/package.json +1 -1
  37. package/public-api.d.ts +4 -1
  38. package/esm2015/lib/elements/datepicker/datepicker.component.js +0 -111
  39. package/lib/elements/datepicker/datepicker.component.d.ts +0 -26
@@ -1,16 +1,17 @@
1
- import { Component, SkipSelf, Optional, Input, InjectionToken, Host, Inject, ViewChild, HostBinding, EventEmitter, Output, ContentChild, TemplateRef, NgModule } from '@angular/core';
1
+ import { Directive, Input, Component, SkipSelf, Optional, InjectionToken, Host, Inject, ViewChild, HostBinding, EventEmitter, Output, ContentChild, TemplateRef, ChangeDetectorRef, NgModule } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
- import { FormGroup, FormArray, FormControl, FormGroupName, ControlContainer, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
3
+ import { FormArray, FormGroup, FormControl, ControlContainer, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
4
4
  import { isString } from 'lodash';
5
5
  import { SortablejsModule } from 'ngx-sortablejs';
6
6
  import { NgSelectModule } from '@ng-select/ng-select';
7
+ import { parse, format, startOfMonth, endOfMonth } from 'date-fns';
7
8
  import { MAT_NATIVE_DATE_FORMATS, MAT_DATE_FORMATS, MatNativeDateModule } from '@angular/material/core';
8
9
  import { MatDatepickerModule } from '@angular/material/datepicker';
9
10
  import { MatFormFieldModule } from '@angular/material/form-field';
10
11
  import { MatInputModule } from '@angular/material/input';
11
12
  import { MatButtonModule } from '@angular/material/button';
12
13
 
13
- function stringIsSetAndNotEmpty(s) {
14
+ function stringIsSetAndFilled(s) {
14
15
  return isString(s) && s.length > 0;
15
16
  }
16
17
  function isNullOrUndefined(value) {
@@ -22,24 +23,15 @@ function numberIsSet(value) {
22
23
  function isValueSet(value) {
23
24
  return value !== null && value !== undefined;
24
25
  }
26
+ function removeDuplicatesFromArray(array) {
27
+ return array.filter((c, i) => {
28
+ const firstOccurrenceIndex = array.findIndex((c2) => c2 === c);
29
+ return i === firstOccurrenceIndex;
30
+ });
31
+ }
25
32
  function stringOrArrayIsSetAndEmpty(value) {
26
33
  return value !== null && value !== undefined && value.length === 0;
27
34
  }
28
- function useIfStringIsSet(s) {
29
- if (stringIsSetAndNotEmpty(s)) {
30
- return s;
31
- }
32
- return undefined;
33
- }
34
- function useIfArrayIsSetWithOneItem(a) {
35
- if (!isNullOrUndefined(a) && a.length === 1) {
36
- return a[0];
37
- }
38
- return undefined;
39
- }
40
- function convertParentToChild(originalClass, newClass) {
41
- return Object.assign(newClass, originalClass);
42
- }
43
35
  function truncateString(s, length) {
44
36
  if (s.length < length) {
45
37
  return s;
@@ -48,29 +40,73 @@ function truncateString(s, length) {
48
40
  }
49
41
 
50
42
  const invalidFieldsSymbol = Symbol('Not all fields are valid');
51
- // Only used as a 'marker' to define a property will be filled in by a sub form
52
- class SubForm extends FormGroup {
53
- constructor() {
54
- super({}, null);
55
- }
43
+ class SubFormDirective {
56
44
  }
45
+ SubFormDirective.decorators = [
46
+ { type: Directive, args: [{
47
+ // tslint:disable-next-line:directive-selector
48
+ selector: 'klp-sub-form',
49
+ },] }
50
+ ];
51
+ SubFormDirective.propDecorators = {
52
+ injectInto: [{ type: Input }],
53
+ at: [{ type: Input }]
54
+ };
57
55
  class FormComponent {
58
- constructor(parent, surroundingFormGroupName) {
56
+ constructor(parent, subFormPlaceholder) {
59
57
  this.parent = parent;
60
- this.surroundingFormGroupName = surroundingFormGroupName;
58
+ this.subFormPlaceholder = subFormPlaceholder;
61
59
  // we keep track of what form controls are actually rendered. Only those count when looking at form validation
62
60
  this.activeControls = [];
63
61
  }
64
62
  ngOnInit() {
65
- var _a;
66
- if (this.parent && isValueSet((_a = this.surroundingFormGroupName) === null || _a === void 0 ? void 0 : _a.name)) {
67
- const groupName = String(this.surroundingFormGroupName.name);
68
- const groupToOverwrite = this.parent.formGroup.get(groupName);
69
- if (groupToOverwrite instanceof SubForm) {
70
- this.parent.formGroup.setControl(groupName, this.formGroup);
63
+ if (isValueSet(this.parent) && isValueSet(this.subFormPlaceholder)) {
64
+ const injectInto = this.subFormPlaceholder.injectInto;
65
+ const injectAt = this.subFormPlaceholder.at;
66
+ if (injectInto instanceof FormArray) {
67
+ if (typeof injectAt !== 'number') {
68
+ throw new Error(`cannot index FormArray with ${typeof injectAt}`);
69
+ }
70
+ injectInto.setControl(injectAt, this.formGroup);
71
+ }
72
+ else if (injectInto instanceof FormGroup) {
73
+ if (typeof injectAt !== 'string') {
74
+ throw new Error(`cannot index FormGroup with ${typeof injectAt}`);
75
+ }
76
+ injectInto.setControl(injectAt, this.formGroup);
77
+ }
78
+ }
79
+ if (isValueSet(this.patchValueInterceptor)) {
80
+ this.addSupportForPatchValueInterceptor();
81
+ }
82
+ }
83
+ ngOnDestroy() {
84
+ if (isValueSet(this.parent) && isValueSet(this.subFormPlaceholder)) {
85
+ const injectInto = this.subFormPlaceholder.injectInto;
86
+ const injectAt = this.subFormPlaceholder.at;
87
+ if (injectInto instanceof FormArray) {
88
+ const idx = injectInto.controls.findIndex(e => e === this.formGroup);
89
+ injectInto.removeAt(idx);
90
+ }
91
+ else if (injectInto instanceof FormGroup) {
92
+ if (typeof injectAt !== 'string') {
93
+ throw new Error(`cannot index FormGroup with ${typeof injectAt}`);
94
+ }
95
+ injectInto.removeControl(injectAt);
71
96
  }
72
97
  }
73
98
  }
99
+ addSupportForPatchValueInterceptor() {
100
+ const fn = this.formGroup.patchValue;
101
+ const newFn = (value, options) => {
102
+ this.patchValueInterceptor(value).then((val) => {
103
+ setTimeout(() => {
104
+ fn.call(this.formGroup, val, options);
105
+ });
106
+ });
107
+ };
108
+ this.formGroup.patchValue = newFn;
109
+ }
74
110
  registerControl(formControl, formElement) {
75
111
  this.activeControls.push({ formControl, formElement });
76
112
  if (this.parent) {
@@ -124,6 +160,7 @@ class FormComponent {
124
160
  }
125
161
  trySubmit() {
126
162
  var _a, _b;
163
+ this.formGroup.updateValueAndValidity();
127
164
  this.formGroup.markAllAsTouched();
128
165
  const allControls = this.getAllFormControls();
129
166
  const originalDisabledStates = allControls.map(e => {
@@ -161,23 +198,24 @@ FormComponent.decorators = [
161
198
  ];
162
199
  FormComponent.ctorParameters = () => [
163
200
  { type: FormComponent, decorators: [{ type: SkipSelf }, { type: Optional }] },
164
- { type: FormGroupName, decorators: [{ type: Optional }] }
201
+ { type: SubFormDirective, decorators: [{ type: Optional }] }
165
202
  ];
166
203
  FormComponent.propDecorators = {
167
- formGroup: [{ type: Input }]
204
+ formGroup: [{ type: Input }],
205
+ patchValueInterceptor: [{ type: Input }]
168
206
  };
169
207
 
170
208
  const FORM_ERROR_MESSAGES = new InjectionToken('form.error.messages');
171
209
  const DEFAULT_ERROR_MESSAGES = {
172
- min: "Use a number larger than %min%",
173
- max: "Use a number smaller than %max%",
174
- required: "This field is required",
175
- email: "Use a valid email address",
176
- minLength: "Has to be longer than %minLength% character(s)",
177
- maxLength: "Has to be shorter than %maxLength% character(s)",
178
- pattern: "This input is not valid",
179
- matchPassword: "Passwords must match",
180
- date: "Enter a valid date",
210
+ min: 'Use a number larger than %min%',
211
+ max: 'Use a number smaller than %max%',
212
+ required: 'This field is required',
213
+ email: 'Use a valid email address',
214
+ minLength: 'Has to be longer than %minLength% character(s)',
215
+ maxLength: 'Has to be shorter than %maxLength% character(s)',
216
+ pattern: 'This input is not valid',
217
+ matchPassword: 'Passwords must match',
218
+ date: 'Enter a valid date',
181
219
  };
182
220
  class FormElementComponent {
183
221
  constructor(parent, customMessages) {
@@ -195,8 +233,6 @@ class FormElementComponent {
195
233
  }, message);
196
234
  }
197
235
  registerControl(formControl) {
198
- // console.log('register');
199
- // console.log(this.caption);
200
236
  this.attachedControl = formControl;
201
237
  this.parent.registerControl(formControl, this);
202
238
  }
@@ -243,7 +279,7 @@ class FormElementComponent {
243
279
  // to give some breathing room, we scroll 100px more to the top
244
280
  (_a = this.getScrollableParent(this.internalComponentRef.nativeElement)) === null || _a === void 0 ? void 0 : _a.scrollBy(0, -100);
245
281
  }
246
- getErrorMessages(key) {
282
+ getErrorMessage(key) {
247
283
  var _a, _b, _c;
248
284
  return (_c = (_b = (_a = this.customMessages) === null || _a === void 0 ? void 0 : _a[key]) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : this.errorMessages[key];
249
285
  }
@@ -251,7 +287,7 @@ class FormElementComponent {
251
287
  FormElementComponent.decorators = [
252
288
  { type: Component, args: [{
253
289
  selector: 'klp-form-element',
254
- template: "<ng-template #errorRef>\n\t<div *ngIf=\"getErrorToShow()\" class=\"errorContainer\">\n\t\t<div *ngIf=\"showDefaultError('min')\">{{substituteParameters(getErrorMessages(\"min\"), {min: attachedControl.errors.min.min})}}</div>\n\t\t<div *ngIf=\"showDefaultError('max')\">{{substituteParameters(getErrorMessages(\"max\"), {max: attachedControl.errors.max.max})}}</div>\n\t\t<div *ngIf=\"showDefaultError('required')\">{{getErrorMessages(\"required\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('email')\">{{getErrorMessages(\"email\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('minlength')\">{{substituteParameters(getErrorMessages(\"minLength\"), {minLength: attachedControl.errors.minlength.requiredLength})}}</div>\n\t\t<div *ngIf=\"showDefaultError('maxlength')\">{{substituteParameters(getErrorMessages(\"maxLength\"), {maxLength: attachedControl.errors.maxlength.requiredLength})}}</div>\n\t\t<div *ngIf=\"showDefaultError('pattern')\">{{getErrorMessages(\"pattern\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('MatchPassword')\">{{getErrorMessages(\"matchPassword\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('date')\">{{getErrorMessages(\"date\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('message')\">{{attachedControl.errors.message.value}}</div>\n\t\t<div [ngTemplateOutlet]=\"getCustomErrorHandler(getErrorToShow())?.templateRef\"></div>\n\t</div>\n</ng-template>\n<ng-container *ngIf=\"direction === 'horizontal'\" [ngTemplateOutlet]=\"errorRef\"></ng-container>\n<div *ngIf=\"attachedControl\" class=\"componentContainer\" [ngClass]=\"{vertical: direction === 'vertical', reverseOrder: swapInputAndCaption}\" #internalComponentRef>\n\t<div class=\"caption\" [ngClass]=\"{ hasErrors: getErrorToShow() && attachedControl.touched, percentageSpacing: captionSpacing === 'percentages' }\">\n\t\t<div *ngIf=\"captionRef\" [ngTemplateOutlet]=\"captionRef\"></div>\n\t\t<div *ngIf=\"!captionRef\">{{caption}}</div>\n\t</div>\n\t<ng-container *ngIf=\"direction === 'vertical'\" [ngTemplateOutlet]=\"errorRef\"></ng-container>\n\t<div class=\"inputContainer\" [ngClass]=\"{ percentageSpacing: captionSpacing === 'percentages' }\">\n\t\t<ng-content></ng-content>\n\t</div>\n</div>\n",
290
+ template: "<ng-template #errorRef>\n\t<div *ngIf=\"getErrorToShow()\" class=\"errorContainer\">\n\t\t<div *ngIf=\"showDefaultError('min')\">{{substituteParameters(getErrorMessage(\"min\"), {min: attachedControl.errors.min.min})}}</div>\n\t\t<div *ngIf=\"showDefaultError('max')\">{{substituteParameters(getErrorMessage(\"max\"), {max: attachedControl.errors.max.max})}}</div>\n\t\t<div *ngIf=\"showDefaultError('required')\">{{getErrorMessage(\"required\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('email')\">{{getErrorMessage(\"email\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('minlength')\">{{substituteParameters(getErrorMessage(\"minLength\"), {minLength: attachedControl.errors.minlength.requiredLength})}}</div>\n\t\t<div *ngIf=\"showDefaultError('maxlength')\">{{substituteParameters(getErrorMessage(\"maxLength\"), {maxLength: attachedControl.errors.maxlength.requiredLength})}}</div>\n\t\t<div *ngIf=\"showDefaultError('pattern')\">{{getErrorMessage(\"pattern\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('MatchPassword')\">{{getErrorMessage(\"matchPassword\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('date')\">{{getErrorMessage(\"date\")}}</div>\n\t\t<div *ngIf=\"showDefaultError('message')\">{{attachedControl.errors.message.value}}</div>\n\t\t<div [ngTemplateOutlet]=\"getCustomErrorHandler(getErrorToShow())?.templateRef\"></div>\n\t</div>\n</ng-template>\n<ng-container *ngIf=\"direction === 'horizontal'\" [ngTemplateOutlet]=\"errorRef\"></ng-container>\n<div *ngIf=\"attachedControl\" class=\"componentContainer\" [ngClass]=\"{vertical: direction === 'vertical', reverseOrder: swapInputAndCaption}\" #internalComponentRef>\n\t<div class=\"caption\" [ngClass]=\"{ hasErrors: getErrorToShow() && attachedControl.touched, percentageSpacing: captionSpacing === 'percentages' }\">\n\t\t<div *ngIf=\"captionRef\" [ngTemplateOutlet]=\"captionRef\"></div>\n\t\t<div *ngIf=\"!captionRef\">{{caption}}</div>\n\t</div>\n\t<ng-container *ngIf=\"direction === 'vertical'\" [ngTemplateOutlet]=\"errorRef\"></ng-container>\n\t<div class=\"inputContainer\" [ngClass]=\"{ percentageSpacing: captionSpacing === 'percentages' }\">\n\t\t<ng-content></ng-content>\n\t</div>\n</div>\n",
255
291
  styles: [":host{display:block;margin-top:1.25rem}.componentContainer{align-items:center;display:flex}.componentContainer.reverseOrder{flex-direction:row-reverse;justify-content:flex-end}.componentContainer.vertical{display:block;margin-bottom:1rem}.componentContainer.vertical .inputContainer{margin-top:.3125rem}.componentContainer.vertical .errorContainer{margin-left:0}.caption{color:#515365;flex:0 0 auto;font-weight:700}.caption.percentageSpacing{flex:0 0 40%}.caption.hasErrors{color:#ff8000}.inputContainer{flex:0 0 auto}.inputContainer.percentageSpacing{flex:0 0 60%}.errorContainer{color:#ff8000;margin-left:40%}"]
256
292
  },] }
257
293
  ];
@@ -292,7 +328,7 @@ class ValueAccessorBase {
292
328
  if (this.formControl) {
293
329
  this.attachedFormControl = this.formControl;
294
330
  }
295
- else if (stringIsSetAndNotEmpty(this.formControlName)) {
331
+ else if (stringIsSetAndFilled(this.formControlName)) {
296
332
  this.attachedFormControl = (_a = this.controlContainer) === null || _a === void 0 ? void 0 : _a.control.get(this.formControlName);
297
333
  if (isNullOrUndefined(this.attachedFormControl)) {
298
334
  throw new Error(`Form element '${this.formControlName}' with caption '${(_b = this.parent) === null || _b === void 0 ? void 0 : _b.caption}' is not declared in your FormGroup.`);
@@ -457,92 +493,173 @@ class PasswordFieldComponent extends ValueAccessorBase {
457
493
  constructor() {
458
494
  super(...arguments);
459
495
  this.placeholder = 'Password';
496
+ this.isPasswordConfirm = false;
460
497
  }
461
498
  }
462
499
  PasswordFieldComponent.decorators = [
463
500
  { type: Component, args: [{
464
501
  selector: 'klp-form-password-field',
465
- template: "<input\n\ttype=\"password\"\n\tclass=\"form-control\"\n [ngClass]=\"{showErrors: isInErrorState()}\"\n\t[(ngModel)]=\"innerValue\"\n\t(input)=\"setInnerValueAndNotify($event.target.value)\"\n\t[placeholder]=\"placeholder\"\n\t(blur)=\"touch()\"\n/>\n",
502
+ template: "<input\n\ttype=\"password\"\n\tclass=\"form-control\"\n [ngClass]=\"{showErrors: isInErrorState()}\"\n\t[(ngModel)]=\"innerValue\"\n\t(input)=\"setInnerValueAndNotify($event.target.value)\"\n\t[placeholder]=\"isPasswordConfirm ? 'Confirm password': 'Password'\"\n\t(blur)=\"touch()\"\n/>\n",
466
503
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: PasswordFieldComponent, multi: true }],
467
504
  styles: [":host,input{display:block}input{-moz-transition:all .2s ease-in;-ms-transition:all .2s ease-in;-o-transition:all .2s ease-in;-webkit-transition:all .2s ease-in;border:1px solid #e6ecf5;border-radius:2px;box-shadow:none;color:#888da8;font-size:14px;height:42px;outline:none;padding:.375rem .625rem;transition:all .2s ease-in;width:100%}input::-webkit-input-placeholder{color:#adadad}input:-moz-placeholder,input::-moz-placeholder{color:#adadad}input:-ms-input-placeholder{color:#adadad}input:focus{border-color:#3ed778;box-shadow:none;outline:0 none}input.input-sm{height:30px}input.input-lg{height:50px}input.error{background-color:#ffeff4;border-color:#ff3c7e}input.valid{background-color:#ebfaeb;border-color:#37c936;color:#278d26}.showErrors{border-color:#ff8000}"]
468
505
  },] }
469
506
  ];
470
507
  PasswordFieldComponent.propDecorators = {
471
- placeholder: [{ type: Input }]
508
+ placeholder: [{ type: Input }],
509
+ isPasswordConfirm: [{ type: Input }]
472
510
  };
473
511
 
512
+ const SELECT_TRANSLATIONS = new InjectionToken('klp.form.select.translations');
474
513
  class SelectComponent extends ValueAccessorBase {
475
- constructor(parent, controlContainer) {
514
+ constructor(parent, controlContainer, translations) {
476
515
  super(parent, controlContainer);
477
516
  this.parent = parent;
478
517
  this.controlContainer = controlContainer;
479
- this.placeholder = 'Pick an option';
518
+ this.translations = translations;
480
519
  this.multiple = false;
520
+ this.multipleDisplayedAsAmount = false;
481
521
  this.clearable = true;
482
522
  this.onSearch = new EventEmitter();
483
523
  }
524
+ onTextInput(value) {
525
+ this.currentQueryString = value;
526
+ this.onSearch.emit(value);
527
+ }
528
+ getDefaultTranslation(key) {
529
+ switch (key) {
530
+ case 'placeholder':
531
+ return () => { var _a; return (_a = this.placeholder) !== null && _a !== void 0 ? _a : 'Pick an option'; };
532
+ case 'amountSelected':
533
+ return (amount) => `${amount} selected`;
534
+ }
535
+ }
536
+ getTranslation(key, params = null) {
537
+ var _a, _b, _c;
538
+ return (_c = (_b = (_a = this.translations) === null || _a === void 0 ? void 0 : _a[key]) === null || _b === void 0 ? void 0 : _b.call(_a, params)) !== null && _c !== void 0 ? _c : this.getDefaultTranslation(key)(params);
539
+ }
484
540
  }
485
541
  SelectComponent.decorators = [
486
542
  { type: Component, args: [{
487
543
  selector: 'klp-form-select',
488
- template: "<ng-select\n\t[placeholder]=\"placeholder\"\n\tbindLabel=\"name\"\n\tbindValue=\"id\"\n\t[items]=\"options\"\n\t[clearable]=\"clearable\"\n\t[(ngModel)]=\"innerValue\"\n\t[ngClass]=\"{showErrors: isInErrorState()}\"\n\t(change)=\"setInnerValueAndNotify(innerValue)\"\n\t[multiple]=\"multiple\"\n\t[disabled]=\"disabled\"\n\t(blur)=\"touch()\"\n\t(search)=\"onSearch.emit($event.term)\"\n\t[dropdownPosition]=\"dropdownPosition\"\n\t[searchFn]=\"customSearchFn\"\n>\n\t<ng-template let-item=\"item\" ng-option-tmp>\n\t\t{{ item.name }}\n\t\t<div *ngIf=\"item.description\" class=\"dropdown-item-description\">\n\t\t\t{{ item.description }}\n\t\t</div>\n\t</ng-template>\n</ng-select>\n",
544
+ template: "<ng-select\n\t[placeholder]=\"getTranslation('placeholder')\"\n\tbindLabel=\"name\"\n\tbindValue=\"id\"\n\t[items]=\"options\"\n\t[clearable]=\"clearable\"\n\t[(ngModel)]=\"innerValue\"\n\t[ngClass]=\"{showErrors: isInErrorState()}\"\n\t(change)=\"setInnerValueAndNotify(innerValue)\"\n\t[multiple]=\"multiple\"\n\t[disabled]=\"disabled\"\n\t(blur)=\"touch()\"\n\t(search)=\"onSearch.emit($event.term)\"\n\t[dropdownPosition]=\"dropdownPosition\"\n\t[searchFn]=\"customSearchFn\"\n\t(input)=\"onTextInput($event.target.value)\"\n>\n\t<ng-template let-item=\"item\" ng-option-tmp>\n\t\t{{ item.name }}\n\t\t<div *ngIf=\"item.description\" class=\"dropdown-item-description\">\n\t\t\t{{ item.description }}\n\t\t</div>\n\t</ng-template>\n\t<ng-container *ngIf=\"multiple && multipleDisplayedAsAmount && innerValue?.length > 1\">\n\t\t<ng-template ng-multi-label-tmp>\n\t\t\t<div class=\"ng-value\">\n\t\t\t\t<span class=\"ng-value-label\">{{getTranslation('amountSelected', innerValue?.length)}}</span>\n\t\t\t</div>\n\t\t</ng-template>\n\t</ng-container>\n\t<ng-template ng-footer-tmp *ngIf=\"footerElement\">\n\t\t<ng-container [ngTemplateOutlet]=\"footerElement\"></ng-container>\n\t</ng-template>\n</ng-select>\n",
489
545
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: SelectComponent, multi: true }],
490
- styles: [":host{display:block}.showErrors ::ng-deep .ng-select-container,:host.showErrors ::ng-deep .ng-select-container{border-color:#ff8000}:host ::ng-deep ng-select.ng-select .ng-select-container{color:#888da8}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container{background:#fff;border-color:#3ed778}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container:hover{box-shadow:none}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container .ng-arrow{border-color:transparent transparent #999;border-width:0 5px 5px;top:-2px}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container .ng-arrow:hover{border-color:transparent transparent #666}:host ::ng-deep .ng-select.ng-select-opened.ng-select-bottom>.ng-select-container{border-bottom-left-radius:0;border-bottom-right-radius:0}:host ::ng-deep .ng-select.ng-select-opened.ng-select-top>.ng-select-container{border-top-left-radius:0;border-top-right-radius:0}:host ::ng-deep .ng-select.ng-select-disabled>.ng-select-container{background-color:#f9f9f9}:host ::ng-deep .ng-select .ng-has-value .ng-placeholder{display:none}:host ::ng-deep .ng-select .ng-select-container{align-items:center;background-clip:padding-box;background-color:#fff;border:1px solid #e6ecf5;border-radius:4px;border-radius:2px;box-shadow:none;box-sizing:border-box;color:#888da8;display:flex;flex-direction:row;font-size:1rem;font-size:14px;line-height:1.5;margin:0;min-height:42px;outline:none;overflow:visible;padding:.375rem .75rem;transition-delay:0s;transition-duration:.2s;transition-property:all;transition-timing-function:ease-in;width:100%}:host ::ng-deep .ng-select .ng-select-container:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}:host ::ng-deep .ng-select .ng-select-container .ng-value-container{align-items:center;overflow:hidden;padding-left:10px}:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-placeholder{color:#aaa}:host ::ng-deep .ng-select.ng-select-single .ng-select-container{height:42px}:host ::ng-deep .ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{left:0;padding-left:10px;padding-right:50px;top:5px}:host ::ng-deep .ng-select.ng-select-multiple.ng-select-disabled>.ng-select-container .ng-value-container .ng-value{background-color:#f9f9f9;border:1px solid #e3e3e3}:host ::ng-deep .ng-select.ng-select-multiple.ng-select-disabled>.ng-select-container .ng-value-container .ng-value .ng-value-label{padding:0 5px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container{padding-left:7px;padding-right:10px;padding-top:2px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value{background-color:#e7faee;border:1px solid #93e8b3;border-radius:2px;display:flex;font-size:.9em;margin-bottom:5px;margin-right:5px;overflow:hidden}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value.ng-value-disabled{background-color:#f9f9f9;border:1px solid #e3e3e3}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value.ng-value-disabled .ng-value-label{padding-left:5px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-label{display:inline-block;overflow:hidden;padding:0 5px;text-overflow:ellipsis}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon{display:inline-block;padding:0 5px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon:hover{background-color:#93e8b3}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon.left{border-right:1px solid #93e8b3}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon.right{border-left:1px solid #c2e0ff}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-input{padding-bottom:3px;padding-left:3px}:host ::ng-deep ng-select.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder{padding-bottom:3px;padding-left:3px;position:static;top:5px}:host ::ng-deep .ng-select .ng-clear-wrapper{color:#999}:host ::ng-deep .ng-select .ng-clear-wrapper .ng-clear:hover{color:#ff3c7e}:host ::ng-deep .ng-select .ng-spinner-zone{padding-right:5px;padding-top:5px}:host ::ng-deep .ng-select .ng-arrow-wrapper{padding-right:5px;width:25px}:host ::ng-deep .ng-select .ng-arrow-wrapper:hover .ng-arrow{border-top-color:#666}:host ::ng-deep .ng-select .ng-arrow-wrapper .ng-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px}:host ::ng-deep .ng-dropdown-panel{background-color:#fff;border:1px solid #3ed778;box-shadow:0 1px 0 rgba(0,0,0,.06)}:host ::ng-deep .ng-dropdown-panel.ng-select-bottom{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-top-color:#e6e6e6;margin-top:-1px;top:100%}:host ::ng-deep .ng-dropdown-panel.ng-select-bottom .ng-dropdown-panel-items .ng-option:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px}:host ::ng-deep .ng-dropdown-panel.ng-select-top{border-bottom-color:#e6e6e6;border-top-left-radius:4px;border-top-right-radius:4px;bottom:100%;margin-bottom:-1px}:host ::ng-deep .ng-dropdown-panel.ng-select-top .ng-dropdown-panel-items .ng-option:first-child{border-top-left-radius:4px;border-top-right-radius:4px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-header{border-bottom:1px solid #ccc;padding:5px 7px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-footer{border-top:1px solid #ccc;padding:5px 7px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items{margin-bottom:1px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;color:rgba(0,0,0,.54);cursor:default;cursor:pointer;font-weight:500;padding:8px 10px;user-select:none}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup.ng-option-disabled{cursor:default}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup.ng-option-marked{background-color:#ebf5ff}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup.ng-option-selected{background-color:#f5faff;font-weight:600}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option{background-color:#fff;color:rgba(0,0,0,.87);padding:8px 10px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-selected{background-color:#e7faee;color:#333}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-selected .ng-option-label{font-weight:600}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked{background-color:#e7faee;color:#333}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-disabled{color:#ccc}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-child{padding-left:22px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option .ng-tag-label{font-size:80%;font-weight:400;padding-right:5px}:host ::ng-deep ng-select.ng-select .ng-select-container .ng-value-container{padding-left:0}:host ::ng-deep ng-select.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{color:#888da8;top:9px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option,:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input{color:#888da8}:host ::ng-deep .ng-select.ng-select-auto-grow{max-width:inherit}:host ::ng-deep .ng-select.ng-select-auto-grow .ng-dropdown-panel{width:auto}"]
546
+ styles: [":host{display:block}.showErrors ::ng-deep .ng-select-container,:host.showErrors ::ng-deep .ng-select-container{border-color:#ff8000}:host ::ng-deep ng-select.ng-select .ng-select-container{color:#888da8}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container{background:#fff;border-color:#3ed778}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container:hover{box-shadow:none}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container .ng-arrow{border-color:transparent transparent #999;border-width:0 5px 5px;top:-2px}:host ::ng-deep .ng-select.ng-select-opened>.ng-select-container .ng-arrow:hover{border-color:transparent transparent #666}:host ::ng-deep .ng-select.ng-select-opened.ng-select-bottom>.ng-select-container{border-bottom-left-radius:0;border-bottom-right-radius:0}:host ::ng-deep .ng-select.ng-select-opened.ng-select-top>.ng-select-container{border-top-left-radius:0;border-top-right-radius:0}:host ::ng-deep .ng-select.ng-select-disabled>.ng-select-container{background-color:#f9f9f9}:host ::ng-deep .ng-select .ng-has-value .ng-placeholder{display:none}:host ::ng-deep .ng-select .ng-select-container{align-items:center;background-clip:padding-box;background-color:#fff;border:1px solid #e6ecf5;border-radius:4px;border-radius:2px;box-shadow:none;box-sizing:border-box;color:#888da8;display:flex;flex-direction:row;font-size:1rem;font-size:14px;line-height:1.5;margin:0;min-height:42px;outline:none;overflow:visible;padding:.375rem .75rem;transition-delay:0s;transition-duration:.2s;transition-property:all;transition-timing-function:ease-in;width:100%}:host ::ng-deep .ng-select .ng-select-container:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}:host ::ng-deep .ng-select .ng-select-container .ng-value-container{align-items:center;overflow:hidden;padding-left:10px}:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-placeholder{color:#aaa}:host ::ng-deep .ng-select.ng-select-single .ng-select-container{height:42px}:host ::ng-deep .ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{left:0;padding-left:10px;padding-right:50px;top:5px}:host ::ng-deep .ng-select.ng-select-multiple.ng-select-disabled>.ng-select-container .ng-value-container .ng-value{background-color:#f9f9f9;border:1px solid #e3e3e3}:host ::ng-deep .ng-select.ng-select-multiple.ng-select-disabled>.ng-select-container .ng-value-container .ng-value .ng-value-label{padding:0 5px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value{background-color:#e7faee;border:1px solid #93e8b3;border-radius:2px;display:flex;font-size:.9em;margin-bottom:3px;margin-right:5px;margin-top:3px;overflow:hidden}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value.ng-value-disabled{background-color:#f9f9f9;border:1px solid #e3e3e3}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value.ng-value-disabled .ng-value-label{padding-left:5px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-label{display:inline-block;overflow:hidden;padding:0 5px;text-overflow:ellipsis}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon{display:inline-block;padding:0 5px}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon:hover{background-color:#93e8b3}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon.left{border-right:1px solid #93e8b3}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-value .ng-value-icon.right{border-left:1px solid #c2e0ff}:host ::ng-deep .ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-input{padding-bottom:3px;padding-left:3px}:host ::ng-deep ng-select.ng-select.ng-select-multiple .ng-select-container .ng-value-container .ng-placeholder{padding-bottom:3px;padding-left:3px;position:static;top:5px}:host ::ng-deep .ng-select .ng-clear-wrapper{color:#999}:host ::ng-deep .ng-select .ng-clear-wrapper .ng-clear:hover{color:#ff3c7e}:host ::ng-deep .ng-select .ng-spinner-zone{padding-right:5px;padding-top:5px}:host ::ng-deep .ng-select .ng-arrow-wrapper{padding-right:5px;width:25px}:host ::ng-deep .ng-select .ng-arrow-wrapper:hover .ng-arrow{border-top-color:#666}:host ::ng-deep .ng-select .ng-arrow-wrapper .ng-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px}:host ::ng-deep .ng-dropdown-panel{background-color:#fff;border:1px solid #3ed778;box-shadow:0 1px 0 rgba(0,0,0,.06)}:host ::ng-deep .ng-dropdown-panel.ng-select-bottom{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-top-color:#e6e6e6;margin-top:-1px;top:100%}:host ::ng-deep .ng-dropdown-panel.ng-select-bottom .ng-dropdown-panel-items .ng-option:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px}:host ::ng-deep .ng-dropdown-panel.ng-select-top{border-bottom-color:#e6e6e6;border-top-left-radius:4px;border-top-right-radius:4px;bottom:100%;margin-bottom:-1px}:host ::ng-deep .ng-dropdown-panel.ng-select-top .ng-dropdown-panel-items .ng-option:first-child{border-top-left-radius:4px;border-top-right-radius:4px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-header{border-bottom:1px solid #ccc;padding:5px 7px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-footer{border-top:1px solid #ccc;padding:5px 7px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items{margin-bottom:1px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;color:rgba(0,0,0,.54);cursor:default;cursor:pointer;font-weight:500;padding:8px 10px;user-select:none}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup.ng-option-disabled{cursor:default}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup.ng-option-marked{background-color:#ebf5ff}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup.ng-option-selected{background-color:#f5faff;font-weight:600}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option{background-color:#fff;color:rgba(0,0,0,.87);padding:8px 10px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-selected{background-color:#e7faee;color:#333}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-selected .ng-option-label{font-weight:600}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked{background-color:#e7faee;color:#333}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-disabled{color:#ccc}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-child{padding-left:22px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option .ng-tag-label{font-size:80%;font-weight:400;padding-right:5px}:host ::ng-deep ng-select.ng-select .ng-select-container .ng-value-container{padding-left:0}:host ::ng-deep ng-select.ng-select.ng-select-single .ng-select-container .ng-value-container .ng-input{color:#888da8;top:9px}:host ::ng-deep .ng-dropdown-panel .ng-dropdown-panel-items .ng-option,:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input{color:#888da8}:host ::ng-deep .ng-select.ng-select-auto-grow{max-width:inherit}:host ::ng-deep .ng-select.ng-select-auto-grow .ng-dropdown-panel{width:auto}"]
491
547
  },] }
492
548
  ];
493
549
  SelectComponent.ctorParameters = () => [
494
550
  { type: FormElementComponent, decorators: [{ type: Optional }, { type: Host }] },
495
- { type: ControlContainer, decorators: [{ type: Optional }, { type: Host }] }
551
+ { type: ControlContainer, decorators: [{ type: Optional }, { type: Host }] },
552
+ { type: undefined, decorators: [{ type: Inject, args: [SELECT_TRANSLATIONS,] }, { type: Optional }] }
496
553
  ];
497
554
  SelectComponent.propDecorators = {
498
555
  placeholder: [{ type: Input }],
499
556
  options: [{ type: Input }],
500
557
  multiple: [{ type: Input }],
558
+ multipleDisplayedAsAmount: [{ type: Input }],
501
559
  clearable: [{ type: Input }],
502
560
  dropdownPosition: [{ type: Input }],
503
561
  customSearchFn: [{ type: Input }],
562
+ footerElement: [{ type: Input }],
504
563
  onSearch: [{ type: Output }]
505
564
  };
506
565
 
566
+ class SelectFooterComponent {
567
+ }
568
+ SelectFooterComponent.decorators = [
569
+ { type: Component, args: [{
570
+ selector: 'klp-select-footer',
571
+ template: "<div class=\"componentContainer\">\n\t<span class=\"prefix\">{{prefix}}</span>\n\t<span class=\"text\">{{text}}</span>\n</div>\n",
572
+ styles: [":host{display:block}.componentContainer{cursor:pointer;padding:6px 10px}.componentContainer:hover{background:#e7faee}.prefix{font-size:80%;padding-right:.625rem}"]
573
+ },] }
574
+ ];
575
+ SelectFooterComponent.propDecorators = {
576
+ prefix: [{ type: Input }],
577
+ text: [{ type: Input }]
578
+ };
579
+
507
580
  class SortableItemsComponent extends ValueAccessorBase {
508
581
  constructor() {
509
582
  super(...arguments);
510
583
  this.sortableItemSize = 'lg';
584
+ this.useCustomScroll = false;
585
+ this.scrollInterval = null;
511
586
  this.itemsOrderChanged = () => {
512
587
  this.setInnerValueAndNotify(this.innerValue);
513
588
  };
589
+ this.onItemDrag = (data) => {
590
+ // if the item you're dragging is reaching the top, start scrolling.
591
+ if (data.relatedRect.top < 100) {
592
+ this.scrollPage(100);
593
+ }
594
+ else {
595
+ this.stopScrolling();
596
+ }
597
+ };
598
+ this.onEnd = () => {
599
+ this.stopScrolling();
600
+ };
601
+ }
602
+ ngOnInit() {
603
+ super.ngOnInit();
604
+ if (this.useCustomScroll) {
605
+ this.sortablejsOptions = { onUpdate: this.itemsOrderChanged, onMove: this.onItemDrag, onEnd: this.onEnd };
606
+ }
607
+ else {
608
+ this.sortablejsOptions = { onUpdate: this.itemsOrderChanged };
609
+ }
610
+ }
611
+ scrollPage(scrollAmount) {
612
+ if (!isValueSet(this.scrollInterval)) {
613
+ this.scrollInterval = setInterval(() => {
614
+ window.scroll({
615
+ top: document.scrollingElement.scrollTop - scrollAmount,
616
+ behavior: 'smooth',
617
+ });
618
+ if (document.scrollingElement.scrollTop <= 100) {
619
+ this.stopScrolling();
620
+ }
621
+ }, 100);
622
+ }
623
+ }
624
+ stopScrolling() {
625
+ clearInterval(this.scrollInterval);
626
+ this.scrollInterval = null;
514
627
  }
515
628
  }
516
629
  SortableItemsComponent.decorators = [
517
630
  { type: Component, args: [{
518
631
  selector: 'klp-form-sortable-items',
519
- template: "<ng-template #listItem>\n\t<li *ngFor=\"let item of innerValue; index as index\"\n\t class=\"sortableItem\"\n\t [ngClass]=\"{\n\t\t\tlargeSortableItem: sortableItemSize === 'lg',\n\t\t \tsmallSortableItem: sortableItemSize === 'sm'\n\t\t }\"\n\t>\n\t\t<ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ item: item, index:index }\" ></ng-container>\n\t</li>\n</ng-template>\n\n<ol *ngIf='!disabled'\n [sortablejs]=\"innerValue\"\n [sortablejsOptions]=\"{ onUpdate: itemsOrderChanged }\"\n class=\"itemsContainer\"\n>\n\t<ng-container *ngTemplateOutlet=\"listItem\"></ng-container>\n</ol>\n\n<ol *ngIf='disabled' class=\"itemsContainer disabled-mouse-cursor\">\n\t<ng-container *ngTemplateOutlet=\"listItem\"></ng-container>\n</ol>\n",
632
+ template: "<ng-template #listItem>\n\t<li *ngFor=\"let item of innerValue; index as index\"\n\t\t\tclass=\"sortableItem\"\n\t\t\t[ngClass]=\"{\n\t\t\tlargeSortableItem: sortableItemSize === 'lg',\n\t\t \tsmallSortableItem: sortableItemSize === 'sm'\n\t\t }\"\n\t>\n\t\t<ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"{ item: item, index:index }\"></ng-container>\n\t</li>\n</ng-template>\n\n<ol *ngIf='!disabled'\n\t\t[sortablejs]=\"innerValue\"\n\t\t[sortablejsOptions]=\"sortablejsOptions\"\n\t\tclass=\"itemsContainer\"\n>\n\t<ng-container *ngTemplateOutlet=\"listItem\"></ng-container>\n</ol>\n\n<ol *ngIf='disabled' class=\"itemsContainer disabled-mouse-cursor\">\n\t<ng-container *ngTemplateOutlet=\"listItem\"></ng-container>\n</ol>\n",
520
633
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: SortableItemsComponent, multi: true }],
521
634
  styles: [":host{display:block}.itemsContainer{border:1px solid #e6ecf5;border-radius:5px;margin-bottom:0;padding:0}.sortableItem{border-top:1px solid #e6ecf5;cursor:-webkit-grab;cursor:grab;list-style:none}.sortableItem:hover{background:#f8f9fa}.sortableItem:first-child{border-top:none}.largeSortableItem{padding:15px}.smallSortableItem{padding:0 10px}.disabled-mouse-cursor li{cursor:no-drop}"]
522
635
  },] }
523
636
  ];
524
637
  SortableItemsComponent.propDecorators = {
525
638
  template: [{ type: ContentChild, args: [TemplateRef,] }],
526
- sortableItemSize: [{ type: Input }]
639
+ sortableItemSize: [{ type: Input }],
640
+ useCustomScroll: [{ type: Input }]
527
641
  };
528
642
 
529
643
  class TextInputComponent extends ValueAccessorBase {
530
644
  constructor() {
531
645
  super(...arguments);
532
646
  this.type = 'text';
647
+ this.clearable = false;
533
648
  }
534
649
  }
535
650
  TextInputComponent.decorators = [
536
651
  { type: Component, args: [{
537
652
  selector: 'klp-form-text-input',
538
- template: "<input\n\t[type]=\"type\"\n\tclass=\"form-control\"\n\t[ngClass]=\"{showErrors: isInErrorState()}\"\n\t[(ngModel)]=\"innerValue\"\n\t(input)=\"setInnerValueAndNotify($event.target.value)\"\n\t[placeholder]=\"placeholder ? placeholder : ''\"\n\t(blur)=\"touch()\"\n\t[disabled]='disabled'\n/>\n",
653
+ template: "<div class=\"componentContainer\">\n\t<ng-container *ngIf=\"icon?.length > 0\">\n\t\t<i class=\"ti-search\" *ngIf=\"icon === 'search'\"></i>\n\t</ng-container>\n\t<input\n\t\t[type]=\"type\"\n\t\t[ngClass]=\"{showErrors: isInErrorState(), hasIcon: icon?.length > 0, hasClearButton: clearable}\"\n\t\t[(ngModel)]=\"innerValue\"\n\t\t(input)=\"setInnerValueAndNotify($event.target.value)\"\n\t\t[placeholder]=\"placeholder ? placeholder : ''\"\n\t\t(blur)=\"touch()\"\n\t\t[disabled]='disabled'\n\t/>\n\t<div class=\"clearIcon\" *ngIf=\"clearable && innerValue?.length > 0\" (click)=\"resetToNull()\">\u00D7</div>\n</div>\n",
539
654
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: TextInputComponent, multi: true }],
540
- styles: [":host,input{display:block}input{-moz-transition:all .2s ease-in;-ms-transition:all .2s ease-in;-o-transition:all .2s ease-in;-webkit-transition:all .2s ease-in;border:1px solid #e6ecf5;border-radius:2px;box-shadow:none;color:#888da8;font-size:14px;height:42px;outline:none;padding:.375rem .625rem;transition:all .2s ease-in;width:100%}input::-webkit-input-placeholder{color:#adadad}input:-moz-placeholder,input::-moz-placeholder{color:#adadad}input:-ms-input-placeholder{color:#adadad}input:focus{border-color:#3ed778;box-shadow:none;outline:0 none}input.input-sm{height:30px}input.input-lg{height:50px}input.error{background-color:#ffeff4;border-color:#ff3c7e}input.valid{background-color:#ebfaeb;border-color:#37c936;color:#278d26}.showErrors{border-color:#ff8000}"]
655
+ styles: [":host{display:block}:host input:disabled{cursor:not-allowed}.componentContainer{position:relative}i{left:.625rem;position:absolute;top:14px}input{-moz-transition:all .2s ease-in;-ms-transition:all .2s ease-in;-o-transition:all .2s ease-in;-webkit-transition:all .2s ease-in;border:1px solid #e6ecf5;border-radius:2px;box-shadow:none;color:#888da8;display:block;font-size:14px;height:42px;outline:none;padding:.375rem .625rem;transition:all .2s ease-in;width:100%}input::-webkit-input-placeholder{color:#adadad}input:-moz-placeholder,input::-moz-placeholder{color:#adadad}input:-ms-input-placeholder{color:#adadad}input:focus{border-color:#3ed778;box-shadow:none;outline:0 none}input.input-sm{height:30px}input.input-lg{height:50px}input.error{background-color:#ffeff4;border-color:#ff3c7e}input.valid{background-color:#ebfaeb;border-color:#37c936;color:#278d26}input.hasIcon{padding-left:1.875rem}input.hasClearButton{padding-right:1.875rem}.clearIcon{cursor:pointer;font-size:18px;position:absolute;right:.625rem;top:7px}.showErrors{border-color:#ff8000}"]
541
656
  },] }
542
657
  ];
543
658
  TextInputComponent.propDecorators = {
544
659
  placeholder: [{ type: Input }],
545
- type: [{ type: Input }]
660
+ type: [{ type: Input }],
661
+ clearable: [{ type: Input }],
662
+ icon: [{ type: Input }]
546
663
  };
547
664
 
548
665
  class ToggleComponent extends ValueAccessorBase {
@@ -672,33 +789,213 @@ FormSubmitButtonComponent.propDecorators = {
672
789
  _: [{ type: HostBinding, args: ['class._fullWidth',] }]
673
790
  };
674
791
 
792
+ class MultipleValueAccessorBase extends ValueAccessorBase {
793
+ constructor(parent, controlContainer) {
794
+ super(parent, controlContainer);
795
+ this.parent = parent;
796
+ this.controlContainer = controlContainer;
797
+ this.multiple = false;
798
+ }
799
+ writeValue(value) {
800
+ if (this.multiple) {
801
+ if (Array.isArray(value)) {
802
+ super.writeValue(value.filter(isValueSet));
803
+ }
804
+ else {
805
+ super.writeValue([value].filter(isValueSet));
806
+ }
807
+ }
808
+ else {
809
+ if (Array.isArray(value)) {
810
+ super.writeValue(value[0]);
811
+ }
812
+ else {
813
+ super.writeValue(value);
814
+ }
815
+ }
816
+ }
817
+ setInnerValueAndNotify(value) {
818
+ if (this.multiple) {
819
+ if (Array.isArray(value)) {
820
+ super.setInnerValueAndNotify(value.filter(isValueSet));
821
+ }
822
+ else {
823
+ super.setInnerValueAndNotify([value].filter(isValueSet));
824
+ }
825
+ }
826
+ else {
827
+ if (Array.isArray(value)) {
828
+ super.setInnerValueAndNotify(value[0]);
829
+ }
830
+ else {
831
+ super.setInnerValueAndNotify(value);
832
+ }
833
+ }
834
+ }
835
+ }
836
+ MultipleValueAccessorBase.decorators = [
837
+ { type: Component, args: [{
838
+ selector: '',
839
+ template: ''
840
+ },] }
841
+ ];
842
+ MultipleValueAccessorBase.ctorParameters = () => [
843
+ { type: FormElementComponent, decorators: [{ type: Host }, { type: Optional }] },
844
+ { type: ControlContainer, decorators: [{ type: Host }, { type: Optional }] }
845
+ ];
846
+ MultipleValueAccessorBase.propDecorators = {
847
+ multiple: [{ type: Input }]
848
+ };
849
+
675
850
  const invalidDateKey = '--invalid_date--';
676
851
  function dateValidator(control) {
677
852
  const invalid = control.value === invalidDateKey;
678
853
  return invalid ? { date: control.value } : null;
679
854
  }
680
855
 
856
+ const DATE_PICKER_TRANSLATIONS = new InjectionToken('klp.form.date.translations');
857
+ class DatePickerComponent extends MultipleValueAccessorBase {
858
+ constructor(parent, controlContainer, translations) {
859
+ super(parent, controlContainer);
860
+ this.parent = parent;
861
+ this.controlContainer = controlContainer;
862
+ this.translations = translations;
863
+ this.minDate = undefined;
864
+ this.maxDate = undefined;
865
+ this.sameMonthOnly = false;
866
+ this.format = 'dd-MM-yyyy';
867
+ this.clearable = false;
868
+ }
869
+ writeValue(value) {
870
+ if (value === invalidDateKey) {
871
+ super.writeValue(invalidDateKey);
872
+ }
873
+ else if (isNullOrUndefined(value)) {
874
+ this.dateValue = null;
875
+ super.writeValue(null);
876
+ }
877
+ else {
878
+ if (Array.isArray(value)) {
879
+ this.dateValue = value.map((e) => parse(e, 'yyyy-MM-dd', new Date()));
880
+ }
881
+ else {
882
+ this.dateValue = parse(value, 'yyyy-MM-dd', new Date());
883
+ }
884
+ super.writeValue(value);
885
+ }
886
+ }
887
+ dateChanged(value) {
888
+ if (value === invalidDateKey) {
889
+ this.setInnerValueAndNotify(invalidDateKey);
890
+ }
891
+ else if (isNullOrUndefined(value)) {
892
+ this.dateValue = null;
893
+ this.setInnerValueAndNotify(null);
894
+ }
895
+ else {
896
+ this.dateValue = value;
897
+ if (Array.isArray(value)) {
898
+ this.setInnerValueAndNotify(value.map((e) => format(e, 'yyyy-MM-dd')));
899
+ }
900
+ else {
901
+ this.setInnerValueAndNotify(format(value, 'yyyy-MM-dd'));
902
+ }
903
+ }
904
+ }
905
+ getDefaultTranslation(key) {
906
+ switch (key) {
907
+ case 'placeholder':
908
+ return () => { var _a; return (_a = this.placeholder) !== null && _a !== void 0 ? _a : 'Select date'; };
909
+ }
910
+ }
911
+ getTranslation(key, params = null) {
912
+ var _a, _b, _c;
913
+ if (key === 'placeholder' && this.multiple) {
914
+ return '';
915
+ }
916
+ return (_c = (_b = (_a = this.translations) === null || _a === void 0 ? void 0 : _a[key]) === null || _b === void 0 ? void 0 : _b.call(_a, params)) !== null && _c !== void 0 ? _c : this.getDefaultTranslation(key)(params);
917
+ }
918
+ }
919
+ DatePickerComponent.decorators = [
920
+ { type: Component, args: [{
921
+ selector: 'klp-form-date-picker',
922
+ template: "<div class=\"componentContainer\" [ngClass]=\"{showErrors: isInErrorState()}\">\n\t<klp-form-date-time-picker\n\t\t[multiple]=\"multiple\"\n\t\t[disabled]=\"disabled\"\n\t\t[(ngModel)]=\"dateValue\"\n\t\t(ngModelChange)=\"dateChanged($event)\"\n\t\t[minDate]=\"minDate\"\n\t\t[maxDate]=\"maxDate\"\n\t\t[sameMonthOnly]=\"sameMonthOnly\"\n\t\t[format]=\"format\"\n\t\t[placeholder]=\"getTranslation('placeholder')\"\n\t\t[clearable]=\"clearable\"\n\t\t[showTimeInput]=\"false\"\n\t\t(onTouch)=\"touch()\"\n\t></klp-form-date-time-picker>\n</div>\n",
923
+ providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: DatePickerComponent, multi: true }],
924
+ styles: [":host{display:block}.componentContainer.showErrors ::ng-deep klp-form-date-time-picker .clearButton,.componentContainer.showErrors ::ng-deep klp-form-date-time-picker .dateContainer{border-color:#ff8000}"]
925
+ },] }
926
+ ];
927
+ DatePickerComponent.ctorParameters = () => [
928
+ { type: FormElementComponent, decorators: [{ type: Host }, { type: Optional }] },
929
+ { type: ControlContainer, decorators: [{ type: Host }, { type: Optional }] },
930
+ { type: undefined, decorators: [{ type: Inject, args: [DATE_PICKER_TRANSLATIONS,] }, { type: Optional }] }
931
+ ];
932
+ DatePickerComponent.propDecorators = {
933
+ minDate: [{ type: Input }],
934
+ maxDate: [{ type: Input }],
935
+ sameMonthOnly: [{ type: Input }],
936
+ format: [{ type: Input }],
937
+ placeholder: [{ type: Input }],
938
+ clearable: [{ type: Input }]
939
+ };
940
+
681
941
  const KLP_DATE_FORMATS = new InjectionToken('klp.form.date.formats');
942
+ const DATE_TIME_PICKER_TRANSLATIONS = new InjectionToken('klp.form.dateTime.translations');
682
943
  function matDateFormatsFactory(component, dateFormats) {
683
944
  var _a;
684
945
  return (_a = dateFormats === null || dateFormats === void 0 ? void 0 : dateFormats(component.format)) !== null && _a !== void 0 ? _a : MAT_NATIVE_DATE_FORMATS;
685
946
  }
686
- class DatepickerComponent extends ValueAccessorBase {
687
- constructor() {
688
- super(...arguments);
947
+ class DateTimePickerComponent extends MultipleValueAccessorBase {
948
+ constructor(parent, controlContainer, translations, cdr) {
949
+ super(parent, controlContainer);
950
+ this.parent = parent;
951
+ this.controlContainer = controlContainer;
952
+ this.translations = translations;
953
+ this.cdr = cdr;
689
954
  this.minDate = undefined;
690
955
  this.maxDate = undefined;
691
- this.placeholder = 'Select date';
956
+ this.sameMonthOnly = false;
957
+ this.format = 'dd-MM-yyyy';
692
958
  this.clearable = false;
959
+ this.showTimeInput = true;
960
+ this.invalidTimeAsMidnight = false; // if the time is not valid, use 00:00 as the time
693
961
  this.minDateStartOfDay = undefined;
694
962
  this.maxDateEndOfDay = undefined;
963
+ this.selectedDates = [];
964
+ this.dateTouched = false;
965
+ this.hoursTouched = false;
966
+ this.minutesTouched = false;
967
+ this.isSelected = (d) => {
968
+ if (this.multiple) {
969
+ return this.selectedDates.some((e) => e.getTime() === d.getTime()) ? 'selected' : '';
970
+ }
971
+ return '';
972
+ };
973
+ this.filterDates = (e) => {
974
+ if (this.disabled) {
975
+ return false;
976
+ }
977
+ return true;
978
+ };
979
+ }
980
+ ngOnInit() {
981
+ super.ngOnInit();
982
+ if (this.multiple) {
983
+ this.placeholder = '';
984
+ this.showTimeInput = false;
985
+ }
986
+ }
987
+ ngAfterViewInit() {
988
+ if (this.multiple) {
989
+ // we are going to overwrite the datepicker closing fn later, so we are saving it here to restore it when needed
990
+ this.datePickingClosingFn = this.datePickerRef.close;
991
+ }
695
992
  }
696
993
  ngOnChanges(changes) {
697
994
  if (changes.minDate) {
698
- this.setMinDate(changes.minDate.currentValue);
995
+ this.determineMinAndMaxDates();
699
996
  }
700
997
  if (changes.maxDate) {
701
- this.setMaxDate(changes.maxDate.currentValue);
998
+ this.determineMinAndMaxDates();
702
999
  }
703
1000
  }
704
1001
  setMinDate(minDate) {
@@ -719,66 +1016,227 @@ class DatepickerComponent extends ValueAccessorBase {
719
1016
  this.maxDateEndOfDay = undefined;
720
1017
  }
721
1018
  }
1019
+ getSelectedMonths() {
1020
+ return removeDuplicatesFromArray(this.selectedDates.map((e) => format(e, 'MMMM'))).length;
1021
+ }
722
1022
  // dateChanged is called when the output of the datepicker is changed and
723
1023
  // parsed correctly. If the date is invalid, it will be called the first time
724
1024
  // with null but never again until a valid input is provided.
725
1025
  dateChanged(event) {
726
- const nativeInputValue = this.nativeInputRef.nativeElement.value;
727
1026
  const date = event.value;
728
- if (isNullOrUndefined(date) && stringIsSetAndNotEmpty(nativeInputValue)) {
729
- this.setInnerValueAndNotify(invalidDateKey);
1027
+ if (this.multiple) {
1028
+ this.datePickerRef.close = () => {
1029
+ };
1030
+ if (this.selectedDates.some((e) => e.getTime() === date.getTime())) {
1031
+ this.selectedDates = this.selectedDates.filter((e) => e.getTime() !== date.getTime());
1032
+ }
1033
+ else {
1034
+ this.selectedDates = [...this.selectedDates, date];
1035
+ }
1036
+ // START HACK
1037
+ // the date picker does not provide any rerender calls. Therefore, we are going to change the minDate in order to force the render
1038
+ // This is needed to show all selected days in our date picker
1039
+ // We also set the innerValue to null (with this.valueForMaterialDatePicker = null;), otherwise you can not
1040
+ // deselect your last picked date
1041
+ this.cdr.detectChanges();
1042
+ this.valueForMaterialDatePicker = null;
1043
+ const oldMinDate = this.minDateStartOfDay;
1044
+ this.minDateStartOfDay = new Date(0);
1045
+ this.cdr.detectChanges();
1046
+ this.minDateStartOfDay = oldMinDate;
1047
+ // END HACK
1048
+ if (this.sameMonthOnly) {
1049
+ if (this.selectedDates.length >= 2) {
1050
+ if (date < startOfMonth(this.selectedDates[0]) || date > endOfMonth(this.selectedDates[0])) {
1051
+ this.selectedDates = [date];
1052
+ }
1053
+ }
1054
+ this.determineMinAndMaxDates();
1055
+ }
1056
+ this.setInnerValueAndNotify(this.selectedDates);
1057
+ setTimeout(() => {
1058
+ this.datePickerRef.close = this.datePickingClosingFn;
1059
+ });
730
1060
  }
731
1061
  else {
732
- this.setInnerValueAndNotify(date);
1062
+ this.notifyNewDate();
733
1063
  }
734
1064
  }
1065
+ determineMinAndMaxDates() {
1066
+ if (this.sameMonthOnly) {
1067
+ if (this.selectedDates.length >= 2) {
1068
+ this.setMinDate(startOfMonth(this.selectedDates[0]));
1069
+ this.setMaxDate(endOfMonth(this.selectedDates[0]));
1070
+ }
1071
+ else {
1072
+ this.setMinDate(this.minDate);
1073
+ this.setMaxDate(this.maxDate);
1074
+ }
1075
+ }
1076
+ else {
1077
+ this.setMinDate(this.minDate);
1078
+ this.setMinDate(this.maxDate);
1079
+ }
1080
+ }
1081
+ notifyNewDate() {
1082
+ const nativeInputValue = this.nativeInputRef.nativeElement.value;
1083
+ const parsedHours = Number(this.hours);
1084
+ const parsedMinutes = Number(this.minutes);
1085
+ // if we dont have the time element
1086
+ if (!this.showTimeInput) {
1087
+ if (!stringIsSetAndFilled(nativeInputValue)) {
1088
+ this.setInnerValueAndNotify(null);
1089
+ return;
1090
+ }
1091
+ if (this.valueForMaterialDatePicker instanceof Date) {
1092
+ this.setInnerValueAndNotify(this.valueForMaterialDatePicker);
1093
+ return;
1094
+ }
1095
+ }
1096
+ // when all inputs are empty
1097
+ if (!stringIsSetAndFilled(nativeInputValue) && !stringIsSetAndFilled(this.hours) && !stringIsSetAndFilled(this.minutes)) {
1098
+ this.setInnerValueAndNotify(null);
1099
+ return;
1100
+ }
1101
+ // if we have date and time
1102
+ if (stringIsSetAndFilled(this.hours) &&
1103
+ Number.isFinite(parsedHours) &&
1104
+ parsedHours >= 0 &&
1105
+ parsedHours <= 23 &&
1106
+ stringIsSetAndFilled(this.minutes) &&
1107
+ Number.isFinite(parsedMinutes) &&
1108
+ parsedMinutes >= 0 &&
1109
+ parsedMinutes <= 59 &&
1110
+ this.valueForMaterialDatePicker instanceof Date) {
1111
+ const newDateWithHours = new Date(this.valueForMaterialDatePicker.setHours(parsedHours));
1112
+ const newDateWithMinutes = new Date(newDateWithHours.setMinutes(parsedMinutes));
1113
+ this.setInnerValueAndNotify(newDateWithMinutes);
1114
+ return;
1115
+ }
1116
+ if (this.invalidTimeAsMidnight) {
1117
+ if (this.valueForMaterialDatePicker instanceof Date) {
1118
+ this.setInnerValueAndNotify(this.valueForMaterialDatePicker);
1119
+ return;
1120
+ }
1121
+ }
1122
+ // all other cases, we are not in a valid state
1123
+ this.setInnerValueAndNotify(invalidDateKey);
1124
+ }
735
1125
  writeValue(value) {
736
1126
  super.writeValue(value);
737
- this.valueForMaterialDatePicker = value === invalidDateKey ? null : value;
1127
+ if (Array.isArray(value)) {
1128
+ this.selectedDates = value;
1129
+ this.determineMinAndMaxDates();
1130
+ this.valueForMaterialDatePicker = null;
1131
+ }
1132
+ else {
1133
+ this.valueForMaterialDatePicker = value === invalidDateKey ? null : value;
1134
+ if (value instanceof Date) {
1135
+ this.hours = String(value.getHours());
1136
+ this.minutes = String(value.getMinutes());
1137
+ this.formatTime();
1138
+ }
1139
+ else {
1140
+ this.hours = '';
1141
+ this.minutes = '';
1142
+ }
1143
+ }
738
1144
  }
739
1145
  // nativeValueChanged is called when the internal text value changes, but not
740
1146
  // when the date is changed via the date picker. We need this so that we can
741
1147
  // determine if the datepicker is empty or invalid.
742
- nativeValueChanged(event) {
743
- const nativeInputValue = event.target.value;
744
- const date = this.valueForMaterialDatePicker;
1148
+ nativeValueChanged() {
745
1149
  if (this.datePickerRef.opened) {
746
1150
  // if the user is typing instead of using the picker, close it.
747
1151
  this.datePickerRef.close();
748
1152
  }
749
- if (isNullOrUndefined(date) && stringIsSetAndNotEmpty(nativeInputValue)) {
750
- this.setInnerValueAndNotify(invalidDateKey);
751
- }
752
- else {
753
- this.setInnerValueAndNotify(date);
754
- }
1153
+ this.notifyNewDate();
755
1154
  }
756
1155
  resetToNull() {
757
1156
  this.setInnerValueAndNotify(null);
758
1157
  this.valueForMaterialDatePicker = null;
759
1158
  this.nativeInputRef.nativeElement.value = null;
1159
+ this.hours = '';
1160
+ this.minutes = '';
1161
+ this.selectedDates = [];
1162
+ }
1163
+ formatTime() {
1164
+ if (Number.isFinite(Number(this.hours)) && this.hours.length === 1) {
1165
+ this.hours = '0' + this.hours;
1166
+ }
1167
+ if (Number.isFinite(Number(this.minutes)) && this.minutes.length === 1) {
1168
+ this.minutes = '0' + this.minutes;
1169
+ }
1170
+ }
1171
+ touchDate() {
1172
+ this.dateTouched = true;
1173
+ this.determineAllTouched();
1174
+ }
1175
+ touchHours() {
1176
+ this.hoursTouched = true;
1177
+ this.determineAllTouched();
1178
+ }
1179
+ touchMinutes() {
1180
+ this.minutesTouched = true;
1181
+ this.determineAllTouched();
1182
+ }
1183
+ determineAllTouched() {
1184
+ if ((this.dateTouched && this.hoursTouched && this.minutesTouched) || (this.dateTouched && !this.showTimeInput)) {
1185
+ this.touch();
1186
+ }
1187
+ }
1188
+ getDefaultTranslation(key) {
1189
+ switch (key) {
1190
+ case 'placeholder':
1191
+ return () => { var _a; return (_a = this.placeholder) !== null && _a !== void 0 ? _a : 'Select date'; };
1192
+ case 'selectDays':
1193
+ return () => 'Select day(s)';
1194
+ case 'selectedDate':
1195
+ return (d) => d.toLocaleDateString();
1196
+ case 'daysSelected':
1197
+ return (amount) => `${amount} days selected`;
1198
+ case 'selectedInMonth':
1199
+ return (d) => ` in ${format(d, 'MMMM')}`;
1200
+ }
1201
+ }
1202
+ getTranslation(key, params = null) {
1203
+ var _a, _b, _c;
1204
+ if (key === 'placeholder' && this.multiple) {
1205
+ return '';
1206
+ }
1207
+ return (_c = (_b = (_a = this.translations) === null || _a === void 0 ? void 0 : _a[key]) === null || _b === void 0 ? void 0 : _b.call(_a, params)) !== null && _c !== void 0 ? _c : this.getDefaultTranslation(key)(params);
760
1208
  }
761
1209
  }
762
- DatepickerComponent.decorators = [
1210
+ DateTimePickerComponent.decorators = [
763
1211
  { type: Component, args: [{
764
- selector: 'klp-form-datepicker',
765
- template: "<div class=\"componentContainer\" [ngClass]=\"{showErrors: isInErrorState()}\">\n\t<mat-form-field [floatLabel]=\"'never'\">\n\t\t<input\n\t\t\t#nativeInput\n\t\t\tmatInput\n\t\t\t[matDatepicker]=\"picker\"\n\t\t\t[(ngModel)]=\"valueForMaterialDatePicker\"\n\t\t\t(dateInput)=\"dateChanged($event)\"\n\t\t\t(input)=\"nativeValueChanged($event)\"\n\t\t\t[min]=\"minDateStartOfDay\"\n\t\t\t[max]=\"maxDateEndOfDay\"\n\t\t\t[placeholder]=\"placeholder\"\n\t\t\t(click)=\"picker.open()\"\n\t\t\t(blur)=\"touch()\"\n\t\t>\n\t\t<mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n\t\t<mat-datepicker #picker\n\t\t></mat-datepicker>\n\t</mat-form-field>\n\t<button *ngIf=\"clearable\" class=\"clearButton\" (click)=\"resetToNull()\">\u00D7</button>\n</div>\n",
1212
+ selector: 'klp-form-date-time-picker',
1213
+ template: "<div class=\"componentContainer\" [ngClass]=\"{showErrors: isInErrorState()}\">\n\t<div class=\"dateContainer\" [ngClass]=\"{noRightBorder: !showTimeInput && clearable && !disabled, disabled: disabled}\">\n\t\t<mat-form-field floatLabel=\"never\">\n\t\t\t<div *ngIf=\"multiple\" class=\"daysSelectedCaption\" (click)=\"picker.open()\" [ngClass]=\"{disabled: disabled}\">\n\t\t\t\t<ng-container *ngIf=\"selectedDates.length >= 2\">\n\t\t\t\t\t<span>{{getTranslation('daysSelected', selectedDates.length)}}</span>\n\t\t\t\t\t<span *ngIf=\"getSelectedMonths() === 1\">{{getTranslation('selectedInMonth', selectedDates[0])}}</span>\n\t\t\t\t</ng-container>\n\t\t\t\t<span *ngIf=\"selectedDates.length === 1\">{{getTranslation('selectedDate', selectedDates[0])}}</span>\n\t\t\t\t<span *ngIf=\"selectedDates.length === 0\" class=\"placeholderForMultipleSelection\">{{getTranslation('selectDays')}}</span>\n\t\t\t</div>\n\t\t\t<input\n\t\t\t\t#nativeInput\n\t\t\t\tmatInput\n\t\t\t\t[matDatepicker]=\"picker\"\n\t\t\t\t[matDatepickerFilter]=\"filterDates\"\n\t\t\t\t[(ngModel)]=\"valueForMaterialDatePicker\"\n\t\t\t\t(dateInput)=\"dateChanged($event)\"\n\t\t\t\t(input)=\"nativeValueChanged()\"\n\t\t\t\t[min]=\"minDateStartOfDay\"\n\t\t\t\t[max]=\"maxDateEndOfDay\"\n\t\t\t\t[placeholder]=\"getTranslation('placeholder')\"\n\t\t\t\t(click)=\"picker.open()\"\n\t\t\t\t(blur)=\"touchDate()\"\n\t\t\t\t[ngClass]=\"{inputForMultipleDays: multiple}\"\n\t\t\t>\n\t\t\t<mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n\t\t\t<mat-datepicker\n\t\t\t\t#picker\n\t\t\t\t[dateClass]=\"isSelected\"\n\t\t\t></mat-datepicker>\n\t\t</mat-form-field>\n\t</div>\n\t<div class=\"timeContainer\" *ngIf=\"showTimeInput\" [ngClass]=\"{disabled: disabled}\">\n\t\t<input maxlength=\"2\" placeholder=\"__\" [disabled]=\"disabled\" [(ngModel)]=\"hours\" (ngModelChange)=\"notifyNewDate()\" (blur)=\"formatTime(); touchHours()\">\n\t\t<div class=\"divider\">:</div>\n\t\t<input maxlength=\"2\" placeholder=\"__\" [disabled]=\"disabled\" [(ngModel)]=\"minutes\" (ngModelChange)=\"notifyNewDate()\" (blur)=\"formatTime(); touchMinutes()\">\n\t</div>\n\t<button *ngIf=\"clearable && !disabled\" class=\"clearButton\" (click)=\"resetToNull()\" [ngClass]=\"{withoutSpacing: !showTimeInput}\">\u00D7</button>\n</div>\n",
766
1214
  providers: [
767
- { provide: NG_VALUE_ACCESSOR, useExisting: DatepickerComponent, multi: true },
768
- { provide: MAT_DATE_FORMATS,
769
- deps: [DatepickerComponent, [new Optional(), KLP_DATE_FORMATS]],
1215
+ { provide: NG_VALUE_ACCESSOR, useExisting: DateTimePickerComponent, multi: true },
1216
+ {
1217
+ provide: MAT_DATE_FORMATS,
1218
+ deps: [DateTimePickerComponent, [new Optional(), KLP_DATE_FORMATS]],
770
1219
  useFactory: matDateFormatsFactory,
771
1220
  }
772
1221
  ],
773
- styles: [":host{display:block}:host ::ng-deep mat-form-field{display:block;height:100%}:host ::ng-deep mat-form-field.mat-focused .mat-form-field-label,:host ::ng-deep mat-form-field .mat-form-field-label{color:#adadad}:host ::ng-deep .mat-datepicker-toggle-active{color:#666}:host ::ng-deep .mat-form-field-wrapper{padding-bottom:none}:host ::ng-deep .mat-form-field-flex{flex-direction:row-reverse}:host ::ng-deep .mat-form-field-infix{border-top:none}:host ::ng-deep .mat-form-field-suffix{margin-right:.625rem}:host ::ng-deep .mat-form-field-suffix:hover .mat-button-focus-overlay{opacity:.1}:host ::ng-deep .mat-form-field-underline{display:none}.componentContainer{-moz-transition:all .2s ease-in;-ms-transition:all .2s ease-in;-o-transition:all .2s ease-in;-webkit-transition:all .2s ease-in;border:1px solid #e6ecf5;border-radius:2px;box-shadow:none;color:#888da8;display:block;font-size:14px;height:42px;padding:.375rem .625rem;position:relative;transition:all .2s ease-in;width:100%}.componentContainer::-webkit-input-placeholder{color:#adadad}.componentContainer:-moz-placeholder,.componentContainer::-moz-placeholder{color:#adadad}.componentContainer:-ms-input-placeholder{color:#adadad}.componentContainer:focus{border-color:#3ed778;box-shadow:none;outline:0 none}.componentContainer.input-sm{height:30px}.componentContainer.input-lg{height:50px}.componentContainer.error{background-color:#ffeff4;border-color:#ff3c7e}.componentContainer.valid{background-color:#ebfaeb;border-color:#37c936;color:#278d26}.componentContainer.showErrors{border-color:#ff8000}.componentContainer .clearButton{align-items:center;background:none;border:none;bottom:0;color:#7b7b7b;display:flex;font-size:18px;position:absolute;right:1.25rem;top:0}"]
1222
+ styles: [":host{display:block}:host ::ng-deep mat-form-field{display:block;height:100%}:host ::ng-deep mat-form-field.mat-focused .mat-form-field-label,:host ::ng-deep mat-form-field .mat-form-field-label{color:#adadad}:host ::ng-deep .mat-datepicker-toggle-active{color:#666}:host ::ng-deep .mat-form-field-wrapper{padding-bottom:0}:host ::ng-deep .mat-form-field-flex{flex-direction:row-reverse}:host ::ng-deep .mat-form-field-infix{border-top:none;width:auto}:host ::ng-deep .mat-form-field-suffix{margin-right:.625rem}:host ::ng-deep .mat-form-field-suffix:hover .mat-button-focus-overlay{opacity:.1}:host ::ng-deep .mat-form-field-underline{display:none}:host ::ng-deep .daysSelectedCaption{cursor:pointer}:host ::ng-deep .daysSelectedCaption.disabled{cursor:zoom-in}:host ::ng-deep .inputForMultipleDays{display:none}.componentContainer{border-radius:2px;color:#888da8;display:flex;position:relative}.componentContainer .placeholderForMultipleSelection{color:#adadad}.componentContainer.showErrors .clearButton.withoutSpacing,.componentContainer.showErrors .dateContainer,.componentContainer.showErrors .timeContainer{border-color:#ff8000}.componentContainer .clearButton{align-items:center;background:#fff;border:1px solid #e6ecf5;color:#7b7b7b;display:flex;flex:0 0 auto;font-size:18px;margin-left:1.25rem;padding:6px 14px}.componentContainer .clearButton.withoutSpacing{border-left:none;margin-left:0}.componentContainer .clearButton:disabled{background:#f9f9f9;border:1px solid #e6ecf5}.componentContainer .dateContainer{background:#fff;border:1px solid #e6ecf5;flex:1 1 auto;padding:6px}.componentContainer .dateContainer.noRightBorder{border-right:none}.componentContainer .dateContainer.disabled{background:#f9f9f9}.componentContainer .timeContainer{align-items:center;background:#fff;border:1px solid #e6ecf5;display:flex;flex:0 0 auto;margin-left:1.25rem;padding:6px .625rem}.componentContainer .timeContainer.disabled{background:#f9f9f9}.componentContainer .timeContainer input{border:none;color:#888da8;padding:0;text-align:center;width:20px}.componentContainer .timeContainer input::-moz-placeholder{color:#adadad}.componentContainer .timeContainer input:-ms-input-placeholder{color:#adadad}.componentContainer .timeContainer input::placeholder{color:#adadad}.componentContainer .timeContainer .divider{margin:0 .3125rem}"]
774
1223
  },] }
775
1224
  ];
776
- DatepickerComponent.propDecorators = {
1225
+ DateTimePickerComponent.ctorParameters = () => [
1226
+ { type: FormElementComponent, decorators: [{ type: Host }, { type: Optional }] },
1227
+ { type: ControlContainer, decorators: [{ type: Host }, { type: Optional }] },
1228
+ { type: undefined, decorators: [{ type: Inject, args: [DATE_TIME_PICKER_TRANSLATIONS,] }, { type: Optional }] },
1229
+ { type: ChangeDetectorRef }
1230
+ ];
1231
+ DateTimePickerComponent.propDecorators = {
777
1232
  minDate: [{ type: Input }],
778
1233
  maxDate: [{ type: Input }],
1234
+ sameMonthOnly: [{ type: Input }],
779
1235
  format: [{ type: Input }],
780
1236
  placeholder: [{ type: Input }],
781
1237
  clearable: [{ type: Input }],
1238
+ showTimeInput: [{ type: Input }],
1239
+ invalidTimeAsMidnight: [{ type: Input }],
782
1240
  nativeInputRef: [{ type: ViewChild, args: ['nativeInput',] }],
783
1241
  datePickerRef: [{ type: ViewChild, args: ['picker',] }]
784
1242
  };
@@ -807,14 +1265,17 @@ NgxEnhancyFormsModule.decorators = [
807
1265
  ],
808
1266
  declarations: [
809
1267
  ValueAccessorBase,
1268
+ MultipleValueAccessorBase,
810
1269
  ButtonComponent,
811
1270
  CheckboxComponent,
812
- DatepickerComponent,
1271
+ DatePickerComponent,
1272
+ DateTimePickerComponent,
813
1273
  EmailInputComponent,
814
1274
  LoadingIndicatorComponent,
815
1275
  NumberInputComponent,
816
1276
  PasswordFieldComponent,
817
1277
  SelectComponent,
1278
+ SelectFooterComponent,
818
1279
  SortableItemsComponent,
819
1280
  TextInputComponent,
820
1281
  ToggleComponent,
@@ -823,17 +1284,21 @@ NgxEnhancyFormsModule.decorators = [
823
1284
  FormErrorComponent,
824
1285
  FormSubmitButtonComponent,
825
1286
  FormComponent,
1287
+ SubFormDirective,
826
1288
  ],
827
1289
  exports: [
828
1290
  ValueAccessorBase,
1291
+ MultipleValueAccessorBase,
829
1292
  ButtonComponent,
830
- DatepickerComponent,
1293
+ DatePickerComponent,
1294
+ DateTimePickerComponent,
831
1295
  CheckboxComponent,
832
1296
  EmailInputComponent,
833
1297
  LoadingIndicatorComponent,
834
1298
  NumberInputComponent,
835
1299
  PasswordFieldComponent,
836
1300
  SelectComponent,
1301
+ SelectFooterComponent,
837
1302
  SortableItemsComponent,
838
1303
  TextInputComponent,
839
1304
  ToggleComponent,
@@ -842,6 +1307,7 @@ NgxEnhancyFormsModule.decorators = [
842
1307
  FormErrorComponent,
843
1308
  FormSubmitButtonComponent,
844
1309
  FormComponent,
1310
+ SubFormDirective,
845
1311
  ]
846
1312
  },] }
847
1313
  ];
@@ -854,5 +1320,5 @@ NgxEnhancyFormsModule.decorators = [
854
1320
  * Generated bundle index. Do not edit.
855
1321
  */
856
1322
 
857
- export { ButtonComponent, CheckboxComponent, DEFAULT_ERROR_MESSAGES, DatepickerComponent, EmailInputComponent, FORM_ERROR_MESSAGES, FormCaptionComponent, FormComponent, FormElementComponent, FormErrorComponent, FormSubmitButtonComponent, KLP_DATE_FORMATS, LoadingIndicatorComponent, NgxEnhancyFormsModule, NumberInputComponent, PasswordFieldComponent, SelectComponent, SortableItemsComponent, SubForm, TextInputComponent, ToggleComponent, ValueAccessorBase, dateValidator, invalidDateKey, invalidFieldsSymbol, matDateFormatsFactory, MaterialModule as ɵa };
1323
+ export { ButtonComponent, CheckboxComponent, DATE_PICKER_TRANSLATIONS, DATE_TIME_PICKER_TRANSLATIONS, DEFAULT_ERROR_MESSAGES, DatePickerComponent, DateTimePickerComponent, EmailInputComponent, FORM_ERROR_MESSAGES, FormCaptionComponent, FormComponent, FormElementComponent, FormErrorComponent, FormSubmitButtonComponent, KLP_DATE_FORMATS, LoadingIndicatorComponent, MultipleValueAccessorBase, NgxEnhancyFormsModule, NumberInputComponent, PasswordFieldComponent, SELECT_TRANSLATIONS, SelectComponent, SelectFooterComponent, SortableItemsComponent, SubFormDirective, TextInputComponent, ToggleComponent, ValueAccessorBase, dateValidator, invalidDateKey, invalidFieldsSymbol, matDateFormatsFactory, MaterialModule as ɵa };
858
1324
  //# sourceMappingURL=klippa-ngx-enhancy-forms.js.map