UNPKG

zmp-react

Version:

Build full featured iOS & Android apps using ZMP & React

516 lines (485 loc) 16.5 kB
import { getMonthlyDay } from './utils'; import { VALID_DATE_PICKER_COLUMN_FORMAT } from './constants'; function initPickerColumnsIndex() { const colsIndex = {}; this.columns.forEach((col, indx) => { if (col.name) { colsIndex[col.name] = indx; } }); return colsIndex; } class DateTime { constructor(config = {}) { this.config = { el: config.el || '', dateFormat: config.dateFormat || (config.dateTimePicker ? 'dd/mm/yyyy hh::mm A' : 'dd/mm/yyyy'), is24Hours: config.is24Hours || false, dateTimePicker: config.dateTimePicker || false, datePicker: config.datePicker || !config.dateTimePicker, startYear: config.startYear || 1900, endYear: config.endYear || 2200, startDate: config.startDate, endDate: config.endDate, defaultValue: config.defaultValue || null, value: config.value || null, minStep: config.minStep || 1, locale: config.locale || 'vi-VN', datePickerColumns: config.datePickerColumns, }; this.isUpdating = false; this.initializing = false; this.dateTime = config.defaultValue || config.value || new Date(); this.dayFormatter = new Intl.DateTimeFormat(this.config.locale, { day: '2-digit' }); this.monthFormatter = new Intl.DateTimeFormat(this.config.locale, { month: config.dateTimePicker ? 'short' : 'long', }); this.longMonthFormatter = new Intl.DateTimeFormat(this.config.locale, { month: 'long' }); this.shortMonthFormatter = new Intl.DateTimeFormat(this.config.locale, { month: 'short' }); this.yearFormatter = new Intl.DateTimeFormat(this.config.locale, { year: 'numeric' }); this.createPickerColumns = this.createPickerColumns.bind(this); this.setValue = this.setValue.bind(this); this.columns = this.createPickerColumns(); const initIndex = initPickerColumnsIndex.bind(this); this.pickerColumnsIndex = initIndex(); this.setDate = this.setDate.bind(this); this.setInitializing = this.setInitializing.bind(this); this.getDateRange = this.getDateRange.bind(this); this.getPosition = this.getPosition.bind(this); } setValue(values) { const is12Hours = !this.config.is24Hours; let isPM = false; if (is12Hours) { const ampmIndex = Object.keys(this.pickerColumnsIndex).findIndex((col) => col === '12Hours'); if (ampmIndex >= 0) { isPM = values[values.length - 1] === 'pm'; } } Object.keys(this.pickerColumnsIndex).forEach((key, i) => { switch (key) { case 'YYYY': this.dateTime.setFullYear(values[i]); break; case 'MM': this.dateTime.setMonth(values[i]); break; case 'DD': this.dateTime.setDate(values[i]); break; case 'hh': { let hour = parseFloat(values[i]); if (isPM) { hour = hour < 12 ? hour + 12 : hour; } else { hour = hour === 12 ? 0 : hour; } this.dateTime.setHours(hour); } break; case 'mm': this.dateTime.setMinutes(values[i]); break; default: break; } }); } setDate(date) { this.dateTime = date; } setInitializing(init) { this.initializing = init; } getValue() { return this.dateTime; } getDateRange() { const result = {}; if (this.config.startDate) { const startDate = new Date(this.config.startDate); result.startDate = startDate.getDate(); result.startMonth = startDate.getMonth() + 1; result.startYear = startDate.getFullYear(); } if (this.config.endDate) { const endDate = new Date(this.config.endDate); result.endDate = endDate.getDate(); result.endMonth = endDate.getMonth() + 1; result.endYear = endDate.getFullYear(); } return result; } // get current picked date is at start or end of range getPosition({ currentMonth, currentYear } = {}) { const { startMonth, startYear, endMonth, endYear } = this.getDateRange(); const curMonth = currentMonth || this.dateTime.getMonth() + 1; const curYear = currentYear || this.dateTime.getFullYear(); return { isStartMonth: curYear === startYear && curMonth <= startMonth, isEndMonth: curYear === endYear && curMonth >= endMonth, isStartYear: curYear <= startYear, isEndYear: curYear >= endYear, }; } getPickerValues() { const values = []; const dateTime = this.dateTime; const year = dateTime.getFullYear(); const month = dateTime.getMonth(); const day = dateTime.getDate(); const hour = dateTime.getHours(); const minutes = dateTime.getMinutes(); Object.keys(this.pickerColumnsIndex).forEach((key, i) => { switch (key) { case 'YYYY': values[i] = year; break; case 'MM': values[i] = month; break; case 'DD': values[i] = day; break; case 'hh': { if (!this.config.is24Hours) { if (hour >= 1 && hour <= 11) { values[i] = hour; } else if (hour === 0 || hour === 12) { values[i] = '12'; } else { values[i] = hour - 12; } } else { values[i] = hour; } break; } case 'mm': values[i] = minutes; break; case '12Hours': { if (hour >= 0 && hour <= 11) { values[i] = 'am'; } else { values[i] = 'pm'; } break; } default: break; } }); return values; } updateDateColumns() { const days = { values: [], displayValues: [], name: 'DD', }; const { startDate, endDate } = this.getDateRange(); const { isEndMonth, isStartMonth } = this.getPosition(); const curMonth = this.dateTime.getMonth() + 1; const curYear = this.dateTime.getFullYear(); const monthlyDays = getMonthlyDay(curYear, curMonth); const start = isStartMonth ? startDate : 1; const end = isEndMonth ? endDate : monthlyDays; for (let l = start; l <= end; l += 1) { days.values.push(l); days.displayValues.push(l); } this.columns = this.columns.map((col) => { if (col.name === 'DD') { return days; } return col; }); } updateMonthColumns() { const months = { values: [], displayValues: [], name: 'MM', }; const { startMonth, endMonth } = this.getDateRange(); const { isEndYear, isStartYear } = this.getPosition(); const start = isStartYear ? startMonth - 1 : 0; const end = isEndYear ? endMonth - 1 : 11; for (let l = start; l <= end; l += 1) { months.values.push(l); const monthName = this.monthFormatter.format(new Date('0001-01-01').setMonth(l)); months.displayValues.push(monthName); } this.columns = this.columns.map((col) => { if (col.name === 'MM') { return months; } return col; }); } createPickerColumns() { const config = this.config; const defaultPickerFormat = config.dateTimePicker ? 'YYYY-MM-DD hh:mm' : 'YYYY-MM-DD'; let customPickerFormat; if ( config.datePickerColumns && typeof config.datePickerColumns === 'string' && VALID_DATE_PICKER_COLUMN_FORMAT.includes(config.datePickerColumns.toUpperCase()) ) { customPickerFormat = config.datePickerColumns.toUpperCase(); } if (config.dateTimePicker && customPickerFormat) { customPickerFormat = `${customPickerFormat} hh:mm`; } const dateFormatArr = customPickerFormat ? customPickerFormat.split(/-|\/|\s|:/g) : defaultPickerFormat.split(/-|\/|\s|:/g); const { startDate, startMonth, startYear, endDate, endMonth, endYear } = this.getDateRange(); const formatArr = dateFormatArr; const len = formatArr.length; const columns = []; const date = this.dateTime; for (let i = 0; i < len; i += 1) { const f = formatArr[i]; if (f === 'YYYY') { const years = { values: [], displayValues: [], name: f, onChange: (picker, value) => { if (this.initializing) { return; } this.isUpdating = true; const dateIndex = this.pickerColumnsIndex.DD; const monthIndex = this.pickerColumnsIndex.MM; const pickedYear = parseInt(value, 10); const { isEndYear, isStartYear } = this.getPosition({ currentYear: pickedYear, }); let currentMonth = parseInt(picker.cols[monthIndex].value, 10) + 1; if (isStartYear && currentMonth < startMonth) { currentMonth = startMonth; } if (isEndYear && currentMonth > endMonth) { currentMonth = endMonth; } const { isStartMonth, isEndMonth } = this.getPosition({ currentYear: pickedYear, currentMonth, }); const maxDay = getMonthlyDay(pickedYear, currentMonth); let currentDay = parseInt(picker.cols[dateIndex].value, 10) > maxDay ? maxDay : parseInt(picker.cols[dateIndex].value, 10); if (isStartMonth && currentDay < startDate) { currentDay = startDate; } if (isEndMonth && currentDay > endDate) { currentDay = endDate; } this.dateTime.setFullYear(value, currentMonth - 1, currentDay); this.updateDateColumns(); this.updateMonthColumns(); if (picker.cols[monthIndex] && picker.cols[monthIndex].replaceValues) { picker.cols[monthIndex].replaceValues( this.columns[monthIndex].values, this.columns[monthIndex].displayValues, ); } if (picker.cols[monthIndex] && picker.cols[monthIndex].setValue) { picker.cols[monthIndex].setValue(currentMonth - 1, 0); } if (picker.cols[dateIndex] && picker.cols[dateIndex].replaceValues) { picker.cols[dateIndex].replaceValues( this.columns[dateIndex].values, this.columns[dateIndex].displayValues, ); } if (picker.cols[dateIndex] && picker.cols[dateIndex].setValue) { picker.cols[dateIndex].setValue(currentDay, 0); } this.isUpdating = false; }, }; const start = startYear || config.startYear; const end = endYear || config.endYear; for (let j = start; j <= end; j += 1) { years.values.push(j); years.displayValues.push(this.yearFormatter.format(new Date().setFullYear(j))); } columns.push(years); } else if (f === 'MM') { const months = { values: [], displayValues: [], name: f, onChange: (picker, value) => { if (this.initializing) { return; } const pickedMonth = parseInt(value, 10) + 1; const { isEndMonth, isStartMonth } = this.getPosition({ currentMonth: pickedMonth }); this.isUpdating = true; const index = this.pickerColumnsIndex.DD; const maxDay = getMonthlyDay(this.dateTime.getFullYear(), pickedMonth); let currentDay = picker.cols[index].value > maxDay ? maxDay : picker.cols[index].value; if (isEndMonth && currentDay >= endDate) { currentDay = endDate; } if (isStartMonth && currentDay <= startDate) { currentDay = startDate; } this.dateTime.setMonth(value, currentDay); this.updateDateColumns(); if (picker.cols[index] && picker.cols[index].replaceValues) { picker.cols[index].replaceValues( this.columns[index].values, this.columns[index].displayValues, ); } if (picker.cols[index] && picker.cols[index].setValue) { picker.cols[index].setValue(currentDay, 0); } this.isUpdating = false; }, }; const { isEndYear, isStartYear } = this.getPosition(); let start = 0; let end = 11; if (isStartYear) { start = startMonth - 1; } if (isEndYear) { end = endMonth - 1; } for (let k = start; k <= end; k += 1) { months.values.push(k); const monthName = this.monthFormatter.format(new Date('0001-01-01').setMonth(k)); months.displayValues.push(monthName); } columns.push(months); } else if (f === 'DD') { const days = { values: [], displayValues: [], name: f }; const { isEndMonth, isStartMonth } = this.getPosition(); let end = getMonthlyDay(date.getFullYear(), date.getMonth() + 1); let start = 1; if (isStartMonth) { start = startDate; } if (isEndMonth) { end = endDate; } for (let l = start; l <= end; l += 1) { days.values.push(l); days.displayValues.push(this.dayFormatter.format(new Date('0001-01-01').setDate(l))); } columns.push(days); } else if (f === 'hh') { if (config.dateTimePicker) { columns.push({ divider: true, content: '&nbsp;&nbsp;', }); } const hours = { values: [], displayValues: [], name: f, }; const maxHour = config.is24Hours ? 23 : 12; const minHour = config.is24Hours ? 0 : 1; for (let m = minHour; m <= maxHour; m += 1) { hours.values.push(m); hours.displayValues.push(m < 10 ? `0${m}` : m); } columns.push(hours); } else if (f === 'mm') { const minutes = { values: [], displayValues: [], name: f, }; for (let n = 0; n <= 59; n += config.minStep) { minutes.values.push(n); minutes.displayValues.push(n < 10 ? `0${n}` : n); } columns.push(minutes); } } if (!config.is24Hours && !config.datePicker) { columns.push({ name: '12Hours', values: ['am', 'pm'], displayValues: ['AM', 'PM'], }); } return columns; } formatDate(d) { const calendar = this; const date = new Date(d); const year = date.getFullYear(); const month = date.getMonth(); const month1 = month + 1; const day = date.getDate(); const { dateFormat, locale, dateTimePicker } = calendar.config; function twoDigits(number) { return number < 10 ? `0${number}` : number; } if (typeof dateFormat === 'string') { const tokens = { yyyy: year, yy: String(year).substring(2), mm: twoDigits(month1), m: month1, MM: this.longMonthFormatter.format(date), M: this.shortMonthFormatter.format(date), dd: twoDigits(day), d: day, }; if (dateTimePicker) { const hours = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); let hours12 = hours; if (hours > 12) hours12 = hours - 12; if (hours === 0) hours12 = 12; const a = hours >= 12 && hours !== 0 ? 'pm' : 'am'; Object.assign(tokens, { HH: twoDigits(hours), H: hours, hh: twoDigits(hours12), h: hours12, ss: twoDigits(seconds), s: seconds, ':mm': twoDigits(minutes), ':m': minutes, a, A: a.toUpperCase(), }); } const regexp = new RegExp( Object.keys(tokens) .map((t) => `(${t})`) .join('|'), 'g', ); return dateFormat.replace(regexp, (token) => { if (token in tokens) return tokens[token]; return token; }); } if (typeof dateFormat === 'function') { return dateFormat(date); } // Intl Object const formatter = new Intl.DateTimeFormat(locale, dateFormat); return formatter.format(date); } } export default DateTime;