@ohos-ports/flowbite-datepicker 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +73 -0
  3. package/.stylelintrc +12 -0
  4. package/CHANGELOG.md +72 -0
  5. package/LICENSE +21 -0
  6. package/README.md +71 -0
  7. package/demo/index.html +582 -0
  8. package/demo/live-demo.js +471 -0
  9. package/dist/DateRangePicker.cjs.js +2996 -0
  10. package/dist/DateRangePicker.esm.js +2994 -0
  11. package/dist/DateRangePicker.umd.js +3002 -0
  12. package/dist/Datepicker.cjs.js +2778 -0
  13. package/dist/Datepicker.esm.js +2776 -0
  14. package/dist/Datepicker.umd.js +2784 -0
  15. package/dist/css/datepicker-bs4.css +276 -0
  16. package/dist/css/datepicker-bs4.min.css +1 -0
  17. package/dist/css/datepicker-bs5.css +276 -0
  18. package/dist/css/datepicker-bs5.min.css +1 -0
  19. package/dist/css/datepicker-bulma.css +258 -0
  20. package/dist/css/datepicker-bulma.min.css +1 -0
  21. package/dist/css/datepicker-foundation.css +262 -0
  22. package/dist/css/datepicker-foundation.min.css +1 -0
  23. package/dist/css/datepicker.css +306 -0
  24. package/dist/css/datepicker.min.css +1403 -0
  25. package/dist/js/datepicker-full.js +2777 -0
  26. package/dist/js/datepicker-full.min.js +1 -0
  27. package/dist/js/datepicker.js +2569 -0
  28. package/dist/js/datepicker.min.js +1 -0
  29. package/dist/js/locales/ar-tn.js +15 -0
  30. package/dist/js/locales/ar.js +15 -0
  31. package/dist/js/locales/az.js +14 -0
  32. package/dist/js/locales/bg.js +14 -0
  33. package/dist/js/locales/bm.js +18 -0
  34. package/dist/js/locales/bn.js +19 -0
  35. package/dist/js/locales/br.js +18 -0
  36. package/dist/js/locales/bs.js +15 -0
  37. package/dist/js/locales/ca.js +18 -0
  38. package/dist/js/locales/cs.js +19 -0
  39. package/dist/js/locales/cy.js +14 -0
  40. package/dist/js/locales/da.js +19 -0
  41. package/dist/js/locales/de.js +18 -0
  42. package/dist/js/locales/el.js +16 -0
  43. package/dist/js/locales/en-AU.js +18 -0
  44. package/dist/js/locales/en-CA.js +18 -0
  45. package/dist/js/locales/en-GB.js +18 -0
  46. package/dist/js/locales/en-IE.js +17 -0
  47. package/dist/js/locales/en-NZ.js +17 -0
  48. package/dist/js/locales/en-ZA.js +17 -0
  49. package/dist/js/locales/eo.js +17 -0
  50. package/dist/js/locales/es.js +18 -0
  51. package/dist/js/locales/et.js +18 -0
  52. package/dist/js/locales/eu.js +18 -0
  53. package/dist/js/locales/fa.js +17 -0
  54. package/dist/js/locales/fi.js +17 -0
  55. package/dist/js/locales/fo.js +15 -0
  56. package/dist/js/locales/fr-CH.js +21 -0
  57. package/dist/js/locales/fr.js +18 -0
  58. package/dist/js/locales/gl.js +16 -0
  59. package/dist/js/locales/he.js +15 -0
  60. package/dist/js/locales/hi.js +18 -0
  61. package/dist/js/locales/hr.js +13 -0
  62. package/dist/js/locales/hu.js +18 -0
  63. package/dist/js/locales/hy.js +18 -0
  64. package/dist/js/locales/id.js +15 -0
  65. package/dist/js/locales/is.js +14 -0
  66. package/dist/js/locales/it-CH.js +20 -0
  67. package/dist/js/locales/it.js +18 -0
  68. package/dist/js/locales/ja.js +17 -0
  69. package/dist/js/locales/ka.js +17 -0
  70. package/dist/js/locales/kk.js +15 -0
  71. package/dist/js/locales/km.js +15 -0
  72. package/dist/js/locales/ko.js +18 -0
  73. package/dist/js/locales/lt.js +19 -0
  74. package/dist/js/locales/lv.js +18 -0
  75. package/dist/js/locales/me.js +17 -0
  76. package/dist/js/locales/mk.js +15 -0
  77. package/dist/js/locales/mn.js +17 -0
  78. package/dist/js/locales/mr.js +18 -0
  79. package/dist/js/locales/ms.js +15 -0
  80. package/dist/js/locales/nl-BE.js +18 -0
  81. package/dist/js/locales/nl.js +18 -0
  82. package/dist/js/locales/no.js +18 -0
  83. package/dist/js/locales/oc.js +17 -0
  84. package/dist/js/locales/pl.js +17 -0
  85. package/dist/js/locales/pt-BR.js +17 -0
  86. package/dist/js/locales/pt.js +18 -0
  87. package/dist/js/locales/ro.js +17 -0
  88. package/dist/js/locales/ru.js +18 -0
  89. package/dist/js/locales/si.js +18 -0
  90. package/dist/js/locales/sk.js +18 -0
  91. package/dist/js/locales/sl.js +15 -0
  92. package/dist/js/locales/sq.js +18 -0
  93. package/dist/js/locales/sr-latn.js +16 -0
  94. package/dist/js/locales/sr.js +16 -0
  95. package/dist/js/locales/sv.js +17 -0
  96. package/dist/js/locales/sw.js +15 -0
  97. package/dist/js/locales/ta.js +18 -0
  98. package/dist/js/locales/tg.js +19 -0
  99. package/dist/js/locales/th.js +14 -0
  100. package/dist/js/locales/tk.js +18 -0
  101. package/dist/js/locales/tr.js +17 -0
  102. package/dist/js/locales/uk.js +17 -0
  103. package/dist/js/locales/uz-cyrl.js +18 -0
  104. package/dist/js/locales/uz-latn.js +18 -0
  105. package/dist/js/locales/vi.js +16 -0
  106. package/dist/js/locales/zh-CN.js +19 -0
  107. package/dist/js/locales/zh-TW.js +18 -0
  108. package/dist/locales/index.cjs.js +1028 -0
  109. package/dist/locales/index.esm.js +1028 -0
  110. package/dist/main.cjs.js +2999 -0
  111. package/dist/main.esm.js +2994 -0
  112. package/dist/main.umd.js +3005 -0
  113. package/docs/.nojekyll +0 -0
  114. package/docs/README.md +266 -0
  115. package/docs/_sidebar.md +6 -0
  116. package/docs/api.md +284 -0
  117. package/docs/date-string+format.md +90 -0
  118. package/docs/i18n.md +99 -0
  119. package/docs/images/datepicker-inline.jpg +0 -0
  120. package/docs/images/datepicker.jpg +0 -0
  121. package/docs/images/multidate.jpg +0 -0
  122. package/docs/images/picker-structure.jpg +0 -0
  123. package/docs/images/rangepicker.jpg +0 -0
  124. package/docs/images/view-days.jpg +0 -0
  125. package/docs/images/view-decades.jpg +0 -0
  126. package/docs/images/view-months.jpg +0 -0
  127. package/docs/images/view-years.jpg +0 -0
  128. package/docs/index.html +26 -0
  129. package/docs/options.md +354 -0
  130. package/docs/overview.md +174 -0
  131. package/extras/twoDigitToFullYear/test/twoDigitToFullYear.js +58 -0
  132. package/extras/twoDigitToFullYear/twoDigitToFullYear.js +26 -0
  133. package/js/DateRangePicker.js +210 -0
  134. package/js/Datepicker.js +487 -0
  135. package/js/datepicker-full.js +5 -0
  136. package/js/events/functions.js +48 -0
  137. package/js/events/inputFieldListeners.js +206 -0
  138. package/js/events/otherListeners.js +15 -0
  139. package/js/events/pickerListeners.js +70 -0
  140. package/js/i18n/base-locales.js +13 -0
  141. package/js/i18n/locales/ar-tn.js +15 -0
  142. package/js/i18n/locales/ar.js +15 -0
  143. package/js/i18n/locales/az.js +14 -0
  144. package/js/i18n/locales/bg.js +14 -0
  145. package/js/i18n/locales/bm.js +18 -0
  146. package/js/i18n/locales/bn.js +19 -0
  147. package/js/i18n/locales/br.js +18 -0
  148. package/js/i18n/locales/bs.js +15 -0
  149. package/js/i18n/locales/ca.js +18 -0
  150. package/js/i18n/locales/cs.js +19 -0
  151. package/js/i18n/locales/cy.js +14 -0
  152. package/js/i18n/locales/da.js +19 -0
  153. package/js/i18n/locales/de.js +18 -0
  154. package/js/i18n/locales/el.js +16 -0
  155. package/js/i18n/locales/en-AU.js +18 -0
  156. package/js/i18n/locales/en-CA.js +18 -0
  157. package/js/i18n/locales/en-GB.js +18 -0
  158. package/js/i18n/locales/en-IE.js +17 -0
  159. package/js/i18n/locales/en-NZ.js +17 -0
  160. package/js/i18n/locales/en-ZA.js +17 -0
  161. package/js/i18n/locales/eo.js +17 -0
  162. package/js/i18n/locales/es.js +18 -0
  163. package/js/i18n/locales/et.js +18 -0
  164. package/js/i18n/locales/eu.js +18 -0
  165. package/js/i18n/locales/fa.js +17 -0
  166. package/js/i18n/locales/fi.js +17 -0
  167. package/js/i18n/locales/fo.js +15 -0
  168. package/js/i18n/locales/fr-CH.js +21 -0
  169. package/js/i18n/locales/fr.js +18 -0
  170. package/js/i18n/locales/gl.js +16 -0
  171. package/js/i18n/locales/he.js +15 -0
  172. package/js/i18n/locales/hi.js +18 -0
  173. package/js/i18n/locales/hr.js +13 -0
  174. package/js/i18n/locales/hu.js +18 -0
  175. package/js/i18n/locales/hy.js +18 -0
  176. package/js/i18n/locales/id.js +15 -0
  177. package/js/i18n/locales/is.js +14 -0
  178. package/js/i18n/locales/it-CH.js +20 -0
  179. package/js/i18n/locales/it.js +18 -0
  180. package/js/i18n/locales/ja.js +17 -0
  181. package/js/i18n/locales/ka.js +17 -0
  182. package/js/i18n/locales/kk.js +15 -0
  183. package/js/i18n/locales/km.js +15 -0
  184. package/js/i18n/locales/ko.js +18 -0
  185. package/js/i18n/locales/lt.js +19 -0
  186. package/js/i18n/locales/lv.js +18 -0
  187. package/js/i18n/locales/me.js +17 -0
  188. package/js/i18n/locales/mk.js +15 -0
  189. package/js/i18n/locales/mn.js +17 -0
  190. package/js/i18n/locales/mr.js +18 -0
  191. package/js/i18n/locales/ms.js +15 -0
  192. package/js/i18n/locales/nl-BE.js +18 -0
  193. package/js/i18n/locales/nl.js +18 -0
  194. package/js/i18n/locales/no.js +18 -0
  195. package/js/i18n/locales/oc.js +17 -0
  196. package/js/i18n/locales/pl.js +17 -0
  197. package/js/i18n/locales/pt-BR.js +17 -0
  198. package/js/i18n/locales/pt.js +18 -0
  199. package/js/i18n/locales/ro.js +17 -0
  200. package/js/i18n/locales/ru.js +18 -0
  201. package/js/i18n/locales/si.js +18 -0
  202. package/js/i18n/locales/sk.js +18 -0
  203. package/js/i18n/locales/sl.js +15 -0
  204. package/js/i18n/locales/sq.js +18 -0
  205. package/js/i18n/locales/sr-latn.js +16 -0
  206. package/js/i18n/locales/sr.js +16 -0
  207. package/js/i18n/locales/sv.js +17 -0
  208. package/js/i18n/locales/sw.js +15 -0
  209. package/js/i18n/locales/ta.js +18 -0
  210. package/js/i18n/locales/tg.js +19 -0
  211. package/js/i18n/locales/th.js +14 -0
  212. package/js/i18n/locales/tk.js +18 -0
  213. package/js/i18n/locales/tr.js +17 -0
  214. package/js/i18n/locales/uk.js +17 -0
  215. package/js/i18n/locales/uz-cyrl.js +18 -0
  216. package/js/i18n/locales/uz-latn.js +18 -0
  217. package/js/i18n/locales/vi.js +16 -0
  218. package/js/i18n/locales/zh-CN.js +19 -0
  219. package/js/i18n/locales/zh-TW.js +18 -0
  220. package/js/lib/date-format.js +185 -0
  221. package/js/lib/date.js +82 -0
  222. package/js/lib/dom.js +55 -0
  223. package/js/lib/event.js +68 -0
  224. package/js/lib/utils.js +61 -0
  225. package/js/main.js +4 -0
  226. package/js/options/defaultOptions.js +38 -0
  227. package/js/options/processOptions.js +263 -0
  228. package/js/picker/Picker.js +334 -0
  229. package/js/picker/templates/calendarWeeksTemplate.js +8 -0
  230. package/js/picker/templates/daysTemplate.js +8 -0
  231. package/js/picker/templates/pickerTemplate.js +23 -0
  232. package/js/picker/views/DaysView.js +250 -0
  233. package/js/picker/views/MonthsView.js +198 -0
  234. package/js/picker/views/View.js +55 -0
  235. package/js/picker/views/YearsView.js +170 -0
  236. package/package.json +81 -0
  237. package/postcss.config.js +5 -0
  238. package/rollup.config.js +75 -0
  239. package/sass/_mixins.scss +12 -0
  240. package/sass/datepicker-bs4.scss +91 -0
  241. package/sass/datepicker-bs5.scss +91 -0
  242. package/sass/datepicker-bulma.scss +69 -0
  243. package/sass/datepicker-foundation.scss +66 -0
  244. package/sass/datepicker.scss +436 -0
  245. package/sass/index-bs4.scss +5 -0
  246. package/sass/index-bs5.scss +5 -0
  247. package/sass/index-bulma.scss +6 -0
  248. package/sass/index-foundation.scss +3 -0
  249. package/scripts/build-css.js +39 -0
  250. package/scripts/build-locales.js +57 -0
  251. package/scripts/convert-bsdp-locales.js +65 -0
  252. package/scripts/minify-css.js +31 -0
  253. package/scripts/minify-js.js +17 -0
  254. package/style.css +10 -0
  255. package/tailwind.config.js +16 -0
  256. package/tailwind.css +1389 -0
  257. package/test/DateRangePicker/DateRangePicker.js +547 -0
  258. package/test/DateRangePicker/api-methods.js +415 -0
  259. package/test/DateRangePicker/date-selection.js +472 -0
  260. package/test/DateRangePicker/options.js +308 -0
  261. package/test/Datepicker/Datepicker-object.js +236 -0
  262. package/test/Datepicker/api-methods.js +328 -0
  263. package/test/_utils/date.js +102 -0
  264. package/test/_utils/dom.js +70 -0
  265. package/test/events.js +785 -0
  266. package/test/inline-mode.js +45 -0
  267. package/test/keyboard-operation/arrow-down.js +293 -0
  268. package/test/keyboard-operation/arrow-left.js +255 -0
  269. package/test/keyboard-operation/arrow-right.js +215 -0
  270. package/test/keyboard-operation/arrow-up.js +461 -0
  271. package/test/keyboard-operation/edit-mode.js +458 -0
  272. package/test/keyboard-operation/keyboard-operation.js +146 -0
  273. package/test/mouse-operation.js +580 -0
  274. package/test/options/before-show.js +635 -0
  275. package/test/options/bulma-datepicker.sublime-workspace +1280 -0
  276. package/test/options/buttons.js +383 -0
  277. package/test/options/date-restrictions.js +802 -0
  278. package/test/options/format.js +281 -0
  279. package/test/options/multidate.js +380 -0
  280. package/test/options/options.js +727 -0
  281. package/test/options/orientation.js +410 -0
  282. package/test/options/pick-levle+view.js +357 -0
  283. package/test/test.html +94 -0
  284. package/test/unit/DateRangePicker.js +192 -0
  285. package/test/unit/Datepicker.js +307 -0
  286. package/test/unit/_setup.js +28 -0
  287. package/test/unit/lib/date-format.js +310 -0
  288. package/test/unit/lib/date.js +211 -0
  289. package/test/unit/lib/dom.js +142 -0
  290. package/test/unit/lib/event.js +207 -0
  291. package/test/unit/lib/utils.js +135 -0
