UNPKG

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
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