igniteui-webcomponents
Version:
Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.
230 lines • 8.28 kB
JavaScript
import { asNumber, clamp } from '../common/util.js';
import { MaskParser } from '../mask-input/mask-parser.js';
import { createDatePart, DatePartType } from './date-part.js';
export { DatePartType as DateParts };
export { createDatePart };
const FORMAT_CHAR_TO_DATE_PART = new Map([
['d', DatePartType.Date],
['D', DatePartType.Date],
['M', DatePartType.Month],
['y', DatePartType.Year],
['Y', DatePartType.Year],
['h', DatePartType.Hours],
['H', DatePartType.Hours],
['m', DatePartType.Minutes],
['s', DatePartType.Seconds],
['S', DatePartType.Seconds],
['t', DatePartType.AmPm],
['T', DatePartType.AmPm],
]);
const DATE_FORMAT_CHARS = new Set(FORMAT_CHAR_TO_DATE_PART.keys());
const CENTURY_THRESHOLD = 50;
const CENTURY_BASE = 2000;
const DEFAULT_DATE_VALUES = {
year: 2000,
month: 0,
date: 1,
hours: 0,
minutes: 0,
seconds: 0,
};
const DEFAULT_DATETIME_FORMAT = 'MM/dd/yyyy';
export class DateTimeMaskParser extends MaskParser {
get dateParts() {
return this._dateParts;
}
constructor(options) {
const format = options?.format || DEFAULT_DATETIME_FORMAT;
super(options?.promptCharacter
? { format, promptCharacter: options.promptCharacter }
: { format });
}
set mask(value) {
super.mask = value;
this._parseDateFormat();
}
get mask() {
return super.mask;
}
_parseDateFormat() {
const format = this.mask;
const builders = [];
const chars = Array.from(format);
const length = chars.length;
let currentBuilder = null;
let position = 0;
for (let i = 0; i < length; i++, position++) {
const char = chars[i];
const partType = FORMAT_CHAR_TO_DATE_PART.get(char);
if (partType) {
if (currentBuilder?.format.includes(char)) {
currentBuilder.end = position + 1;
currentBuilder.format += char;
}
else {
if (currentBuilder) {
builders.push(currentBuilder);
}
currentBuilder = {
type: partType,
start: position,
end: position + 1,
format: char,
};
}
}
else {
if (currentBuilder) {
builders.push(currentBuilder);
currentBuilder = null;
}
builders.push({
type: DatePartType.Literal,
start: position,
end: position + 1,
format: char,
});
}
}
if (currentBuilder) {
builders.push(currentBuilder);
}
this._normalizeYearFormatBuilder(builders);
this._dateParts = builders.map((b) => createDatePart(b.type, { start: b.start, end: b.end, format: b.format }));
}
_normalizeYearFormatBuilder(builders) {
const yearBuilder = builders.find((b) => b.type === DatePartType.Year);
if (yearBuilder && yearBuilder.format.length !== 2) {
const expansion = 4 - yearBuilder.format.length;
yearBuilder.end += expansion;
yearBuilder.format = 'yyyy';
}
}
parseDate(masked) {
const parts = this._extractDateValues(masked);
if (parts[DatePartType.Month] !== undefined) {
parts[DatePartType.Month] -= 1;
}
if (parts[DatePartType.Year] !== undefined &&
parts[DatePartType.Year] < CENTURY_THRESHOLD) {
parts[DatePartType.Year] += CENTURY_BASE;
}
if (!this._validateDateParts(parts)) {
return null;
}
this._applyAmPmConversion(parts, masked);
return this._createDateFromParts(parts);
}
_extractDateValues(masked) {
const parts = {};
const prompt = this.prompt;
for (const datePart of this._dateParts) {
if (datePart.type === DatePartType.Literal)
continue;
const isMonthOrDate = datePart.type === DatePartType.Date ||
datePart.type === DatePartType.Month;
const raw = masked.substring(datePart.start, datePart.end);
const cleaned = raw.replaceAll(prompt, '');
const value = asNumber(cleaned);
parts[datePart.type] = clamp(value, isMonthOrDate ? 1 : 0, Number.MAX_SAFE_INTEGER);
}
return parts;
}
_validateDateParts(parts) {
const context = {
year: parts[DatePartType.Year],
month: parts[DatePartType.Month],
};
for (const datePart of this._dateParts) {
if (datePart.type === DatePartType.Literal)
continue;
const value = parts[datePart.type];
if (value === undefined)
continue;
if (!datePart.validate(value, context)) {
return false;
}
}
if (parts[DatePartType.Date] !== undefined &&
parts[DatePartType.Month] !== undefined &&
parts[DatePartType.Year] !== undefined) {
if (parts[DatePartType.Date] >
this._daysInMonth(parts[DatePartType.Year], parts[DatePartType.Month])) {
return false;
}
}
return true;
}
_applyAmPmConversion(parts, masked) {
const amPmPart = this._dateParts.find((p) => p.type === DatePartType.AmPm);
if (!amPmPart)
return;
parts[DatePartType.Hours] %= 12;
const amPmValue = masked
.substring(amPmPart.start, amPmPart.end)
.replaceAll(this.prompt, '');
if (amPmValue.toLowerCase() === 'pm') {
parts[DatePartType.Hours] += 12;
}
}
_createDateFromParts(parts) {
const d = DEFAULT_DATE_VALUES;
return new Date(parts[DatePartType.Year] ?? d.year, parts[DatePartType.Month] ?? d.month, parts[DatePartType.Date] ?? d.date, parts[DatePartType.Hours] ?? d.hours, parts[DatePartType.Minutes] ?? d.minutes, parts[DatePartType.Seconds] ?? d.seconds);
}
_daysInMonth(year, month) {
return new Date(year, month + 1, 0).getDate();
}
formatDate(date) {
return date
? this._dateParts.map((part) => part.getValue(date)).join('')
: this.emptyMask;
}
getDatePartAtPosition(position) {
return this._dateParts.find((p) => p.type !== DatePartType.Literal &&
position >= p.start &&
position < p.end);
}
getDatePartForCursor(position) {
return this._dateParts.find((p) => p.type !== DatePartType.Literal &&
position >= p.start &&
position <= p.end);
}
hasDateParts() {
return this._dateParts.some((p) => p.type === DatePartType.Date ||
p.type === DatePartType.Month ||
p.type === DatePartType.Year);
}
hasTimeParts() {
return this._dateParts.some((p) => p.type === DatePartType.Hours ||
p.type === DatePartType.Minutes ||
p.type === DatePartType.Seconds);
}
getFirstDatePart() {
return this._dateParts.find((p) => p.type !== DatePartType.Literal);
}
getPartByType(type) {
return this._dateParts.find((p) => p.type === type);
}
_parseMaskLiterals() {
const dateFormat = this._options.format;
const maskFormat = this._convertToMaskFormat(dateFormat);
const originalFormat = this._options.format;
this._options.format = maskFormat;
super._parseMaskLiterals();
this._options.format = originalFormat;
this._parseDateFormat();
}
_convertToMaskFormat(dateFormat) {
let result = '';
for (const char of dateFormat) {
if (DATE_FORMAT_CHARS.has(char)) {
result += char === 't' || char === 'T' ? 'L' : '0';
}
else {
result += char;
}
}
return result;
}
}
//# sourceMappingURL=datetime-mask-parser.js.map