DaysView.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import {hasProperty, pushUnique} from '../../lib/utils.js';
  2. import {today, dateValue, addDays, addWeeks, dayOfTheWeekOf, getWeek} from '../../lib/date.js';
  3. import {formatDate} from '../../lib/date-format.js';
  4. import {parseHTML, showElement, hideElement} from '../../lib/dom.js';
  5. import daysTemplate from '../templates/daysTemplate.js';
  6. import calendarWeeksTemplate from '../templates/calendarWeeksTemplate.js';
  7. import View from './View.js';
  8. export default class DaysView extends View {
  9. constructor(picker) {
  10. super(picker, {
  11. id: 0,
  12. name: 'days',
  13. cellClass: 'day',
  14. });
  15. }
  16. init(options, onConstruction = true) {
  17. if (onConstruction) {
  18. const inner = parseHTML(daysTemplate).firstChild;
  19. this.dow = inner.firstChild;
  20. this.grid = inner.lastChild;
  21. this.element.appendChild(inner);
  22. }
  23. super.init(options);
  24. }
  25. setOptions(options) {
  26. let updateDOW;
  27. if (hasProperty(options, 'minDate')) {
  28. this.minDate = options.minDate;
  29. }
  30. if (hasProperty(options, 'maxDate')) {
  31. this.maxDate = options.maxDate;
  32. }
  33. if (options.datesDisabled) {
  34. this.datesDisabled = options.datesDisabled;
  35. }
  36. if (options.daysOfWeekDisabled) {
  37. this.daysOfWeekDisabled = options.daysOfWeekDisabled;
  38. updateDOW = true;
  39. }
  40. if (options.daysOfWeekHighlighted) {
  41. this.daysOfWeekHighlighted = options.daysOfWeekHighlighted;
  42. }
  43. if (options.todayHighlight !== undefined) {
  44. this.todayHighlight = options.todayHighlight;
  45. }
  46. if (options.weekStart !== undefined) {
  47. this.weekStart = options.weekStart;
  48. this.weekEnd = options.weekEnd;
  49. updateDOW = true;
  50. }
  51. if (options.locale) {
  52. const locale = this.locale = options.locale;
  53. this.dayNames = locale.daysMin;
  54. this.switchLabelFormat = locale.titleFormat;
  55. updateDOW = true;
  56. }
  57. if (options.beforeShowDay !== undefined) {
  58. this.beforeShow = typeof options.beforeShowDay === 'function'
  59. ? options.beforeShowDay
  60. : undefined;
  61. }
  62. if (options.calendarWeeks !== undefined) {
  63. if (options.calendarWeeks && !this.calendarWeeks) {
  64. const weeksElem = parseHTML(calendarWeeksTemplate).firstChild;
  65. this.calendarWeeks = {
  66. element: weeksElem,
  67. dow: weeksElem.firstChild,
  68. weeks: weeksElem.lastChild,
  69. };
  70. this.element.insertBefore(weeksElem, this.element.firstChild);
  71. } else if (this.calendarWeeks && !options.calendarWeeks) {
  72. this.element.removeChild(this.calendarWeeks.element);
  73. this.calendarWeeks = null;
  74. }
  75. }
  76. if (options.showDaysOfWeek !== undefined) {
  77. if (options.showDaysOfWeek) {
  78. showElement(this.dow);
  79. if (this.calendarWeeks) {
  80. showElement(this.calendarWeeks.dow);
  81. }
  82. } else {
  83. hideElement(this.dow);
  84. if (this.calendarWeeks) {
  85. hideElement(this.calendarWeeks.dow);
  86. }
  87. }
  88. }
  89. // update days-of-week when locale, daysOfweekDisabled or weekStart is changed
  90. if (updateDOW) {
  91. Array.from(this.dow.children).forEach((el, index) => {
  92. const dow = (this.weekStart + index) % 7;
  93. el.textContent = this.dayNames[dow];
  94. el.className = this.daysOfWeekDisabled.includes(dow) ? 'dow disabled' : 'dow';
  95. });
  96. }
  97. }
  98. // Apply update on the focused date to view's settings
  99. updateFocus() {
  100. const viewDate = new Date(this.picker.viewDate);
  101. const viewYear = viewDate.getFullYear();
  102. const viewMonth = viewDate.getMonth();
  103. const firstOfMonth = dateValue(viewYear, viewMonth, 1);
  104. const start = dayOfTheWeekOf(firstOfMonth, this.weekStart, this.weekStart);
  105. this.first = firstOfMonth;
  106. this.last = dateValue(viewYear, viewMonth + 1, 0);
  107. this.start = start;
  108. this.focused = this.picker.viewDate;
  109. }
  110. // Apply update on the selected dates to view's settings
  111. updateSelection() {
  112. const {dates, rangepicker} = this.picker.datepicker;
  113. this.selected = dates;
  114. if (rangepicker) {
  115. this.range = rangepicker.dates;
  116. }
  117. }
  118. // Update the entire view UI
  119. render() {
  120. // update today marker on ever render
  121. this.today = this.todayHighlight ? today() : undefined;
  122. // refresh disabled dates on every render in order to clear the ones added
  123. // by beforeShow hook at previous render
  124. this.disabled = [...this.datesDisabled];
  125. const switchLabel = formatDate(this.focused, this.switchLabelFormat, this.locale);
  126. this.picker.setViewSwitchLabel(switchLabel);
  127. this.picker.setPrevBtnDisabled(this.first <= this.minDate);
  128. this.picker.setNextBtnDisabled(this.last >= this.maxDate);
  129. if (this.calendarWeeks) {
  130. // start of the UTC week (Monday) of the 1st of the month
  131. const startOfWeek = dayOfTheWeekOf(this.first, 1, 1);
  132. Array.from(this.calendarWeeks.weeks.children).forEach((el, index) => {
  133. el.textContent = getWeek(addWeeks(startOfWeek, index));
  134. });
  135. }
  136. Array.from(this.grid.children).forEach((el, index) => {
  137. const classList = el.classList;
  138. const current = addDays(this.start, index);
  139. const date = new Date(current);
  140. const day = date.getDay();
  141. el.className = `datepicker-cell ${this.cellClass}`;
  142. el.dataset.date = current;
  143. el.textContent = date.getDate();
  144. if (current < this.first) {
  145. classList.add('prev');
  146. } else if (current > this.last) {
  147. classList.add('next');
  148. }
  149. if (this.today === current) {
  150. classList.add('today');
  151. }
  152. if (current < this.minDate || current > this.maxDate || this.disabled.includes(current)) {
  153. classList.add('disabled');
  154. }
  155. if (this.daysOfWeekDisabled.includes(day)) {
  156. classList.add('disabled');
  157. pushUnique(this.disabled, current);
  158. }
  159. if (this.daysOfWeekHighlighted.includes(day)) {
  160. classList.add('highlighted');
  161. }
  162. if (this.range) {
  163. const [rangeStart, rangeEnd] = this.range;
  164. if (current > rangeStart && current < rangeEnd) {
  165. classList.add('range');
  166. }
  167. if (current === rangeStart) {
  168. classList.add('range-start');
  169. }
  170. if (current === rangeEnd) {
  171. classList.add('range-end');
  172. }
  173. }
  174. if (this.selected.includes(current)) {
  175. classList.add('selected');
  176. }
  177. if (current === this.focused) {
  178. classList.add('focused');
  179. }
  180. if (this.beforeShow) {
  181. this.performBeforeHook(el, current, current);
  182. }
  183. });
  184. }
  185. // Update the view UI by applying the changes of selected and focused items
  186. refresh() {
  187. const [rangeStart, rangeEnd] = this.range || [];
  188. this.grid
  189. .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
  190. .forEach((el) => {
  191. el.classList.remove('range', 'range-start', 'range-end', 'selected', 'focused');
  192. });
  193. Array.from(this.grid.children).forEach((el) => {
  194. const current = Number(el.dataset.date);
  195. const classList = el.classList;
  196. if (current > rangeStart && current < rangeEnd) {
  197. classList.add('range');
  198. }
  199. if (current === rangeStart) {
  200. classList.add('range-start');
  201. }
  202. if (current === rangeEnd) {
  203. classList.add('range-end');
  204. }
  205. if (this.selected.includes(current)) {
  206. classList.add('selected');
  207. }
  208. if (current === this.focused) {
  209. classList.add('focused');
  210. }
  211. });
  212. }
  213. // Update the view UI by applying the change of focused item
  214. refreshFocus() {
  215. const index = Math.round((this.focused - this.start) / 86400000);
  216. this.grid.querySelectorAll('.focused').forEach((el) => {
  217. el.classList.remove('focused');
  218. });
  219. this.grid.children[index].classList.add('focused');
  220. }
  221. }