lu2
Version:
Simple and flexible UI component library based on native HTML and JavaScript
1,443 lines (1,205 loc) • 49.8 kB
JavaScript
/**
* @DateTime.js
* @author zhangxinxu
* @version
* @created: 15-07-03
* @edited: 19-11-12
* @edited: 20-07-27 edit by wanghao
* @edited: 22-06-? by zhangxinxu
*/
/**/import '../Popup/index.js';
/**/import '../ScrollSnap/index.js';
/**
* 日期,时间选择器
* 基于HTML5时间类输入框
* @example is="ui-datetime"
* @trigger 触发的元素,可以是文本框也可以是文本框容器(父级)
* @options 可选参数
*/
/**/(async () => {
/**/ if (!CSS.supports('overflow-anchor:auto') || !CSS.supports('offset:none')) {
/**/ await import('../safari-polyfill.js');
/**/ }
// 样式类名统一处理
const CL = {
toString: () => 'ui-datetime'
};
['date', 'day', 'year', 'month', 'hour', 'minute', 'time', 'second'].forEach((key) => {
CL[key] = (...args) => ['ui', key, ...args].join('-');
});
const SELECTED = 'selected';
const regDate = /-|\//g;
// 拓展方法之字符串变时间对象
String.prototype.toDate = function () {
let year, month, day;
const arrDate = this.replace(/年|月|日/g, '-').split(regDate);
// 字符串变数值
year = arrDate[0] * 1;
month = arrDate[1] || 1;
day = arrDate[2] || 1;
// 年份需要是数值字符串
if (!year) {
return new Date();
}
return new Date(year, month - 1, day);
};
String.prototype.toTime = function () {
let arrTime = this.trim().split(':').map((hm, index) => {
if (!hm || /\D/.test(hm)) {
return '';
}
if (hm < 0) {
return '00';
} else if (index === 0) {
if (hm > 23) {
hm = '23';
}
} else if (hm > 59) {
hm = '59';
}
return hm.padStart(2, '0');
}).filter(_ => _).slice(0, 3);
// 至少有时分
if (arrTime.length == 1) {
arrTime.push('00');
}
return arrTime;
};
// 日期对象变成年月日数组
Date.prototype.toArray = function () {
let year = this.getFullYear();
let month = this.getMonth() + 1;
let date = this.getDate();
if (month < 10) {
month = `0${month}`;
}
if (date < 10) {
date = `0${date}`;
}
return [year, month, date];
};
class UiDateTime extends HTMLInputElement {
constructor () {
super();
// 暴露的一些数据
// 关联的元素
this.element = this.element || {};
// 显示时间选项的容器
let eleContainer = document.createElement('div');
eleContainer.className = CL.date('container');
// 替换
this.element.container = eleContainer;
}
get min () {
let strAttrMin = this.getAttribute('min') || '';
if (strAttrMin) {
return this.minMaxConvert(strAttrMin).toString();
}
return strAttrMin;
}
set min (value) {
if (!value) {
this.removeAttribute('min');
return;
}
this.setAttribute('min', this.minMaxConvert(value));
}
get max () {
let strAttrMax = this.getAttribute('max') || '';
if (strAttrMax) {
return this.minMaxConvert(strAttrMax).toString();
}
return strAttrMax;
}
set max (value) {
if (!value) {
this.removeAttribute('max');
return;
}
this.setAttribute('max', this.minMaxConvert(value));
}
get step () {
let strStep = this.getAttribute('step');
let strType = this.params.type;
let numStep = Number(strStep);
if (strStep && /^\d+$/.test(strStep)) {
if (strType == 'time') {
if (strStep > 60) {
if (strStep % 60 != 0 || strStep / 60 > 30) {
numStep = 1;
}
} else if (numStep > 30) {
numStep = 1;
}
} else if (strType == 'hour') {
if (numStep > 12) {
numStep = 1;
}
} else if (strType == 'minute' && numStep > 30) {
numStep = 1;
}
return numStep;
}
return '';
}
set step (value) {
if (!value) {
this.removeAttribute('step');
return;
}
this.setAttribute('step', value);
}
minMaxConvert (value) {
// 直接设置为数值
if (typeof value == 'number' && Number.isInteger(value)) {
// 认为是时间戳
if (value > 10000000) {
value = new Date(value);
} else if (value < 9999) {
// 认为是年份
value = String(value);
}
}
let arrDate = [];
// value可以是Date对象
if (value.toArray) {
arrDate = value.toArray();
} else if (typeof value == 'string') {
arrDate = value.toDate().toArray();
}
let strType = this.getAttribute('type') || 'date';
// 赋值
if (strType == 'date' || strType == 'date-range') {
value = arrDate.join('-');
} else if (strType == 'year') {
value = arrDate[0];
} else if (strType == 'month' || strType == 'month-range') {
value = arrDate.slice(0, 2).join('-');
} else {
if (value.toArray) {
// 其他日期类型转换成 时:分 模式
value = value.getHours() + ':' + value.getMinutes();
}
// 补0的处理
let arrHourMin = value.toTime();
if (!arrHourMin[0]) {
return '';
}
value = arrHourMin.join(':');
}
return value;
}
setError () {
let eleContainer = this.element.container;
let strSelector = '[disabled][selected]';
// 是否超出范围的提示
eleContainer.toggleAttribute('is-error', eleContainer.querySelector(strSelector));
}
/**
*
* @returns 基于当前的选择,设置列表容器的状态
**/
setStatus () {
// 范围限制
let min = this.min;
let max = this.max;
if (!min && !max) {
return;
}
let arrMin = min.split(/\D/);
let arrMax = max.split(/\D/);
let eleContainer = this.element.container;
let strSelector = '[disabled][selected]';
// 当前日期
let arrDate = this[SELECTED];
// 类型
let type = this.params.type;
if (type == 'time' || type == 'hour' || type == 'minute') {
let strMin = min || '00:00:00';
let strMax = max || '23:59:59';
let numMinHour = Number(strMin.split(':')[0]) || 0;
let numMinMinute = Number(strMin.split(':')[1]) || 0;
let numMinSecond = Number(strMin.split(':')[2]) || 0;
let numMaxHour = Number(strMax.split(':')[0]);
if (isNaN(numMaxHour)) {
numMaxHour = 23;
}
let numMaxMinute = Number(strMax.split(':')[1]);
let numMaxSecond = Number(strMax.split(':')[2]);
if (isNaN(numMaxMinute)) {
numMaxMinute = 59;
}
if (isNaN(numMaxSecond)) {
numMaxSecond = 59;
}
// 列表容器的处理
let eleSnapMinute = eleContainer.querySelector('ui-scroll-snap[list="minute"]');
let eleSnapSecond = eleContainer.querySelector('ui-scroll-snap[list="second"]');
if (!eleSnapMinute) {
this.setError();
return;
}
// 分钟的禁用态处理
if (arrDate[0] < numMinHour || arrDate[0] > numMaxHour) {
[...eleSnapMinute.children].forEach(item => {
item.disabled = true;
});
} else if (arrDate[0] == numMinHour) {
// 小于分钟的禁用
[...eleSnapMinute.children].forEach(item => {
let minute = parseInt(item.textContent);
if (minute < numMinMinute) {
item.disabled = true;
} else {
item.disabled = false;
}
});
} else if (arrDate[0] == numMaxHour) {
// 大于分钟的禁用
[...eleSnapMinute.children].forEach(item => {
let minute = parseInt(item.textContent);
if (minute > numMaxMinute) {
item.disabled = true;
} else {
item.disabled = false;
}
});
} else {
[...eleSnapMinute.children].forEach(item => {
item.disabled = false;
});
}
// 秒的处理
if (!eleSnapSecond) {
// 是否超出范围的提示
this.setError();
return;
}
// 如果月份选中项同时禁用
if (eleSnapMinute.querySelector(strSelector)) {
[...eleSnapSecond.children].forEach(item => {
item.disabled = true;
});
} else if (arrDate[0] == numMinHour && Number(arrDate[1]) == Number(numMinMinute)) {
// 小于日期的禁用
[...eleSnapSecond.children].forEach(item => {
item.disabled = (parseInt(item.textContent) < numMinSecond);
});
} else if (arrDate[0] == numMaxHour && Number(arrDate[1]) == Number(numMaxMinute)) {
// 小于日期的禁用
[...eleSnapSecond.children].forEach(item => {
item.disabled = (parseInt(item.textContent) > numMaxSecond);
});
} else {
[...eleSnapSecond.children].forEach(item => {
item.disabled = false;
});
}
this.setError();
return;
}
// 年月日选择处理
let minYear = arrMin[0] || 0;
let maxYear = arrMax[0] || 9999;
let minMonth = arrMin[1] || 1;
let maxMonth = arrMax[1] || 12;
let minDay = arrMin[2] || 1;
let maxDay = arrMax[2] || 31;
let eleSnapMonth = eleContainer.querySelector('[list="month"]');
let eleSnapDay = eleContainer.querySelector('[list="day"]');
if (!eleSnapMonth) {
this.setError();
return;
}
// 月份的禁用与否处理
if (arrDate[0] < minYear || arrDate[1] > maxYear) {
[...eleSnapMonth.children].forEach(item => {
item.disabled = true;
});
} else if (arrDate[0] == minYear) {
// 小于月份的禁用
[...eleSnapMonth.children].forEach(item => {
let month = parseInt(item.textContent);
if (month < minMonth) {
item.disabled = true;
} else {
item.disabled = false;
}
});
} else if (arrDate[0] == maxYear) {
// 大于月份的禁用
[...eleSnapMonth.children].forEach(item => {
let month = parseInt(item.textContent);
if (month > maxMonth) {
item.disabled = true;
} else {
item.disabled = false;
}
});
} else {
[...eleSnapMonth.children].forEach(item => {
item.disabled = false;
});
}
// 如果日期禁用
if (!eleSnapDay) {
// 是否超出范围的提示
this.setError();
return;
}
// 如果月份选中项同时禁用
if (eleSnapMonth.querySelector(strSelector)) {
[...eleSnapDay.children].forEach(item => {
item.disabled = true;
});
} else if (arrDate[0] == minYear && Number(arrDate[1]) == Number(minMonth)) {
// 小于日期的禁用
[...eleSnapDay.children].forEach(item => {
let day = parseInt(item.textContent);
if (day < minDay) {
item.disabled = true;
} else {
item.disabled = false;
}
});
} else if (arrDate[0] == maxYear && Number(arrDate[1]) == Number(maxMonth)) {
// 小于日期的禁用
[...eleSnapDay.children].forEach(item => {
let day = parseInt(item.textContent);
if (day > maxDay) {
item.disabled = true;
} else {
item.disabled = false;
}
});
} else {
[...eleSnapDay.children].forEach(item => {
item.disabled = false;
});
}
this.setError();
}
/**
* 事件
* @return {[type]} [description]
*/
events () {
// 点击显示 popup
this.addEventListener('click', function () {
let popup = this.element.popup;
let type = this.params.type;
if (popup) {
popup.show();
} else {
popup = new Popup({
content: this.element.container
});
popup.addEventListener('show', () => {
if (this[type]) {
this[type]();
}
// 触发显示回调
this.show();
});
popup.addEventListener('hide', () => {
this.hide();
});
this.element.popup = popup;
}
});
return this;
}
/**
* 赋值
* @return {Object} 返回当前输入框的值
*/
setValue () {
const arrSelected = this[SELECTED];
const strValue = this.value;
switch (this.params.type) {
case 'date': {
this.value = arrSelected.join('-');
break;
}
case 'month': {
this.value = arrSelected.slice(0, 2).join('-');
break;
}
case 'year': {
this.value = arrSelected[0];
break;
}
case 'time': case 'minute': {
this.value = arrSelected.join(':');
break;
}
case 'hour': {
this.value = `${arrSelected[0]}:${arrSelected[1] || '00'}`;
break;
}
}
if (this.value != strValue) {
this.dispatchEvent(new CustomEvent('change', {
'bubbles': true
}));
}
return this.value;
}
/**
* 返回日历HTML字符串等数据的私有方法
* 因date和range浮层主结构类似,因此这里公用下
* @param {Array} arrDate 格式化为数组的日期
* @return {Object} 返回包含需要的数据的对象,生成的HTML字符内容以及最大最小月份等
*/
getYearData (arrDate) {
let year = new Date().getFullYear();
if (arrDate && arrDate[0]) {
year = arrDate[0] || year;
}
// 前后各10年
let htmlYear = '';
for (let start = year - 5; start <= year + 5; start++) {
htmlYear += `<ui-scroll-snap-item${start === year ? ' selected' : ''}>${start}年</ui-scroll-snap-item>`;
}
return htmlYear;
}
/**
* 月份组装
* @param {Array} arrDate 数组化的日期表示值
* @return {Object} 返回的是后续方法需要的数据的纯对象,包括组装的HTML字符数据,月份最大和最小值。
*/
getMonthData (arrDate) {
let month = 0;
if (arrDate && arrDate[1]) {
// 返回当前月份有几天
month = arrDate[1] - 1 || month;
}
// HTML 组装
return Array(12).fill().map((_, index) => {
return `<ui-scroll-snap-item${index === month ? ' selected' : ''}>${String(index + 1).padStart(2, '0')}月</ui-scroll-snap-item>`;
}).join('');
}
/**
* 当前日期下这一年每月最大的日期数目
* @param {Array} date 格式化为数组的日期
* @return {Array} 返回这一年每月最大的日期数目
*/
getMonthDay (date) {
let arrDate = date;
// 如果不是数组
if (typeof date != 'object' && !date.map) {
arrDate = date.toArray();
}
const arrMonthDay = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
// 如果是闰年
if (((arrDate[0] % 4 == 0) && (arrDate[0] % 100 != 0)) || (arrDate[0] % 400 == 0)) {
arrMonthDay[1] = 29;
}
return arrMonthDay;
}
/**
* 获取日期列表
*/
getDayData (arrDate) {
// 目前日期对象
let currentDate = new Date();
if (arrDate) {
currentDate = arrDate.join('-').toDate();
}
// 返回当前月份有几天
let month = currentDate.getMonth();
let fullDate = this.getMonthDay(currentDate)[month];
// 当前日期数量
let date = currentDate.getDate();
// HTML 组装
return Array(fullDate).fill().map((_, index) => {
return `<ui-scroll-snap-item${(index + 1) === date ? ' selected' : ''}>${String(index + 1).padStart(2, '0')}日</ui-scroll-snap-item>`;
}).join('');
}
/**
* 获取小时列表
*/
getHourData (arrTime) {
let numHour = new Date().getHours();
if (arrTime) {
numHour = Number(arrTime[0]);
}
let length = 24;
// 上午下午12小时制
if (this.datetimeformat == 'ah:mm') {
length = 12;
if (numHour > 12) {
numHour = numHour - 12;
}
}
let strMin = this.min || '00:00:00';
let strMax = this.max || '23:59:59';
let numMinHour = Number(strMin.split(':')[0]) || 0;
let numMaxHour = Number(strMax.split(':')[0]);
if (isNaN(numMaxHour)) {
numMaxHour = 23;
}
// HTML 组装
return Array(length).fill().map((_, index) => {
return `<ui-scroll-snap-item${index === numHour ? ' selected' : ''}${index < numMinHour || index > numMaxHour ? ' disabled' : ''}>${String(index).padStart(2, '0')}时</ui-scroll-snap-item>`;
}).join('');
}
/**
* 分钟是数据列表获取
*/
getMinuteData (value) {
let currentHour = this[SELECTED][0];
let currentMinute = this[SELECTED][1];
if (!currentMinute && currentMinute !== 0) {
currentMinute = new Date().getMinutes();
}
// 时间间隔, 默认为1秒或者1分钟
let numStep = this.step * 1 || 1;
if (numStep > 60) {
numStep = Math.floor(numStep / 60);
}
if (!value && numStep == 60) {
value = '00';
}
const strEmpty = '<ui-scroll-snap-item></ui-scroll-snap-item>';
if (typeof value != 'undefined') {
return strEmpty + `<ui-scroll-snap-item selected>${String(value).padStart(2, '0')}分</ui-scroll-snap-item>`;
}
let length = Math.floor(60 / numStep);
// 范围限制处理
let strMin = this.min;
let strMax = this.max;
if (!strMin || strMin.split(':').length < 2) {
strMin = '00:00:00';
}
if (!strMax || strMax.split(':').length < 2) {
strMin = '23:59:59';
}
let arrMin = strMin.split(':');
let arrMax = strMax.split(':');
// HTML 组装
let html = Array(length).fill().map((_, index) => {
let minute = numStep * index;
let disabled = '';
if ((Number(currentHour) == Number(arrMin[0]) && index < arrMin[1]) || (Number(currentHour) == Number(arrMax[0]) && index > arrMax[1])) {
disabled = ' disabled';
}
return `<ui-scroll-snap-item${minute === currentMinute ? ' selected' : ''}${disabled}>${String(minute).padStart(2, '0')}分</ui-scroll-snap-item>`;
}).join('');
if (length < 6) {
html = strEmpty + html + strEmpty;
if (this.element.minute) {
this.element.minute.removeAttribute('loop');
}
}
return html;
}
/**
* 秒的数据列表获取
*/
getSecondData () {
let currentSecond = this[SELECTED][2];
if (!currentSecond && currentSecond !== 0) {
currentSecond = new Date().getSeconds();
}
// 时间间隔, 默认为1秒
let numStep = this.step * 1 || 1;
if (numStep > 60) {
numStep = Math.floor(numStep / 60);
}
const strEmpty = '<ui-scroll-snap-item></ui-scroll-snap-item>';
let length = Math.floor(60 / numStep);
// 根据 step 值取整
currentSecond = Math.floor(currentSecond / numStep) * numStep;
// HTML 组装
let html = Array(length).fill().map((_, index) => {
let second = numStep * index;
return `<ui-scroll-snap-item${second === currentSecond ? ' selected' : ''}>${String(second).padStart(2, '0')}秒</ui-scroll-snap-item>`;
}).join('');
if (length < 6) {
html = strEmpty + html + strEmpty;
if (this.element.second) {
this.element.second.removeAttribute('loop');
}
}
return html;
}
/**
* 秒的数据列表获取
*/
getAmpmData () {
const strEmpty = '<ui-scroll-snap-item></ui-scroll-snap-item>';
let hour = this[SELECTED][0];
let indexMatch = 1;
if (hour * 1 < 12) {
indexMatch = 0;
}
return strEmpty + `<ui-scroll-snap-item${indexMatch === 0 ? ' selected' : ''}>上午</ui-scroll-snap-item><ui-scroll-snap-item${indexMatch === 1 ? ' selected' : ''}>下午</ui-scroll-snap-item>` + strEmpty;
}
/**
* 选择日期
* @return {Object} 返回当前DOM对象
*/
date () {
this.month();
this.day();
return this;
}
/**
* 选择月份
* @return {Object} 返回当前DOM对象
*/
month () {
// 月份选择必须年份出马
this.year();
let eleScrollMonth = this.element.month;
// 当前日期
let arrDate = this[SELECTED];
if (!eleScrollMonth) {
eleScrollMonth = document.createElement('ui-scroll-snap');
eleScrollMonth.setAttribute('type', 'y');
eleScrollMonth.setAttribute('loop', '');
eleScrollMonth.setAttribute('list', 'month');
// 事件处理
eleScrollMonth.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
arrDate[1] = detail.item.textContent.replace(/\D/g, '');
this[SELECTED] = arrDate;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollMonth.innerHTML = this.getMonthData();
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.month('container');
eleWrap.append(eleScrollMonth);
// 载入
this.element.year.parentElement.after(eleWrap);
// 暴露出去
this.element.month = eleScrollMonth;
}
// 高亮并定位选中的列表项
let eleSelected = eleScrollMonth.querySelector(':scope > [selected]');
if (eleSelected) {
eleSelected.removeAttribute('selected');
}
[...eleScrollMonth.children].some(item => {
if (item.textContent.replace(/\D/g, '') == String(arrDate[1])) {
item.selected = true;
item.position();
}
});
}
/**
* 选择日
* @return {Object} 返回当前DOM对象
*/
day () {
let eleScrollDay = this.element.day;
let eleContainer = this.element.container;
// 当前日期
let arrDate = this[SELECTED];
if (!eleScrollDay) {
eleScrollDay = document.createElement('ui-scroll-snap');
eleScrollDay.setAttribute('type', 'y');
eleScrollDay.setAttribute('loop', '');
eleScrollDay.setAttribute('list', 'day');
// 事件处理
eleScrollDay.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
arrDate[2] = detail.item.textContent.replace(/\D/g, '');
this[SELECTED] = arrDate;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollDay.innerHTML = this.getDayData();
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.day('container');
eleWrap.append(eleScrollDay);
// 载入
eleContainer.append(eleWrap);
// 暴露
this.element.day = eleScrollDay;
}
// 高亮并定位选中的列表项
let eleSelected = eleScrollDay.querySelector(':scope > [selected]');
if (eleSelected) {
eleSelected.removeAttribute('selected');
}
[...eleScrollDay.children].some(item => {
if (item.textContent.replace(/\D/g, '') == String(arrDate[2])) {
item.selected = true;
item.position();
}
});
return this;
}
/**
* 选择年份
* @return {Object} 返回当前DOM对象
*/
year () {
let eleScrollYear = this.element.year;
let eleContainer = this.element.container;
let arrDate = this[SELECTED];
// 范围限制
let min = this.min;
let max = this.max;
let minYear = min.split(/\D/)[0] || 0;
let maxYear = max.split(/\D/)[0] || 9999;
if (!eleScrollYear) {
eleScrollYear = document.createElement('ui-scroll-snap');
eleScrollYear.setAttribute('type', 'y');
eleScrollYear.setAttribute('loop', '');
eleScrollYear.setAttribute('list', 'year');
// 事件处理
eleScrollYear.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
arrDate[0] = detail.item.textContent.replace(/\D/g, '');
this[SELECTED] = arrDate;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollYear.innerHTML = this.getYearData();
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.year('container');
eleWrap.append(eleScrollYear);
// 初始化处理
eleScrollYear.onScrollInited = function (items, originItems) {
let yearStart = parseInt(originItems[0].textContent);
let lengthOrigin = originItems.length;
// 时间重新变化
items.forEach((item, index) => {
let year = yearStart - lengthOrigin + index;
item.textContent = year + '年';
// 是否禁用
item.disabled = (year < minYear || year > maxYear);
});
};
// 事件处理
eleScrollYear.onScrollStart = function (items, originItems) {
let yearStart = parseInt(items[0].textContent);
let lengthOrigin = originItems.length;
items.forEach((item, index) => {
if (index < lengthOrigin) {
let year = yearStart - lengthOrigin + index;
item.textContent = year + '年';
// 是否禁用
item.disabled = (year < minYear || year > maxYear);
}
});
originItems.forEach((item, index) => {
let year = yearStart + index;
item.textContent = year + '年';
// 是否禁用
item.disabled = (year < minYear || year > maxYear);
});
};
eleScrollYear.onScrollEnd = function (items, originItems, offset) {
// 往下无限滚动
let yearEnd = parseInt(items[items.length - 1].textContent);
// 主列表项目长度
let lengthOrigin = originItems.length;
[...items].reverse().forEach(function (item, index) {
let year = yearEnd + lengthOrigin - index;
item.textContent = year + '年';
// 是否禁用
item.disabled = (year < minYear || year > maxYear);
});
};
// 载入
eleContainer.prepend(eleWrap);
// 暴露
this.element.year = eleScrollYear;
}
// 高亮并定位选中的列表项
let eleSelected = eleScrollYear.querySelector(':scope > [selected]');
if (eleSelected) {
eleSelected.removeAttribute('selected');
}
[...eleScrollYear.children].some(item => {
if (item.textContent.replace(/\D/g, '') == String(arrDate[0])) {
item.selected = true;
item.position();
}
});
}
/**
* 选择小时
* @return {Object} 返回当前DOM对象
*/
hour () {
let eleScrollHour = this.element.hour;
let eleContainer = this.element.container;
// 当前选择时间
let arrTime = this[SELECTED];
if (!eleScrollHour) {
eleScrollHour = document.createElement('ui-scroll-snap');
eleScrollHour.setAttribute('type', 'y');
eleScrollHour.setAttribute('loop', '');
eleScrollHour.setAttribute('list', 'hour');
// 事件处理
eleScrollHour.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
arrTime[0] = detail.item.textContent.replace(/\D/g, '');
// 区分上午下午
let eleScrollAmpm = this.element.ampm;
let eleAmpmSelected = eleScrollAmpm && eleScrollAmpm.querySelector('[selected]');
if (eleAmpmSelected && eleAmpmSelected.textContent == '下午') {
arrTime[0] = arrTime[0] * 1 + 12;
}
this[SELECTED] = arrTime;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollHour.innerHTML = this.getHourData(arrTime);
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.hour('container');
eleWrap.append(eleScrollHour);
// 载入
eleContainer.append(eleWrap);
// 暴露
this.element.hour = eleScrollHour;
}
// 高亮并定位选中的列表项
let eleSelected = eleScrollHour.querySelector(':scope > [selected]');
if (eleSelected) {
eleSelected.removeAttribute('selected');
}
[...eleScrollHour.children].some(item => {
if (item.textContent.replace(/\D/g, '') == String(arrTime[0])) {
item.selected = true;
item.position();
}
});
// 如果是选择小时
if (this.params.type == 'hour') {
this.minute(arrTime[1]);
}
return this;
}
/**
* 选择时间,多垂直列表选择模式,支持到时分秒
* step如果设置,则可以选择秒
*/
time () {
// 元素
const eleContainer = this.element.container;
// 当前选择时间
const arrTime = this[SELECTED];
// 如果是 ah:mm格式
if (this.datetimeformat != 'H:mm') {
let eleScrollAmpm = this.element.ampm;
if (!eleScrollAmpm) {
eleScrollAmpm = document.createElement('ui-scroll-snap');
eleScrollAmpm.setAttribute('type', 'y');
eleScrollAmpm.setAttribute('list', 'ampm');
}
// 事件处理
eleScrollAmpm.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
if (detail.item.textContent == '上午') {
if (arrTime[0] >= 12) {
arrTime[0] -= 12;
}
} else if (arrTime[0] < 12) {
arrTime[0] = arrTime[0] * 1 + 12;
}
arrTime[0] = String(arrTime[0]).padStart(2, '0');
this[SELECTED] = arrTime;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollAmpm.innerHTML = this.getAmpmData();
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.hour('container');
eleWrap.append(eleScrollAmpm);
// 载入
eleContainer.prepend(eleWrap);
// 暴露
this.element.ampm = eleScrollAmpm;
}
this.hour().minute();
if (arrTime.length == 3) {
this.second();
}
}
/**
* 选择分钟
* @param value 固定的时间
* @return {Object} 返回当前DOM对象
*/
minute (value) {
if (this.params.type == 'minute') {
this.hour();
}
let eleScrollMinute = this.element.minute;
let eleContainer = this.element.container;
// 当前选择时间
let arrTime = this[SELECTED];
if (!eleScrollMinute) {
eleScrollMinute = document.createElement('ui-scroll-snap');
eleScrollMinute.setAttribute('type', 'y');
if (typeof value == 'undefined') {
eleScrollMinute.setAttribute('loop', '');
}
eleScrollMinute.setAttribute('list', 'minute');
// 暴露
this.element.minute = eleScrollMinute;
// 事件处理
eleScrollMinute.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
arrTime[1] = detail.item.textContent.replace(/\D/g, '');
this[SELECTED] = arrTime;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollMinute.innerHTML = this.getMinuteData(value);
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.minute('container');
eleWrap.append(eleScrollMinute);
// 载入
eleContainer.append(eleWrap);
}
// 高亮并定位选中的列表项
let eleSelected = eleScrollMinute.querySelector(':scope > [selected]');
if (eleSelected) {
eleSelected.removeAttribute('selected');
}
[...eleScrollMinute.children].some(item => {
if (item.textContent.replace(/\D/g, '') == String(arrTime[1])) {
item.selected = true;
item.position();
return true;
}
});
return this;
}
/**
* 选择秒
* @return {Object} 返回当前DOM对象
*/
second () {
let eleScrollSecond = this.element.second;
let eleContainer = this.element.container;
// 当前选择时间
let arrTime = this[SELECTED];
if (!eleScrollSecond) {
eleScrollSecond = document.createElement('ui-scroll-snap');
eleScrollSecond.setAttribute('type', 'y');
eleScrollSecond.setAttribute('loop', '');
eleScrollSecond.setAttribute('list', 'second');
// 暴露
this.element.minute = eleScrollSecond;
// 事件处理
eleScrollSecond.addEventListener('scrollEnd', event => {
let detail = event.detail;
if (detail.item) {
arrTime[2] = detail.item.textContent.replace(/\D/g, '');
this[SELECTED] = arrTime;
this.setValue();
// 错误提示
this.setStatus();
}
});
// 内容更新
eleScrollSecond.innerHTML = this.getSecondData();
// 外部的列表容器元素
let eleWrap = document.createElement('div');
eleWrap.className = CL.second('container');
eleWrap.append(eleScrollSecond);
// 载入
eleContainer.append(eleWrap);
}
// 高亮并定位选中的列表项
let eleSelected = eleScrollSecond.querySelector(':scope > [selected]');
if (eleSelected) {
eleSelected.removeAttribute('selected');
}
[...eleScrollSecond.children].some(item => {
if (item.textContent.replace(/\D/g, '') == String(arrTime[2])) {
item.selected = true;
item.position();
return true;
}
});
return this;
}
/**
* 日期选择面板的显示
* @return {Object} 当前DOM元素对象
*/
show () {
// 触发自定义事件 - show
this.dispatchEvent(new CustomEvent('show', {
detail: {
type: 'ui-datetime'
}
}));
return this;
}
/**
* 日期选择面板的隐藏
* @return {Object} 当前DOM元素对象
*/
hide () {
// 改变显示与隐藏的标志量
this.display = false;
// 触发自定义事件 - hide
this.dispatchEvent(new CustomEvent('hide', {
detail: {
type: 'ui-datetime'
}
}));
return this;
}
// 自定义组件进入页面时候
connectedCallback () {
// 普通文本类型变成日期类型
let strType = this.getAttribute('type');
if (['date', 'year', 'month', 'time', 'hour', 'minute'].includes(strType) == false) {
strType = 'date';
// 移除type属性可以和CSS中设置的尺寸的选择器匹配
if (strType) {
this.removeAttribute('type');
}
}
// 关联的参数
this.params = this.params || {};
// 输入框应用的时间选择类型
this.params.type = strType;
// readonly 文本输入框不可直接修改
this.setAttribute('readonly', 'readonly');
this.toggleAttribute('required', true);
// 初始值
let strInitValue = this.getAttribute('value') || this.value;
// 初始值转换成时间值
switch (strType) {
case 'date': case 'year': case 'month': {
// 日期
const objInitDate = strInitValue.toDate();
const arrDate = objInitDate.toArray();
// 赋值
if (strType == 'date') {
this.value = arrDate.join('-');
} else if (strType == 'year') {
this.value = arrDate[0];
} else if (strType == 'month') {
this.value = arrDate.slice(0, 2).join('-');
}
// eg. [2015,07,20]
this[SELECTED] = arrDate;
break;
}
case 'time': {
const arrTime = strInitValue.toTime();
// 当前时分秒
let dateCurrent = new Date();
let numHour = dateCurrent.getHours();
let numMinute = dateCurrent.getMinutes();
let numSecond = dateCurrent.getSeconds();
// 时间
let strHour = String(arrTime[0] || numHour).padStart(2, '0');
let strMinute = String(arrTime[1] || numMinute).padStart(2, '0');
let strSecond = String(arrTime[2] || numSecond).padStart(2, '0');
// 如果有设置合理的step值,且类型是time,则支持秒
let numStep = this.step;
if (strType == 'time' && ((!numStep && arrTime[2]) || (numStep > 0 && numStep <= 30))) {
this.value = [strHour, strMinute, strSecond].join(':');
this[SELECTED] = [strHour, strMinute, strSecond];
} else {
this.value = [strHour, strMinute].join(':');
this[SELECTED] = [strHour, strMinute];
}
// console.log(this[SELECTED]);
break;
}
case 'hour': case 'minute': {
const arrTime = strInitValue.toTime();
// 当前时分秒
let dateCurrent = new Date();
let numHour = dateCurrent.getHours();
let numMinute = dateCurrent.getMinutes();
// 时间
let strHour = String(arrTime[0] || numHour).padStart(2, '0');
let strMinute = String(arrTime[1] || numMinute).padStart(2, '0');
this.value = [strHour, strMinute].join(':');
this[SELECTED] = [strHour, strMinute];
break;
}
}
// time时间类型的是H:mm还是ah:mm的判断
if (/time/.test(strType)) {
// 默认是24小时类型,不支持time类型输入框的浏览器会使用此类型,例如Safari
this.datetimeformat = 'H:mm';
// 根据尺寸判断
let eleInputTmp = document.createElement('input');
eleInputTmp.setAttribute('type', 'time');
eleInputTmp.value = '00:00';
document.body.append(eleInputTmp);
// 默认不可见
eleInputTmp.style.position = 'absolute';
eleInputTmp.style.left = '-999px';
eleInputTmp.style.fontFamily = 'revert';
// 如果浏览器不支持time类型输入框,例如Safari,则使用H:mm格式
if (eleInputTmp.type == 'time') {
eleInputTmp.style.fontSize = '20px';
// 获取此时输入框的尺寸
let numWidthOrigin = eleInputTmp.clientWidth;
// 修改尺寸
eleInputTmp.style.fontSize = '30px';
// 比较前后尺寸变化的差异
let numDiffWidth = eleInputTmp.clientWidth - numWidthOrigin;
// 可以判断输入框字符个数
let numLetters = Math.ceil(numDiffWidth / 10);
if (numLetters >= 5) {
this.datetimeformat = 'ah:mm';
}
eleInputTmp.remove();
}
}
this.events();
// 全局事件
this.dispatchEvent(new CustomEvent('connected', {
detail: {
type: 'ui-datetime'
}
}));
this.isConnectedCallback = true;
}
}
if (!customElements.get('ui-datetime')) {
customElements.define('ui-datetime', UiDateTime, {
extends: 'input'
});
}
/**/})();