@skyux/datetime 5.5.0 → 5.6.2

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 (70) hide show
  1. package/bundles/skyux-datetime.umd.js +1090 -764
  2. package/documentation.json +793 -932
  3. package/esm2015/lib/modules/date-pipe/date-format-utility.js +0 -2
  4. package/esm2015/lib/modules/date-pipe/date-format-utility.js.map +1 -1
  5. package/esm2015/lib/modules/date-pipe/date-pipe.module.js.map +1 -1
  6. package/esm2015/lib/modules/date-pipe/date.pipe.js.map +1 -1
  7. package/esm2015/lib/modules/date-pipe/fuzzy-date.pipe.js.map +1 -1
  8. package/esm2015/lib/modules/date-range-picker/date-range-picker.component.js +49 -60
  9. package/esm2015/lib/modules/date-range-picker/date-range-picker.component.js.map +1 -1
  10. package/esm2015/lib/modules/date-range-picker/date-range-picker.module.js.map +1 -1
  11. package/esm2015/lib/modules/date-range-picker/date-range.service.js.map +1 -1
  12. package/esm2015/lib/modules/date-range-picker/types/date-range-calculation.js.map +1 -1
  13. package/esm2015/lib/modules/date-range-picker/types/date-range-calculator-config.js.map +1 -1
  14. package/esm2015/lib/modules/date-range-picker/types/date-range-calculator.js.map +1 -1
  15. package/esm2015/lib/modules/date-range-picker/types/date-range-default-calculator-config.js.map +1 -1
  16. package/esm2015/lib/modules/date-range-picker/types/date-range-default-calculator-configs.js.map +1 -1
  17. package/esm2015/lib/modules/date-range-picker/types/date-range-relative-value.js +0 -3
  18. package/esm2015/lib/modules/date-range-picker/types/date-range-relative-value.js.map +1 -1
  19. package/esm2015/lib/modules/datepicker/date-formatter.js.map +1 -1
  20. package/esm2015/lib/modules/datepicker/datepicker-calendar-change.js.map +1 -1
  21. package/esm2015/lib/modules/datepicker/datepicker-calendar-inner.component.js +6 -6
  22. package/esm2015/lib/modules/datepicker/datepicker-calendar-inner.component.js.map +1 -1
  23. package/esm2015/lib/modules/datepicker/datepicker-calendar.component.js +1 -1
  24. package/esm2015/lib/modules/datepicker/datepicker-calendar.component.js.map +1 -1
  25. package/esm2015/lib/modules/datepicker/datepicker-input-fuzzy.directive.js +62 -52
  26. package/esm2015/lib/modules/datepicker/datepicker-input-fuzzy.directive.js.map +1 -1
  27. package/esm2015/lib/modules/datepicker/datepicker-input.directive.js +53 -42
  28. package/esm2015/lib/modules/datepicker/datepicker-input.directive.js.map +1 -1
  29. package/esm2015/lib/modules/datepicker/datepicker.component.js +2 -2
  30. package/esm2015/lib/modules/datepicker/datepicker.component.js.map +1 -1
  31. package/esm2015/lib/modules/datepicker/datepicker.module.js +10 -10
  32. package/esm2015/lib/modules/datepicker/datepicker.module.js.map +1 -1
  33. package/esm2015/lib/modules/datepicker/daypicker-button.component.js.map +1 -1
  34. package/esm2015/lib/modules/datepicker/daypicker-cell.component.js +1 -1
  35. package/esm2015/lib/modules/datepicker/daypicker-cell.component.js.map +1 -1
  36. package/esm2015/lib/modules/datepicker/daypicker.component.js +13 -13
  37. package/esm2015/lib/modules/datepicker/daypicker.component.js.map +1 -1
  38. package/esm2015/lib/modules/datepicker/fuzzy-date.service.js +10 -10
  39. package/esm2015/lib/modules/datepicker/fuzzy-date.service.js.map +1 -1
  40. package/esm2015/lib/modules/datepicker/monthpicker.component.js +5 -5
  41. package/esm2015/lib/modules/datepicker/monthpicker.component.js.map +1 -1
  42. package/esm2015/lib/modules/datepicker/yearpicker.component.js +2 -2
  43. package/esm2015/lib/modules/datepicker/yearpicker.component.js.map +1 -1
  44. package/esm2015/lib/modules/shared/sky-datetime-resources.module.js +1 -1
  45. package/esm2015/lib/modules/shared/sky-datetime-resources.module.js.map +1 -1
  46. package/esm2015/lib/modules/timepicker/timepicker.component.js +8 -8
  47. package/esm2015/lib/modules/timepicker/timepicker.component.js.map +1 -1
  48. package/esm2015/lib/modules/timepicker/timepicker.directive.js +7 -8
  49. package/esm2015/lib/modules/timepicker/timepicker.directive.js.map +1 -1
  50. package/esm2015/lib/modules/timepicker/timepicker.module.js +3 -3
  51. package/esm2015/lib/modules/timepicker/timepicker.module.js.map +1 -1
  52. package/esm2015/testing/datepicker-fixture.js.map +1 -1
  53. package/esm2015/testing/timepicker-fixture.js.map +1 -1
  54. package/fesm2015/skyux-datetime-testing.js.map +1 -1
  55. package/fesm2015/skyux-datetime.js +528 -524
  56. package/fesm2015/skyux-datetime.js.map +1 -1
  57. package/lib/modules/date-pipe/date-format-utility.d.ts +0 -1
  58. package/lib/modules/date-range-picker/date-range-picker.component.d.ts +6 -5
  59. package/lib/modules/date-range-picker/date-range.service.d.ts +1 -1
  60. package/lib/modules/date-range-picker/types/date-range-calculation.d.ts +1 -1
  61. package/lib/modules/date-range-picker/types/date-range-calculator.d.ts +1 -1
  62. package/lib/modules/date-range-picker/types/date-range-relative-value.d.ts +0 -1
  63. package/lib/modules/datepicker/datepicker-calendar-change.d.ts +1 -1
  64. package/lib/modules/datepicker/datepicker-calendar-inner.component.d.ts +17 -14
  65. package/lib/modules/datepicker/datepicker-calendar.component.d.ts +1 -1
  66. package/lib/modules/datepicker/datepicker-input-fuzzy.directive.d.ts +7 -2
  67. package/lib/modules/datepicker/datepicker-input.directive.d.ts +7 -3
  68. package/lib/modules/datepicker/datepicker.component.d.ts +2 -2
  69. package/lib/modules/datepicker/daypicker.component.d.ts +1 -1
  70. package/package.json +18 -16
@@ -15,15 +15,13 @@ import * as i1 from '@skyux/core';
15
15
  import { SkyAffixAutoFitContext, SkyAffixModule, SkyOverlayModule } from '@skyux/core';
16
16
  import * as i2 from '@skyux/indicators';
17
17
  import { SkyIconModule, SkyWaitModule } from '@skyux/indicators';
18
- import * as i3$2 from '@skyux/theme';
19
- import { SkyThemeModule } from '@skyux/theme';
20
18
  import * as i3$1 from '@skyux/popovers';
21
19
  import { SkyPopoverMessageType, SkyPopoverModule } from '@skyux/popovers';
20
+ import * as i3$2 from '@skyux/theme';
21
+ import { SkyThemeModule } from '@skyux/theme';
22
22
 
23
23
  // This class is mostly ported from the Angular 4.x DatePipe in order to maintain the old
