@progress/kendo-dateinputs-common 0.2.0-dev.202301131210 → 0.2.0-dev.202301171717

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.
@@ -530,6 +530,14 @@ export class DateObject {
530
530
  literal: ""
531
531
  };
532
532
  });
533
+ const flatDateParts = dateParts.partMap
534
+ .map((x) => {
535
+ return {
536
+ type: x.type,
537
+ pattern: x.pattern,
538
+ text: ""
539
+ };
540
+ });
533
541
  for (let i = 0; i < datePartsLiterals.length; i++) {
534
542
  const datePart = datePartsLiterals[i];
535
543
  for (let j = 0; j < datePart.pattern.length; j++) {
@@ -539,13 +547,23 @@ export class DateObject {
539
547
  }
540
548
  i += datePart.pattern.length - 1;
541
549
  }
550
+ for (let i = 0; i < flatDateParts.length; i++) {
551
+ const datePart = flatDateParts[i];
552
+ for (let j = 0; j < datePart.pattern.length; j++) {
553
+ if (flatDateParts[i + j]) {
554
+ flatDateParts[i + j].text = datePart.pattern[j];
555
+ }
556
+ }
557
+ i += datePart.pattern.length - 1;
558
+ }
542
559
  let shouldResetPart = isInCaretMode && symbol === "M" && dateParts.partMap
543
560
  .filter(x => x.type === "month")
544
561
  .some(x => x.pattern.length > MONTH_PART_WITH_WORDS_THRESHOLD);
545
562
  let parseResult = {
546
563
  value: null,
547
564
  switchPart: false,
548
- resetPart: shouldResetPart
565
+ resetPart: shouldResetPart,
566
+ hasInvalidDatePart: false
549
567
  };
550
568
  if (!currentChar) {
551
569
  if (isInCaretMode) {
@@ -579,14 +597,20 @@ export class DateObject {
579
597
  let basePrefix = '';
580
598
  let baseSuffix = '';
581
599
  let suffix = '';
582
- const datePartStartIndex = originalFormat.indexOf(symbol);
583
- const datePartEndIndex = originalFormat.lastIndexOf(symbol);
584
- const segmentLength = datePartEndIndex - datePartStartIndex + 1;
600
+ let convertedBaseFormat = "";
601
+ for (let i = 0; i < flatDateParts.length; i++) {
602
+ convertedBaseFormat += flatDateParts[i].text;
603
+ }
585
604
  const hasFixedFormat = (this.format === baseFormat) ||
605
+ (this.format === convertedBaseFormat) ||
586
606
  (this.format === originalFormat) ||
587
607
  (this.format.length === originalFormat.length);
588
- if (isInCaretMode) {
589
- let processedSegmentCharsCount = 0;
608
+ const datePartStartIndex = (hasFixedFormat ? convertedBaseFormat : originalFormat).indexOf(symbol);
609
+ const datePartEndIndex = (hasFixedFormat ? convertedBaseFormat : originalFormat).lastIndexOf(symbol);
610
+ const segmentLength = datePartEndIndex - datePartStartIndex + 1;
611
+ let processedSegmentCharsCount = 0;
612
+ let formatToTextLengthDiff = originalFormat.length - rawInputValue.length;
613
+ if (isInCaretMode || (!isInCaretMode && !this.autoCorrectParts)) {
590
614
  for (let i = 0; i < baseDate.length; i++) {
591
615
  if (baseFormat[i] === symbol) {
592
616
  const existing = this.getExisting(symbol);
@@ -598,6 +622,11 @@ export class DateObject {
598
622
  }
599
623
  processedSegmentCharsCount++;
600
624
  }
625
+ else if (formatToTextLengthDiff > 0) {
626
+ if (datePartText.length + formatToTextLengthDiff < segmentLength) {
627
+ datePartText += rawInputValue[i] || "";
628
+ }
629
+ }
601
630
  else {
602
631
  datePartText += rawInputValue[i] || "";
603
632
  }
@@ -613,12 +642,12 @@ export class DateObject {
613
642
  }
614
643
  }
615
644
  if (hasFixedFormat) {
616
- if (originalFormat.length < rawInputValue.length) {
645
+ if (convertedBaseFormat.length < rawInputValue.length) {
617
646
  datePartText += currentChar;
618
647
  }
619
648
  else if (!isDeleting && originalFormat.length > rawInputValue.length) {
620
649
  const lengthDiff = originalFormat.length - rawInputValue.length;
621
- const trimmedDatePartText = datePartText.substring(0, datePartText.length - lengthDiff);
650
+ const trimmedDatePartText = datePartText.substr(0, datePartText.length - lengthDiff);
622
651
  if (trimmedDatePartText && trimmedDatePartText.length > 0) {
623
652
  datePartText = trimmedDatePartText;
624
653
  }
@@ -627,7 +656,7 @@ export class DateObject {
627
656
  return extend(parseResult, { value: null, switchToNext: false });
628
657
  }
629
658
  }
630
- else {
659
+ if (!hasFixedFormat || (hasFixedFormat && !this.autoCorrectParts)) {
631
660
  processedSegmentCharsCount = 0;
632
661
  current = "";
633
662
  datePartText = "";
@@ -645,6 +674,11 @@ export class DateObject {
645
674
  }
646
675
  processedSegmentCharsCount++;
647
676
  }
677
+ else if (formatToTextLengthDiff > 0) {
678
+ if (datePartText.length + formatToTextLengthDiff < segmentLength) {
679
+ datePartText += rawInputValue[i] || "";
680
+ }
681
+ }
648
682
  else {
649
683
  datePartText += rawInputValue[i] || "";
650
684
  }
@@ -654,43 +688,52 @@ export class DateObject {
654
688
  prefix += rawInputValue[i] || "";
655
689
  }
656
690
  else {
657
- suffix += rawInputValue[i] || "";
691
+ suffix += rawInputValue[i - formatToTextLengthDiff] || "";
658
692
  }
659
693
  }
660
694
  if (originalFormat.length < rawInputValue.length) {
661
695
  datePartText += currentChar;
662
696
  }
663
- else if (!isDeleting && originalFormat.length > rawInputValue.length) {
664
- const lengthDiff = originalFormat.length - rawInputValue.length;
665
- const trimmedDatePartText = datePartText.substring(0, datePartText.length - lengthDiff);
666
- if (trimmedDatePartText && trimmedDatePartText.length > 0) {
667
- datePartText = trimmedDatePartText;
668
- }
669
- }
670
697
  }
671
698
  }
672
- else {
673
- for (let i = 0; i < baseDate.length; i++) {
674
- if (baseFormat[i] === symbol) {
675
- const existing = this.getExisting(symbol);
676
- current += existing ? baseDate[i] : '0';
677
- replaced = true;
678
- }
679
- else if (!replaced) {
680
- prefix += baseDate[i];
681
- }
682
- else {
683
- suffix += baseDate[i];
699
+ if (!isInCaretMode) {
700
+ if (this.autoCorrectParts) {
701
+ processedSegmentCharsCount = 0;
702
+ current = "";
703
+ datePartText = "";
704
+ prefix = "";
705
+ suffix = "";
706
+ replaced = false;
707
+ for (let i = 0; i < baseDate.length; i++) {
708
+ if (baseFormat[i] === symbol) {
709
+ const existing = this.getExisting(symbol);
710
+ current += existing ? baseDate[i] : '0';
711
+ replaced = true;
712
+ }
713
+ else if (!replaced) {
714
+ prefix += baseDate[i];
715
+ }
716
+ else {
717
+ suffix += baseDate[i];
718
+ }
684
719
  }
685
720
  }
721
+ else {
722
+ current = resetSegmentValue ? datePartText : current;
723
+ }
686
724
  }
687
725
  let parsedDate = null;
688
- const month = this.matchMonth(currentChar);
726
+ let month = this.matchMonth(currentChar);
689
727
  const dayPeriod = this.matchDayPeriod(currentChar, symbol);
690
728
  const isZeroCurrentChar = currentChar === '0';
691
729
  const leadingZero = this.leadingZero || {};
692
- if (isZeroCurrentChar && !isInCaretMode) {
693
- let valueNumber = parseInt(resetSegmentValue ? currentChar : current + currentChar, 10);
730
+ if (isZeroCurrentChar) {
731
+ if (datePartText === "0") {
732
+ datePartText = current;
733
+ }
734
+ let valueNumber = parseToInt(resetSegmentValue ?
735
+ currentChar :
736
+ (isInCaretMode ? datePartText : current) + currentChar);
694
737
  if (valueNumber === 0 && !this.isAbbrMonth(dateParts.partMap, symbol)) {
695
738
  this.incrementLeadingZero(symbol);
696
739
  }
@@ -708,32 +751,25 @@ export class DateObject {
708
751
  }
709
752
  const currentMaxLength = current.length - 3;
710
753
  let tryParse = true;
754
+ let middle = isInCaretMode ? datePartText : current;
711
755
  for (let i = Math.max(0, currentMaxLength); i <= current.length; i++) {
712
756
  if (!tryParse) {
713
757
  break;
714
758
  }
715
- let middle = resetSegmentValue ?
759
+ middle = resetSegmentValue ?
716
760
  currentChar :
717
761
  isInCaretMode ?
718
762
  datePartText :
719
763
  (current.substring(i) + currentChar);
720
- if (!this.autoCorrectParts) {
721
- tryParse = false;
722
- if (!isInCaretMode) {
723
- // try to make an exact match as there will be only 1 attempt
724
- middle = unpadZero(middle);
725
- middle = padZero(segmentLength - middle.length) + middle;
726
- }
727
- }
728
- if (isInCaretMode) {
729
- // try to make an exact match as there will be only 1 attempt
764
+ if (isInCaretMode || !this.autoCorrectParts) {
730
765
  tryParse = false;
731
766
  middle = unpadZero(middle);
767
+ middle = padZero(segmentLength - middle.length) + middle;
732
768
  }
733
769
  let middleNumber = parseInt(middle, 10);
734
770
  const candidateDateString = prefix + middle + suffix;
735
771
  parsedDate = this.intl.parseDate(candidateDateString, this.format, this.localeId);
736
- if (isInCaretMode && !hasFixedFormat && !isValidDate(parsedDate)) {
772
+ if (isInCaretMode && !isValidDate(parsedDate)) {
737
773
  // if part of the date is not available, e.g. "d"
738
774
  // but an expanded format like "F" is used
739
775
  // the element value can be "EEEE, February 1, 2022 3:04:05 AM"
@@ -741,11 +777,12 @@ export class DateObject {
741
777
  // use the base prefix and suffix, e.g. convert the candidate date string
742
778
  // to "Thursday, February 1, 2022 3:04:05 AM"
743
779
  // as "EEEE, February..." is not parsable
744
- parsedDate = this.intl.parseDate(basePrefix + middle + baseSuffix, this.format, this.localeId);
745
- datePartText = middle;
780
+ if (this.autoCorrectParts) {
781
+ parsedDate = this.intl.parseDate(basePrefix + middle + baseSuffix, this.format, this.localeId);
782
+ }
746
783
  }
747
784
  const isCurrentCharParsable = !isNaN(parseInt(currentChar, 10)) || (isInCaretMode && isDeleting && currentChar === "");
748
- if (!parsedDate && !isNaN(middleNumber) && isCurrentCharParsable) {
785
+ if (!parsedDate && !isNaN(middleNumber) && isCurrentCharParsable && this.autoCorrectParts) {
749
786
  if (symbol === MONTH_SYMBOL && !month) {
750
787
  // JS months start from 0 (January) instead of 1 (January)
751
788
  const monthNumber = middleNumber - JS_MONTH_OFFSET;
@@ -780,16 +817,34 @@ export class DateObject {
780
817
  }
781
818
  this._value = parsedDate;
782
819
  this.setExisting(symbol, true);
820
+ this.resetInvalidDateSymbol(symbol);
821
+ if (!this.autoCorrectParts) {
822
+ if (symbol === "M") {
823
+ if (this.getExisting("M") && this.getExisting("y")) {
824
+ // changing from 28/Feb to 29/Feb to 29/March
825
+ this.setExisting("d", true);
826
+ this.resetInvalidDateSymbol("d");
827
+ }
828
+ }
829
+ else if (symbol === "d") {
830
+ if (this.getExisting("d") && this.getExisting("y")) {
831
+ // changing from 31/Jan to 31/Feb to 28/Feb
832
+ this.setExisting("M", true);
833
+ this.resetInvalidDateSymbol("M");
834
+ }
835
+ }
836
+ if (!this.hasInvalidDatePart()) {
837
+ this.markDatePartsAsExisting();
838
+ }
839
+ }
783
840
  if (isInCaretMode && switchToNext) {
784
841
  if (symbol === "M") {
785
- if (segmentLength <= MONTH_PART_WITH_WORDS_THRESHOLD) {
786
- const datePartValue = parseToInt(datePartText);
787
- if (datePartValue >= 2) {
788
- switchToNext = true;
789
- }
790
- else {
791
- switchToNext = false;
792
- }
842
+ const datePartValue = parseToInt(datePartText);
843
+ if (datePartValue >= 2 || datePartText.length >= 2) {
844
+ switchToNext = true;
845
+ }
846
+ else {
847
+ switchToNext = false;
793
848
  }
794
849
  }
795
850
  else {
@@ -822,29 +877,157 @@ export class DateObject {
822
877
  this.leadingZero = !this.isAbbrMonth(dateParts.partMap, symbol) ? { [symbol]: true } : null;
823
878
  this.setExisting(symbol, false);
824
879
  }
825
- if (isInCaretMode && datePartText.length > segmentLength) {
826
- return extend(parseResult, { value: null, switchToNext: false });
827
- }
828
880
  if (!this.autoCorrectParts) {
829
- this.setExisting(symbol, false);
830
881
  let datePartValue;
831
- const textToParse = isInCaretMode ? datePartText : current;
882
+ const textToParse = isInCaretMode ? datePartText : middle;
832
883
  const parsedValue = parseToInt(textToParse);
833
884
  if (isNumber(parsedValue)) {
885
+ if ((symbol === "d" && (parsedValue <= 0 || parsedValue > 31)) ||
886
+ (symbol === "M" && (parsedValue < 0 || parsedValue > 11))) {
887
+ return extend(parseResult, { value: null, switchToNext: false });
888
+ }
834
889
  datePartValue = symbol === "M" ?
835
- clamp(parsedValue - JS_MONTH_OFFSET, 0, 12 - JS_MONTH_OFFSET) :
890
+ parsedValue - JS_MONTH_OFFSET :
836
891
  parsedValue;
837
- }
838
- if (isNumber(datePartValue)) {
839
- const newDate = this.modifyDateSymbolWithValue(this.value, symbol, datePartValue);
840
- // if (!isEqual(newDate, this.value)) {
841
- this.setExisting(symbol, false);
842
- this.setInvalidDatePart(symbol, {
843
- value: datePartValue,
844
- date: cloneDate(newDate),
845
- startDate: this._partiallyInvalidDate.startDate || cloneDate(this.value)
892
+ const isMonth = symbol === "M";
893
+ const isDay = symbol === "d";
894
+ let newValue = cloneDate(this._value);
895
+ const invalidDateParts = this._partiallyInvalidDate.invalidDateParts || {};
896
+ let year = invalidDateParts.y.value || newValue.getFullYear();
897
+ /* tslint:disable:no-shadowed-variable */
898
+ let month = isMonth ? datePartValue : invalidDateParts.M.value || newValue.getMonth();
899
+ /* tslint:enable:no-shadowed-variable */
900
+ let day = isDay ? datePartValue : invalidDateParts.d.value || invalidDateParts.E.value || newValue.getDate();
901
+ let hour = invalidDateParts.h.value || invalidDateParts.H.value || newValue.getHours();
902
+ let minutes = invalidDateParts.m.value || newValue.getMinutes();
903
+ let seconds = invalidDateParts.s.value || newValue.getSeconds();
904
+ let milliseconds = invalidDateParts.S.value || newValue.getMilliseconds();
905
+ const dateCandidate = createDate(year, month, day, hour, minutes, seconds, milliseconds);
906
+ const dateCandidateExists = areDatePartsEqualTo(dateCandidate, year, month, day, hour, minutes, seconds, milliseconds);
907
+ const newValueCandidate = isMonth || isDay ?
908
+ this.modifyDateSymbolWithValue(newValue, symbol, isMonth ? month : day) :
909
+ null;
910
+ let invalidDateFound = false;
911
+ if (isMonth && newValueCandidate) {
912
+ if (newValueCandidate.getMonth() === month) {
913
+ if (this.getExisting("d")) {
914
+ if (dateCandidateExists) {
915
+ newValue = cloneDate(dateCandidate);
916
+ this.resetInvalidDateSymbol(symbol);
917
+ }
918
+ else {
919
+ invalidDateFound = true;
920
+ this.setInvalidDatePart(symbol, {
921
+ value: month,
922
+ date: cloneDate(newValueCandidate),
923
+ // startDateOffset: offset,
924
+ startDate: cloneDate(this.value)
925
+ });
926
+ this.setExisting(symbol, false);
927
+ }
928
+ }
929
+ else if (dateCandidateExists) {
930
+ this.resetInvalidDateSymbol(symbol);
931
+ newValue = cloneDate(dateCandidate);
932
+ if (this.getExisting("M") && this.getExisting("y")) {
933
+ // changing from 28/Feb to 29/Feb to 29/March
934
+ this.setExisting("d", true);
935
+ this.resetInvalidDateSymbol("d");
936
+ }
937
+ }
938
+ else {
939
+ this.resetInvalidDateSymbol(symbol);
940
+ newValue = cloneDate(newValueCandidate);
941
+ }
942
+ }
943
+ else {
944
+ invalidDateFound = true;
945
+ this.setInvalidDatePart(symbol, {
946
+ value: month,
947
+ date: cloneDate(newValueCandidate),
948
+ // startDateOffset: offset,
949
+ startDate: cloneDate(this.value)
950
+ });
951
+ this.setExisting(symbol, false);
952
+ }
953
+ }
954
+ else if (isDay && newValueCandidate) {
955
+ if (newValueCandidate.getDate() === day) {
956
+ if (this.getExisting("M")) {
957
+ if (dateCandidateExists) {
958
+ newValue = cloneDate(dateCandidate);
959
+ this.resetInvalidDateSymbol(symbol);
960
+ }
961
+ else {
962
+ invalidDateFound = true;
963
+ this.setInvalidDatePart(symbol, {
964
+ value: day,
965
+ date: cloneDate(newValueCandidate),
966
+ // startDateOffset: offset,
967
+ startDate: cloneDate(this.value)
968
+ });
969
+ this.setExisting(symbol, false);
970
+ }
971
+ }
972
+ else if (dateCandidateExists) {
973
+ newValue = cloneDate(dateCandidate);
974
+ this.resetInvalidDateSymbol(symbol);
975
+ if (this.getExisting("d") && this.getExisting("y")) {
976
+ // changing from 31/Jan to 31/Feb to 28/Feb
977
+ this.setExisting("M", true);
978
+ this.resetInvalidDateSymbol("M");
979
+ }
980
+ }
981
+ else {
982
+ this.resetInvalidDateSymbol(symbol);
983
+ newValue = cloneDate(newValueCandidate);
984
+ }
985
+ }
986
+ else {
987
+ invalidDateFound = true;
988
+ this.setInvalidDatePart(symbol, {
989
+ value: day,
990
+ date: cloneDate(this.value),
991
+ // startDateOffset: offset,
992
+ startDate: cloneDate(this.value)
993
+ });
994
+ this.setExisting(symbol, false);
995
+ }
996
+ }
997
+ if (!invalidDateFound) {
998
+ this.setExisting(symbol, true);
999
+ if (isInCaretMode && !isValidDate(parsedDate)) {
1000
+ const valueCandidate = this.intl.parseDate(basePrefix + middle + baseSuffix, this.format, this.localeId);
1001
+ if (isValidDate(valueCandidate)) {
1002
+ this._value = valueCandidate;
1003
+ }
1004
+ }
1005
+ else {
1006
+ this._value = newValue;
1007
+ }
1008
+ if (this.getValue()) {
1009
+ this.resetInvalidDate();
1010
+ }
1011
+ }
1012
+ let switchToNext = false;
1013
+ if (symbol === "M") {
1014
+ if (parsedValue >= 2 || textToParse.length >= 2) {
1015
+ switchToNext = true;
1016
+ }
1017
+ else {
1018
+ switchToNext = false;
1019
+ }
1020
+ }
1021
+ else {
1022
+ switchToNext = hasFixedFormat ?
1023
+ textToParse.length === segmentLength :
1024
+ textToParse.length > segmentLength;
1025
+ }
1026
+ return extend(parseResult, {
1027
+ value: null,
1028
+ switchToNext: switchToNext,
1029
+ hasInvalidDatePart: invalidDateFound
846
1030
  });
847
- // }
848
1031
  }
849
1032
  }
850
1033
  return extend(parseResult, { value: null, switchToNext: false });
@@ -866,6 +1049,12 @@ export class DateObject {
866
1049
  setLeadingZero(leadingZero) {
867
1050
  this.leadingZero = leadingZero;
868
1051
  }
1052
+ /**
1053
+ * @hidden
1054
+ */
1055
+ getLeadingZero() {
1056
+ return this.leadingZero || {};
1057
+ }
869
1058
  /**
870
1059
  * @hidden
871
1060
  */
@@ -948,7 +1137,6 @@ export class DateObject {
948
1137
  const dateFormatParts = this.intl.splitDateFormat(this.format, this.localeId);
949
1138
  for (let i = 0; i < dateFormatParts.length; i++) {
950
1139
  if (dateFormatParts[i].type === 'month' && dateFormatParts[i].names) {
951
- // return this.intl.dateFormatNames(dateFormatParts[i].names);
952
1140
  return this.intl.dateFormatNames(locale, dateFormatParts[i].names);
953
1141
  }
954
1142
  }
@@ -1059,13 +1247,16 @@ export class DateObject {
1059
1247
  }
1060
1248
  else {
1061
1249
  datePartText = (parseToInt(this.getInvalidDatePartValue(symbol)) + JS_MONTH_OFFSET).toString();
1062
- resultText = datePartText + resultText;
1250
+ const formattedDatePart = padZero(partsForSegment.length - datePartText.length) + datePartText;
1251
+ resultText = formattedDatePart + resultText;
1252
+ formatSymbolIndexModifier = partsForSegment.length - 1;
1063
1253
  ignoreFormatSymbolsCount = datePartText.length - partsForSegment.length;
1064
1254
  }
1065
1255
  }
1066
1256
  else {
1067
- resultText = datePartText + resultText;
1068
- formatSymbolIndexModifier = datePartText.length - 1;
1257
+ const formattedDatePart = padZero(partsForSegment.length - datePartText.length) + datePartText;
1258
+ resultText = formattedDatePart + resultText;
1259
+ formatSymbolIndexModifier = partsForSegment.length - 1;
1069
1260
  ignoreFormatSymbolsCount = datePartText.length - partsForSegment.length;
1070
1261
  }
1071
1262
  }
@@ -1160,6 +1351,19 @@ export class DateObject {
1160
1351
  this._partiallyInvalidDate.startDate = startDate;
1161
1352
  }
1162
1353
  }
1354
+ /**
1355
+ * @hidden
1356
+ */
1357
+ hasInvalidDatePart() {
1358
+ let hasInvalidDatePart = false;
1359
+ Object.keys(this._partiallyInvalidDate.invalidDateParts).forEach(key => {
1360
+ if (this._partiallyInvalidDate.invalidDateParts[key] &&
1361
+ isPresent(this._partiallyInvalidDate.invalidDateParts[key].value)) {
1362
+ hasInvalidDatePart = true;
1363
+ }
1364
+ });
1365
+ return hasInvalidDatePart;
1366
+ }
1163
1367
  /**
1164
1368
  * @hidden
1165
1369
  */