UNPKG

react-datepicker-cn

Version:
633 lines (597 loc) 23.2 kB
/** * 日历选择器组件 */ //require('./assets/calendar-style.css'); 'use strict'; var React = require('react'); var Calendar = { /** * 日期对象转为字符串 * @param date 日期对象 * @param type 默认 : '20150302'; 1 : '2015-03-02' */ changeDateObjToStr : function(date, type) { var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); month = month < 10 ? '0' + month : month; day = day < 10 ? '0' + day : day; var Arr = [year, month, day]; var dateStr = ''; if (!type) { dateStr = Arr.join(''); } else if (type === 1) { dateStr = Arr.join('-'); }; return dateStr; }, /** * 比较两个日期对象的先后 * return {number} 小于零:a > b 大于零:b > a 等于零:a == b */ compareTwoDate : function(a, b) { var yearA = a.getFullYear(), monthA = a.getMonth(), dateA = a.getDate(); var yearB = b.getFullYear(), monthB = b.getMonth(), dateB = b.getDate(); if (yearA !== yearB) { return (yearB - yearA); } else if (monthA !== monthB) { return (monthB - monthA); } else { return (dateB - dateA); }; }, /** * 农历1900-2100的润大小信息表 * @Array Of Property * @return Hex * 以十六进制作信息存储,以农历1987年为例:0x0af46 二进制码为 0000 1010 1111 0100 0110 一共五组数据,第五组表示这一年润几月、第一组表示这一年的 * 润月是大月还是小月(大月30天,小月29天),二,三,四组一共十二个二进制位,从左向右分别表示农历的一到十二月是大月还是小月(1:大月 0:小月) */ lunarInfo : [0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909 0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919 0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929 0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939 0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949 0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959 0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969 0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979 0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989 0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990-1999 0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009 0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019 0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029 0x05aa0,0x076a3,0x096d0,0x04bd7,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039 0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049 0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059 0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069 0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079 0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089 0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099 0x0d520],//2100 /** * 返回农历y年一整年的总天数 * @param lunar Year * @return Number * @eg:var count = calendar.lYearDays(1987) ;//count=384 */ lYearDays : function(y) { // 348 = 12 * 29 var i, sum = 348,lunarYear = Calendar.lunarInfo[y-1900]; for(i = 0x8000; i > 0x8; i = i>>1) { sum += (lunarYear & i)? 1: 0; } return(sum+Calendar.leapDays(y)); }, /** * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 * @param lunar Year * @return Number (0-12) * @eg:var leapMonth = Calendar.leapMonth(1987) ;//leapMonth=6 */ leapMonth : function(y) { return(Calendar.lunarInfo[y-1900] & 0xf); }, /** * 返回农历y年闰月的天数 若该年没有闰月则返回0 * @param lunar Year * @return Number (0、29、30) * @eg:var leapMonthDay = Calendar.leapDays(1987) ;//leapMonthDay=29 */ leapDays : function(y) { if(Calendar.leapMonth(y)) { return((Calendar.lunarInfo[y-1900] & 0x10000)? 30: 29); } return(0); }, /** * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 * @param lunar Year * @return Number (-1、29、30) * @eg:var MonthDay = Calendar.monthDays(1987,9) ;//MonthDay=29 */ monthDays : function(y,m) { if(m > 12 || m < 1) {return -1;}//月份参数从1至12,参数错误返回-1 var aa = ( (Calendar.lunarInfo[y-1900] & (0x10000>>m))? 30: 29 ); return aa; }, /** * 传入公历年月日获得详细的公历、农历object信息 <=>JSON * @param y solar year * @param m solar month * @param d solar day * @return JSON object */ solar2lunar : function (y, m, d) { //参数区间1900.1.31~2100.12.31 if(y < 2010 || y > 2030) {return -1;}//年份限定、上限 if(y === 2010 && m === 1 && d < 31) {return -1;}//下限 var objDate; if(!y) { //未传参 获得当天 objDate = new Date(); }else { objDate = new Date(y,parseInt(m, 10)-1,d); } var i, leap=0, temp=0; //修正ymd参数 y = objDate.getFullYear(); m = objDate.getMonth()+1; d = objDate.getDate(); // 得到输入日期到1900-1-31的天数 var offset = (Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate()) - Date.UTC(2010,0,31))/86400000; offset -= 14; for(i = 2010; i < 2031 && offset > 0; i++) { temp=Calendar.lYearDays(i); offset-=temp; } // 得到农历年i,并得到农历年的第offset天 if(offset<0) { offset += temp; i--; } //农历年 var year = i; leap = Calendar.leapMonth(i); //农历i年闰哪个月 var isLeap = false; //效验闰月 for(i=1; i<13 && offset>0; i++) { //闰月 if(leap > 0 && i === (leap+1) && isLeap === false){ --i; isLeap = true; temp = Calendar.leapDays(year); //计算农历闰月天数 } else{ temp = Calendar.monthDays(year, i);//计算农历普通月天数 } //解除闰月 if(isLeap === true && i === (leap+1)) { isLeap = false;} offset -= temp; } if(offset === 0 && leap>0 && i === leap+1) if(isLeap){ isLeap = false; }else{ isLeap = true; --i; } if(offset < 0){ offset += temp; --i; } //农历月 var month = i; //农历日 var day = offset + 1; return {'lYear':year,'lMonth':month,'lDay':day,'isLeap':isLeap}; }, // 日历数据 calendarData : null, // '今天'标记,用来标记‘今天’、‘明天’、‘后天’ todayStart : 0, /** * 日历内容绘制 * @param year {number} 日历年份 * @param month {number} 日历月份 * @param date {number} 日历默认选中的日期 * @param unavailableBefore {object} 日历在当前日期前不可用 */ init : function (state, props) { this.calendarData = this.getSolarMonthData(state, props); var html = this.rendarMonth(this.calendarData, state.unavailableBefore, state.unavailableAfter, props); return html; }, /** * 公历每个月份的天数普通表 * @Array Of Property * @return Number */ solarMonth : [31,28,31,30,31,30,31,31,30,31,30,31], /** * 农历的节日表 */ lunarHoliday : {'1-1' : '春节','1-15' : '元宵', '5-5' : '端午', '8-15' : '中秋'}, /** * 公历的节日表 */ solarHoliday : {'1-1' : '元旦','5-1' : '五一', '10-1' : '国庆', '12-25' : '圣诞'}, /** * 数字转中文速查表 * @Array Of Property * @return Cn string */ nStr1 : ['日','一','二','三','四','五','六','七','八','九','十','十一','十二','十三','十四','十五','十六','十七','十八','十九','廿','廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十'], /** * 公历xxxx年是否为闰年 闰年2月29天,平年2月28天 * @param year * @return Boolean */ isLeapSolarYear : function (year) { if (year%4 === 0 && year%100 !== 0 || year%400 === 0) { return true; } else { return false; } }, /** * 判断传入的日期是否是清明 * 清明节的公历日期有特定规律,2010 & 2011年是4月5日,2012 & 2013年是4月4日,2014 & 2015年又回到4月5日,以此类推 qingmingList : ['4-5','4-5','4-4','4-4','4-5','4-5','4-4','4-4','4-5','4-5','4-4','4-4'] */ isQingMing : function (year, month, date) { if (date !== 4 && date !== 5) { return false; } var d = (year - 2010)/2; var s = d%2; if (s) { return date === 4 ? true : false; } else { return date === 5 ? true : false; } }, /** * 获取公历节日 */ getSolarHoliday : function (year, month, date) { var sHoliday = this.solarHoliday[month + '-' + date]; // 清明节特殊判断 if (month === 4) { sHoliday = this.isQingMing(year, month, date) ? '清明' : ''; } return sHoliday; }, /** * 获取一个公历月的数据 * @param year 公历月所在的年份 * @param month 公历月所在的月份 */ getSolarMonthData : function (state, props) { var dataList = [], showHoliday = props.config.showHoliday, // 今天的年、月、日 tDateObj = new Date(), tYear = tDateObj.getFullYear(), tMonth = tDateObj.getMonth() + 1, tDate = tDateObj.getDate(), year = state.showedYear, month = state.showedMonth; // 该月总天数 var monthTotalDay = this.solarMonth[month - 1]; if (month === 2 && this.isLeapSolarYear(year)) { monthTotalDay++; } // 当前月标记 if (month === tMonth && year === tYear) { dataList.tmonth = true; } dataList.year = year; dataList.month = month; dataList.total = monthTotalDay; var firstDate = new Date(year, month-1, 1); var firstDay = firstDate.getDay(); var lunarData = null, tData = null, lHoliday, sHoliday, holiday; for (var d = 1; d <= monthTotalDay; d++) { if (showHoliday) { lunarData = Calendar.solar2lunar(year, month, d); lHoliday = lunarData.isLeap ? undefined : this.lunarHoliday[lunarData.lMonth + '-' + lunarData.lDay]; sHoliday = this.getSolarHoliday(year, month, d); holiday = sHoliday || lHoliday; } tData = { 'year' : year, 'month' : month, 'date' : d, 'week' : (firstDay + d - 1) % 7, 'holiday' : holiday, 'lunarData' : lunarData }; // 今天特殊标记 if (d === tDate && month === tMonth && year === tYear) { tData.today = true; tData.info = '今天'; } // 选择的日期标记 if (d === state.date && month === state.month && year === state.year) { tData.start = true; } dataList.push(tData); } return dataList; }, /** * 绘制一个月的数据 * @param monthData 要绘制月份的数据 */ rendarMonth : function (monthData, unavailableBefore, unavailableAfter, props) { var tbodyHTML, week, data, date, value, contentHTML = '<tr>', cTD = '', blankTD = '<td class="calendar-passed">&nbsp;</td>', fillWeek = [blankTD,blankTD,blankTD,blankTD,blankTD,blankTD]; var addClass = 'calendar-date-useful ', specialClass = '', dataInfo = ''; for (var i = 0; i < monthData.length; i++) { data = monthData[i]; week = data.week; // 补空第一个星期 if ((i === 0) && week !== 1) { if (week === 0) { contentHTML += fillWeek.join(''); } else { contentHTML += (fillWeek.slice(7 - week)).join(''); } } date = data.date; // 星期标记 dataInfo = '周' + this.nStr1[data.week]; // ‘明天’、‘后天’标记 if (this.todayStart === 1) { dataInfo = '明天'; this.todayStart++; } else if (this.todayStart === 2) { dataInfo = '后天'; this.todayStart = 0; } // 显示‘今天’ if (data.today) { specialClass = ''; dataInfo = '今天'; if (props.config.showToday) { date = '今天'; specialClass = 'calendar-special calendar-today'; addClass = 'calendar-date-useful '; } this.todayStart++; } // 当前选中日期 if (data.start) { specialClass += ' date-start'; } // 显示节日,节日的优先级大于'今天' if (props.config.showHoliday && data.holiday) { date = data.holiday; specialClass += ' calendar-special'; } if (week === 6 || week === 0) { specialClass += ' week' + week; } if (unavailableBefore) { var calendarDateObj = new Date(data.year, data.month-1, data.date); var compare = Calendar.compareTwoDate(calendarDateObj, unavailableBefore); if (compare > 0) { specialClass += ' date-unavailable'; }; }; if (unavailableAfter) { var calendarDateObj = new Date(data.year, data.month-1, data.date); var compare = Calendar.compareTwoDate(calendarDateObj, unavailableAfter); if (compare < 0) { specialClass += ' date-unavailable'; }; }; // 各位的月份&日期加上‘0’ var monthStr = data.month < 10 ? '0' + data.month : data.month; var dateStr = data.date < 10 ? '0' + data.date : data.date; cTD += '<td data-value="'+ data.year + '-' + monthStr + '-' + dateStr +'" data-week="' + week + '" data-info="' + dataInfo + '" class="' + addClass + specialClass + '">'+ '<span class="calendar-date-item">'+ date +'</span></td>'; if (week === 0 && i !== monthData.length) { contentHTML += cTD + '</tr><tr>'; cTD = ''; } else if (i === (monthData.length - 1)) { contentHTML += cTD + '</tr>'; } specialClass = ''; addClass = 'calendar-date-useful '; dataInfo = ''; } tbodyHTML = '<tbody><tr class="calendar-week"><th class="week1">一</th><th class="week2">二</th><th class="week3">三</th><th class="week4">四</th>'+ '<th class="week5">五</th><th class="week6">六</th><th class="week0">日</th></tr>'+ contentHTML +'</tbody>'; return tbodyHTML; } }; var ReactCalendar = React.createClass({displayName: "ReactCalendar", propTypes : { show : React.PropTypes.bool.isRequired, changeSelectedDate : React.PropTypes.func.isRequired, config : React.PropTypes.object.isRequired, // 日历不可用日期,在此日期之前不可用 unavailableBefore : React.PropTypes.object, hideCalendar : React.PropTypes.func }, getInitialState : function() { var config = this.props.config; return { // 默认选中的年、月、日 year : config.year, month : config.month, date : config.date, // 当前显示的年、月 showedYear : config.year, showedMonth : config.month, unavailableBefore : this.props.unavailableBefore, unavailableAfter : this.props.unavailableAfter, // calendar 的显示&隐藏 show : this.props.show }; }, _renderCalendar : function() { var html = Calendar.init(this.state, this.props); return html; }, handlePrev : function(e) { var date = new Date(this.state.showedYear, this.state.showedMonth-2, 1); this.setState({ showedYear : date.getFullYear(), showedMonth : date.getMonth()+1 }); }, handleNext : function(e) { var date = new Date(this.state.showedYear, this.state.showedMonth, 1); this.setState({ showedYear : date.getFullYear(), showedMonth : date.getMonth()+1 }); }, // 日历中某个日期被选中 handleSelectDate : function(e) { var target = e.target; var dateStr = ''; if (target.tagName === 'SPAN') { target = target.parentNode; }; var className = target.className; var ifUnavailableExp = /date-unavailable/g; // 如果className中包含date-unavailable 则该日期不可选 if (ifUnavailableExp.test(className)) { return; }; dateStr = target.getAttribute('data-value'); if (dateStr) { this.changeSelectedDate(dateStr, true); this.props.changeSelectedDate(dateStr); }; }, changeSelectedDate : function(dateStr, clicked) { var dateArr = dateStr.split('-'); this.setState({ year : parseInt(dateArr[0]), month : parseInt(dateArr[1]), date : parseInt(dateArr[2]) }); }, focusCalendar : function() { var that = this; // 如果calendar处于隐藏状态无法设置focus,所以设置延时等待Calendar绘制到页面 setTimeout(function(){ that.getDOMNode().focus(); },100); }, componentWillReceiveProps : function(nextProp) { this.setState({ show : nextProp.show }); if (!this.state.show && nextProp.show) { this.focusCalendar(); }; var thisConfig = this.props.config; var nextConfig = nextProp.config; var thisUnavaDate = this.props.unavailableBefore; var nextUnavaDate = nextProp.unavailableBefore; var unavailableChange = false; if (thisUnavaDate !== nextUnavaDate) { if (typeof thisUnavaDate !== typeof nextUnavaDate) { unavailableChange = true; } else { var thisDateStr = Calendar.changeDateObjToStr(thisUnavaDate); var nextDateStr = Calendar.changeDateObjToStr(nextUnavaDate); if (thisDateStr !== nextDateStr) { unavailableChange = true; } } }; if (thisConfig.year !== nextConfig.year || thisConfig.month !== nextConfig.month || thisConfig.date !== nextConfig.date || unavailableChange) { this.setState({ year : nextConfig.year, month : nextConfig.month, date : nextConfig.date, showedYear : nextConfig.year, showedMonth : nextConfig.month, unavailableBefore : nextProp.unavailableBefore, unavailableAfter : nextProp.unavailableAfter }); }; }, render : function() { var calendarHTML = ''; var year = this.state.showedYear; var month = this.state.showedMonth; calendarHTML = this._renderCalendar(); var calendarTitle = year + '-' + (month < 10 ? '0'+ month : month); var style = { display : this.state.show ? 'block' : 'none' }; return (React.createElement("div", {className: "pms-calendar", id: "react-calendar", style: style, tabIndex: 0, onBlur: this.props.hideCalendar}, React.createElement("div", {className: "calendar-wrapper"}, React.createElement("div", {className: "calendar-month"}, React.createElement("div", {className: "calendar-month-title"}, calendarTitle ), React.createElement("span", {className: "cal-month-nav nav-prev", onClick: this.handlePrev}, "<"), React.createElement("span", {className: "cal-month-nav nav-next", onClick: this.handleNext}, ">"), React.createElement("table", {dangerouslySetInnerHTML: { __html:calendarHTML}, onClick: this.handleSelectDate}) ) ) )); } }); var DatePicker = React.createClass({displayName: "DatePicker", getInitialState: function() { var nowDate = this.props.date || new Date(), year = nowDate.getFullYear(), month = (nowDate.getMonth() + 1), date = nowDate.getDate(); return { show : false, config : { year : year, month : month, date : date, showToday : this.props.showToday, showHoliday : this.props.showHoliday, showLunarDate : this.props.showLunarDate }, unavailableBefore : this.props.unavailableBefore, selectedDate : year + '-' + (month < 10 ? '0'+month : month) + '-' + date } }, changeSelectedDate: function (dateStr) { this.setState({ show : false, selectedDate : dateStr }); this.props.changeSelectedDate(dateStr); }, showCalendar: function () { if (!this.state.show && !this.props.disabled) { this.setState({ show : true }); } }, hideCalendar: function () { this.setState({ show : false }); }, render: function() { var spanClassName = 'input-normal'; spanClassName += this.state.show ? ' input-active' : ''; spanClassName += this.props.disabled ? ' input-disabled' : ''; return ( React.createElement("div", {id: "datepicker-wrapper"}, React.createElement("span", {onClick: this.showCalendar, className: spanClassName }, this.state.selectedDate), React.createElement(ReactCalendar, {show: this.state.show, changeSelectedDate: this.changeSelectedDate, config: this.state.config, unavailableBefore: this.props.unavailableBefore, unavailableAfter: this.props.unavailableAfter, hideCalendar: this.hideCalendar}) ) ); } }); module.exports = DatePicker;