Skip to content

Commit

Permalink
feat(date-time-editor): mask input on spinning #6271
Browse files Browse the repository at this point in the history
  • Loading branch information
jackofdiamond5 committed Mar 17, 2020
1 parent 0dee1ca commit 17466ae
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 54 deletions.
27 changes: 15 additions & 12 deletions projects/igniteui-angular/src/lib/date-picker/date-picker.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,6 @@ const enum DateParts {
Year = 'year'
}

/** @hidden */
const enum TimeParts {
Hour = 'hour',
Minute = 'minute',
Second = 'second',
AmPm = 'ampm'
}

/**
* @hidden1
*/
Expand All @@ -90,7 +82,7 @@ export abstract class DatePickerUtil {
});

if (parts[DatePart.Month] < 1 || 12 < parts[DatePart.Month]) {
return { state: DateState.Invalid, value: null };
return { state: DateState.Invalid, value: new Date(NaN) };
}

// TODO: Century threshold
Expand All @@ -99,11 +91,11 @@ export abstract class DatePickerUtil {
}

if (parts[DatePart.Date] > DatePickerUtil.daysInMonth(parts[DatePart.Year], parts[DatePart.Month])) {
return { state: DateState.Invalid, value: null };
return { state: DateState.Invalid, value: new Date(NaN) };
}

if (parts[DatePart.Hours] > 23 || parts[DatePart.Minutes] > 59 || parts[DatePart.Seconds] > 59) {
return { state: DateState.Invalid, value: null };
return { state: DateState.Invalid, value: new Date(NaN) };
}