@@ -0,0 +1,250 @@
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
+
9
+ export default class DaysView extends View {
10
+ constructor(picker) {
11
+ super(picker, {
12
+ id: 0,
13
+ name: 'days',
14
+ cellClass: 'day',
15
+ });
16
+ }
17
+
18
+ init(options, onConstruction = true) {
19
+ if (onConstruction) {
20
+ const inner = parseHTML(daysTemplate).firstChild;
21
+ this.dow = inner.firstChild;
22
+ this.grid = inner.lastChild;
23
+ this.element.appendChild(inner);
24
+ }
25
+ super.init(options);
26
+ }
27
+
28
+ setOptions(options) {
29
+ let updateDOW;
30
+
31
+ if (hasProperty(options, 'minDate')) {
32
+ this.minDate = options.minDate;
33
+ }
34
+ if (hasProperty(options, 'maxDate')) {
35
+ this.maxDate = options.maxDate;
36
+ }
37
+ if (options.datesDisabled) {
38
+ this.datesDisabled = options.datesDisabled;
39
+ }
40
+ if (options.daysOfWeekDisabled) {
41
+ this.daysOfWeekDisabled = options.daysOfWeekDisabled;
42
+ updateDOW = true;
43
+ }
44
+ if (options.daysOfWeekHighlighted) {
45
+ this.daysOfWeekHighlighted = options.daysOfWeekHighlighted;
46
+ }
47
+ if (options.todayHighlight !== undefined) {
48
+ this.todayHighlight = options.todayHighlight;
49
+ }
50
+ if (options.weekStart !== undefined) {
51
+ this.weekStart = options.weekStart;
52
+ this.weekEnd = options.weekEnd;
53
+ updateDOW = true;
54
+ }
55
+ if (options.locale) {
56
+ const locale = this.locale = options.locale;
57
+ this.dayNames = locale.daysMin;
58
+ this.switchLabelFormat = locale.titleFormat;
59
+ updateDOW = true;
60
+ }
61
+ if (options.beforeShowDay !== undefined) {
62
+ this.beforeShow = typeof options.beforeShowDay === 'function'
63
+ ? options.beforeShowDay
64
+ : undefined;
65
+ }
66
+
67
+ if (options.calendarWeeks !== undefined) {
68
+ if (options.calendarWeeks && !this.calendarWeeks) {
69
+ const weeksElem = parseHTML(calendarWeeksTemplate).firstChild;
70
+ this.calendarWeeks = {
71
+ element: weeksElem,
72
+ dow: weeksElem.firstChild,
73
+ weeks: weeksElem.lastChild,
74
+ };
75
+ this.element.insertBefore(weeksElem, this.element.firstChild);
76
+ } else if (this.calendarWeeks && !options.calendarWeeks) {
77
+ this.element.removeChild(this.calendarWeeks.element);
78
+ this.calendarWeeks = null;
79
+ }
80
+ }
81
+ if (options.showDaysOfWeek !== undefined) {
82
+ if (options.showDaysOfWeek) {
83
+ showElement(this.dow);
84
+ if (this.calendarWeeks) {
85
+ showElement(this.calendarWeeks.dow);
86
+ }
87
+ } else {
88
+ hideElement(this.dow);
89
+ if (this.calendarWeeks) {
90
+ hideElement(this.calendarWeeks.dow);
91
+ }
92
+ }
93
+ }
94
+
95
+ // update days-of-week when locale, daysOfweekDisabled or weekStart is changed
96
+ if (updateDOW) {
97
+ Array.from(this.dow.children).forEach((el, index) => {
98
+ const dow = (this.weekStart + index) % 7;
99
+ el.textContent = this.dayNames[dow];
100
+ el.className = this.daysOfWeekDisabled.includes(dow) ? 'dow disabled text-center h-6 leading-6 text-sm font-medium text-fg-disabled cursor-not-allowed' : 'dow text-center h-6 leading-6 text-sm font-medium text-body';
101
+ });
102
+ }
103
+ }
104
+
105
+ // Apply update on the focused date to view's settings
106
+ updateFocus() {
107
+ const viewDate = new Date(this.picker.viewDate);
108
+ const viewYear = viewDate.getFullYear();
109
+ const viewMonth = viewDate.getMonth();
110
+ const firstOfMonth = dateValue(viewYear, viewMonth, 1);
111
+ const start = dayOfTheWeekOf(firstOfMonth, this.weekStart, this.weekStart);
112
+
113
+ this.first = firstOfMonth;
114
+ this.last = dateValue(viewYear, viewMonth + 1, 0);
115
+ this.start = start;
116
+ this.focused = this.picker.viewDate;
117
+ }
118
+
119
+ // Apply update on the selected dates to view's settings
120
+ updateSelection() {
121
+ const {dates, rangepicker} = this.picker.datepicker;
122
+ this.selected = dates;
123
+ if (rangepicker) {
124
+ this.range = rangepicker.dates;
125
+ }
126
+ }
127
+
128
+ // Update the entire view UI
129
+ render() {
130
+ // update today marker on ever render
131
+ this.today = this.todayHighlight ? today() : undefined;
132
+ // refresh disabled dates on every render in order to clear the ones added
133
+ // by beforeShow hook at previous render
134
+ this.disabled = [...this.datesDisabled];
135
+
136
+ const switchLabel = formatDate(this.focused, this.switchLabelFormat, this.locale);
137
+ this.picker.setViewSwitchLabel(switchLabel);
138
+ this.picker.setPrevBtnDisabled(this.first <= this.minDate);
139
+ this.picker.setNextBtnDisabled(this.last >= this.maxDate);
140
+
141
+ if (this.calendarWeeks) {
142
+ // start of the UTC week (Monday) of the 1st of the month
143
+ const startOfWeek = dayOfTheWeekOf(this.first, 1, 1);
144
+ Array.from(this.calendarWeeks.weeks.children).forEach((el, index) => {
145
+ el.textContent = getWeek(addWeeks(startOfWeek, index));
146
+ });
147
+ }
148
+ Array.from(this.grid.children).forEach((el, index) => {
149
+ const classList = el.classList;
150
+ const current = addDays(this.start, index);
151
+ const date = new Date(current);
152
+ const day = date.getDay();
153
+
154
+ el.className = `datepicker-cell hover:bg-neutral-tertiary-medium block flex-1 leading-9 border-0 rounded-base cursor-pointer text-center text-body font-medium text-sm ${this.cellClass}`;
155
+ el.dataset.date = current;
156
+ el.textContent = date.getDate();
157
+
158
+ if (current < this.first) {
159
+ classList.add('prev', 'text-fg-disabled');
160
+ } else if (current > this.last) {
161
+ classList.add('next', 'text-fg-disabled');
162
+ }
163
+ if (this.today === current) {
164
+ classList.add('today', 'bg-gray-100', 'dark:bg-gray-600');
165
+ }
166
+ if (current < this.minDate || current > this.maxDate || this.disabled.includes(current)) {
167
+ classList.add('disabled', 'cursor-not-allowed', 'text-fg-disabled');
168
+ classList.remove('hover:bg-neutral-tertiary-medium', 'text-body', 'cursor-pointer');
169
+ }
170
+ if (this.daysOfWeekDisabled.includes(day)) {
171
+ classList.add('disabled', 'cursor-not-allowed', 'text-fg-disabled');
172
+ classList.remove('hover:bg-neutral-tertiary-medium', 'text-body', 'cursor-pointer');
173
+ pushUnique(this.disabled, current);
174
+ }
175
+ if (this.daysOfWeekHighlighted.includes(day)) {
176
+ classList.add('highlighted');
177
+ }
178
+ if (this.range) {
179
+ const [rangeStart, rangeEnd] = this.range;
180
+ if (current > rangeStart && current < rangeEnd) {
181
+ classList.add('range', 'bg-neutral-tertiary-medium');
182
+ classList.remove('rounded-base', 'rounded-s-base', 'rounded-e-base')
183
+ }
184
+ if (current === rangeStart) {
185
+ classList.add('range-start', 'bg-brand', 'rounded-s-base');
186
+ classList.remove('rounded-base', 'rounded-e-base');
187
+ }
188
+ if (current === rangeEnd) {
189
+ classList.add('range-end', 'bg-neutral-tertiary-medium', 'rounded-e-base');
190
+ classList.remove('rounded-base', 'rounded-s-base');
191
+ }
192
+ }
193
+ if (this.selected.includes(current)) {
194
+ classList.add('selected', 'bg-brand', 'text-white');
195
+ classList.remove('text-body', 'hover:bg-neutral-tertiary-medium', 'bg-neutral-tertiary-medium');
196
+ }
197
+ if (current === this.focused) {
198
+ classList.add('focused');
199
+ }
200
+
201
+ if (this.beforeShow) {
202
+ this.performBeforeHook(el, current, current);
203
+ }
204
+ });
205
+ }
206
+
207
+ // Update the view UI by applying the changes of selected and focused items
208
+ refresh() {
209
+ const [rangeStart, rangeEnd] = this.range || [];
210
+ this.grid
211
+ .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
212
+ .forEach((el) => {
213
+ el.classList.remove('range', 'range-start', 'range-end', 'selected', 'bg-brand', 'text-white', 'focused');
214
+ el.classList.add('text-body', 'rounded-base');
215
+ });
216
+ Array.from(this.grid.children).forEach((el) => {
217
+ const current = Number(el.dataset.date);
218
+ const classList = el.classList;
219
+ classList.remove('bg-neutral-tertiary-medium', 'rounded-s-base', 'rounded-e-base')
220
+ if (current > rangeStart && current < rangeEnd) {
221
+ classList.add('range', 'bg-neutral-tertiary-medium');
222
+ classList.remove('rounded-base');
223
+ }
224
+ if (current === rangeStart) {
225
+ classList.add('range-start', 'bg-brand', 'text-white', 'rounded-s-base');
226
+ classList.remove('rounded-base');
227
+ }
228
+ if (current === rangeEnd) {
229
+ classList.add('range-end', 'bg-neutral-tertiary-medium', 'rounded-e-base');
230
+ classList.remove('rounded-base');
231
+ }
232
+ if (this.selected.includes(current)) {
233
+ classList.add('selected', 'bg-brand', 'text-white');
234
+ classList.remove('text-body', 'hover:bg-neutral-tertiary-medium', 'bg-neutral-tertiary-medium');
235
+ }
236
+ if (current === this.focused) {
237
+ classList.add('focused');
238
+ }
239
+ });
240
+ }
241
+
242
+ // Update the view UI by applying the change of focused item
243
+ refreshFocus() {
244
+ const index = Math.round((this.focused - this.start) / 86400000);
245
+ this.grid.querySelectorAll('.focused').forEach((el) => {
246
+ el.classList.remove('focused');
247
+ });
248
+ this.grid.children[index].classList.add('focused');
249
+ }
250
+ }
@@ -0,0 +1,198 @@
1
+ import {hasProperty, pushUnique, createTagRepeat} from '../../lib/utils.js';
2
+ import {dateValue} from '../../lib/date.js';
3
+ import {parseHTML} from '../../lib/dom.js';
4
+ import View from './View.js';
5
+
6
+ function computeMonthRange(range, thisYear) {
7
+ if (!range || !range[0] || !range[1]) {
8
+ return;
9
+ }
10
+
11
+ const [[startY, startM], [endY, endM]] = range;
12
+ if (startY > thisYear || endY < thisYear) {
13
+ return;
14
+ }
15
+ return [
16
+ startY === thisYear ? startM : -1,
17
+ endY === thisYear ? endM : 12,
18
+ ];
19
+ }
20
+
21
+ export default class MonthsView extends View {
22
+ constructor(picker) {
23
+ super(picker, {
24
+ id: 1,
25
+ name: 'months',
26
+ cellClass: 'month',
27
+ });
28
+ }
29
+
30
+ init(options, onConstruction = true) {
31
+ if (onConstruction) {
32
+ this.grid = this.element;
33
+ this.element.classList.add('months', 'datepicker-grid', 'w-64', 'grid', 'grid-cols-4');
34
+ this.grid.appendChild(parseHTML(createTagRepeat('span', 12, {'data-month': ix => ix})));
35
+ }
36
+ super.init(options);
37
+ }
38
+
39
+ setOptions(options) {
40
+ if (options.locale) {
41
+ this.monthNames = options.locale.monthsShort;
42
+ }
43
+ if (hasProperty(options, 'minDate')) {
44
+ if (options.minDate === undefined) {
45
+ this.minYear = this.minMonth = this.minDate = undefined;
46
+ } else {
47
+ const minDateObj = new Date(options.minDate);
48
+ this.minYear = minDateObj.getFullYear();
49
+ this.minMonth = minDateObj.getMonth();
50
+ this.minDate = minDateObj.setDate(1);
51
+ }
52
+ }
53
+ if (hasProperty(options, 'maxDate')) {
54
+ if (options.maxDate === undefined) {
55
+ this.maxYear = this.maxMonth = this.maxDate = undefined;
56
+ } else {
57
+ const maxDateObj = new Date(options.maxDate);
58
+ this.maxYear = maxDateObj.getFullYear();
59
+ this.maxMonth = maxDateObj.getMonth();
60
+ this.maxDate = dateValue(this.maxYear, this.maxMonth + 1, 0);
61
+ }
62
+ }
63
+ if (options.beforeShowMonth !== undefined) {
64
+ this.beforeShow = typeof options.beforeShowMonth === 'function'
65
+ ? options.beforeShowMonth
66
+ : undefined;
67
+ }
68
+ }
69
+
70
+ // Update view's settings to reflect the viewDate set on the picker
71
+ updateFocus() {
72
+ const viewDate = new Date(this.picker.viewDate);
73
+ this.year = viewDate.getFullYear();
74
+ this.focused = viewDate.getMonth();
75
+ }
76
+
77
+ // Update view's settings to reflect the selected dates
78
+ updateSelection() {
79
+ const {dates, rangepicker} = this.picker.datepicker;
80
+ this.selected = dates.reduce((selected, timeValue) => {
81
+ const date = new Date(timeValue);
82
+ const year = date.getFullYear();
83
+ const month = date.getMonth();
84
+ if (selected[year] === undefined) {
85
+ selected[year] = [month];
86
+ } else {
87
+ pushUnique(selected[year], month);
88
+ }
89
+ return selected;
90
+ }, {});
91
+ if (rangepicker && rangepicker.dates) {
92
+ this.range = rangepicker.dates.map(timeValue => {
93
+ const date = new Date(timeValue);
94
+ return isNaN(date) ? undefined : [date.getFullYear(), date.getMonth()];
95
+ });
96
+ }
97
+ }
98
+
99
+ // Update the entire view UI
100
+ render() {
101
+ // refresh disabled months on every render in order to clear the ones added
102
+ // by beforeShow hook at previous render
103
+ this.disabled = [];
104
+
105
+ this.picker.setViewSwitchLabel(this.year);
106
+ this.picker.setPrevBtnDisabled(this.year <= this.minYear);
107
+ this.picker.setNextBtnDisabled(this.year >= this.maxYear);
108
+
109
+ const selected = this.selected[this.year] || [];
110
+ const yrOutOfRange = this.year < this.minYear || this.year > this.maxYear;
111
+ const isMinYear = this.year === this.minYear;
112
+ const isMaxYear = this.year === this.maxYear;
113
+ const range = computeMonthRange(this.range, this.year);
114
+
115
+ Array.from(this.grid.children).forEach((el, index) => {
116
+ const classList = el.classList;
117
+ const date = dateValue(this.year, index, 1);
118
+
119
+ el.className = `datepicker-cell hover:bg-neutral-tertiary-medium block flex-1 leading-9 border-0 rounded-base cursor-pointer text-center text-body font-medium text-sm ${this.cellClass}`;
120
+ if (this.isMinView) {
121
+ el.dataset.date = date;
122
+ }
123
+ // reset text on every render to clear the custom content set
124
+ // by beforeShow hook at previous render
125
+ el.textContent = this.monthNames[index];
126
+
127
+ if (
128
+ yrOutOfRange
129
+ || isMinYear && index < this.minMonth
130
+ || isMaxYear && index > this.maxMonth
131
+ ) {
132
+ classList.add('disabled');
133
+ }
134
+ if (range) {
135
+ const [rangeStart, rangeEnd] = range;
136
+ if (index > rangeStart && index < rangeEnd) {
137
+ classList.add('range');
138
+ }
139
+ if (index === rangeStart) {
140
+ classList.add('range-start');
141
+ }
142
+ if (index === rangeEnd) {
143
+ classList.add('range-end');
144
+ }
145
+ }
146
+ if (selected.includes(index)) {
147
+ classList.add('selected', 'bg-brand', 'text-white', 'dark:text-white');
148
+ classList.remove('text-body', 'hover:bg-neutral-tertiary-medium', 'dark:text-white');
149
+ }
150
+ if (index === this.focused) {
151
+ classList.add('focused');
152
+ }
153
+
154
+ if (this.beforeShow) {
155
+ this.performBeforeHook(el, index, date);
156
+ }
157
+ });
158
+ }
159
+
160
+ // Update the view UI by applying the changes of selected and focused items
161
+ refresh() {
162
+ const selected = this.selected[this.year] || [];
163
+ const [rangeStart, rangeEnd] = computeMonthRange(this.range, this.year) || [];
164
+ this.grid
165
+ .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
166
+ .forEach((el) => {
167
+ el.classList.remove('range', 'range-start', 'range-end', 'selected', 'bg-brand', 'dark:text-white', 'text-white', 'focused');
168
+ el.classList.add('text-body', 'hover:bg-neutral-tertiary-medium', 'dark:text-white');
169
+ });
170
+ Array.from(this.grid.children).forEach((el, index) => {
171
+ const classList = el.classList;
172
+ if (index > rangeStart && index < rangeEnd) {
173
+ classList.add('range');
174
+ }
175
+ if (index === rangeStart) {
176
+ classList.add('range-start');
177
+ }
178
+ if (index === rangeEnd) {
179
+ classList.add('range-end');
180
+ }
181
+ if (selected.includes(index)) {
182
+ classList.add('selected', 'bg-brand', 'text-white', 'dark:text-white');
183
+ classList.remove('text-body', 'hover:bg-neutral-tertiary-medium', 'dark:text-white');
184
+ }
185
+ if (index === this.focused) {
186
+ classList.add('focused');
187
+ }
188
+ });
189
+ }
190
+
191
+ // Update the view UI by applying the change of focused item
192
+ refreshFocus() {
193
+ this.grid.querySelectorAll('.focused').forEach((el) => {
194
+ el.classList.remove('focused');
195
+ });
196
+ this.grid.children[this.focused].classList.add('focused');
197
+ }
198
+ }
@@ -0,0 +1,55 @@
1
+ import {pushUnique} from '../../lib/utils.js';
2
+ import {parseHTML, replaceChildNodes} from '../../lib/dom.js';
3
+
4
+ // Base class of the view classes
5
+ export default class View {
6
+ constructor(picker, config) {
7
+ Object.assign(this, config, {
8
+ picker,
9
+ element: parseHTML(`<div class="datepicker-view flex"></div>`).firstChild,
10
+ selected: [],
11
+ });
12
+ this.init(this.picker.datepicker.config);
13
+ }
14
+
15
+ init(options) {
16
+ if (options.pickLevel !== undefined) {
17
+ this.isMinView = this.id === options.pickLevel;
18
+ }
19
+ this.setOptions(options);
20
+ this.updateFocus();
21
+ this.updateSelection();
22
+ }
23
+
24
+ // Execute beforeShow() callback and apply the result to the element
25
+ // args:
26
+ // - current - current value on the iteration on view rendering
27
+ // - timeValue - time value of the date to pass to beforeShow()
28
+ performBeforeHook(el, current, timeValue) {
29
+ let result = this.beforeShow(new Date(timeValue));
30
+ switch (typeof result) {
31
+ case 'boolean':
32
+ result = {enabled: result};
33
+ break;
34
+ case 'string':
35
+ result = {classes: result};
36
+ }
37
+
38
+ if (result) {
39
+ if (result.enabled === false) {
40
+ el.classList.add('disabled');
41
+ pushUnique(this.disabled, current);
42
+ }
43
+ if (result.classes) {
44
+ const extraClasses = result.classes.split(/\s+/);
45
+ el.classList.add(...extraClasses);
46
+ if (extraClasses.includes('disabled')) {
47
+ pushUnique(this.disabled, current);
48
+ }
49
+ }
50
+ if (result.content) {
51
+ replaceChildNodes(el, result.content);
52
+ }
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,170 @@
1
+ import {hasProperty, pushUnique, createTagRepeat} from '../../lib/utils.js';
2
+ import {dateValue, startOfYearPeriod} from '../../lib/date.js';
3
+ import {parseHTML} from '../../lib/dom.js';
4
+ import View from './View.js';
5
+
6
+ function toTitleCase(word) {
7
+ return [...word].reduce((str, ch, ix) => str += ix ? ch : ch.toUpperCase(), '');
8
+ }
9
+
10
+ // Class representing the years and decades view elements
11
+ export default class YearsView extends View {
12
+ constructor(picker, config) {
13
+ super(picker, config);
14
+ }
15
+
16
+ init(options, onConstruction = true) {
17
+ if (onConstruction) {
18
+ this.navStep = this.step * 10;
19
+ this.beforeShowOption = `beforeShow${toTitleCase(this.cellClass)}`;
20
+ this.grid = this.element;
21
+ this.element.classList.add(this.name, 'datepicker-grid', 'w-64', 'grid', 'grid-cols-4');
22
+ this.grid.appendChild(parseHTML(createTagRepeat('span', 12)));
23
+ }
24
+ super.init(options);
25
+ }
26
+
27
+ setOptions(options) {
28
+ if (hasProperty(options, 'minDate')) {
29
+ if (options.minDate === undefined) {
30
+ this.minYear = this.minDate = undefined;
31
+ } else {
32
+ this.minYear = startOfYearPeriod(options.minDate, this.step);
33
+ this.minDate = dateValue(this.minYear, 0, 1);
34
+ }
35
+ }
36
+ if (hasProperty(options, 'maxDate')) {
37
+ if (options.maxDate === undefined) {
38
+ this.maxYear = this.maxDate = undefined;
39
+ } else {
40
+ this.maxYear = startOfYearPeriod(options.maxDate, this.step);
41
+ this.maxDate = dateValue(this.maxYear, 11, 31);
42
+ }
43
+ }
44
+ if (options[this.beforeShowOption] !== undefined) {
45
+ const beforeShow = options[this.beforeShowOption];
46
+ this.beforeShow = typeof beforeShow === 'function' ? beforeShow : undefined;
47
+ }
48
+ }
49
+
50
+ // Update view's settings to reflect the viewDate set on the picker
51
+ updateFocus() {
52
+ const viewDate = new Date(this.picker.viewDate);
53
+ const first = startOfYearPeriod(viewDate, this.navStep);
54
+ const last = first + 9 * this.step;
55
+
56
+ this.first = first;
57
+ this.last = last;
58
+ this.start = first - this.step;
59
+ this.focused = startOfYearPeriod(viewDate, this.step);
60
+ }
61
+
62
+ // Update view's settings to reflect the selected dates
63
+ updateSelection() {
64
+ const {dates, rangepicker} = this.picker.datepicker;
65
+ this.selected = dates.reduce((years, timeValue) => {
66
+ return pushUnique(years, startOfYearPeriod(timeValue, this.step));
67
+ }, []);
68
+ if (rangepicker && rangepicker.dates) {
69
+ this.range = rangepicker.dates.map(timeValue => {
70
+ if (timeValue !== undefined) {
71
+ return startOfYearPeriod(timeValue, this.step);
72
+ }
73
+ });
74
+ }
75
+ }
76
+
77
+ // Update the entire view UI
78
+ render() {
79
+ // refresh disabled years on every render in order to clear the ones added
80
+ // by beforeShow hook at previous render
81
+ this.disabled = [];
82
+
83
+ this.picker.setViewSwitchLabel(`${this.first}-${this.last}`);
84
+ this.picker.setPrevBtnDisabled(this.first <= this.minYear);
85
+ this.picker.setNextBtnDisabled(this.last >= this.maxYear);
86
+
87
+ Array.from(this.grid.children).forEach((el, index) => {
88
+ const classList = el.classList;
89
+ const current = this.start + (index * this.step);
90
+ const date = dateValue(current, 0, 1);
91
+
92
+ el.className = `datepicker-cell hover:bg-neutral-tertiary-medium block flex-1 leading-9 border-0 rounded-base cursor-pointer text-center text-body font-medium text-sm ${this.cellClass}`;
93
+ if (this.isMinView) {
94
+ el.dataset.date = date;
95
+ }
96
+ el.textContent = el.dataset.year = current;
97
+
98
+ if (index === 0) {
99
+ classList.add('prev');
100
+ } else if (index === 11) {
101
+ classList.add('next');
102
+ }
103
+ if (current < this.minYear || current > this.maxYear) {
104
+ classList.add('disabled');
105
+ }
106
+ if (this.range) {
107
+ const [rangeStart, rangeEnd] = this.range;
108
+ if (current > rangeStart && current < rangeEnd) {
109
+ classList.add('range');
110
+ }
111
+ if (current === rangeStart) {
112
+ classList.add('range-start');
113
+ }
114
+ if (current === rangeEnd) {
115
+ classList.add('range-end');
116
+ }
117
+ }
118
+ if (this.selected.includes(current)) {
119
+ classList.add('selected', 'bg-brand', 'text-white', 'dark:text-white');
120
+ classList.remove('text-body', 'hover:bg-neutral-tertiary-medium', 'dark:text-white');
121
+ }
122
+ if (current === this.focused) {
123
+ classList.add('focused');
124
+ }
125
+
126
+ if (this.beforeShow) {
127
+ this.performBeforeHook(el, current, date);
128
+ }
129
+ });
130
+ }
131
+
132
+ // Update the view UI by applying the changes of selected and focused items
133
+ refresh() {
134
+ const [rangeStart, rangeEnd] = this.range || [];
135
+ this.grid
136
+ .querySelectorAll('.range, .range-start, .range-end, .selected, .focused')
137
+ .forEach((el) => {
138
+ el.classList.remove('range', 'range-start', 'range-end', 'selected', 'bg-brand', 'text-white', 'dark:text-white', 'focused');
139
+ });
140
+ Array.from(this.grid.children).forEach((el) => {
141
+ const current = Number(el.textContent);
142
+ const classList = el.classList;
143
+ if (current > rangeStart && current < rangeEnd) {
144
+ classList.add('range');
145
+ }
146
+ if (current === rangeStart) {
147
+ classList.add('range-start');
148
+ }
149
+ if (current === rangeEnd) {
150
+ classList.add('range-end');
151
+ }
152
+ if (this.selected.includes(current)) {
153
+ classList.add('selected', 'bg-brand', 'text-white', 'hover:text-heading');
154
+ classList.remove('text-body', 'hover:bg-neutral-tertiary-medium', 'hover:text-heading');
155
+ }
156
+ if (current === this.focused) {
157
+ classList.add('focused');
158
+ }
159
+ });
160
+ }
161
+
162
+ // Update the view UI by applying the change of focused item
163
+ refreshFocus() {
164
+ const index = Math.round((this.focused - this.start) / this.step);
165
+ this.grid.querySelectorAll('.focused').forEach((el) => {
166
+ el.classList.remove('focused');
167
+ });
168
+ this.grid.children[index].classList.add('focused');
169
+ }
170
+ }