UNPKG

20.7 kBJavaScriptView Raw
1// Calendar 日历 (require PrototypeDate.js)
2var Calendar = function Calendar(container, params) {
3 if (!window._seeds_lang) window._seeds_lang = {}; // 国际化数据
4 /* --------------------
5 Model
6 -------------------- */
7 var defaults = {
8 viewType: 'month', // 值为month|week
9 disableBeforeDate: null, // 禁用之前的日期
10 disableAfterDate: null, // 禁用之后的日期
11 defaultDate: null, // 默认选中的日期
12 threshold: '50',
13 duration: '300',
14 dateHeight: '40',
15 verticalDrag: true, // 是否允许垂直拖动
16
17 titleFormat: 'YYYY-MM-DD', // 格式化标题, YYYY-MM-DD 第Q季 第WW周 周EE
18 showTitleWeek: false, // 是否显示周数
19 showTitleDay: false, // 是否显示周几
20 // DOM
21 calendarClass: 'calendar',
22 disableClass: 'calendar-disabled',
23
24 headerClass: 'calendar-header',
25 titleClass: 'calendar-title',
26 prevClass: 'calendar-prev',
27 nextClass: 'calendar-next',
28 prevHTML: '&lt',
29 nextHTML: '&gt',
30
31 weekContainerClass: 'calendar-day-box',
32 weekClass: 'calendar-day',
33
34 wrapperClass: 'calendar-wrapper',
35 wrapperXClass: 'calendar-wrapper-x',
36 wrapperYClass: 'calendar-wrapper-y',
37 monthClass: 'calendar-month',
38 monthRowClass: 'calendar-monthrow',
39 dateClass: 'calendar-date',
40 dateNumClass: 'calendar-datenum',
41
42 // 状态
43 currentClass: 'calendar-current',
44 notcurrentClass: 'calendar-notcurrent',
45 todayClass: 'calendar-today',
46 activeClass: 'calendar-active',
47 selectedClass: 'calendar-selected'
48
49 /*
50 Callbacks:
51 onClick:function(Calendar)
52 onChange:function(Calendar)
53 onHeightChange:function(Calendar)// 高度变化
54 onTransitionEnd:function(Calendar)// 动画结束
55 onHorizontalTransitionEnd:function(Calendar)// 横滑动画结束
56 onVerticalTransitionEnd:function(Calendar)// 竖滑动画结束
57 onError:function(msg) // 错误回调
58 */
59 };
60 params = params || {};
61 for (var def in defaults) {
62 if (params[def] === undefined) {
63 params[def] = defaults[def];
64 }
65 }
66 var s = this;
67 s.params = params;
68 s.params.wrapperHeight = s.params.dateHeight * 6;
69 // 禁止修改默认值
70 Object.defineProperty(s.params, 'defaultDate', {
71 enumerable: true,
72 configurable: true,
73 writable: false
74 });
75
76 // 今天
77 s.today = new Date();
78 // 激活天
79 s.activeDate = new Date(s.params.defaultDate || null);
80 // 选中天
81 s.selectedDate = new Date(s.params.defaultDate || null);
82 // Container
83 s.container = typeof container === 'string' ? document.querySelector(container) : container;
84 if (!s.container) {
85 console.log('SeedsUI Error:未找到Calendar的DOM对象,请检查传入参数是否正确');
86 return;
87 }
88 s.container.width = s.container.clientWidth;
89 if (!s.container.width) s.container.width = window.innerWidth || document.documentElement.clientWidth;
90 // Header
91 s.header = null;
92 s.title = null;
93 s.prev = null;
94 s.next = null;
95 // Week
96 s.weekContainer = null;
97 s.weeks = [];
98 // Wrapper
99 s.wrapper = null;
100 s.wrapperX = null;
101 s.wrapperY = null;
102 s.months = new Array(3);
103 s.dates = [];
104 // Touch信息
105 s.touches = {
106 startX: 0,
107 startY: 0,
108 currentX: 0,
109 currentY: 0,
110 endX: 0,
111 endY: 0,
112 diffX: 0,
113 diffY: 0,
114 posX: 0,
115 posY: 0,
116 maxPosY: s.params.wrapperHeight - s.params.dateHeight,
117 h: s.params.wrapperHeight,
118 direction: 0,
119 horizontal: 0,
120 vertical: 0
121 // Header
122 };s.createHeader = function () {
123 var header = document.createElement('div');
124 header.setAttribute('class', s.params.headerClass);
125 return header;
126 };
127 s.createPrev = function () {
128 var prev = document.createElement('div');
129 prev.setAttribute('class', s.params.prevClass);
130 prev.innerHTML = s.params.prevHTML;
131 return prev;
132 };
133 s.createNext = function () {
134 var next = document.createElement('div');
135 next.setAttribute('class', s.params.nextClass);
136 next.innerHTML = s.params.nextHTML;
137 return next;
138 };
139 s.createTitle = function () {
140 var title = document.createElement('div');
141 title.setAttribute('class', s.params.titleClass);
142 return title;
143 };
144 // WeekContainer
145 s.createWeekContainer = function () {
146 var weekContainer = document.createElement('div');
147 weekContainer.setAttribute('class', s.params.weekContainerClass);
148
149 var weekNames = ['日', '一', '二', '三', '四', '五', '六'];
150 /* eslint-disable */
151 for (var i = 0, weekName; weekName = weekNames[i++];) {
152 var week = document.createElement('div');
153 week.setAttribute('class', s.params.weekClass);
154 week.innerHTML = weekName;
155 weekContainer.appendChild(week);
156 s.weeks.push(week);
157 }
158 /* eslint-enable */
159
160 return weekContainer;
161 };
162 // Wrapper
163 s.createWrapper = function () {
164 var wrapper = document.createElement('div');
165 wrapper.setAttribute('class', s.params.wrapperClass);
166 return wrapper;
167 };
168 s.createWrapperY = function () {
169 var wrapperY = document.createElement('div');
170 wrapperY.setAttribute('class', s.params.wrapperYClass);
171 return wrapperY;
172 };
173 s.createWrapperX = function () {
174 var wrapperX = document.createElement('div');
175 wrapperX.setAttribute('class', s.params.wrapperXClass);
176 wrapperX.width = s.container.width * 3;
177 wrapperX.style.width = s.container.width * 3 + 'px';
178 /*
179 wrapperX.width=s.container.width*3
180 wrapperX.style.width=wrapperX.width+'px'
181 */
182 for (var i = 0; i < 3; i++) {
183 s.months[i] = document.createElement('div');
184 s.months[i].setAttribute('class', s.params.monthClass);
185 s.months[i].style.width = s.container.width + 'px';
186 wrapperX.appendChild(s.months[i]);
187 }
188 return wrapperX;
189 };
190 s.createDates = function () {
191 for (var i = 0; i < 3; i++) {
192 // 注入到月
193 for (var j = 0; j < 6; j++) {
194 // 注入行
195 var monthRow = document.createElement('div');
196 monthRow.setAttribute('class', s.params.monthRowClass);
197
198 for (var k = 0; k < 7; k++) {
199 // 注入到星期
200 var elDate = document.createElement('div');
201 elDate.setAttribute('class', s.params.dateClass);
202 elDate.style.height = s.params.dateHeight + 'px';
203 elDate.style.lineHeight = s.params.dateHeight + 'px';
204 var elDateNum = document.createElement('div');
205 elDateNum.setAttribute('class', s.params.dateNumClass);
206
207 elDate.appendChild(elDateNum);
208 monthRow.appendChild(elDate);
209
210 s.dates.push(elDateNum);
211 }
212 s.months[i].appendChild(monthRow);
213 }
214 }
215 };
216 // 创建DOM
217 s.create = function () {
218 // 创建头部
219 if (s.container.querySelector('.' + s.params.headerClass)) {
220 s.header = s.container.querySelector('.' + s.params.headerClass);
221 s.prev = s.container.querySelector('.' + s.params.prevClass);
222 s.next = s.container.querySelector('.' + s.params.nextClass);
223 s.title = s.container.querySelector('.' + s.params.titleClass);
224 } else {
225 s.header = s.createHeader();
226 s.prev = s.createPrev();
227 s.next = s.createNext();
228 s.title = s.createTitle();
229
230 s.header.appendChild(s.prev);
231 s.header.appendChild(s.title);
232 s.header.appendChild(s.next);
233 s.container.appendChild(s.header);
234 }
235 // 创建周
236 if (s.container.querySelector('.' + s.params.weekContainerClass)) {
237 s.weekContainer = s.container.querySelector('.' + s.params.weekContainerClass);
238 } else {
239 s.weekContainer = s.createWeekContainer();
240
241 s.container.appendChild(s.weekContainer);
242 }
243 // 创建主体
244 s.wrapper = s.createWrapper();
245 s.wrapperX = s.createWrapperX();
246 s.wrapperY = s.createWrapperY();
247 s.wrapperY.appendChild(s.wrapperX);
248 s.wrapper.appendChild(s.wrapperY);
249 s.container.appendChild(s.wrapper);
250 s.createDates();
251 };
252 s.create();
253 /* --------------------
254 Method
255 -------------------- */
256 // 容器操作类
257 s.addDuration = function () {
258 s.wrapper.style.webkitTransitionDuration = s.params.duration + 'ms';
259 s.wrapperY.style.webkitTransitionDuration = s.params.duration + 'ms';
260 s.wrapperX.style.webkitTransitionDuration = s.params.duration + 'ms';
261 };
262 s.removeDuration = function () {
263 s.wrapper.style.webkitTransitionDuration = '0ms';
264 s.wrapperY.style.webkitTransitionDuration = '0ms';
265 s.wrapperX.style.webkitTransitionDuration = '0ms';
266 };
267 s.updateTranslateX = function () {
268 s.removeDuration();
269 s.touches.posX = -s.container.width;
270 s.wrapperX.style.webkitTransform = 'translateX(' + s.touches.posX + 'px)';
271 };
272 s.updateContainerHeight = function () {
273 // 更新高度
274 if (s.params.viewType === 'month') {
275 // 展开
276 s.touches.h = s.params.wrapperHeight;
277 } else if (s.params.viewType === 'week') {
278 // 收缩
279 s.touches.h = s.params.dateHeight;
280 }
281 s.wrapper.style.height = s.touches.h + 'px';
282 s.wrapperY.style.webkitTransform = 'translateY(-' + s.touches.posY + 'px)';
283 };
284 s.updateContainerWidth = function () {
285 // 更新宽度
286 s.container.width = s.container.clientWidth;
287 s.wrapperX.width = s.container.width * 3 + 'px';
288 /*
289 s.wrapperX.width=s.container.width*3
290 s.wrapperX.style.width=s.wrapperX.width.width+'px'
291 */
292 for (var i = 0; i < 3; i++) {
293 s.months[i].style.width = s.container.width + 'px';
294 }
295 };
296 s.updateContainerSize = function () {
297 s.updateContainerHeight();
298 s.updateContainerWidth();
299 s.updateTranslateX();
300 };
301 s.updateClasses = function () {
302 // 更新容器尺寸
303 s.updateContainerHeight();
304 // 位置还原
305 s.updateTranslateX();
306 };
307 s.updateClasses();
308 // 左右滑动
309 s.slideXTo = function (index) {
310 s.touches.posX = -s.container.width * index;
311 s.addDuration();
312 s.wrapperX.style.webkitTransform = 'translateX(' + s.touches.posX + 'px)';
313 // 刷新数据
314 if (index === 0) {
315 // 上一页
316 if (s.params.viewType === 'month') {
317 s.activeDate.prevMonth();
318 } else if (s.params.viewType === 'week') {
319 s.wrapperY.style.webkitTransitionDuration = '0ms';
320 s.activeDate.prevWeek();
321 }
322 } else if (index === 2) {
323 // 下一页
324 if (s.params.viewType === 'month') {
325 s.activeDate.nextMonth();
326 } else if (s.params.viewType === 'week') {
327 s.wrapperY.style.webkitTransitionDuration = '0ms';
328 s.activeDate.nextWeek();
329 }
330 }
331 /*
332 // 滑动到禁用
333 if((s.params.disableBeforeDate && s.activeDate < s.params.disableBeforeDate)||(s.params.disableAfterDate && s.activeDate > s.params.disableAfterDate)){
334 return
335 }
336 */
337 s.draw();
338 };
339 // 上下滑动
340 s.dragY = function (heightY) {
341 s.wrapper.style.height = heightY + 'px';
342 var translateY = s.params.wrapperHeight - heightY;
343 if (translateY <= s.touches.maxPosY) {
344 s.wrapperY.style.webkitTransform = 'translateY(-' + translateY + 'px)';
345 }
346 };
347 s.slideYTo = function (index) {
348 s.addDuration();
349 if (index === 1) {
350 // 展开
351 s.params.viewType = 'month';
352 s.touches.posY = 0;
353 s.draw(1);
354 } else if (index === -1) {
355 // 收缩
356 s.params.viewType = 'week';
357 s.touches.posY = s.touches.maxPosY;
358 s.draw(-1);
359 } else {
360 s.dragY(s.touches.h);
361 }
362 };
363 // 绘制日历
364 var today = new Date();
365 s.isToday = function (date) {
366 if (date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear()) return true;
367 return false;
368 };
369 s.data = [];
370 s.updateData = function () {
371 s.data = s.activeDate.getCalendarData();
372 var activeRowIndex = s.data.activeRowIndex;
373 if (s.params.viewType === 'week') {
374 s.touches.maxPosY = activeRowIndex * s.params.dateHeight;
375 s.touches.posY = s.touches.maxPosY;
376 var prevWeek = s.activeDate.getPrevWeekData();
377 var nextWeek = s.activeDate.getNextWeekData();
378 var start1 = activeRowIndex * 7;
379 var start2 = start1 + 84;
380 // 修改同行上周
381 for (var i = 0, datIndex1 = start1; i < 7; i++) {
382 s.data[datIndex1] = prevWeek[i];
383 datIndex1++;
384 }
385 // 修改同行下周
386 for (var j = 0, datIndex2 = start2; j < 7; j++) {
387 s.data[datIndex2] = nextWeek[j];
388 datIndex2++;
389 }
390 }
391 };
392 s.drawHeader = function () {
393 var activeDate = s.activeDate;
394 var titleFormatStr = s.params.titleFormat;
395 if (titleFormatStr) {
396 s.title.innerHTML = activeDate.format(titleFormatStr);
397 } else {
398 activeDate.format('YYYY-MM-DD');
399 }
400 };
401 s.draw = function (vertical) {
402 // vertical:上下拖动(-1上 | 1下 | 其它为非上下拖动)
403 // 更新选中日期
404 s.updateData();
405 // 注入身体
406 var activeIndex = s.data.activeIndex;
407 for (var i = 0; i < s.dates.length; i++) {
408 s.dates[i].innerHTML = s.data[i].getDate();
409 // index
410 s.dates[i].index = i;
411 // class
412 s.dates[i].className = s.params.dateNumClass;
413 // class-currentClass
414 if (s.data[i].isCurrent) s.dates[i].classList.add(s.params.currentClass);else s.dates[i].classList.add(s.params.notcurrentClass);
415 // class-todayClass
416 if (s.isToday(s.data[i])) s.dates[i].classList.add(s.params.todayClass);
417 // class-activeClass
418 if (i === activeIndex && s.activeDate) s.dates[i].classList.add(s.params.activeClass);
419 // class-selectedClass
420 if (s.data[i].toLocaleDateString() === s.selectedDate.toLocaleDateString()) {
421 s.dates[i].classList.add(s.params.selectedClass);
422 }
423 // 禁用日期
424 if (s.params.disableBeforeDate && s.data[i].setHours(0, 0, 0, 0) < s.params.disableBeforeDate.setHours(0, 0, 0, 0)) {
425 s.dates[i].classList.add(s.params.disableClass);
426 }
427 if (s.params.disableAfterDate && s.data[i].setHours(0, 0, 0, 0) > s.params.disableAfterDate.setHours(0, 0, 0, 0)) {
428 s.dates[i].classList.add(s.params.disableClass);
429 }
430 }
431 s.updateContainerHeight();
432 // 滑动到禁用
433 if (s.params.disableBeforeDate && s.activeDate < s.params.disableBeforeDate) {
434 var msg = '禁止访问' + s.params.disableBeforeDate.format('YYYY年MM月DD日') + '前的日期';
435 console.log('SeedsUI Warn:' + msg);
436 if (s.params.onError) s.params.onError(msg);
437 s.activeDate.nextMonth();
438 s.draw();
439 return;
440 }
441 if (s.params.disableAfterDate && s.activeDate > s.params.disableAfterDate) {
442 msg = '禁止访问' + s.params.disableAfterDate.format('YYYY年MM月DD日') + '后的日期';
443 console.log('SeedsUI Warn:' + msg);
444 if (s.params.onError) s.params.onError(msg);
445 s.activeDate.prevMonth();
446 s.draw();
447 return;
448 }
449 // 注入头部
450 s.drawHeader();
451 if (vertical) {
452 s.vertical = vertical;
453 // Callback onHeightChange
454 if (s.params.onHeightChange) s.params.onHeightChange(s);
455 } else {
456 // Callback onChange
457 if (s.params.onChange) s.params.onChange(s);
458 }
459 };
460 s.draw();
461 s.setActiveDate = function (target) {
462 for (var i = 0; i < s.dates.length; i++) {
463 s.dates[i].classList.remove(s.params.activeClass);
464 s.dates[i].classList.remove(s.params.selectedClass);
465 }
466 // 选中日期
467 s.activeDate.setTime(s.data[target.index].getTime());
468 s.selectedDate.setTime(s.data[target.index].getTime());
469 // 重新绘制
470 s.draw();
471 };
472 s.showMonth = function () {
473 s.slideYTo(1);
474 };
475 s.showWeek = function () {
476 s.slideYTo(-1);
477 };
478 s.setToday = function () {
479 s.activeDate.setTime(s.today.getTime());
480 s.draw();
481 };
482 s.setDefaultDate = function () {
483 if (!s.params.defaultDate) {
484 console.log('SeedsUI Warn: 没有设置defaultDate默认时间');
485 return;
486 }
487 // 选中日期
488 s.activeDate.setTime(s.params.defaultDate.getTime());
489 // 重新绘制
490 s.draw();
491 };
492 /* --------------------
493 Control
494 -------------------- */
495 s.events = function (detach) {
496 var action = detach ? 'removeEventListener' : 'addEventListener';
497 s.wrapper[action]('touchstart', s.onTouchStart, false);
498 s.wrapper[action]('touchmove', s.onTouchMove, false);
499 s.wrapper[action]('touchend', s.onTouchEnd, false);
500 s.wrapper[action]('touchcancel', s.onTouchEnd, false);
501 s.wrapper[action]('webkitTransitionEnd', s.onTransitionEnd, false);
502 s.wrapper[action]('click', s.onClick, false);
503
504 s.prev[action]('click', s.onClickPrev, false);
505 s.next[action]('click', s.onClickNext, false);
506 };
507 // attach、dettach事件
508 s.attach = function (event) {
509 s.events();
510 };
511 s.detach = function (event) {
512 s.events(true);
513 };
514 s.preventDefault = function (e) {
515 e.preventDefault();
516 };
517 // Event Handler
518 s.onClickPrev = function (e) {
519 s.slideXTo(0);
520 };
521 s.onClickNext = function (e) {
522 s.slideXTo(2);
523 };
524 s.onClick = function (e) {
525 s.target = e.target;
526 // 点击禁用日期
527 if (e.target.classList.contains(s.params.disableClass)) return;
528 // 点击日期
529 s.removeDuration();
530 if (e.target.classList.contains(s.params.dateNumClass)) {
531 s.setActiveDate(e.target);
532 }
533 // Callback onClick
534 if (s.params.onClick) s.params.onClick(s);
535 };
536 s.onTouchStart = function (e) {
537 e.stopPropagation();
538 s.container.addEventListener('touchmove', s.preventDefault, false);
539 s.touches.startX = e.touches[0].clientX;
540 s.touches.startY = e.touches[0].clientY;
541 };
542 s.onTouchMove = function (e) {
543 e.stopPropagation();
544 s.touches.currentX = e.touches[0].clientX;
545 s.touches.currentY = e.touches[0].clientY;
546 s.touches.diffX = s.touches.startX - s.touches.currentX;
547 s.touches.diffY = s.touches.startY - s.touches.currentY;
548
549 // 设置滑动方向(-1上下 | 1左右)
550 if (s.touches.direction === 0) {
551 s.touches.direction = Math.abs(s.touches.diffX) > Math.abs(s.touches.diffY) ? 1 : -1;
552 }
553
554 if (s.touches.direction === 1) {
555 // 左右滑动
556 var moveX = s.touches.posX - s.touches.diffX;
557 if (moveX < 0 && Math.abs(moveX) < s.container.width * 2) {
558 // 判断是否是边缘
559 s.touches.horizontal = moveX < s.touches.posX ? 1 : -1; // 设置方向(左右)
560 s.wrapperX.style.webkitTransform = 'translateX(' + moveX + 'px)';
561 }
562 } else if (s.touches.direction === -1) {
563 // 上下滑动
564 if (s.params.verticalDrag === true) {
565 // 允许Y滑动的情况下
566 var heightY = s.touches.h - s.touches.diffY;
567 if (heightY > s.params.dateHeight && heightY < s.params.wrapperHeight) {
568 // 判断是否是边缘
569 s.touches.vertical = heightY > s.touches.h ? 1 : -1; // 设置方向(上下)
570 s.dragY(heightY);
571 }
572 } else {
573 s.container.removeEventListener('touchmove', s.preventDefault, false);
574 }
575 }
576 };
577 s.onTouchEnd = function (e) {
578 e.stopPropagation();
579 if (s.touches.direction === 1) {
580 // 左右滑动
581 if (Math.abs(s.touches.diffX) < s.params.threshold) s.touches.horizontal = 0;
582 if (s.touches.horizontal === 1) s.slideXTo(2); // 下一页
583 else if (s.touches.horizontal === -1) s.slideXTo(0); // 上一页
584 else s.slideXTo(1); // 还原当前页
585 } else if (s.touches.direction === -1) {
586 // 上下滑动
587 if (s.params.verticalDrag === true) {
588 // 允许Y滑动的情况下
589 if (Math.abs(s.touches.diffY) < s.params.threshold) s.touches.vertical = 0;
590 if (s.touches.vertical === 1) s.slideYTo(1); // 展开
591 else if (s.touches.vertical === -1) s.slideYTo(-1); // 收缩
592 else s.slideYTo(0); // 还原当前页
593 }
594 }
595
596 // 清空滑动方向
597 s.touches.direction = 0;
598 s.touches.horizontal = 0;
599 s.touches.vertical = 0;
600 };
601 s.onTransitionEnd = function (e) {
602 s.target = e.target;
603 // 横向滑动时需要还原位置
604 if (s.target.classList.contains(s.params.wrapperXClass)) {
605 // 还原位置
606 s.updateTranslateX();
607 // Callback onHorizontalTransitionEnd
608 if (s.params.onHorizontalTransitionEnd) s.params.onHorizontalTransitionEnd(s);
609 }
610 // 竖向滑动
611 if (s.target.classList.contains(s.params.wrapperYClass)) {
612 // Callback onVerticalTransitionEnd
613 if (s.params.onVerticalTransitionEnd) s.params.onVerticalTransitionEnd(s);
614 }
615 // Callback onTransitionEnd
616 if (s.params.onTransitionEnd) s.params.onTransitionEnd(s);
617 };
618 /* --------------------
619 Init
620 -------------------- */
621 s.init = function () {
622 s.attach();
623 };
624 s.init();
625};
626
627export default Calendar;
\No newline at end of file