return {
Expand Down Expand Up @@ -180,7 +172,7 @@ export abstract class DatePickerUtil {
newDate = new Date(newDate.setDate(newDate.getDate() + delta));
if (isSpinLoop) {
if (currentDate.getMonth() > newDate.getMonth()) {
return new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
return new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); // add delta instead of 1?
} else if (currentDate.getMonth() < newDate.getMonth()) {
return new Date(currentDate.setDate(1));
}
Expand Down Expand Up @@ -261,6 +253,17 @@ export abstract class DatePickerUtil {
return currentDate;
}

public static calculateAmPmOnSpin(delta: number, newDate: Date, currentDate: Date) {
newDate = delta > 0
? new Date(newDate.setHours(newDate.getHours() + 12))
: new Date(newDate.setHours(newDate.getHours() - 12));
if (newDate.getDate() !== currentDate.getDate()) {
return currentDate;
}

return newDate;
}

private static getCleanVal(inputData: string, datePart: DatePartInfo): string {
return DatePickerUtil.trimUnderlines(inputData.substring(datePart.start, datePart.end));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ fdescribe('IgxDateTimeEditor', () => {
it('Should revert to empty mask on clear()', () => {
// TODO
// should clear inner value and emit valueChanged
})
});

it('Should not block the user from typing/pasting/dragging dates outside of min/max range', () => {
// TODO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,22 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn

public clear(): void {
this.showMask('');
this.value = null;
this.valueChanged.emit({ oldValue: this._oldValue, newValue: this.value });
this.updateValue(null);
}

public increment(datePart?: DatePart): void {
const newValue = datePart ? this.calculateValueOnSpin(datePart, 1) : this.calculateValueOnSpin(this.targetDatePart, 1);
if (newValue && this.value && newValue !== this.value) {
this.updateValue(newValue);
this.updateMask();
}

// TODO: update mask
// this.updateMask();
}

public decrement(datePart?: DatePart): void {
const newValue = datePart ? this.calculateValueOnSpin(datePart, -1) : this.calculateValueOnSpin(this.targetDatePart, -1);
if (newValue && this.value && newValue !== this.value) {
this.updateValue(newValue);

// TODO: update mask
// this.updateMask(true);
this.updateMask();
}
}

Expand Down Expand Up @@ -148,9 +143,7 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn
}

if (event.ctrlKey && event.key === KEYS.SEMICOLON) {
// TODO: emit success & update mask?
this.value = new Date();
this.valueChanged.emit({ oldValue: this._oldValue, newValue: this.value });
this.updateValue(new Date());
this.updateMask();
}

Expand All @@ -172,7 +165,7 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn
}

// TODO: display value pipe
// this.updateMask(); TODO: fill in any empty date parts
// this.updateMask();
this.onTouchCallback();
super.onBlur(event);
}
Expand All @@ -181,9 +174,18 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn
protected handleInputChanged(): void {
// the mask must be updated before any date operations
super.handleInputChanged();
if (this.inputValue === this.maskParser.applyMask('', this.maskOptions)) {
this.updateValue(null);
return;
}

const parsedDate = this.parseDate(this.inputValue);
if (parsedDate.state === DateState.Valid && this.inputValue.indexOf(this.promptChar) === -1) {
this.updateValue(parsedDate.value);
if (this.inputValue.indexOf(this.promptChar) === -1) { // better way to check for filled input?
if (parsedDate.state === DateState.Valid) {
this.updateValue(parsedDate.value);
} else {
this.validationFailed.emit({ oldValue: this.value, newValue: parsedDate.value });
}
}

super.afterInput();
Expand All @@ -198,6 +200,7 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn
}

private valueInRange(value: Date): boolean {
if (!value) { return; }
const maxValueAsDate = this.isDate(this.maxValue) ? this.maxValue : this.parseDate(this.maxValue).value;
const minValueAsDate = this.isDate(this.minValue) ? this.minValue : this.parseDate(this.minValue).value;
if (maxValueAsDate && minValueAsDate) {
Expand Down Expand Up @@ -228,6 +231,8 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn
return DatePickerUtil.calculateMinutesOnSpin(delta, newDate, currentDate, this.isSpinLoop);
case DatePart.Seconds:
return DatePickerUtil.calculateSecondsOnSpin(delta, newDate, currentDate, this.isSpinLoop);
case DatePart.AmPm:
return DatePickerUtil.calculateAmPmOnSpin(delta, newDate, currentDate);
}
}

Expand All @@ -244,47 +249,54 @@ export class IgxDateTimeEditorDirective extends IgxMaskDirective implements OnIn
private updateMask() {
const cursor = this.selectionEnd;
this._dateTimeFormatParts.forEach(p => {
// TODO: cycle through the parts and update the mask based on their indices
let value: number = this.breakUpDate(p.type);
// TODO: append all date parts from the date object to one another
// if a part of that date object's length is less than the expected length (taken from the format) -> prepend prompt chars
value = p.type === DatePart.Month ? value + 1 : value;
this.inputValue = this.maskParser.replaceInMask(this.inputValue, `${value}`, this.maskOptions, p.start, p.end).value;
const partLength = p.end - p.start;
let targetValue: string = this.getMaskedValue(p.type, partLength);

if (p.type === DatePart.Month) {
targetValue = this.prependPromptChars(
parseInt(targetValue.replace(new RegExp(this.promptChar, 'g'), '0'), 10) + 1, partLength);
}

this.inputValue = this.maskParser.replaceInMask(this.inputValue, targetValue, this.maskOptions, p.start, p.end).value;
});
this.setSelectionRange(cursor);
}

private breakUpDate(datePart: DatePart): number {
const valueAsDate = this.value as Date;
private getMaskedValue(datePart: DatePart, partLength: number): string {
let maskedValue;
switch (datePart) {
case DatePart.Date:
return valueAsDate.getDate();
maskedValue = this.value.getDate();
break;
case DatePart.Month:
return valueAsDate.getMonth();
maskedValue = this.value.getMonth();
break;
case DatePart.Year:
return valueAsDate.getFullYear();
maskedValue = this.value.getFullYear();
break;
case DatePart.Hours:
return valueAsDate.getHours();
maskedValue = this.value.getHours();
break;
case DatePart.Minutes:
return valueAsDate.getMinutes();
maskedValue = this.value.getMinutes();
break;
case DatePart.Seconds:
return valueAsDate.getSeconds();
maskedValue = this.value.getSeconds();
break;
case DatePart.AmPm:
maskedValue = this.value.getHours() >= 12 ? 'PM' : 'AM';
break;
}
}

private updateMaskOnSpin(parsedValue: number, editedParts: DatePartInfo[], addPromptChar?: boolean) {
let start = editedParts[0].start;
const end = editedParts[editedParts.length - 1].start;
if (parsedValue.toString().length < editedParts.length) {
start += editedParts.length - parsedValue.toString().length;
if (addPromptChar) {
this.inputValue = this.maskParser.replaceCharAt(this.inputValue, start - 1, this.promptChar);
}
if (datePart !== DatePart.AmPm) {
return this.prependPromptChars(maskedValue, partLength);
}

this.inputValue = this.maskParser.replaceInMask(
this.inputValue, `${parsedValue} `, this.maskOptions, start, end).value;
this.setSelectionRange(this.end);
return maskedValue;
}

private prependPromptChars(value: number, partLength: number): string {
return (this.promptChar + value.toString()).slice(-partLength);
}

private spin(event: KeyboardEvent): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ export class MaskParsingService {
}
continue;
}
if (chars[0] && !this.validateCharOnPosition(chars[0], i, maskOptions.format)) {
if (chars[0]
&& !this.validateCharOnPosition(chars[0], i, maskOptions.format)
&& chars[0] !== maskOptions.promptChar) {
break;
}

Expand Down

0 comments on commit 17466ae

Please sign in to comment.