@progress/kendo-dateinputs-common 0.2.0-dev.202301101148 → 0.2.0-dev.202301130811

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.
@@ -147,16 +147,18 @@ export class DateObject {
147
147
  partiallyInvalidText += text[i];
148
148
  }
149
149
  else if (this.getInvalidDatePartValue(symbol)) {
150
+ const partsForSegment = this.getPartsForSegment(mask, i);
150
151
  if (symbol === "M") {
151
- if (mask.partMap[i].pattern.length > MONTH_PART_WITH_WORDS_THRESHOLD) {
152
+ const datePartText = (parseToInt(this.getInvalidDatePartValue(symbol)) + JS_MONTH_OFFSET).toString();
153
+ if (partsForSegment.length > MONTH_PART_WITH_WORDS_THRESHOLD) {
152
154
  partiallyInvalidText += formattedDates[symbol][i];
153
155
  }
154
156
  else {
155
157
  if (this.getInvalidDatePartValue(symbol)) {
156
- const month = parseToInt(this.getInvalidDatePartValue(symbol) + JS_MONTH_OFFSET).toString();
157
- const formattedMonth = padZero(Math.abs(mask.partMap[i].pattern.length - month.length)) + month;
158
- partiallyInvalidText += formattedMonth;
159
- i += Math.max(0, formattedMonth.length - 1);
158
+ const formattedDatePart = padZero(partsForSegment.length - datePartText.length) + datePartText;
159
+ partiallyInvalidText += formattedDatePart;
160
+ // add -1 as the first character in the segment is at index i
161
+ i += partsForSegment.length - 1;
160
162
  }
161
163
  else {
162
164
  partiallyInvalidText += formattedDates[symbol][i];
@@ -165,8 +167,11 @@ export class DateObject {
165
167
  }
166
168
  else {
167
169
  if (this.getInvalidDatePartValue(symbol)) {
168
- partiallyInvalidText += this.getInvalidDatePartValue(symbol);
169
- i += Math.max(0, this.getInvalidDatePartValue(symbol).toString().length - 1);
170
+ const datePartText = this.getInvalidDatePartValue(symbol).toString();
171
+ const formattedDatePart = padZero(partsForSegment.length - datePartText.length) + datePartText;
172
+ partiallyInvalidText += formattedDatePart;
173
+ // add -1 as the first character in the segment is at index i
174
+ i += partsForSegment.length - 1;
170
175
  }
171
176
  else {
172
177
  partiallyInvalidText += formattedDates[symbol][i];
@@ -275,16 +280,18 @@ export class DateObject {
275
280
  }
276
281
  }
277
282
  modifyPart(symbol, offset) {
283
+ if (!isPresent(symbol) || !isPresent(offset) || offset === 0) {
284
+ return;
285
+ }
278
286
  let newValue = cloneDate(this.value);
279
- let originalValue = cloneDate(this.value);
280
287
  let timeModified = false;
281
288
  let invalidDateFound;
282
- let currentInvalidDatePartValue = 0;
283
- if (!this.autoCorrectParts) {
284
- const isMonth = symbol === "M";
285
- const isDay = symbol === "d" || symbol === "E";
289
+ const isMonth = symbol === "M";
290
+ const isDay = symbol === "d" || symbol === "E";
291
+ const symbolExists = this.getExisting(symbol);
292
+ if (!this.autoCorrectParts && (isDay || isMonth)) {
286
293
  const invalidDateParts = this._partiallyInvalidDate.invalidDateParts || {};
287
- const invalidDatePart = invalidDateParts[symbol];
294
+ const invalidDatePartValue = this.getInvalidDatePartValue(symbol);
288
295
  let year = invalidDateParts.y.value || newValue.getFullYear();
289
296
  let month = invalidDateParts.M.value || newValue.getMonth();
290
297
  let day = invalidDateParts.d.value || invalidDateParts.E.value || newValue.getDate();
@@ -303,33 +310,61 @@ export class DateObject {
303
310
  case 'E':
304
311
  day += offset;
305
312
  break;
306
- case 'h':
307
- case 'H':
308
- hour += offset;
309
- break;
310
- case 'm':
311
- minutes += offset;
312
- break;
313
- case 's':
314
- seconds += offset;
315
- break;
316
- case 'S':
317
- milliseconds += offset;
318
- break;
319
- // case 'a': newValue.setHours(newValue.getHours() + (12 * offset)); timeModified = true; break;
313
+ // case 'h':
314
+ // case 'H': hour += offset; break;
315
+ // case 'm': minutes += offset; break;
316
+ // case 's': seconds += offset; break;
317
+ // case 'S': milliseconds += offset; break;
320
318
  default: break;
321
319
  }
322
320
  if (symbol === "M") {
323
- if ((month < 0 || month > 11) && this.getExisting(symbol)) {
324
- // do not cycle months
325
- this.setExisting(symbol, false);
326
- return;
321
+ if ((month < 0 || month > 11)) {
322
+ if (symbolExists) {
323
+ this.setExisting(symbol, false);
324
+ this.resetInvalidDateSymbol(symbol);
325
+ return;
326
+ }
327
+ }
328
+ if (!symbolExists) {
329
+ if (month < 0) {
330
+ month = clamp(11 + ((month % 11) + 1), 0, 11);
331
+ }
332
+ else {
333
+ const monthValue = isPresent(invalidDatePartValue) ?
334
+ month :
335
+ ((offset - JS_MONTH_OFFSET) % 12);
336
+ month = clamp(monthValue, 0, 11);
337
+ }
338
+ month = clamp(month, 0, 11);
339
+ }
340
+ month = clamp(month, 0, 11);
341
+ }
342
+ else if (symbol === "d") {
343
+ if (symbolExists) {
344
+ if (day <= 0 || day > 31) {
345
+ this.setExisting(symbol, false);
346
+ this.resetInvalidDateSymbol(symbol);
347
+ return;
348
+ }
349
+ }
350
+ else if (!symbolExists) {
351
+ if (isPresent(invalidDatePartValue)) {
352
+ if (day <= 0 || day > 31) {
353
+ this.setExisting(symbol, false);
354
+ this.resetInvalidDateSymbol(symbol);
355
+ return;
356
+ }
357
+ }
358
+ if (offset < 0) {
359
+ const dayValue = isPresent(invalidDatePartValue) ? day : 1 + (31 - Math.abs(offset % 31));
360
+ day = clamp(dayValue, 1, 31);
361
+ }
362
+ else {
363
+ const dayValue = isPresent(invalidDatePartValue) ? day : offset % 31;
364
+ day = clamp(dayValue, 1, 31);
365
+ }
366
+ day = clamp(day, 1, 31);
327
367
  }
328
- // const mask = this.dateFormatString(this.value, this.format);
329
- // const monthPart = mask.partMap.filter(x => x.type === "month");
330
- // if (monthPart && monthPart[0] && monthPart[0].pattern.length > MONTH_PART_WITH_WORDS_THRESHOLD) {
331
- month = (12 + month) % 12;
332
- // }
333
368
  }
334
369
  const dateCandidate = createDate(year, month, day, hour, minutes, seconds, milliseconds);
335
370
  const newValueCandidate = isMonth || isDay ?
@@ -426,92 +461,6 @@ export class DateObject {
426
461
  this.setExisting(symbol, false);
427
462
  }
428
463
  }
429
- else {
430
- // this.modifyDateSymbol()
431
- switch (symbol) {
432
- case 'y':
433
- newValue.setFullYear(newValue.getFullYear() + offset);
434
- break;
435
- case 'M':
436
- newValue = addMonths(this.value, offset);
437
- break;
438
- case 'd':
439
- case 'E':
440
- newValue.setDate(newValue.getDate() + offset);
441
- break;
442
- case 'h':
443
- case 'H':
444
- newValue.setHours(newValue.getHours() + offset);
445
- timeModified = true;
446
- break;
447
- case 'm':
448
- newValue.setMinutes(newValue.getMinutes() + offset);
449
- timeModified = true;
450
- break;
451
- case 's':
452
- newValue.setSeconds(newValue.getSeconds() + offset);
453
- timeModified = true;
454
- break;
455
- case "S":
456
- newValue.setMilliseconds(newValue.getMilliseconds() + offset);
457
- break;
458
- case 'a':
459
- newValue.setHours(newValue.getHours() + (12 * offset));
460
- timeModified = true;
461
- break;
462
- default: break;
463
- }
464
- invalidDateFound = true;
465
- if (invalidDatePart && invalidDatePart.value) {
466
- currentInvalidDatePartValue = parseToInt(invalidDatePart.value);
467
- }
468
- else {
469
- if (!isPresent(invalidDatePart.value)) {
470
- newValue = cloneDate(originalValue);
471
- // this.modifyDateSymbol()
472
- switch (symbol) {
473
- case 'y':
474
- currentInvalidDatePartValue = originalValue.getFullYear();
475
- break;
476
- case 'M':
477
- currentInvalidDatePartValue = originalValue.getMonth();
478
- break;
479
- case 'd':
480
- case 'E':
481
- currentInvalidDatePartValue = originalValue.getDate();
482
- break;
483
- case 'h':
484
- case 'H':
485
- currentInvalidDatePartValue = originalValue.getHours();
486
- break;
487
- case 'm':
488
- currentInvalidDatePartValue = originalValue.getMinutes();
489
- break;
490
- case 's':
491
- currentInvalidDatePartValue = originalValue.getSeconds();
492
- break;
493
- case 'S':
494
- currentInvalidDatePartValue = originalValue.getMilliseconds();
495
- break;
496
- // case 'a': newValue.setHours(newValue.getHours() + (12 * offset)); timeModified = true; break;
497
- default: break;
498
- }
499
- }
500
- else {
501
- }
502
- }
503
- let invalidDatePartValue = Math.max(0, currentInvalidDatePartValue + offset);
504
- if (symbol !== "y") {
505
- invalidDatePartValue = clamp(currentInvalidDatePartValue + offset, 0, 99);
506
- }
507
- this.setInvalidDatePart(symbol, {
508
- value: invalidDatePartValue,
509
- date: cloneDate(newValue),
510
- startDateOffset: (this.getInvalidDatePart(symbol).startDateOffset || 0) + offset,
511
- startDate: cloneDate(this.value)
512
- });
513
- this.setExisting(symbol, false);
514
- }
515
464
  }
516
465
  else {
517
466
  switch (symbol) {
@@ -774,6 +723,7 @@ export class DateObject {
774
723
  // to "Thursday, February 1, 2022 3:04:05 AM"
775
724
  // as "EEEE, February..." is not parsable
776
725
  parsedDate = this.intl.parseDate(basePrefix + middle + baseSuffix, this.format, this.localeId);
726
+ datePartText = middle;
777
727
  }
778
728
  const isCurrentCharParsable = !isNaN(parseInt(currentChar, 10)) || (isInCaretMode && isDeleting && currentChar === "");
779
729
  if (!parsedDate && !isNaN(middleNumber) && isCurrentCharParsable) {
@@ -803,7 +753,7 @@ export class DateObject {
803
753
  const patternLength = this.patternLength(patternValue) || patternValue.length;
804
754
  const leadingZeroOffset = (this.leadingZero || {})[symbol] || 0;
805
755
  const patternSatisfied = (leadingZeroOffset + (unpadZero(middle) || currentChar).length) >= patternLength;
806
- const switchToNext = peekDate === null ||
756
+ let switchToNext = peekDate === null ||
807
757
  (leadingZero[symbol] && patternValue.length <= middle.length) ||
808
758
  patternSatisfied;
809
759
  if (this.shouldNormalizeCentury()) {
@@ -811,6 +761,26 @@ export class DateObject {
811
761
  }
812
762
  this._value = parsedDate;
813
763
  this.setExisting(symbol, true);
764
+ if (isInCaretMode && switchToNext) {
765
+ if (symbol === "M") {
766
+ if (segmentLength <= MONTH_PART_WITH_WORDS_THRESHOLD) {
767
+ const datePartValue = parseToInt(datePartText);
768
+ if (datePartValue >= 2) {
769
+ switchToNext = true;
770
+ }
771
+ else {
772
+ switchToNext = false;
773
+ }
774
+ }
775
+ }
776
+ else {
777
+ switchToNext = switchToNext ?
778
+ hasFixedFormat ?
779
+ datePartText.length === segmentLength :
780
+ datePartText.length > segmentLength :
781
+ switchToNext;
782
+ }
783
+ }
814
784
  return extend(parseResult, { value: this.value, switchToNext: switchToNext });
815
785
  }
816
786
  }
@@ -1020,27 +990,68 @@ export class DateObject {
1020
990
  let resultText = '';
1021
991
  let resultFormat = '';
1022
992
  let format = mask.symbols;
993
+ let processTextSymbolsEnded = false;
994
+ let ignoreFormatSymbolsCount = 0;
995
+ const formattedDates = this.getFormattedInvalidDates(format);
1023
996
  for (let formatSymbolIndex = format.length - 1; formatSymbolIndex >= 0; formatSymbolIndex--) {
997
+ const partsForSegment = this.getPartsForSegment(mask, formatSymbolIndex);
1024
998
  if (this.knownParts.indexOf(format[formatSymbolIndex]) === -1 || this.getExisting(format[formatSymbolIndex])) {
1025
- resultText = text[formatSymbolIndex] + resultText;
999
+ if (this.autoCorrectParts) {
1000
+ resultText = text[formatSymbolIndex] + resultText;
1001
+ }
1002
+ else {
1003
+ if (text.length !== format.length) {
1004
+ if (processTextSymbolsEnded) {
1005
+ resultText = text[formatSymbolIndex] + resultText;
1006
+ }
1007
+ else if (ignoreFormatSymbolsCount > 0) {
1008
+ resultText = text[formatSymbolIndex] + resultText;
1009
+ ignoreFormatSymbolsCount--;
1010
+ if (ignoreFormatSymbolsCount <= 0) {
1011
+ processTextSymbolsEnded = true;
1012
+ }
1013
+ }
1014
+ else {
1015
+ resultText = (text[formatSymbolIndex + text.length - format.length] || "") + resultText;
1016
+ }
1017
+ }
1018
+ else {
1019
+ resultText = text[formatSymbolIndex] + resultText;
1020
+ }
1021
+ }
1026
1022
  resultFormat = format[formatSymbolIndex] + resultFormat;
1027
1023
  }
1028
1024
  else {
1029
1025
  const symbol = format[formatSymbolIndex];
1030
- while (formatSymbolIndex >= 0 && symbol === format[formatSymbolIndex]) {
1031
- formatSymbolIndex--;
1026
+ let formatSymbolIndexModifier = 0;
1027
+ if (this.autoCorrectParts || (!this.autoCorrectParts && !this.getInvalidDatePartValue(symbol))) {
1028
+ while (formatSymbolIndex >= 0 && symbol === format[formatSymbolIndex]) {
1029
+ formatSymbolIndex--;
1030
+ }
1031
+ formatSymbolIndex++;
1032
1032
  }
1033
- formatSymbolIndex++;
1034
1033
  if (this.leadingZero && this.leadingZero[symbol]) {
1035
1034
  resultText = '0' + resultText;
1036
1035
  }
1037
1036
  else {
1038
1037
  if (!this.autoCorrectParts && this.getInvalidDatePartValue(symbol)) {
1039
- // use mask.symbols instead of mask.partMap
1040
- const part = mask.partMap[formatSymbolIndex];
1041
- const pattern = mask.partMap.filter(x => x.type === part.type && x.pattern === part.pattern);
1042
- const segmentText = text.substr(formatSymbolIndex, pattern.length);
1043
- resultText = segmentText + resultText;
1038
+ let datePartText = this.getInvalidDatePartValue(symbol).toString();
1039
+ if (symbol === "M") {
1040
+ datePartText = (parseToInt(this.getInvalidDatePartValue(symbol)) + JS_MONTH_OFFSET).toString();
1041
+ if (partsForSegment.length > MONTH_PART_WITH_WORDS_THRESHOLD) {
1042
+ resultText = formattedDates[symbol][formatSymbolIndex] + resultText;
1043
+ }
1044
+ else {
1045
+ datePartText = (parseToInt(this.getInvalidDatePartValue(symbol)) + JS_MONTH_OFFSET).toString();
1046
+ resultText = datePartText + resultText;
1047
+ ignoreFormatSymbolsCount = datePartText.length - partsForSegment.length;
1048
+ }
1049
+ }
1050
+ else {
1051
+ resultText = datePartText + resultText;
1052
+ formatSymbolIndexModifier = datePartText.length - 1;
1053
+ ignoreFormatSymbolsCount = datePartText.length - partsForSegment.length;
1054
+ }
1044
1055
  }
1045
1056
  else {
1046
1057
  resultText = this.dateFieldName(mask.partMap[formatSymbolIndex]) + resultText;
@@ -1049,6 +1060,9 @@ export class DateObject {
1049
1060
  while (resultFormat.length < resultText.length) {
1050
1061
  resultFormat = format[formatSymbolIndex] + resultFormat;
1051
1062
  }
1063
+ if (formatSymbolIndexModifier !== 0) {
1064
+ formatSymbolIndex = (formatSymbolIndex - formatSymbolIndexModifier) + (text.length - format.length);
1065
+ }
1052
1066
  }
1053
1067
  }
1054
1068
  return { text: resultText, format: resultFormat };
@@ -1213,4 +1227,30 @@ export class DateObject {
1213
1227
  markDatePartsAsExisting() {
1214
1228
  this.modifyExisting(true);
1215
1229
  }
1230
+ /**
1231
+ * @hidden
1232
+ */
1233
+ getPartsForSegment(mask, partIndex) {
1234
+ const segmentPart = mask.partMap[partIndex];
1235
+ const partsForSegment = [];
1236
+ for (let maskPartIndex = partIndex; maskPartIndex < mask.partMap.length; maskPartIndex++) {
1237
+ const part = mask.partMap[maskPartIndex];
1238
+ if (segmentPart.type === part.type && segmentPart.pattern === part.pattern) {
1239
+ partsForSegment.push(part);
1240
+ }
1241
+ else {
1242
+ break;
1243
+ }
1244
+ }
1245
+ for (let maskPartIndex = partIndex - 1; maskPartIndex >= 0; maskPartIndex--) {
1246
+ const part = mask.partMap[maskPartIndex];
1247
+ if (segmentPart.type === part.type && segmentPart.pattern === part.pattern) {
1248
+ partsForSegment.unshift(part);
1249
+ }
1250
+ else {
1251
+ break;
1252
+ }
1253
+ }
1254
+ return partsForSegment;
1255
+ }
1216
1256
  }
@@ -83,7 +83,7 @@ export class DateInput extends Observable {
83
83
  dateValue = null;
84
84
  }
85
85
  this.element = element;
86
- this.element._kendoWidget = this;
86
+ // this.element._kendoWidget = this;
87
87
  this.options = extend({}, defaultDateInputOptions, options);
88
88
  this.intl = this.options.intlService;
89
89
  this.formatPlaceholder = this.options.formatPlaceholder ? this.options.formatPlaceholder : 'formatPattern';
@@ -92,6 +92,7 @@ export class DateInput extends Observable {
92
92
  this.setTextAndFormat();
93
93
  this.bindEvents();
94
94
  this.resetSegmentValue = true;
95
+ this.interactionMode = DateInputInteractionMode.None;
95
96
  this.forceUpdate();
96
97
  }
97
98
  destroy() {
@@ -146,8 +147,8 @@ export class DateInput extends Observable {
146
147
  setOptions(options, refresh = false) {
147
148
  this.options = extend(this.options, options);
148
149
  if (refresh) {
149
- this.destroy();
150
- this.init(this.element, options);
150
+ this.unbindEvents();
151
+ this.init(this.element, this.options);
151
152
  }
152
153
  }
153
154
  /**
@@ -360,14 +361,10 @@ export class DateInput extends Observable {
360
361
  }
361
362
  else if (hasCaret) {
362
363
  if (this.options.format.length !== this.currentFormat.length) {
363
- if (hasDateValueChanged && isPresent(this.dateObject.value)) {
364
- const elementValueLength = this.elementValue.length;
365
- this.forceUpdate();
366
- const selectionOffset = this.elementValue.length - elementValueLength;
367
- this.setSelection({
368
- start: currentSelection.start + selectionOffset,
369
- end: currentSelection.start + selectionOffset
370
- });
364
+ if (hasDateValueChanged && isPresent(this.dateObject.getValue())) {
365
+ if (switchPart) {
366
+ this.switchDateSegment(1);
367
+ }
371
368
  }
372
369
  }
373
370
  else {
@@ -656,6 +653,14 @@ export class DateInput extends Observable {
656
653
  if (start < 0) {
657
654
  start = 0;
658
655
  }
656
+ if (!this.options.autoCorrectParts && this.currentFormat.length !== this.currentText.length) {
657
+ if (this.currentFormat.length < this.currentText.length) {
658
+ end += this.currentText.length - this.currentFormat.length;
659
+ }
660
+ else {
661
+ end = Math.max(0, end - (this.currentFormat.length - this.currentText.length));
662
+ }
663
+ }
659
664
  return { start, end };
660
665
  }
661
666
  /**
@@ -679,24 +684,36 @@ export class DateInput extends Observable {
679
684
  const selection = this.selection;
680
685
  if (this.isInCaretMode()) {
681
686
  let start = selection.start;
682
- let closestNonSeparatorSymbol = this.currentFormat[start];
683
- for (let i = start; i >= 0; i--) {
684
- closestNonSeparatorSymbol = this.currentFormat[i];
685
- if (closestNonSeparatorSymbol !== Constants.formatSeparator) {
686
- start = i;
687
- break;
687
+ const currentSymbol = this.currentFormat[start - (this.elementValue.length - this.currentFormat.length)];
688
+ let symbol = "";
689
+ let symbolCandidate = "";
690
+ if (offset < 0) {
691
+ for (let i = 0; i < start + offset; i++) {
692
+ symbolCandidate = this.currentFormat[i];
693
+ if (symbolCandidate !== Constants.formatSeparator &&
694
+ symbolCandidate !== currentSymbol) {
695
+ start = i;
696
+ symbol = symbolCandidate;
697
+ break;
698
+ }
688
699
  }
689
700
  }
690
- let symbol;
691
- for (let i = start; i < this.currentFormat.length; i++) {
692
- symbol = this.currentFormat[i];
693
- if (symbol !== Constants.formatSeparator) {
694
- break;
701
+ else {
702
+ for (let i = start + offset; i < this.currentFormat.length; i++) {
703
+ symbolCandidate = this.currentFormat[i];
704
+ if (symbolCandidate !== Constants.formatSeparator &&
705
+ symbolCandidate !== currentSymbol) {
706
+ start = i;
707
+ symbol = symbolCandidate;
708
+ break;
709
+ }
695
710
  }
696
711
  }
697
712
  if (symbol) {
698
713
  this.forceUpdate();
699
714
  this.setSelection(this.selectionBySymbol(symbol));
715
+ this.interactionMode = DateInputInteractionMode.Selection;
716
+ return;
700
717
  }
701
718
  }
702
719
  let { start: selectionStart, end: selectionEnd } = this.selection;
@@ -170,4 +170,8 @@ export declare class DateObject {
170
170
  */
171
171
  modifyDateSymbolWithValue(date: any, symbol: any, value: any): Date;
172
172
  markDatePartsAsExisting(): void;
173
+ /**
174
+ * @hidden
175
+ */
176
+ getPartsForSegment(mask: any, partIndex: any): any[];
173
177
  }