24
24
  class SkyDateFormatUtility {
25
- /* istanbul ignore next */
26
- constructor() { }
27
25
  static format(locale, value, pattern) {
28
26
  let date;
29
27
  if (isBlank(value) || value !== value) {
@@ -293,10 +291,10 @@ class SkyFuzzyDateService {
293
291
  return '';
294
292
  }
295
293
  const separator = this.getDateSeparator(format);
296
- let dateParts = [];
297
- let formatTokens = format.split(separator);
294
+ const dateParts = [];
295
+ const formatTokens = format.split(separator);
298
296
  locale = locale || this.currentLocale;
299
- let fuzzyDateMoment = this.getMomentFromFuzzyDate(fuzzyDate).locale(locale);
297
+ const fuzzyDateMoment = this.getMomentFromFuzzyDate(fuzzyDate).locale(locale);
300
298
  for (let index = 0; index < formatTokens.length; index++) {
301
299
  const token = formatTokens[index];
302
300
  /* istanbul ignore else */
@@ -348,7 +346,7 @@ class SkyFuzzyDateService {
348
346
  const dateFormatIndexes = this.getDateFormatIndexes(dateFormat);
349
347
  let dateString = '';
350
348
  // Get the components of the date in the order expected of the local format.
351
- let dateComponents = [
349
+ const dateComponents = [
352
350
  { value: fuzzyDate.year || 0, index: dateFormatIndexes.yearIndex },
353
351
  { value: fuzzyDate.month || 0, index: dateFormatIndexes.monthIndex },
354
352
  { value: fuzzyDate.day || 0, index: dateFormatIndexes.dayIndex },
@@ -368,7 +366,7 @@ class SkyFuzzyDateService {
368
366
  if (!selectedDate || !dateFormat) {
369
367
  return;
370
368
  }
371
- let fuzzyDate = {};
369
+ const fuzzyDate = {};
372
370
  const dateFormatIndexes = this.getDateFormatIndexes(dateFormat);
373
371
  if (dateFormatIndexes.yearIndex > -1) {
374
372
  fuzzyDate.year = selectedDate.getFullYear();
@@ -451,7 +449,7 @@ class SkyFuzzyDateService {
451
449
  // Check if day is valid.
452
450
  if (day) {
453
451
  day = parseInt(day, 10);
454
- let fuzzyMoment = this.getMomentFromFuzzyDate({
452
+ const fuzzyMoment = this.getMomentFromFuzzyDate({
455
453
  month: month,
456
454
  day: day,
457
455
  year: year,
@@ -502,7 +500,7 @@ class SkyFuzzyDateService {
502
500
  };
503
501
  }
504
502
  getCurrentFuzzyDate() {
505
- let currentDate = moment();
503
+ const currentDate = moment();
506
504
  return {
507
505
  day: currentDate.date(),
508
506
  month: currentDate.month() + 1,
@@ -523,7 +521,7 @@ class SkyFuzzyDateService {
523
521
  */
524
522
  getDateSeparator(dateFormat) {
525
523
  let returnValue;
526
- let separators = ['/', '.', '-', ' '];
524
+ const separators = ['/', '.', '-', ' '];
527
525
  separators.forEach((separator) => {
528
526
  if (!returnValue && dateFormat.indexOf(separator) > 0) {
529
527
  returnValue = separator;
@@ -591,7 +589,7 @@ class SkyFuzzyDateService {
591
589
  // Returns the index of each of the date components in the provided string (month, day, year).
592
590
  getDateValueIndexes(date, dateFormat) {
593
591
  const dateFormatIndexes = this.getDateFormatIndexes(dateFormat);
594
- let dateComponentIndexes = [];
592
+ const dateComponentIndexes = [];
595
593
  if (dateFormatIndexes.yearIndex > -1) {
596
594
  dateComponentIndexes.push(dateFormatIndexes.yearIndex);
597
595
  }
@@ -900,37 +898,6 @@ var SkyDateRangeCalculatorType;
900
898
  SkyDateRangeCalculatorType[SkyDateRangeCalculatorType["Relative"] = 3] = "Relative";
901
899
  })(SkyDateRangeCalculatorType || (SkyDateRangeCalculatorType = {}));
902
900
 
903
- /**
904
- * @internal
905
- */
906
- class SkyDatepickerAdapterService {
907
- constructor(renderer) {
908
- this.renderer = renderer;
909
- }
910
- init(elRef) {
911
- this.el = elRef.nativeElement;
912
- }
913
- elementIsFocused() {
914
- const focusedEl = document.activeElement;
915
- return this.el.contains(focusedEl);
916
- }
917
- elementIsVisible() {
918
- const styles = this.el && getComputedStyle(this.el);
919
- return styles && styles.visibility === 'visible';
920
- }
921
- getPlaceholder(elementRef) {
922
- return elementRef.nativeElement.getAttribute('placeholder');
923
- }
924
- setPlaceholder(elementRef, value) {
925
- this.renderer.setAttribute(elementRef.nativeElement, 'placeholder', value);
926
- }
927
- }
928
- SkyDatepickerAdapterService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerAdapterService, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Injectable });
929
- SkyDatepickerAdapterService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerAdapterService });
930
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerAdapterService, decorators: [{
931
- type: Injectable
932
- }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; } });
933
-
934
901
  class SkyDateFormatter {
935
902
  /**
936
903
  * Sets moment's global locale.
@@ -1114,7 +1081,7 @@ class SkyDatepickerCalendarInnerComponent {
1114
1081
  return false;
1115
1082
  }
1116
1083
  onKeydown(event) {
1117
- let key = this.keys[event.which];
1084
+ const key = this.keys[event.which];
1118
1085
  if (!key || event.shiftKey || event.altKey) {
1119
1086
  return;
1120
1087
  }
@@ -1136,7 +1103,7 @@ class SkyDatepickerCalendarInnerComponent {
1136
1103
  }
1137
1104
  createDateObject(date, format, isSecondary, id) {
1138
1105
  const customDateMatch = this.getCustomDate(date);
1139
- let dateObject = {
1106
+ const dateObject = {
1140
1107
  date: new Date(date.getFullYear(), date.getMonth(), date.getDate()),
1141
1108
  label: this.dateFilter(date, format),
1142
1109
  selected: this.compare(date, this.selectedDate) === 0,
@@ -1150,7 +1117,7 @@ class SkyDatepickerCalendarInnerComponent {
1150
1117
  return dateObject;
1151
1118
  }
1152
1119
  createCalendarRows(dates, size) {
1153
- let rows = [];
1120
+ const rows = [];
1154
1121
  while (dates.length > 0) {
1155
1122
  rows.push(dates.splice(0, size));
1156
1123
  }
@@ -1160,7 +1127,7 @@ class SkyDatepickerCalendarInnerComponent {
1160
1127
  This is ensures that no strangeness happens when converting a date to local time.
1161
1128
  */
1162
1129
  fixTimeZone(date) {
1163
- let newDate = new Date(date);
1130
+ const newDate = new Date(date);
1164
1131
  newDate.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
1165
1132
  return newDate;
1166
1133
  }
@@ -1209,8 +1176,8 @@ class SkyDatepickerCalendarInnerComponent {
1209
1176
  /* istanbul ignore else */
1210
1177
  /* sanity check */
1211
1178
  if (expectedStep) {
1212
- let year = this.activeDate.getFullYear() + direction * (expectedStep.years || 0);
1213
- let month = this.activeDate.getMonth() + direction * (expectedStep.months || 0);
1179
+ const year = this.activeDate.getFullYear() + direction * (expectedStep.years || 0);
1180
+ const month = this.activeDate.getMonth() + direction * (expectedStep.months || 0);
1214
1181
  this.activeDate = new Date(year, month, 1);
1215
1182
  this.refreshView();
1216
1183
  }
@@ -1278,6 +1245,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
1278
1245
  type: Output
1279
1246
  }] } });
1280
1247
 
1248
+ /**
1249
+ * @internal
1250
+ */
1251
+ class SkyDatepickerAdapterService {
1252
+ constructor(renderer) {
1253
+ this.renderer = renderer;
1254
+ }
1255
+ init(elRef) {
1256
+ this.el = elRef.nativeElement;
1257
+ }
1258
+ elementIsFocused() {
1259
+ const focusedEl = document.activeElement;
1260
+ return this.el.contains(focusedEl);
1261
+ }
1262
+ elementIsVisible() {
1263
+ const styles = this.el && getComputedStyle(this.el);
1264
+ return styles && styles.visibility === 'visible';
1265
+ }
1266
+ getPlaceholder(elementRef) {
1267
+ return elementRef.nativeElement.getAttribute('placeholder');
1268
+ }
1269
+ setPlaceholder(elementRef, value) {
1270
+ this.renderer.setAttribute(elementRef.nativeElement, 'placeholder', value);
1271
+ }
1272
+ }
1273
+ SkyDatepickerAdapterService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerAdapterService, deps: [{ token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Injectable });
1274
+ SkyDatepickerAdapterService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerAdapterService });
1275
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerAdapterService, decorators: [{
1276
+ type: Injectable
1277
+ }], ctorParameters: function () { return [{ type: i0.Renderer2 }]; } });
1278
+
1281
1279
  class SkyDatepickerConfigService {
1282
1280
  constructor() {
1283
1281
  /**
@@ -1494,7 +1492,7 @@ class SkyDayPickerComponent {
1494
1492
  this.ngUnsubscribe.complete();
1495
1493
  }
1496
1494
  getDates(startDate, n) {
1497
- let dates = new Array(n);
1495
+ const dates = new Array(n);
1498
1496
  let current = new Date(startDate.getTime());
1499
1497
  let i = 0;
1500
1498
  let date;
@@ -1507,17 +1505,17 @@ class SkyDayPickerComponent {
1507
1505
  return dates;
1508
1506
  }
1509
1507
  compareDays(date1, date2) {
1510
- let d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
1511
- let d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
1508
+ const d1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
1509
+ const d2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
1512
1510
  return d1.getTime() - d2.getTime();
1513
1511
  }
1514
1512
  refreshDayView() {
1515
- let year = this.datepicker.activeDate.getFullYear();
1516
- let month = this.datepicker.activeDate.getMonth();
1517
- let firstDayOfMonth = new Date(year, month, 1);
1518
- let difference = this.datepicker.startingDay - firstDayOfMonth.getDay();
1519
- let numDisplayedFromPreviousMonth = difference > 0 ? 7 - difference : -difference;
1520
- let firstDate = new Date(firstDayOfMonth.getTime());
1513
+ const year = this.datepicker.activeDate.getFullYear();
1514
+ const month = this.datepicker.activeDate.getMonth();
1515
+ const firstDayOfMonth = new Date(year, month, 1);
1516
+ const difference = this.datepicker.startingDay - firstDayOfMonth.getDay();
1517
+ const numDisplayedFromPreviousMonth = difference > 0 ? 7 - difference : -difference;
1518
+ const firstDate = new Date(firstDayOfMonth.getTime());
1521
1519
  if (this.datepicker.activeDate.getDate() !== this.initialDate) {
1522
1520
  this.activeDateHasChanged = true;
1523
1521
  }
@@ -1527,10 +1525,10 @@ class SkyDayPickerComponent {
1527
1525
  firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
1528
1526
  }
1529
1527
  // 42 is the number of days on a six-week calendar
1530
- let days = this.getDates(firstDate, 42);
1531
- let pickerDates = [];
1528
+ const days = this.getDates(firstDate, 42);
1529
+ const pickerDates = [];
1532
1530
  for (let i = 0; i < 42; i++) {
1533
- let _dateObject = this.datepicker.createDateObject(days[i], this.datepicker.formatDay, days[i].getMonth() !== month, this.datepicker.datepickerId + '-' + i);
1531
+ const _dateObject = this.datepicker.createDateObject(days[i], this.datepicker.formatDay, days[i].getMonth() !== month, this.datepicker.datepickerId + '-' + i);
1534
1532
  pickerDates[i] = _dateObject;
1535
1533
  }
1536
1534
  this.labels = [];
@@ -1567,7 +1565,7 @@ class SkyDayPickerComponent {
1567
1565
  date = date + 7;
1568
1566
  }
1569
1567
  else if (key === 'pageup' || key === 'pagedown') {
1570
- let month = this.datepicker.activeDate.getMonth() + (key === 'pageup' ? -1 : 1);
1568
+ const month = this.datepicker.activeDate.getMonth() + (key === 'pageup' ? -1 : 1);
1571
1569
  this.datepicker.activeDate.setMonth(month, 1);
1572
1570
  date = Math.min(this.getDaysInMonth(this.datepicker.activeDate.getFullYear(), this.datepicker.activeDate.getMonth()), date);
1573
1571
  }
@@ -1683,13 +1681,13 @@ class SkyMonthPickerComponent {
1683
1681
  }, 'month');
1684
1682
  }
1685
1683
  compareMonth(date1, date2) {
1686
- let d1 = new Date(date1.getFullYear(), date1.getMonth());
1687
- let d2 = new Date(date2.getFullYear(), date2.getMonth());
1684
+ const d1 = new Date(date1.getFullYear(), date1.getMonth());
1685
+ const d2 = new Date(date2.getFullYear(), date2.getMonth());
1688
1686
  return d1.getTime() - d2.getTime();
1689
1687
  }
1690
1688
  refreshMonthView() {
1691
- let months = new Array(12);
1692
- let year = this.datepicker.activeDate.getFullYear();
1689
+ const months = new Array(12);
1690
+ const year = this.datepicker.activeDate.getFullYear();
1693
1691
  let date;
1694
1692
  for (let i = 0; i < 12; i++) {
1695
1693
  date = new Date(year, i, 1);
@@ -1716,7 +1714,7 @@ class SkyMonthPickerComponent {
1716
1714
  date = date + this.datepicker.monthColLimit;
1717
1715
  }
1718
1716
  else if (key === 'pageup' || key === 'pagedown') {
1719
- let year = this.datepicker.activeDate.getFullYear() + (key === 'pageup' ? -1 : 1);
1717
+ const year = this.datepicker.activeDate.getFullYear() + (key === 'pageup' ? -1 : 1);
1720
1718
  this.datepicker.activeDate.setFullYear(year);
1721
1719
  }
1722
1720
  else if (key === 'home') {
@@ -1767,9 +1765,9 @@ class SkyYearPickerComponent {
1767
1765
  return date1.getFullYear() - date2.getFullYear();
1768
1766
  }
1769
1767
  refreshYearView() {
1770
- let years = new Array(this.datepicker.yearRange);
1768
+ const years = new Array(this.datepicker.yearRange);
1771
1769
  let date;
1772
- let start = this.getStartingYear(this.datepicker.activeDate.getFullYear());
1770
+ const start = this.getStartingYear(this.datepicker.activeDate.getFullYear());
1773
1771
  for (let i = 0; i < this.datepicker.yearRange; i++) {
1774
1772
  date = new Date(this.datepicker.activeDate);
1775
1773
  date.setFullYear(start + i, 0, 1);
@@ -2239,61 +2237,64 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
2239
2237
  }] } });
2240
2238
 
2241
2239
  // tslint:disable:no-forward-ref no-use-before-declare
2242
- const SKY_DATEPICKER_VALUE_ACCESSOR = {
2240
+ const SKY_FUZZY_DATEPICKER_VALUE_ACCESSOR = {
2243
2241
  provide: NG_VALUE_ACCESSOR,
2244
- useExisting: forwardRef(() => SkyDatepickerInputDirective),
2242
+ useExisting: forwardRef(() => SkyFuzzyDatepickerInputDirective),
2245
2243
  multi: true,
2246
2244
  };
2247
- const SKY_DATEPICKER_VALIDATOR = {
2245
+ const SKY_FUZZY_DATEPICKER_VALIDATOR = {
2248
2246
  provide: NG_VALIDATORS,
2249
- useExisting: forwardRef(() => SkyDatepickerInputDirective),
2247
+ useExisting: forwardRef(() => SkyFuzzyDatepickerInputDirective),
2250
2248
  multi: true,
2251
2249
  };
2252
2250
  // tslint:enable
2253
- class SkyDatepickerInputDirective {
2254
- constructor(adapter, changeDetector, configService, elementRef, localeProvider, renderer, resourcesService, datepickerComponent) {
2255
- this.adapter = adapter;
2251
+ class SkyFuzzyDatepickerInputDirective {
2252
+ constructor(changeDetector, configService, elementRef, fuzzyDateService, localeProvider, renderer, resourcesService, datepickerComponent) {
2256
2253
  this.changeDetector = changeDetector;
2257
2254
  this.configService = configService;
2258
2255
  this.elementRef = elementRef;
2256
+ this.fuzzyDateService = fuzzyDateService;
2259
2257
  this.localeProvider = localeProvider;
2260
2258
  this.renderer = renderer;
2261
2259
  this.resourcesService = resourcesService;
2262
2260
  this.datepickerComponent = datepickerComponent;
2263
2261
  /**
2264
- * Indicates whether to disable date validation on the datepicker input.
2262
+ * Indicates whether to disable date validation on the fuzzy datepicker input.
2265
2263
  * @default false
2266
2264
  */
2267
2265
  this.skyDatepickerNoValidate = false;
2268
2266
  this.dateFormatter = new SkyDateFormatter();
2269
- this.isFirstChange = true;
2270
2267
  this.ngUnsubscribe = new Subject();
2268
+ this._futureDisabled = false;
2269
+ this._disabled = false;
2270
+ this._yearRequired = false;
2271
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2271
2272
  this.onChange = (_) => { };
2272
- /*istanbul ignore next */
2273
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2273
2274
  this.onTouched = () => { };
2275
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2274
2276
  this.onValidatorChange = () => { };
2275
- this.initialPlaceholder = this.adapter.getPlaceholder(this.elementRef);
2276
- this.updatePlaceholder();
2277
2277
  this.localeProvider
2278
2278
  .getLocaleInfo()
2279
2279
  .pipe(takeUntil(this.ngUnsubscribe))
2280
2280
  .subscribe((localeInfo) => {
2281
- SkyDateFormatter.setLocale(localeInfo.locale);
2281
+ this.locale = localeInfo.locale;
2282
+ SkyDateFormatter.setLocale(this.locale);
2282
2283
  this.preferredShortDateFormat =
2283
2284
  SkyDateFormatter.getPreferredShortDateFormat();
2284
- this.applyDateFormat();
2285
2285
  });
2286
2286
  }
2287
2287
  /**
2288
2288
  * Specifies the date format for the input. Place this attribute on the `input` element
2289
- * to override the default in the `SkyDatepickerConfigService`.
2289
+ * to override the default in `SkyDatepickerConfigService`.
2290
2290
  * @default "MM/DD/YYYY"
2291
2291
  */
2292
2292
  set dateFormat(value) {
2293
- /* istanbul ignore else */
2294
- if (value !== this._dateFormat) {
2295
- this._dateFormat = value;
2296
- this.applyDateFormat();
2293
+ this._dateFormat = value;
2294
+ if (this.value) {
2295
+ const formattedDate = this.fuzzyDateService.format(this.value, this.dateFormat, this.locale);
2296
+ this.setInputElementValue(formattedDate);
2297
+ this.changeDetector.markForCheck();
2297
2298
  }
2298
2299
  }
2299
2300
  get dateFormat() {
@@ -2311,53 +2312,58 @@ class SkyDatepickerInputDirective {
2311
2312
  this.renderer.setProperty(this.elementRef.nativeElement, 'disabled', value);
2312
2313
  }
2313
2314
  get disabled() {
2314
- return this._disabled || false;
2315
+ return this._disabled;
2315
2316
  }
2316
2317
  /**
2317
- * @internal
2318
- * Indicates if the input element or any of its children have focus.
2318
+ * Indicates whether to prevent users from specifying dates that are in the future.
2319
+ * Place this attribute on the `input` element.
2320
+ * @default false
2319
2321
  */
2320
- get inputIsFocused() {
2321
- return this.adapter.elementIsFocused();
2322
+ set futureDisabled(value) {
2323
+ this._futureDisabled = value;
2324
+ this.onValidatorChange();
2325
+ }
2326
+ get futureDisabled() {
2327
+ return this._futureDisabled;
2322
2328
  }
2323
2329
  /**
2324
- * Specifies the latest date that is available in the calendar. Place this attribute on
2325
- * the `input` element to override the default in `SkyDatepickerConfigService`.
2330
+ * Specifies the latest fuzzy date allowed. Place this attribute on the `input` element
2331
+ * to prevent fuzzy dates after a specified date. This property accepts
2332
+ * a `SkyFuzzyDate` value that includes numeric month, day, and year values.
2333
+ * For example: `{ month: 1, day: 1, year: 2027 }`.
2326
2334
  */
2327
2335
  set maxDate(value) {
2328
2336
  this._maxDate = value;
2329
- this.datepickerComponent.maxDate = this.maxDate;
2337
+ this.datepickerComponent.maxDate = this.getMaxDate();
2330
2338
  this.onValidatorChange();
2331
2339
  }
2332
2340
  get maxDate() {
2333
- return this._maxDate || this.configService.maxDate;
2341
+ return this._maxDate;
2334
2342
  }
2335
2343
  /**
2336
- * Specifies the earliest date that is available in the calendar. Place this attribute on
2337
- * the `input` element to override the default in `SkyDatepickerConfigService`.
2344
+ * Specifies the earliest fuzzy date allowed. Place this attribute on the `input` element
2345
+ * to prevent fuzzy dates before a specified date. This property accepts a `SkyFuzzyDate` value
2346
+ * that includes numeric month, day, and year values.
2347
+ * For example: `{ month: 1, day: 1, year: 2007 }`.
2338
2348
  */
2339
2349
  set minDate(value) {
2340
2350
  this._minDate = value;
2341
- this.datepickerComponent.minDate = this.minDate;
2351
+ this.datepickerComponent.minDate = this.getMinDate();
2342
2352
  this.onValidatorChange();
2343
2353
  }
2344
2354
  get minDate() {
2345
- return this._minDate || this.configService.minDate;
2355
+ return this._minDate;
2346
2356
  }
2347
2357
  /**
2348
- * Creates the datepicker input and calendar. Place this directive on an `input` element,
2349
- * and wrap the input in a `sky-datepicker` component. The value that users select is driven
2358
+ * Creates the fuzzy datepicker input and calendar to let users specify dates that are
2359
+ * not complete. For example, if users know the year but not the month or day, they can
2360
+ * enter just the year. Place this directive on an `input` element, and wrap the `input`
2361
+ * in a `sky-datepicker` component. The value that users select is driven
2350
2362
  * through the `ngModel` attribute specified on the `input` element.
2351
2363
  * @required
2352
2364
  */
2353
- set skyDatepickerInput(value) {
2354
- if (value) {
2355
- console.warn('[Deprecation warning] You no longer need to provide a template reference variable ' +
2356
- 'to the `skyDatepickerInput` attribute (this will be a breaking change in the next ' +
2357
- 'major version release).\n' +
2358
- 'Do this instead:\n' +
2359
- '<sky-datepicker>\n <input skyDatepickerInput />\n</sky-datepicker>');
2360
- }
2365
+ set skyFuzzyDatepickerInput(value) {
2366
+ // TODO: Remove this property in a future version of SKY UX.
2361
2367
  }
2362
2368
  /**
2363
2369
  * Specifies the starting day of the week in the calendar, where `0` sets the starting day
@@ -2374,54 +2380,36 @@ class SkyDatepickerInputDirective {
2374
2380
  return this._startingDay || this.configService.startingDay;
2375
2381
  }
2376
2382
  /**
2377
- * Indicates whether the format of the date value must match the format from the `dateFormat` value.
2378
- * If this property is `true` and the datepicker input directive cannot find an exact match, then
2379
- * the input is marked as invalid.
2380
- * If this property is `false` and the datepicker input directive cannot find an exact match, then
2381
- * it attempts to format the string based on the [ISO 8601 standard format](https://www.iso.org/iso-8601-date-and-time-format.html).
2383
+ * Indicates whether to require the year in fuzzy dates.
2382
2384
  * @default false
2383
2385
  */
2384
- set strict(value) {
2385
- this._strict = value;
2386
+ set yearRequired(value) {
2387
+ this._yearRequired = value;
2388
+ this.onValidatorChange();
2386
2389
  }
2387
- get strict() {
2388
- return this._strict || false;
2390
+ get yearRequired() {
2391
+ return this._yearRequired;
2389
2392
  }
2390
2393
  get value() {
2391
2394
  return this._value;
2392
2395
  }
2393
2396
  set value(value) {
2394
- const dateValue = this.getDateValue(value);
2395
- const areDatesEqual = this._value instanceof Date &&
2396
- dateValue &&
2397
- dateValue.getTime() === this._value.getTime();
2398
- const isValidDateString = this.isDateStringValid(value);
2399
- // If the string value supplied is malformed, do not set the value to its Date equivalent.
2400
- // (JavaScript's Date parser will convert poorly formatted dates to Date objects, such as "abc 123", which isn't ideal.)
2401
- if (!isValidDateString) {
2402
- this._value = value;
2403
- this.notifyUpdatedValue();
2404
- }
2405
- else if (dateValue !== this._value || !areDatesEqual) {
2406
- this._value = dateValue || value;
2407
- this.notifyUpdatedValue();
2408
- }
2409
- if (dateValue && isValidDateString) {
2410
- const formattedDate = this.dateFormatter.format(dateValue, this.dateFormat);
2411
- this.setInputElementValue(formattedDate);
2412
- }
2413
- else {
2414
- this.setInputElementValue(value || '');
2415
- }
2397
+ this.updateValue(value);
2416
2398
  }
2417
2399
  ngOnInit() {
2400
+ if (this.yearRequired) {
2401
+ if (this.dateFormat.toLowerCase().indexOf('y') === -1) {
2402
+ throw new Error('You have configured conflicting settings. Year is required and dateFormat does not include year.');
2403
+ }
2404
+ }
2418
2405
  if (!this.datepickerComponent) {
2419
- throw new Error('You must wrap the `skyDatepickerInput` directive within a ' +
2406
+ throw new Error('You must wrap the `skyFuzzyDatepickerInput` directive within a ' +
2420
2407
  '`<sky-datepicker>` component!');
2421
2408
  }
2422
2409
  const element = this.elementRef.nativeElement;
2423
2410
  this.renderer.addClass(element, 'sky-form-control');
2424
2411
  const hasAriaLabel = element.getAttribute('aria-label');
2412
+ /* istanbul ignore else */
2425
2413
  if (!hasAriaLabel) {
2426
2414
  this.resourcesService
2427
2415
  .getString('skyux_date_field_default_label')
@@ -2433,10 +2421,8 @@ class SkyDatepickerInputDirective {
2433
2421
  }
2434
2422
  ngAfterContentInit() {
2435
2423
  this.datepickerComponent.dateChange
2436
- .pipe(distinctUntilChanged())
2437
- .pipe(takeUntil(this.ngUnsubscribe))
2424
+ .pipe(distinctUntilChanged(), takeUntil(this.ngUnsubscribe))
2438
2425
  .subscribe((value) => {
2439
- this.isFirstChange = false;
2440
2426
  this.value = value;
2441
2427
  this.onTouched();
2442
2428
  });
@@ -2456,39 +2442,26 @@ class SkyDatepickerInputDirective {
2456
2442
  this.changeDetector.markForCheck();
2457
2443
  });
2458
2444
  }
2459
- this.adapter.init(this.elementRef);
2460
2445
  }
2461
2446
  ngOnDestroy() {
2462
2447
  this.ngUnsubscribe.next();
2463
2448
  this.ngUnsubscribe.complete();
2464
2449
  }
2465
2450
  onInputChange(event) {
2466
- const value = event.target.value;
2467
- if (this.skyDatepickerNoValidate) {
2468
- this.onValueChange(value);
2469
- return;
2470
- }
2471
- // Don't try to parse the string value into a Date value if it is malformed.
2472
- if (this.isDateStringValid(value)) {
2473
- this.onValueChange(value);
2474
- return;
2475
- }
2476
- this._value = value;
2477
- this.onChange(value);
2478
- this.control.setErrors({
2479
- skyDate: {
2480
- invalid: true,
2481
- },
2482
- });
2451
+ this.onValueChange(event.target.value);
2483
2452
  }
2484
2453
  onInputBlur() {
2485
2454
  this.onTouched();
2455
+ const formattedDate = this.fuzzyDateService.format(this.value, this.dateFormat, this.locale);
2456
+ if (this.control.valid) {
2457
+ this.setInputElementValue(formattedDate);
2458
+ }
2486
2459
  }
2487
2460
  onInputKeyup() {
2488
2461
  this.control.markAsDirty();
2489
2462
  }
2490
2463
  writeValue(value) {
2491
- this.value = value;
2464
+ this.updateValue(value, false);
2492
2465
  }
2493
2466
  validate(control) {
2494
2467
  if (!this.control) {
@@ -2497,38 +2470,71 @@ class SkyDatepickerInputDirective {
2497
2470
  if (this.skyDatepickerNoValidate) {
2498
2471
  return;
2499
2472
  }
2500
- const value = control.value;
2501
- if (!value) {
2473
+ if (!this.control.value) {
2502
2474
  return;
2503
2475
  }
2504
- const dateValue = this.getDateValue(value);
2505
- const isDateValid = dateValue && this.dateFormatter.dateIsValid(dateValue);
2506
- if (!isDateValid || !this.isDateStringValid(value)) {
2507
- // Mark the invalid control as touched so that the input's invalid CSS styles appear.
2508
- // (This is only required when the invalid value is set by the FormControl constructor.)
2509
- this.control.markAsTouched();
2510
- return {
2511
- skyDate: {
2476
+ const value = control.value;
2477
+ let fuzzyDate;
2478
+ let validationError;
2479
+ if (typeof value === 'string') {
2480
+ fuzzyDate = this.fuzzyDateService.getFuzzyDateFromString(value, this.dateFormat);
2481
+ }
2482
+ else {
2483
+ fuzzyDate = value;
2484
+ }
2485
+ if (!fuzzyDate) {
2486
+ validationError = {
2487
+ skyFuzzyDate: {
2512
2488
  invalid: value,
2513
2489
  },
2514
2490
  };
2515
2491
  }
2516
- const minDate = this.minDate;
2517
- if (minDate && this.dateFormatter.dateIsValid(minDate) && value < minDate) {
2518
- return {
2519
- skyDate: {
2520
- minDate,
2492
+ if (!validationError && !fuzzyDate.year && this.yearRequired) {
2493
+ validationError = {
2494
+ skyFuzzyDate: {
2495
+ yearRequired: value,
2521
2496
  },
2522
2497
  };
2523
2498
  }
2524
- const maxDate = this.maxDate;
2525
- if (maxDate && this.dateFormatter.dateIsValid(maxDate) && value > maxDate) {
2526
- return {
2527
- skyDate: {
2528
- maxDate,
2529
- },
2530
- };
2499
+ if (!validationError && fuzzyDate.year) {
2500
+ let fuzzyDateRange;
2501
+ if (this.maxDate) {
2502
+ fuzzyDateRange = this.fuzzyDateService.getFuzzyDateRange(fuzzyDate, this.maxDate);
2503
+ if (!fuzzyDateRange.valid) {
2504
+ validationError = {
2505
+ skyFuzzyDate: {
2506
+ maxDate: value,
2507
+ },
2508
+ };
2509
+ }
2510
+ }
2511
+ if (!validationError && this.minDate) {
2512
+ fuzzyDateRange = this.fuzzyDateService.getFuzzyDateRange(this.minDate, fuzzyDate);
2513
+ if (!fuzzyDateRange.valid) {
2514
+ validationError = {
2515
+ skyFuzzyDate: {
2516
+ minDate: value,
2517
+ },
2518
+ };
2519
+ }
2520
+ }
2521
+ if (!validationError && this.futureDisabled) {
2522
+ fuzzyDateRange = this.fuzzyDateService.getFuzzyDateRange(fuzzyDate, this.fuzzyDateService.getCurrentFuzzyDate());
2523
+ if (!fuzzyDateRange.valid) {
2524
+ validationError = {
2525
+ skyFuzzyDate: {
2526
+ futureDisabled: value,
2527
+ },
2528
+ };
2529
+ }
2530
+ }
2531
+ }
2532
+ if (validationError) {
2533
+ // Mark the invalid control as touched so that the input's invalid CSS styles appear.
2534
+ // (This is only required when the invalid value is set by the FormControl constructor.)
2535
+ this.control.markAsTouched();
2531
2536
  }
2537
+ return validationError;
2532
2538
  }
2533
2539
  registerOnChange(fn) {
2534
2540
  this.onChange = fn;
@@ -2550,102 +2556,127 @@ class SkyDatepickerInputDirective {
2550
2556
  detectInputValueChange() {
2551
2557
  this.onValueChange(this.elementRef.nativeElement.value);
2552
2558
  }
2553
- applyDateFormat() {
2554
- this.updatePlaceholder();
2555
- if (this.value) {
2556
- const formattedDate = this.dateFormatter.format(this.value, this.dateFormat);
2557
- this.setInputElementValue(formattedDate);
2558
- this.changeDetector.markForCheck();
2559
- }
2560
- }
2561
2559
  onValueChange(newValue) {
2562
- this.isFirstChange = false;
2563
2560
  this.value = newValue;
2564
2561
  }
2565
2562
  setInputElementValue(value) {
2566
2563
  this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
2567
2564
  }
2568
- getDateValue(value) {
2569
- let dateValue;
2570
- if (value instanceof Date) {
2571
- dateValue = value;
2565
+ getMaxDate() {
2566
+ if (this.maxDate) {
2567
+ const maxDate = this.fuzzyDateService.getMomentFromFuzzyDate(this.maxDate);
2568
+ if (maxDate.isValid()) {
2569
+ return maxDate.toDate();
2570
+ }
2572
2571
  }
2573
- else if (typeof value === 'string') {
2574
- const date = this.dateFormatter.getDateFromString(value, this.dateFormat, this.strict);
2575
- if (this.dateFormatter.dateIsValid(date)) {
2576
- dateValue = date;
2572
+ else if (this.futureDisabled) {
2573
+ return new Date();
2574
+ }
2575
+ return this.configService.maxDate;
2576
+ }
2577
+ getMinDate() {
2578
+ if (this.minDate) {
2579
+ const minDate = this.fuzzyDateService.getMomentFromFuzzyDate(this.minDate);
2580
+ if (minDate.isValid()) {
2581
+ return minDate.toDate();
2577
2582
  }
2578
2583
  }
2579
- return dateValue;
2584
+ return this.configService.minDate;
2585
+ }
2586
+ /* istanbul ignore next */
2587
+ fuzzyDatesEqual(dateA, dateB) {
2588
+ return (dateA &&
2589
+ dateB &&
2590
+ ((!dateA.day && !dateB.day) || dateA.day === dateB.day) &&
2591
+ ((!dateA.month && !dateB.month) || dateA.month === dateB.month) &&
2592
+ ((!dateA.year && !dateB.year) || dateA.year === dateB.year));
2580
2593
  }
2581
2594
  /**
2582
- * Validates the input value to ensure it is formatted correctly.
2595
+ * Update the value of the form control and input element
2596
+ * @param emitEvent Denotes if we emit an event to the consumer's form control. We do not want to do this if the value is being updated via a `setValue` call or a `patchValue` call as this is already handled by Angular.
2597
+ * In these cases we do not want to fire `onChange` as it will cause extra `valueChange` and `statusChange` events and the status of the form should not be affected by these changes.
2583
2598
  */
2584
- isDateStringValid(value) {
2585
- if (!value || typeof value !== 'string') {
2586
- return true;
2599
+ updateValue(value, emitEvent = true) {
2600
+ var _a;
2601
+ if (this._value === value) {
2602
+ return;
2587
2603
  }
2588
- // Does the value only include digits, dashes, or slashes?
2589
- const regexp = /^[\d\/\-]+$/;
2590
- const isValid = regexp.test(value);
2591
- if (isValid) {
2592
- return true;
2604
+ let fuzzyDate;
2605
+ let fuzzyMoment;
2606
+ let dateValue;
2607
+ let formattedDate;
2608
+ if (value instanceof Date) {
2609
+ dateValue = value;
2610
+ formattedDate = this.dateFormatter.format(value, this.dateFormat);
2611
+ fuzzyDate = this.fuzzyDateService.getFuzzyDateFromSelectedDate(value, this.dateFormat);
2593
2612
  }
2594
- // If not, does it conform to the standard ISO format?
2595
- const isValidIso = moment(value, moment.ISO_8601).isValid();
2596
- return isValidIso;
2597
- }
2598
- notifyUpdatedValue() {
2599
- this.onChange(this._value);
2600
- // Do not mark the field as "dirty"
2601
- // if the field has been initialized with a value.
2602
- if (this.isFirstChange && this.control) {
2603
- this.control.markAsPristine();
2613
+ else if (typeof value === 'string') {
2614
+ fuzzyDate = this.fuzzyDateService.getFuzzyDateFromString(value, this.dateFormat);
2615
+ formattedDate = this.fuzzyDateService.format(fuzzyDate, this.dateFormat, this.locale);
2616
+ if (!formattedDate) {
2617
+ formattedDate = value;
2618
+ }
2619
+ fuzzyMoment = this.fuzzyDateService.getMomentFromFuzzyDate(fuzzyDate);
2620
+ if (fuzzyMoment) {
2621
+ dateValue = fuzzyMoment.toDate();
2622
+ }
2604
2623
  }
2605
- if (this.isFirstChange && this._value) {
2606
- this.isFirstChange = false;
2624
+ else {
2625
+ fuzzyDate = value;
2626
+ formattedDate = this.fuzzyDateService.format(fuzzyDate, this.dateFormat, this.locale);
2627
+ fuzzyMoment = this.fuzzyDateService.getMomentFromFuzzyDate(fuzzyDate);
2628
+ if (fuzzyMoment) {
2629
+ dateValue = fuzzyMoment.toDate();
2630
+ }
2607
2631
  }
2608
- this.datepickerComponent.selectedDate = this._value;
2609
- }
2610
- updatePlaceholder() {
2611
- if (!this.initialPlaceholder) {
2612
- this.adapter.setPlaceholder(this.elementRef, this.dateFormat);
2632
+ const areFuzzyDatesEqual = this.fuzzyDatesEqual(this._value, fuzzyDate);
2633
+ const isNewValue = fuzzyDate !== this._value || !areFuzzyDatesEqual;
2634
+ this._value = fuzzyDate || value;
2635
+ if (isNewValue) {
2636
+ if (emitEvent) {
2637
+ this.onChange(this._value);
2638
+ }
2639
+ else {
2640
+ (_a = this.control) === null || _a === void 0 ? void 0 : _a.setValue(this._value, { emitEvent: false });
2641
+ }
2642
+ this.datepickerComponent.selectedDate = dateValue;
2613
2643
  }
2644
+ this.setInputElementValue(formattedDate || '');
2614
2645
  }
2615
2646
  }
2616
- SkyDatepickerInputDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerInputDirective, deps: [{ token: SkyDatepickerAdapterService }, { token: i0.ChangeDetectorRef }, { token: SkyDatepickerConfigService }, { token: i0.ElementRef }, { token: i3.SkyAppLocaleProvider }, { token: i0.Renderer2 }, { token: i3.SkyLibResourcesService }, { token: SkyDatepickerComponent, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
2617
- SkyDatepickerInputDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.16", type: SkyDatepickerInputDirective, selector: "[skyDatepickerInput]", inputs: { dateFormat: "dateFormat", disabled: "disabled", maxDate: "maxDate", minDate: "minDate", skyDatepickerInput: "skyDatepickerInput", skyDatepickerNoValidate: "skyDatepickerNoValidate", startingDay: "startingDay", strict: "strict" }, host: { listeners: { "change": "onInputChange($event)", "blur": "onInputBlur()", "keyup": "onInputKeyup()" } }, providers: [
2618
- SKY_DATEPICKER_VALUE_ACCESSOR,
2619
- SKY_DATEPICKER_VALIDATOR,
2620
- SkyDatepickerAdapterService,
2647
+ SkyFuzzyDatepickerInputDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyFuzzyDatepickerInputDirective, deps: [{ token: i0.ChangeDetectorRef }, { token: SkyDatepickerConfigService }, { token: i0.ElementRef }, { token: SkyFuzzyDateService }, { token: i3.SkyAppLocaleProvider }, { token: i0.Renderer2 }, { token: i3.SkyLibResourcesService }, { token: SkyDatepickerComponent, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
2648
+ SkyFuzzyDatepickerInputDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.16", type: SkyFuzzyDatepickerInputDirective, selector: "[skyFuzzyDatepickerInput]", inputs: { dateFormat: "dateFormat", disabled: "disabled", futureDisabled: "futureDisabled", maxDate: "maxDate", minDate: "minDate", skyDatepickerNoValidate: "skyDatepickerNoValidate", skyFuzzyDatepickerInput: "skyFuzzyDatepickerInput", startingDay: "startingDay", yearRequired: "yearRequired" }, host: { listeners: { "change": "onInputChange($event)", "blur": "onInputBlur()", "keyup": "onInputKeyup()" } }, providers: [
2649
+ SKY_FUZZY_DATEPICKER_VALUE_ACCESSOR,
2650
+ SKY_FUZZY_DATEPICKER_VALIDATOR,
2621
2651
  ], ngImport: i0 });
2622
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerInputDirective, decorators: [{
2652
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyFuzzyDatepickerInputDirective, decorators: [{
2623
2653
  type: Directive,
2624
2654
  args: [{
2625
- selector: '[skyDatepickerInput]',
2655
+ selector: '[skyFuzzyDatepickerInput]',
2626
2656
  providers: [
2627
- SKY_DATEPICKER_VALUE_ACCESSOR,
2628
- SKY_DATEPICKER_VALIDATOR,
2629
- SkyDatepickerAdapterService,
2657
+ SKY_FUZZY_DATEPICKER_VALUE_ACCESSOR,
2658
+ SKY_FUZZY_DATEPICKER_VALIDATOR,
2630
2659
  ],
2631
2660
  }]
2632
- }], ctorParameters: function () { return [{ type: SkyDatepickerAdapterService }, { type: i0.ChangeDetectorRef }, { type: SkyDatepickerConfigService }, { type: i0.ElementRef }, { type: i3.SkyAppLocaleProvider }, { type: i0.Renderer2 }, { type: i3.SkyLibResourcesService }, { type: SkyDatepickerComponent, decorators: [{
2661
+ }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: SkyDatepickerConfigService }, { type: i0.ElementRef }, { type: SkyFuzzyDateService }, { type: i3.SkyAppLocaleProvider }, { type: i0.Renderer2 }, { type: i3.SkyLibResourcesService }, { type: SkyDatepickerComponent, decorators: [{
2633
2662
  type: Optional
2634
2663
  }] }]; }, propDecorators: { dateFormat: [{
2635
2664
  type: Input
2636
2665
  }], disabled: [{
2637
2666
  type: Input
2667
+ }], futureDisabled: [{
2668
+ type: Input
2638
2669
  }], maxDate: [{
2639
2670
  type: Input
2640
2671
  }], minDate: [{
2641
2672
  type: Input
2642
- }], skyDatepickerInput: [{
2643
- type: Input
2644
2673
  }], skyDatepickerNoValidate: [{
2645
2674
  type: Input
2675
+ }], skyFuzzyDatepickerInput: [{
2676
+ type: Input
2646
2677
  }], startingDay: [{
2647
2678
  type: Input
2648
- }], strict: [{
2679
+ }], yearRequired: [{
2649
2680
  type: Input
2650
2681
  }], onInputChange: [{
2651
2682
  type: HostListener,
@@ -2659,63 +2690,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
2659
2690
  }] } });
2660
2691
 
2661
2692
  // tslint:disable:no-forward-ref no-use-before-declare
2662
- const SKY_FUZZY_DATEPICKER_VALUE_ACCESSOR = {
2693
+ const SKY_DATEPICKER_VALUE_ACCESSOR = {
2663
2694
  provide: NG_VALUE_ACCESSOR,
2664
- useExisting: forwardRef(() => SkyFuzzyDatepickerInputDirective),
2695
+ useExisting: forwardRef(() => SkyDatepickerInputDirective),
2665
2696
  multi: true,
2666
2697
  };
2667
- const SKY_FUZZY_DATEPICKER_VALIDATOR = {
2698
+ const SKY_DATEPICKER_VALIDATOR = {
2668
2699
  provide: NG_VALIDATORS,
2669
- useExisting: forwardRef(() => SkyFuzzyDatepickerInputDirective),
2700
+ useExisting: forwardRef(() => SkyDatepickerInputDirective),
2670
2701
  multi: true,
2671
2702
  };
2672
2703
  // tslint:enable
2673
- class SkyFuzzyDatepickerInputDirective {
2674
- constructor(changeDetector, configService, elementRef, fuzzyDateService, localeProvider, renderer, resourcesService, datepickerComponent) {
2704
+ class SkyDatepickerInputDirective {
2705
+ constructor(adapter, changeDetector, configService, elementRef, localeProvider, renderer, resourcesService, datepickerComponent) {
2706
+ this.adapter = adapter;
2675
2707
  this.changeDetector = changeDetector;
2676
2708
  this.configService = configService;
2677
2709
  this.elementRef = elementRef;
2678
- this.fuzzyDateService = fuzzyDateService;
2679
2710
  this.localeProvider = localeProvider;
2680
2711
  this.renderer = renderer;
2681
2712
  this.resourcesService = resourcesService;
2682
2713
  this.datepickerComponent = datepickerComponent;
2683
2714
  /**
2684
- * Indicates whether to disable date validation on the fuzzy datepicker input.
2715
+ * Indicates whether to disable date validation on the datepicker input.
2685
2716
  * @default false
2686
2717
  */
2687
2718
  this.skyDatepickerNoValidate = false;
2688
2719
  this.dateFormatter = new SkyDateFormatter();
2689
- this.isFirstChange = true;
2690
2720
  this.ngUnsubscribe = new Subject();
2691
- this._futureDisabled = false;
2692
- this._disabled = false;
2693
- this._yearRequired = false;
2721
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2694
2722
  this.onChange = (_) => { };
2695
- /*istanbul ignore next */
2723
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2696
2724
  this.onTouched = () => { };
2725
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2697
2726
  this.onValidatorChange = () => { };
2727
+ this.initialPlaceholder = this.adapter.getPlaceholder(this.elementRef);
2728
+ this.updatePlaceholder();
2698
2729
  this.localeProvider
2699
2730
  .getLocaleInfo()
2700
2731
  .pipe(takeUntil(this.ngUnsubscribe))
2701
2732
  .subscribe((localeInfo) => {
2702
- this.locale = localeInfo.locale;
2703
- SkyDateFormatter.setLocale(this.locale);
2733
+ SkyDateFormatter.setLocale(localeInfo.locale);
2704
2734
  this.preferredShortDateFormat =
2705
2735
  SkyDateFormatter.getPreferredShortDateFormat();
2736
+ this.applyDateFormat();
2706
2737
  });
2707
2738
  }
2708
2739
  /**
2709
2740
  * Specifies the date format for the input. Place this attribute on the `input` element
2710
- * to override the default in `SkyDatepickerConfigService`.
2741
+ * to override the default in the `SkyDatepickerConfigService`.
2711
2742
  * @default "MM/DD/YYYY"
2712
2743
  */
2713
2744
  set dateFormat(value) {
2714
- this._dateFormat = value;
2715
- if (this.value) {
2716
- const formattedDate = this.fuzzyDateService.format(this.value, this.dateFormat, this.locale);
2717
- this.setInputElementValue(formattedDate);
2718
- this.changeDetector.markForCheck();
2745
+ /* istanbul ignore else */
2746
+ if (value !== this._dateFormat) {
2747
+ this._dateFormat = value;
2748
+ this.applyDateFormat();
2719
2749
  }
2720
2750
  }
2721
2751
  get dateFormat() {
@@ -2733,57 +2763,54 @@ class SkyFuzzyDatepickerInputDirective {
2733
2763
  this.renderer.setProperty(this.elementRef.nativeElement, 'disabled', value);
2734
2764
  }
2735
2765
  get disabled() {
2736
- return this._disabled;
2766
+ return this._disabled || false;
2737
2767
  }
2738
2768
  /**
2739
- * Indicates whether to prevent users from specifying dates that are in the future.
2740
- * Place this attribute on the `input` element.
2741
- * @default false
2769
+ * @internal
2770
+ * Indicates if the input element or any of its children have focus.
2742
2771
  */
2743
- set futureDisabled(value) {
2744
- this._futureDisabled = value;
2745
- this.onValidatorChange();
2746
- }
2747
- get futureDisabled() {
2748
- return this._futureDisabled;
2772
+ get inputIsFocused() {
2773
+ return this.adapter.elementIsFocused();
2749
2774
  }
2750
2775
  /**
2751
- * Specifies the latest fuzzy date allowed. Place this attribute on the `input` element
2752
- * to prevent fuzzy dates after a specified date. This property accepts
2753
- * a `SkyFuzzyDate` value that includes numeric month, day, and year values.
2754
- * For example: `{ month: 1, day: 1, year: 2027 }`.
2776
+ * Specifies the latest date that is available in the calendar. Place this attribute on
2777
+ * the `input` element to override the default in `SkyDatepickerConfigService`.
2755
2778
  */
2756
2779
  set maxDate(value) {
2757
2780
  this._maxDate = value;
2758
- this.datepickerComponent.maxDate = this.getMaxDate();
2781
+ this.datepickerComponent.maxDate = this.maxDate;
2759
2782
  this.onValidatorChange();
2760
2783
  }
2761
2784
  get maxDate() {
2762
- return this._maxDate;
2785
+ return this._maxDate || this.configService.maxDate;
2763
2786
  }
2764
2787
  /**
2765
- * Specifies the earliest fuzzy date allowed. Place this attribute on the `input` element
2766
- * to prevent fuzzy dates before a specified date. This property accepts a `SkyFuzzyDate` value
2767
- * that includes numeric month, day, and year values.
2768
- * For example: `{ month: 1, day: 1, year: 2007 }`.
2788
+ * Specifies the earliest date that is available in the calendar. Place this attribute on
2789
+ * the `input` element to override the default in `SkyDatepickerConfigService`.
2769
2790
  */
2770
2791
  set minDate(value) {
2771
2792
  this._minDate = value;
2772
- this.datepickerComponent.minDate = this.getMinDate();
2793
+ this.datepickerComponent.minDate = this.minDate;
2773
2794
  this.onValidatorChange();
2774
2795
  }
2775
2796
  get minDate() {
2776
- return this._minDate;
2797
+ return this._minDate || this.configService.minDate;
2777
2798
  }
2778
2799
  /**
2779
- * Creates the fuzzy datepicker input and calendar to let users specify dates that are
2780
- * not complete. For example, if users know the year but not the month or day, they can
2781
- * enter just the year. Place this directive on an `input` element, and wrap the `input`
2782
- * in a `sky-datepicker` component. The value that users select is driven
2800
+ * Creates the datepicker input and calendar. Place this directive on an `input` element,
2801
+ * and wrap the input in a `sky-datepicker` component. The value that users select is driven
2783
2802
  * through the `ngModel` attribute specified on the `input` element.
2784
2803
  * @required
2785
2804
  */
2786
- set skyFuzzyDatepickerInput(value) { }
2805
+ set skyDatepickerInput(value) {
2806
+ if (value) {
2807
+ console.warn('[Deprecation warning] You no longer need to provide a template reference variable ' +
2808
+ 'to the `skyDatepickerInput` attribute (this will be a breaking change in the next ' +
2809
+ 'major version release).\n' +
2810
+ 'Do this instead:\n' +
2811
+ '<sky-datepicker>\n <input skyDatepickerInput />\n</sky-datepicker>');
2812
+ }
2813
+ }
2787
2814
  /**
2788
2815
  * Specifies the starting day of the week in the calendar, where `0` sets the starting day
2789
2816
  * to Sunday. Place this attribute on the `input` element to override the default
@@ -2799,79 +2826,33 @@ class SkyFuzzyDatepickerInputDirective {
2799
2826
  return this._startingDay || this.configService.startingDay;
2800
2827
  }
2801
2828
  /**
2802
- * Indicates whether to require the year in fuzzy dates.
2829
+ * Indicates whether the format of the date value must match the format from the `dateFormat` value.
2830
+ * If this property is `true` and the datepicker input directive cannot find an exact match, then
2831
+ * the input is marked as invalid.
2832
+ * If this property is `false` and the datepicker input directive cannot find an exact match, then
2833
+ * it attempts to format the string based on the [ISO 8601 standard format](https://www.iso.org/iso-8601-date-and-time-format.html).
2803
2834
  * @default false
2804
2835
  */
2805
- set yearRequired(value) {
2806
- this._yearRequired = value;
2807
- this.onValidatorChange();
2836
+ set strict(value) {
2837
+ this._strict = value;
2808
2838
  }
2809
- get yearRequired() {
2810
- return this._yearRequired;
2839
+ get strict() {
2840
+ return this._strict || false;
2811
2841
  }
2812
2842
  get value() {
2813
2843
  return this._value;
2814
2844
  }
2815
2845
  set value(value) {
2816
- let fuzzyDate;
2817
- let fuzzyMoment;
2818
- let dateValue;
2819
- let formattedDate;
2820
- if (value instanceof Date) {
2821
- dateValue = value;
2822
- formattedDate = this.dateFormatter.format(value, this.dateFormat);
2823
- fuzzyDate = this.fuzzyDateService.getFuzzyDateFromSelectedDate(value, this.dateFormat);
2824
- }
2825
- else if (typeof value === 'string') {
2826
- fuzzyDate = this.fuzzyDateService.getFuzzyDateFromString(value, this.dateFormat);
2827
- formattedDate = this.fuzzyDateService.format(fuzzyDate, this.dateFormat, this.locale);
2828
- if (!formattedDate) {
2829
- formattedDate = value;
2830
- }
2831
- fuzzyMoment = this.fuzzyDateService.getMomentFromFuzzyDate(fuzzyDate);
2832
- if (fuzzyMoment) {
2833
- dateValue = fuzzyMoment.toDate();
2834
- }
2835
- }
2836
- else {
2837
- fuzzyDate = value;
2838
- formattedDate = this.fuzzyDateService.format(fuzzyDate, this.dateFormat, this.locale);
2839
- fuzzyMoment = this.fuzzyDateService.getMomentFromFuzzyDate(fuzzyDate);
2840
- if (fuzzyMoment) {
2841
- dateValue = fuzzyMoment.toDate();
2842
- }
2843
- }
2844
- const areFuzzyDatesEqual = this.fuzzyDatesEqual(this._value, fuzzyDate);
2845
- const isNewValue = fuzzyDate !== this._value || !areFuzzyDatesEqual;
2846
- this._value = fuzzyDate || value;
2847
- if (isNewValue) {
2848
- this.onChange(this._value);
2849
- // Do not mark the field as "dirty"
2850
- // if the field has been initialized with a value.
2851
- if (this.isFirstChange && this.control) {
2852
- this.control.markAsPristine();
2853
- }
2854
- if (this.isFirstChange && this._value) {
2855
- this.isFirstChange = false;
2856
- }
2857
- this.datepickerComponent.selectedDate = dateValue;
2858
- }
2859
- this.setInputElementValue(formattedDate || '');
2846
+ this.updateValue(value);
2860
2847
  }
2861
- ngOnInit() {
2862
- if (this.yearRequired) {
2863
- if (this.dateFormat.toLowerCase().indexOf('y') === -1) {
2864
- throw new Error('You have configured conflicting settings. Year is required and dateFormat does not include year.');
2865
- }
2866
- }
2848
+ ngOnInit() {
2867
2849
  if (!this.datepickerComponent) {
2868
- throw new Error('You must wrap the `skyFuzzyDatepickerInput` directive within a ' +
2850
+ throw new Error('You must wrap the `skyDatepickerInput` directive within a ' +
2869
2851
  '`<sky-datepicker>` component!');
2870
2852
  }
2871
2853
  const element = this.elementRef.nativeElement;
2872
2854
  this.renderer.addClass(element, 'sky-form-control');
2873
2855
  const hasAriaLabel = element.getAttribute('aria-label');
2874
- /* istanbul ignore else */
2875
2856
  if (!hasAriaLabel) {
2876
2857
  this.resourcesService
2877
2858
  .getString('skyux_date_field_default_label')
@@ -2883,9 +2864,9 @@ class SkyFuzzyDatepickerInputDirective {
2883
2864
  }
2884
2865
  ngAfterContentInit() {
2885
2866
  this.datepickerComponent.dateChange
2886
- .pipe(distinctUntilChanged(), takeUntil(this.ngUnsubscribe))
2867
+ .pipe(distinctUntilChanged())
2868
+ .pipe(takeUntil(this.ngUnsubscribe))
2887
2869
  .subscribe((value) => {
2888
- this.isFirstChange = false;
2889
2870
  this.value = value;
2890
2871
  this.onTouched();
2891
2872
  });
@@ -2905,26 +2886,39 @@ class SkyFuzzyDatepickerInputDirective {
2905
2886
  this.changeDetector.markForCheck();
2906
2887
  });
2907
2888
  }
2889
+ this.adapter.init(this.elementRef);
2908
2890
  }
2909
2891
  ngOnDestroy() {
2910
2892
  this.ngUnsubscribe.next();
2911
2893
  this.ngUnsubscribe.complete();
2912
2894
  }
2913
2895
  onInputChange(event) {
2914
- this.onValueChange(event.target.value);
2896
+ const value = event.target.value;
2897
+ if (this.skyDatepickerNoValidate) {
2898
+ this.onValueChange(value);
2899
+ return;
2900
+ }
2901
+ // Don't try to parse the string value into a Date value if it is malformed.
2902
+ if (this.isDateStringValid(value)) {
2903
+ this.onValueChange(value);
2904
+ return;
2905
+ }
2906
+ this._value = value;
2907
+ this.onChange(value);
2908
+ this.control.setErrors({
2909
+ skyDate: {
2910
+ invalid: true,
2911
+ },
2912
+ });
2915
2913
  }
2916
2914
  onInputBlur() {
2917
2915
  this.onTouched();
2918
- let formattedDate = this.fuzzyDateService.format(this.value, this.dateFormat, this.locale);
2919
- if (this.control.valid) {
2920
- this.setInputElementValue(formattedDate);
2921
- }
2922
2916
  }
2923
2917
  onInputKeyup() {
2924
2918
  this.control.markAsDirty();
2925
2919
  }
2926
2920
  writeValue(value) {
2927
- this.value = value;
2921
+ this.updateValue(value, false);
2928
2922
  }
2929
2923
  validate(control) {
2930
2924
  if (!this.control) {
@@ -2933,71 +2927,38 @@ class SkyFuzzyDatepickerInputDirective {
2933
2927
  if (this.skyDatepickerNoValidate) {
2934
2928
  return;
2935
2929
  }
2936
- if (!this.control.value) {
2937
- return;
2938
- }
2939
2930
  const value = control.value;
2940
- let fuzzyDate;
2941
- let validationError;
2942
- if (typeof value === 'string') {
2943
- fuzzyDate = this.fuzzyDateService.getFuzzyDateFromString(value, this.dateFormat);
2944
- }
2945
- else {
2946
- fuzzyDate = value;
2931
+ if (!value) {
2932
+ return;
2947
2933
  }
2948
- if (!fuzzyDate) {
2949
- validationError = {
2950
- skyFuzzyDate: {
2934
+ const dateValue = this.getDateValue(value);
2935
+ const isDateValid = dateValue && this.dateFormatter.dateIsValid(dateValue);
2936
+ if (!isDateValid || !this.isDateStringValid(value)) {
2937
+ // Mark the invalid control as touched so that the input's invalid CSS styles appear.
2938
+ // (This is only required when the invalid value is set by the FormControl constructor.)
2939
+ this.control.markAsTouched();
2940
+ return {
2941
+ skyDate: {
2951
2942
  invalid: value,
2952
2943
  },
2953
2944
  };
2954
2945
  }
2955
- if (!validationError && !fuzzyDate.year && this.yearRequired) {
2956
- validationError = {
2957
- skyFuzzyDate: {
2958
- yearRequired: value,
2946
+ const minDate = this.minDate;
2947
+ if (minDate && this.dateFormatter.dateIsValid(minDate) && value < minDate) {
2948
+ return {
2949
+ skyDate: {
2950
+ minDate,
2959
2951
  },
2960
2952
  };
2961
2953
  }
2962
- if (!validationError && fuzzyDate.year) {
2963
- let fuzzyDateRange;
2964
- if (this.maxDate) {
2965
- fuzzyDateRange = this.fuzzyDateService.getFuzzyDateRange(fuzzyDate, this.maxDate);
2966
- if (!fuzzyDateRange.valid) {
2967
- validationError = {
2968
- skyFuzzyDate: {
2969
- maxDate: value,
2970
- },
2971
- };
2972
- }
2973
- }
2974
- if (!validationError && this.minDate) {
2975
- fuzzyDateRange = this.fuzzyDateService.getFuzzyDateRange(this.minDate, fuzzyDate);
2976
- if (!fuzzyDateRange.valid) {
2977
- validationError = {
2978
- skyFuzzyDate: {
2979
- minDate: value,
2980
- },
2981
- };
2982
- }
2983
- }
2984
- if (!validationError && this.futureDisabled) {
2985
- fuzzyDateRange = this.fuzzyDateService.getFuzzyDateRange(fuzzyDate, this.fuzzyDateService.getCurrentFuzzyDate());
2986
- if (!fuzzyDateRange.valid) {
2987
- validationError = {
2988
- skyFuzzyDate: {
2989
- futureDisabled: value,
2990
- },
2991
- };
2992
- }
2993
- }
2994
- }
2995
- if (validationError) {
2996
- // Mark the invalid control as touched so that the input's invalid CSS styles appear.
2997
- // (This is only required when the invalid value is set by the FormControl constructor.)
2998
- this.control.markAsTouched();
2954
+ const maxDate = this.maxDate;
2955
+ if (maxDate && this.dateFormatter.dateIsValid(maxDate) && value > maxDate) {
2956
+ return {
2957
+ skyDate: {
2958
+ maxDate,
2959
+ },
2960
+ };
2999
2961
  }
3000
- return validationError;
3001
2962
  }
3002
2963
  registerOnChange(fn) {
3003
2964
  this.onChange = fn;
@@ -3019,76 +2980,134 @@ class SkyFuzzyDatepickerInputDirective {
3019
2980
  detectInputValueChange() {
3020
2981
  this.onValueChange(this.elementRef.nativeElement.value);
3021
2982
  }
2983
+ applyDateFormat() {
2984
+ this.updatePlaceholder();
2985
+ if (this.value) {
2986
+ const formattedDate = this.dateFormatter.format(this.value, this.dateFormat);
2987
+ this.setInputElementValue(formattedDate);
2988
+ this.changeDetector.markForCheck();
2989
+ }
2990
+ }
3022
2991
  onValueChange(newValue) {
3023
- this.isFirstChange = false;
3024
2992
  this.value = newValue;
3025
2993
  }
3026
2994
  setInputElementValue(value) {
3027
2995
  this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
3028
2996
  }
3029
- getMaxDate() {
3030
- if (this.maxDate) {
3031
- const maxDate = this.fuzzyDateService.getMomentFromFuzzyDate(this.maxDate);
3032
- if (maxDate.isValid()) {
3033
- return maxDate.toDate();
2997
+ getDateValue(value) {
2998
+ let dateValue;
2999
+ if (value instanceof Date) {
3000
+ dateValue = value;
3001
+ }
3002
+ else if (typeof value === 'string') {
3003
+ const date = this.dateFormatter.getDateFromString(value, this.dateFormat, this.strict);
3004
+ if (this.dateFormatter.dateIsValid(date)) {
3005
+ dateValue = date;
3034
3006
  }
3035
3007
  }
3036
- else if (this.futureDisabled) {
3037
- return new Date();
3008
+ return dateValue;
3009
+ }
3010
+ /**
3011
+ * Validates the input value to ensure it is formatted correctly.
3012
+ */
3013
+ isDateStringValid(value) {
3014
+ if (!value || typeof value !== 'string') {
3015
+ return true;
3038
3016
  }
3039
- return this.configService.maxDate;
3017
+ // Does the value only include digits, dashes, or slashes?
3018
+ const regexp = /^[\d/-]+$/;
3019
+ const isValid = regexp.test(value);
3020
+ if (isValid) {
3021
+ return true;
3022
+ }
3023
+ // If not, does it conform to the standard ISO format?
3024
+ const isValidIso = moment(value, moment.ISO_8601).isValid();
3025
+ return isValidIso;
3040
3026
  }
3041
- getMinDate() {
3042
- if (this.minDate) {
3043
- const minDate = this.fuzzyDateService.getMomentFromFuzzyDate(this.minDate);
3044
- if (minDate.isValid()) {
3045
- return minDate.toDate();
3046
- }
3027
+ updatePlaceholder() {
3028
+ if (!this.initialPlaceholder) {
3029
+ this.adapter.setPlaceholder(this.elementRef, this.dateFormat);
3047
3030
  }
3048
- return this.configService.minDate;
3049
3031
  }
3050
- /* istanbul ignore next */
3051
- fuzzyDatesEqual(dateA, dateB) {
3052
- return (dateA &&
3053
- dateB &&
3054
- ((!dateA.day && !dateB.day) || dateA.day === dateB.day) &&
3055
- ((!dateA.month && !dateB.month) || dateA.month === dateB.month) &&
3056
- ((!dateA.year && !dateB.year) || dateA.year === dateB.year));
3032
+ /**
3033
+ * Update the value of the form control and input element
3034
+ * @param emitEvent Denotes if we emit an event to the consumer's form control. We do not want to do this if the value is being updated via a `setValue` call or a `patchValue` call as this is already handled by Angular.
3035
+ * In these cases we do not want to fire `onChange` as it will cause extra `valueChange` and `statusChange` events and the status of the form should not be affected by these changes.
3036
+ */
3037
+ updateValue(value, emitEvent = true) {
3038
+ var _a, _b;
3039
+ if (this._value === value) {
3040
+ return;
3041
+ }
3042
+ const dateValue = this.getDateValue(value);
3043
+ const areDatesEqual = this._value instanceof Date &&
3044
+ dateValue &&
3045
+ dateValue.getTime() === this._value.getTime();
3046
+ const isValidDateString = this.isDateStringValid(value);
3047
+ // If the string value supplied is malformed, do not set the value to its Date equivalent.
3048
+ // (JavaScript's Date parser will convert poorly formatted dates to Date objects, such as "abc 123", which isn't ideal.)
3049
+ if (!isValidDateString) {
3050
+ this._value = value;
3051
+ if (emitEvent) {
3052
+ this.onChange(this._value);
3053
+ }
3054
+ else {
3055
+ (_a = this.control) === null || _a === void 0 ? void 0 : _a.setValue(this._value, { emitEvent: false });
3056
+ }
3057
+ this.datepickerComponent.selectedDate = this._value;
3058
+ }
3059
+ else if (dateValue !== this._value || !areDatesEqual) {
3060
+ this._value = dateValue || value;
3061
+ if (emitEvent) {
3062
+ this.onChange(this._value);
3063
+ }
3064
+ else {
3065
+ (_b = this.control) === null || _b === void 0 ? void 0 : _b.setValue(this._value, { emitEvent: false });
3066
+ }
3067
+ this.datepickerComponent.selectedDate = this._value;
3068
+ }
3069
+ if (dateValue && isValidDateString) {
3070
+ const formattedDate = this.dateFormatter.format(dateValue, this.dateFormat);
3071
+ this.setInputElementValue(formattedDate);
3072
+ }
3073
+ else {
3074
+ this.setInputElementValue(value || '');
3075
+ }
3057
3076
  }
3058
3077
  }
3059
- SkyFuzzyDatepickerInputDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyFuzzyDatepickerInputDirective, deps: [{ token: i0.ChangeDetectorRef }, { token: SkyDatepickerConfigService }, { token: i0.ElementRef }, { token: SkyFuzzyDateService }, { token: i3.SkyAppLocaleProvider }, { token: i0.Renderer2 }, { token: i3.SkyLibResourcesService }, { token: SkyDatepickerComponent, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3060
- SkyFuzzyDatepickerInputDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.16", type: SkyFuzzyDatepickerInputDirective, selector: "[skyFuzzyDatepickerInput]", inputs: { dateFormat: "dateFormat", disabled: "disabled", futureDisabled: "futureDisabled", maxDate: "maxDate", minDate: "minDate", skyDatepickerNoValidate: "skyDatepickerNoValidate", skyFuzzyDatepickerInput: "skyFuzzyDatepickerInput", startingDay: "startingDay", yearRequired: "yearRequired" }, host: { listeners: { "change": "onInputChange($event)", "blur": "onInputBlur()", "keyup": "onInputKeyup()" } }, providers: [
3061
- SKY_FUZZY_DATEPICKER_VALUE_ACCESSOR,
3062
- SKY_FUZZY_DATEPICKER_VALIDATOR,
3078
+ SkyDatepickerInputDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerInputDirective, deps: [{ token: SkyDatepickerAdapterService }, { token: i0.ChangeDetectorRef }, { token: SkyDatepickerConfigService }, { token: i0.ElementRef }, { token: i3.SkyAppLocaleProvider }, { token: i0.Renderer2 }, { token: i3.SkyLibResourcesService }, { token: SkyDatepickerComponent, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
3079
+ SkyDatepickerInputDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.16", type: SkyDatepickerInputDirective, selector: "[skyDatepickerInput]", inputs: { dateFormat: "dateFormat", disabled: "disabled", maxDate: "maxDate", minDate: "minDate", skyDatepickerInput: "skyDatepickerInput", skyDatepickerNoValidate: "skyDatepickerNoValidate", startingDay: "startingDay", strict: "strict" }, host: { listeners: { "change": "onInputChange($event)", "blur": "onInputBlur()", "keyup": "onInputKeyup()" } }, providers: [
3080
+ SKY_DATEPICKER_VALUE_ACCESSOR,
3081
+ SKY_DATEPICKER_VALIDATOR,
3082
+ SkyDatepickerAdapterService,
3063
3083
  ], ngImport: i0 });
3064
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyFuzzyDatepickerInputDirective, decorators: [{
3084
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDatepickerInputDirective, decorators: [{
3065
3085
  type: Directive,
3066
3086
  args: [{
3067
- selector: '[skyFuzzyDatepickerInput]',
3087
+ selector: '[skyDatepickerInput]',
3068
3088
  providers: [
3069
- SKY_FUZZY_DATEPICKER_VALUE_ACCESSOR,
3070
- SKY_FUZZY_DATEPICKER_VALIDATOR,
3089
+ SKY_DATEPICKER_VALUE_ACCESSOR,
3090
+ SKY_DATEPICKER_VALIDATOR,
3091
+ SkyDatepickerAdapterService,
3071
3092
  ],
3072
3093
  }]
3073
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: SkyDatepickerConfigService }, { type: i0.ElementRef }, { type: SkyFuzzyDateService }, { type: i3.SkyAppLocaleProvider }, { type: i0.Renderer2 }, { type: i3.SkyLibResourcesService }, { type: SkyDatepickerComponent, decorators: [{
3094
+ }], ctorParameters: function () { return [{ type: SkyDatepickerAdapterService }, { type: i0.ChangeDetectorRef }, { type: SkyDatepickerConfigService }, { type: i0.ElementRef }, { type: i3.SkyAppLocaleProvider }, { type: i0.Renderer2 }, { type: i3.SkyLibResourcesService }, { type: SkyDatepickerComponent, decorators: [{
3074
3095
  type: Optional
3075
3096
  }] }]; }, propDecorators: { dateFormat: [{
3076
3097
  type: Input
3077
3098
  }], disabled: [{
3078
3099
  type: Input
3079
- }], futureDisabled: [{
3080
- type: Input
3081
3100
  }], maxDate: [{
3082
3101
  type: Input
3083
3102
  }], minDate: [{
3084
3103
  type: Input
3085
- }], skyDatepickerNoValidate: [{
3104
+ }], skyDatepickerInput: [{
3086
3105
  type: Input
3087
- }], skyFuzzyDatepickerInput: [{
3106
+ }], skyDatepickerNoValidate: [{
3088
3107
  type: Input
3089
3108
  }], startingDay: [{
3090
3109
  type: Input
3091
- }], yearRequired: [{
3110
+ }], strict: [{
3092
3111
  type: Input
3093
3112
  }], onInputChange: [{
3094
3113
  type: HostListener,
@@ -3182,9 +3201,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
3182
3201
  * @dynamic
3183
3202
  */
3184
3203
  class SkyDateRangeRelativeValue {
3185
- // Abstract classes are not covered properly.
3186
- /* istanbul ignore next */
3187
- constructor() { }
3188
3204
  static get today() {
3189
3205
  const today = new Date();
3190
3206
  return {
@@ -3684,12 +3700,13 @@ let uniqueId = 0;
3684
3700
  * ```
3685
3701
  */
3686
3702
  class SkyDateRangePickerComponent {
3687
- constructor(changeDetector, dateRangeService, formBuilder, localeProvider, windowRef, themeSvc) {
3703
+ constructor(changeDetector, dateRangeService, formBuilder, localeProvider, windowRef, ngZone, themeSvc) {
3688
3704
  this.changeDetector = changeDetector;
3689
3705
  this.dateRangeService = dateRangeService;
3690
3706
  this.formBuilder = formBuilder;
3691
3707
  this.localeProvider = localeProvider;
3692
3708
  this.windowRef = windowRef;
3709
+ this.ngZone = ngZone;
3693
3710
  /**
3694
3711
  * Indicates whether to require users to specify a start date.
3695
3712
  * @default false
@@ -3706,11 +3723,11 @@ class SkyDateRangePickerComponent {
3706
3723
  this.showStartDatePicker = false;
3707
3724
  this.ngUnsubscribe = new Subject();
3708
3725
  this._disabled = false;
3709
- /* istanbul ignore next */
3726
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
3710
3727
  this.onChange = (_) => { };
3711
- /* istanbul ignore next */
3728
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
3712
3729
  this.onTouched = () => { };
3713
- /* istanbul ignore next */
3730
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
3714
3731
  this.onValidatorChange = () => { };
3715
3732
  this.localeProvider
3716
3733
  .getLocaleInfo()
@@ -3833,24 +3850,27 @@ class SkyDateRangePickerComponent {
3833
3850
  this.updateCalculators().then(() => {
3834
3851
  this.addEventListeners();
3835
3852
  this.isReady = true;
3836
- this.resetFormGroupValue();
3837
3853
  this.showRelevantFormFields();
3838
- // Fill in any unprovided values after the calculators have been initialized.
3839
- // For example, if the control is initialized with only the `calculatorId`,
3840
- // allow the calculator to fill in the missing start and end dates.
3841
- const { startDate, endDate } = this.value;
3842
- const defaultValue = this.selectedCalculator.getValue(startDate, endDate);
3843
- const newValue = Object.assign({}, defaultValue, this.value);
3844
- this.setValue(newValue, false);
3845
- // This is needed to address a bug in Angular 4.
3846
- // When a control value is set intially, its value is not represented on the view.
3847
- // See: https://github.com/angular/angular/issues/13792
3848
- /* istanbul ignore else */
3849
- if (this.control) {
3850
- this.control.setValue(this.value, {
3851
- emitEvent: false,
3852
- });
3853
- }
3854
+ // We need to let Angular be stable and have rendered the components prior to setting the values and form controls. This ensures all initial validation will be ran correctly.
3855
+ this.ngZone.onStable.pipe(first()).subscribe(() => {
3856
+ // Fill in any unprovided values after the calculators have been initialized.
3857
+ // For example, if the control is initialized with only the `calculatorId`,
3858
+ // allow the calculator to fill in the missing start and end dates.
3859
+ const { startDate, endDate } = this.value;
3860
+ const defaultValue = this.selectedCalculator.getValue(startDate, endDate);
3861
+ const newValue = Object.assign({}, defaultValue, this.value);
3862
+ this.setValue(newValue, false);
3863
+ this.resetFormGroupValue();
3864
+ // This is needed to address a bug in Angular 4.
3865
+ // When a control value is set intially, its value is not represented on the view.
3866
+ // See: https://github.com/angular/angular/issues/13792
3867
+ /* istanbul ignore else */
3868
+ if (this.control) {
3869
+ this.control.setValue(this.value, {
3870
+ emitEvent: false,
3871
+ });
3872
+ }
3873
+ });
3854
3874
  });
3855
3875
  }
3856
3876
  ngOnChanges(changes) {
@@ -3989,46 +4009,26 @@ class SkyDateRangePickerComponent {
3989
4009
  this.changeDetector.markForCheck();
3990
4010
  }
3991
4011
  resetFormGroupValue(value) {
3992
- // Do not emit a value change event on the underlying form group
3993
- // because we're already watching for changes that are triggered by the end user.
3994
- // For example, if we change the value of the form group internally, we don't want the event
3995
- // listeners to be triggered, as those are reserved for user interactions.
3996
- // (See the event listeners listed below.)
3997
- this.formGroup.reset(value || this.value, {
3998
- emitEvent: false,
3999
- });
4012
+ this.formGroup.reset(value || this.value);
4000
4013
  }
4001
4014
  addEventListeners() {
4002
- // Detect errors from the date pickers
4003
- // when control is initialized with a value.
4004
- combineLatest([
4005
- this.startDateControl.statusChanges,
4006
- this.endDateControl.statusChanges,
4007
- ])
4008
- .pipe(first())
4009
- .subscribe((status) => {
4010
- if (status.indexOf('INVALID') > -1) {
4011
- // Wait for initial validation to complete.
4012
- this.windowRef.nativeWindow.setTimeout(() => {
4013
- this.onValidatorChange();
4014
- });
4015
- }
4016
- });
4017
4015
  // Watch for selected calculator change.
4018
4016
  this.calculatorIdControl.valueChanges
4019
4017
  .pipe(takeUntil(this.ngUnsubscribe))
4020
4018
  .subscribe((value) => {
4021
- const id = parseInt(value, 10);
4022
- // if the component is disabled during form creation, null is passed
4023
- // as the value of the calculator id control
4024
- // only handle the value changes if the calculator id is a number
4025
- /* istanbul ignore else */
4026
- if (!isNaN(id)) {
4027
- const calculator = this.getCalculatorById(id);
4028
- const newValue = calculator.getValue();
4029
- this.setValue(newValue);
4030
- this.resetFormGroupValue(newValue);
4031
- this.showRelevantFormFields();
4019
+ if (value !== this.value.calculatorId) {
4020
+ const id = parseInt(value, 10);
4021
+ // if the component is disabled during form creation, null is passed
4022
+ // as the value of the calculator id control
4023
+ // only handle the value changes if the calculator id is a number
4024
+ /* istanbul ignore else */
4025
+ if (!isNaN(id)) {
4026
+ const calculator = this.getCalculatorById(id);
4027
+ const newValue = calculator.getValue();
4028
+ this.setValue(newValue);
4029
+ this.resetFormGroupValue(newValue);
4030
+ this.showRelevantFormFields();
4031
+ }
4032
4032
  }
4033
4033
  });
4034
4034
  // Watch for start date value changes.
@@ -4051,6 +4051,11 @@ class SkyDateRangePickerComponent {
4051
4051
  .pipe(takeUntil(this.ngUnsubscribe))
4052
4052
  .subscribe(() => {
4053
4053
  this.changeDetector.markForCheck();
4054
+ // Wait for initial validation to complete.
4055
+ this.ngZone.onStable.pipe(first()).subscribe(() => {
4056
+ var _a;
4057
+ (_a = this.control) === null || _a === void 0 ? void 0 : _a.updateValueAndValidity({ emitEvent: false });
4058
+ });
4054
4059
  });
4055
4060
  }
4056
4061
  updateCalculators() {
@@ -4070,7 +4075,7 @@ class SkyDateRangePickerComponent {
4070
4075
  return JSON.stringify(rangeA) === JSON.stringify(rangeB);
4071
4076
  }
4072
4077
  }
4073
- SkyDateRangePickerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDateRangePickerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: SkyDateRangeService }, { token: i2$2.FormBuilder }, { token: i3.SkyAppLocaleProvider }, { token: i1.SkyAppWindowRef }, { token: i3$2.SkyThemeService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
4078
+ SkyDateRangePickerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.16", ngImport: i0, type: SkyDateRangePickerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: SkyDateRangeService }, { token: i2$2.FormBuilder }, { token: i3.SkyAppLocaleProvider }, { token: i1.SkyAppWindowRef }, { token: i0.NgZone }, { token: i3$2.SkyThemeService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
4074
4079
  SkyDateRangePickerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.16", type: SkyDateRangePickerComponent, selector: "sky-date-range-picker", inputs: { calculatorIds: "calculatorIds", dateFormat: "dateFormat", disabled: "disabled", label: "label", startDateRequired: "startDateRequired", endDateRequired: "endDateRequired" }, providers: [
4075
4080
  SKY_DATE_RANGE_PICKER_VALUE_ACCESSOR,
4076
4081
  SKY_DATE_RANGE_PICKER_VALIDATOR,
@@ -4087,7 +4092,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.16", ngImpo
4087
4092
  ],
4088
4093
  changeDetection: ChangeDetectionStrategy.OnPush,
4089
4094
  }]
4090
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: SkyDateRangeService }, { type: i2$2.FormBuilder }, { type: i3.SkyAppLocaleProvider }, { type: i1.SkyAppWindowRef }, { type: i3$2.SkyThemeService, decorators: [{
4095
+ }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: SkyDateRangeService }, { type: i2$2.FormBuilder }, { type: i3.SkyAppLocaleProvider }, { type: i1.SkyAppWindowRef }, { type: i0.NgZone }, { type: i3$2.SkyThemeService, decorators: [{
4091
4096
  type: Optional
4092
4097
  }] }]; }, propDecorators: { calculatorIds: [{
4093
4098
  type: Input
@@ -4177,7 +4182,6 @@ class SkyTimepickerComponent {
4177
4182
  return this._disabled;
4178
4183
  }
4179
4184
  set selectedHour(setHour) {
4180
- let hour;
4181
4185
  let hourOffset = 0;
4182
4186
  if (this.selectedMeridies === 'AM' && setHour === 12) {
4183
4187
  hourOffset = -12;
@@ -4188,7 +4192,7 @@ class SkyTimepickerComponent {
4188
4192
  if (this.is8601) {
4189
4193
  hourOffset = 0;
4190
4194
  }
4191
- hour = moment({ hour: setHour }).add(hourOffset, 'hours').hour();
4195
+ const hour = moment({ hour: setHour }).add(hourOffset, 'hours').hour();
4192
4196
  this.activeTime = moment({
4193
4197
  hour: hour,
4194
4198
  minute: moment(this.activeTime).get('minute') + 0,
@@ -4308,9 +4312,9 @@ class SkyTimepickerComponent {
4308
4312
  localeFormat = 'HH:mm';
4309
4313
  this.is8601 = true;
4310
4314
  }
4311
- let data;
4312
- data = {
4313
- hours: Array.apply(undefined, Array(h)).map(function (x, i) {
4315
+ const data = {
4316
+ // Create a sparse array with a length equal to the hour.
4317
+ hours: Array(...Array(h)).map((_, i) => {
4314
4318
  if (format === 'hh') {
4315
4319
  return ++i;
4316
4320
  }
@@ -4322,7 +4326,8 @@ class SkyTimepickerComponent {
4322
4326
  /* sanity check */
4323
4327
  return 0;
4324
4328
  }),
4325
- minutes: Array.apply(undefined, Array(m)).map(function (x, i) {
4329
+ // Create a sparse array with a length equal to the minute.
4330
+ minutes: Array(...Array(m)).map(function (_, i) {
4326
4331
  return i * minuteMultiplier;
4327
4332
  }),
4328
4333
  localeFormat: localeFormat,
@@ -4512,11 +4517,11 @@ class SkyTimepickerInputDirective {
4512
4517
  this.resourcesService = resourcesService;
4513
4518
  this.changeDetector = changeDetector;
4514
4519
  this._timeFormat = 'hh';
4515
- /*istanbul ignore next */
4520
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
4516
4521
  this._onChange = (_) => { };
4517
- /*istanbul ignore next */
4522
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
4518
4523
  this._onTouched = () => { };
4519
- /* istanbul ignore next */
4524
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
4520
4525
  this._validatorChange = () => { };
4521
4526
  }
4522
4527
  /**
@@ -4625,7 +4630,7 @@ class SkyTimepickerInputDirective {
4625
4630
  if (!this.control) {
4626
4631
  this.control = control;
4627
4632
  }
4628
- let value = control.value;
4633
+ const value = control.value;
4629
4634
  if (!value) {
4630
4635
  return undefined;
4631
4636
  }
@@ -4659,7 +4664,6 @@ class SkyTimepickerInputDirective {
4659
4664
  return '';
4660
4665
  }
4661
4666
  let currentFormat;
4662
- let formatTime;
4663
4667
  if (this.timeFormat === 'hh') {
4664
4668
  currentFormat = 'h:mm A';
4665
4669
  }
@@ -4669,7 +4673,7 @@ class SkyTimepickerInputDirective {
4669
4673
  if (typeof this.returnFormat === 'undefined') {
4670
4674
  this.returnFormat = currentFormat;
4671
4675
  }
4672
- formatTime = {
4676
+ const formatTime = {
4673
4677
  hour: moment(time, currentFormat).hour(),
4674
4678
  minute: moment(time, currentFormat).minute(),
4675
4679
  meridie: moment(time, currentFormat).format('A'),