@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,487 @@
1
+ import {lastItemOf, stringToArray, isInRange} from './lib/utils.js';
2
+ import {today} from './lib/date.js';
3
+ import {parseDate, formatDate} from './lib/date-format.js';
4
+ import {registerListeners, unregisterListeners} from './lib/event.js';
5
+ import {locales} from './i18n/base-locales.js';
6
+ import defaultOptions from './options/defaultOptions.js';
7
+ import processOptions from './options/processOptions.js';
8
+ import Picker from './picker/Picker.js';
9
+ import {triggerDatepickerEvent} from './events/functions.js';
10
+ import {onKeydown, onFocus, onMousedown, onClickInput, onPaste} from './events/inputFieldListeners.js';
11
+ import {onClickOutside} from './events/otherListeners.js';
12
+
13
+ function stringifyDates(dates, config) {
14
+ return dates
15
+ .map(dt => formatDate(dt, config.format, config.locale))
16
+ .join(config.dateDelimiter);
17
+ }
18
+
19
+ // parse input dates and create an array of time values for selection
20
+ // returns undefined if there are no valid dates in inputDates
21
+ // when origDates (current selection) is passed, the function works to mix
22
+ // the input dates into the current selection
23
+ function processInputDates(datepicker, inputDates, clear = false) {
24
+ const {config, dates: origDates, rangepicker} = datepicker;
25
+ if (inputDates.length === 0) {
26
+ // empty input is considered valid unless origiDates is passed
27
+ return clear ? [] : undefined;
28
+ }
29
+
30
+ const rangeEnd = rangepicker && datepicker === rangepicker.datepickers[1];
31
+ let newDates = inputDates.reduce((dates, dt) => {
32
+ let date = parseDate(dt, config.format, config.locale);
33
+ if (date === undefined) {
34
+ return dates;
35
+ }
36
+ if (config.pickLevel > 0) {
37
+ // adjust to 1st of the month/Jan 1st of the year
38
+ // or to the last day of the monh/Dec 31st of the year if the datepicker
39
+ // is the range-end picker of a rangepicker
40
+ const dt = new Date(date);
41
+ if (config.pickLevel === 1) {
42
+ date = rangeEnd
43
+ ? dt.setMonth(dt.getMonth() + 1, 0)
44
+ : dt.setDate(1);
45
+ } else {
46
+ date = rangeEnd
47
+ ? dt.setFullYear(dt.getFullYear() + 1, 0, 0)
48
+ : dt.setMonth(0, 1);
49
+ }
50
+ }
51
+ if (
52
+ isInRange(date, config.minDate, config.maxDate)
53
+ && !dates.includes(date)
54
+ && !config.datesDisabled.includes(date)
55
+ && !config.daysOfWeekDisabled.includes(new Date(date).getDay())
56
+ ) {
57
+ dates.push(date);
58
+ }
59
+ return dates;
60
+ }, []);
61
+ if (newDates.length === 0) {
62
+ return;
63
+ }
64
+ if (config.multidate && !clear) {
65
+ // get the synmetric difference between origDates and newDates
66
+ newDates = newDates.reduce((dates, date) => {
67
+ if (!origDates.includes(date)) {
68
+ dates.push(date);
69
+ }
70
+ return dates;
71
+ }, origDates.filter(date => !newDates.includes(date)));
72
+ }
73
+ // do length check always because user can input multiple dates regardless of the mode
74
+ return config.maxNumberOfDates && newDates.length > config.maxNumberOfDates
75
+ ? newDates.slice(config.maxNumberOfDates * -1)
76
+ : newDates;
77
+ }
78
+
79
+ // refresh the UI elements
80
+ // modes: 1: input only, 2, picker only, 3 both
81
+ function refreshUI(datepicker, mode = 3, quickRender = true) {
82
+ const {config, picker, inputField} = datepicker;
83
+ if (mode & 2) {
84
+ const newView = picker.active ? config.pickLevel : config.startView;
85
+ picker.update().changeView(newView).render(quickRender);
86
+ }
87
+ if (mode & 1 && inputField) {
88
+ inputField.value = stringifyDates(datepicker.dates, config);
89
+ }
90
+ }
91
+
92
+ function setDate(datepicker, inputDates, options) {
93
+ let {clear, render, autohide} = options;
94
+ if (render === undefined) {
95
+ render = true;
96
+ }
97
+ if (!render) {
98
+ autohide = false;
99
+ } else if (autohide === undefined) {
100
+ autohide = datepicker.config.autohide;
101
+ }
102
+
103
+ const newDates = processInputDates(datepicker, inputDates, clear);
104
+ if (!newDates) {
105
+ return;
106
+ }
107
+ if (newDates.toString() !== datepicker.dates.toString()) {
108
+ datepicker.dates = newDates;
109
+ refreshUI(datepicker, render ? 3 : 1);
110
+ triggerDatepickerEvent(datepicker, 'changeDate');
111
+ } else {
112
+ refreshUI(datepicker, 1);
113
+ }
114
+ if (autohide) {
115
+ datepicker.hide();
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Class representing a date picker
121
+ */
122
+ export default class Datepicker {
123
+ /**
124
+ * Create a date picker
125
+ * @param {Element} element - element to bind a date picker
126
+ * @param {Object} [options] - config options
127
+ * @param {DateRangePicker} [rangepicker] - DateRangePicker instance the
128
+ * date picker belongs to. Use this only when creating date picker as a part
129
+ * of date range picker
130
+ */
131
+ constructor(element, options = {}, rangepicker = undefined) {
132
+ element.datepicker = this;
133
+ this.element = element;
134
+
135
+ // set up config
136
+ const config = this.config = Object.assign({
137
+ buttonClass: (options.buttonClass && String(options.buttonClass)) || 'button',
138
+ container: document.body,
139
+ defaultViewDate: today(),
140
+ maxDate: undefined,
141
+ minDate: undefined,
142
+ }, processOptions(defaultOptions, this));
143
+ this._options = options;
144
+ Object.assign(config, processOptions(options, this));
145
+
146
+ // configure by type
147
+ const inline = this.inline = element.tagName !== 'INPUT';
148
+ let inputField;
149
+ let initialDates;
150
+
151
+ if (inline) {
152
+ config.container = element;
153
+ initialDates = stringToArray(element.dataset.date, config.dateDelimiter);
154
+ delete element.dataset.date;
155
+ } else {
156
+ const container = options.container ? document.querySelector(options.container) : null;
157
+ if (container) {
158
+ config.container = container;
159
+ }
160
+ inputField = this.inputField = element;
161
+ inputField.classList.add('datepicker-input');
162
+ initialDates = stringToArray(inputField.value, config.dateDelimiter);
163
+ }
164
+ if (rangepicker) {
165
+ // check validiry
166
+ const index = rangepicker.inputs.indexOf(inputField);
167
+ const datepickers = rangepicker.datepickers;
168
+ if (index < 0 || index > 1 || !Array.isArray(datepickers)) {
169
+ throw Error('Invalid rangepicker object.');
170
+ }
171
+ // attach itaelf to the rangepicker here so that processInputDates() can
172
+ // determine if this is the range-end picker of the rangepicker while
173
+ // setting inital values when pickLevel > 0
174
+ datepickers[index] = this;
175
+ // add getter for rangepicker
176
+ Object.defineProperty(this, 'rangepicker', {
177
+ get() {
178
+ return rangepicker;
179
+ },
180
+ });
181
+ }
182
+
183
+ // set initial dates
184
+ this.dates = [];
185
+ // process initial value
186
+ const inputDateValues = processInputDates(this, initialDates);
187
+ if (inputDateValues && inputDateValues.length > 0) {
188
+ this.dates = inputDateValues;
189
+ }
190
+ if (inputField) {
191
+ inputField.value = stringifyDates(this.dates, config);
192
+ }
193
+
194
+ const picker = this.picker = new Picker(this);
195
+
196
+ if (inline) {
197
+ this.show();
198
+ } else {
199
+ // set up event listeners in other modes
200
+ const onMousedownDocument = onClickOutside.bind(null, this);
201
+ const listeners = [
202
+ [inputField, 'keydown', onKeydown.bind(null, this)],
203
+ [inputField, 'focus', onFocus.bind(null, this)],
204
+ [inputField, 'mousedown', onMousedown.bind(null, this)],
205
+ [inputField, 'click', onClickInput.bind(null, this)],
206
+ [inputField, 'paste', onPaste.bind(null, this)],
207
+ [document, 'mousedown', onMousedownDocument],
208
+ [document, 'touchstart', onMousedownDocument],
209
+ [window, 'resize', picker.place.bind(picker)]
210
+ ];
211
+ registerListeners(this, listeners);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Format Date object or time value in given format and language
217
+ * @param {Date|Number} date - date or time value to format
218
+ * @param {String|Object} format - format string or object that contains
219
+ * toDisplay() custom formatter, whose signature is
220
+ * - args:
221
+ * - date: {Date} - Date instance of the date passed to the method
222
+ * - format: {Object} - the format object passed to the method
223
+ * - locale: {Object} - locale for the language specified by `lang`
224
+ * - return:
225
+ * {String} formatted date
226
+ * @param {String} [lang=en] - language code for the locale to use
227
+ * @return {String} formatted date
228
+ */
229
+ static formatDate(date, format, lang) {
230
+ return formatDate(date, format, lang && locales[lang] || locales.en);
231
+ }
232
+
233
+ /**
234
+ * Parse date string
235
+ * @param {String|Date|Number} dateStr - date string, Date object or time
236
+ * value to parse
237
+ * @param {String|Object} format - format string or object that contains
238
+ * toValue() custom parser, whose signature is
239
+ * - args:
240
+ * - dateStr: {String|Date|Number} - the dateStr passed to the method
241
+ * - format: {Object} - the format object passed to the method
242
+ * - locale: {Object} - locale for the language specified by `lang`
243
+ * - return:
244
+ * {Date|Number} parsed date or its time value
245
+ * @param {String} [lang=en] - language code for the locale to use
246
+ * @return {Number} time value of parsed date
247
+ */
248
+ static parseDate(dateStr, format, lang) {
249
+ return parseDate(dateStr, format, lang && locales[lang] || locales.en);
250
+ }
251
+
252
+ /**
253
+ * @type {Object} - Installed locales in `[languageCode]: localeObject` format
254
+ * en`:_English (US)_ is pre-installed.
255
+ */
256
+ static get locales() {
257
+ return locales;
258
+ }
259
+
260
+ /**
261
+ * @type {Boolean} - Whether the picker element is shown. `true` whne shown
262
+ */
263
+ get active() {
264
+ return !!(this.picker && this.picker.active);
265
+ }
266
+
267
+ /**
268
+ * @type {HTMLDivElement} - DOM object of picker element
269
+ */
270
+ get pickerElement() {
271
+ return this.picker ? this.picker.element : undefined;
272
+ }
273
+
274
+ /**
275
+ * Set new values to the config options
276
+ * @param {Object} options - config options to update
277
+ */
278
+ setOptions(options) {
279
+ const picker = this.picker;
280
+ const newOptions = processOptions(options, this);
281
+ Object.assign(this._options, options);
282
+ Object.assign(this.config, newOptions);
283
+ picker.setOptions(newOptions);
284
+
285
+ refreshUI(this, 3);
286
+ }
287
+
288
+ /**
289
+ * Show the picker element
290
+ */
291
+ show() {
292
+ if (this.inputField) {
293
+ if (this.inputField.disabled) {
294
+ return;
295
+ }
296
+ if (this.inputField !== document.activeElement) {
297
+ this._showing = true;
298
+ this.inputField.focus();
299
+ delete this._showing;
300
+ }
301
+ }
302
+ this.picker.show();
303
+ }
304
+
305
+ /**
306
+ * Hide the picker element
307
+ * Not available on inline picker
308
+ */
309
+ hide() {
310
+ if (this.inline) {
311
+ return;
312
+ }
313
+ this.picker.hide();
314
+ this.picker.update().changeView(this.config.startView).render();
315
+ }
316
+
317
+ /**
318
+ * Destroy the Datepicker instance
319
+ * @return {Detepicker} - the instance destroyed
320
+ */
321
+ destroy() {
322
+ this.hide();
323
+ unregisterListeners(this);
324
+ this.picker.detach();
325
+ if (!this.inline) {
326
+ this.inputField.classList.remove('datepicker-input');
327
+ }
328
+ delete this.element.datepicker;
329
+ return this;
330
+ }
331
+
332
+ /**
333
+ * Get the selected date(s)
334
+ *
335
+ * The method returns a Date object of selected date by default, and returns
336
+ * an array of selected dates in multidate mode. If format string is passed,
337
+ * it returns date string(s) formatted in given format.
338
+ *
339
+ * @param {String} [format] - Format string to stringify the date(s)
340
+ * @return {Date|String|Date[]|String[]} - selected date(s), or if none is
341
+ * selected, empty array in multidate mode and untitled in sigledate mode
342
+ */
343
+ getDate(format = undefined) {
344
+ const callback = format
345
+ ? date => formatDate(date, format, this.config.locale)
346
+ : date => new Date(date);
347
+
348
+ if (this.config.multidate) {
349
+ return this.dates.map(callback);
350
+ }
351
+ if (this.dates.length > 0) {
352
+ return callback(this.dates[0]);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Set selected date(s)
358
+ *
359
+ * In multidate mode, you can pass multiple dates as a series of arguments
360
+ * or an array. (Since each date is parsed individually, the type of the
361
+ * dates doesn't have to be the same.)
362
+ * The given dates are used to toggle the select status of each date. The
363
+ * number of selected dates is kept from exceeding the length set to
364
+ * maxNumberOfDates.
365
+ *
366
+ * With clear: true option, the method can be used to clear the selection
367
+ * and to replace the selection instead of toggling in multidate mode.
368
+ * If the option is passed with no date arguments or an empty dates array,
369
+ * it works as "clear" (clear the selection then set nothing), and if the
370
+ * option is passed with new dates to select, it works as "replace" (clear
371
+ * the selection then set the given dates)
372
+ *
373
+ * When render: false option is used, the method omits re-rendering the
374
+ * picker element. In this case, you need to call refresh() method later in
375
+ * order for the picker element to reflect the changes. The input field is
376
+ * refreshed always regardless of this option.
377
+ *
378
+ * When invalid (unparsable, repeated, disabled or out-of-range) dates are
379
+ * passed, the method ignores them and applies only valid ones. In the case
380
+ * that all the given dates are invalid, which is distinguished from passing
381
+ * no dates, the method considers it as an error and leaves the selection
382
+ * untouched.
383
+ *
384
+ * @param {...(Date|Number|String)|Array} [dates] - Date strings, Date
385
+ * objects, time values or mix of those for new selection
386
+ * @param {Object} [options] - function options
387
+ * - clear: {boolean} - Whether to clear the existing selection
388
+ * defualt: false
389
+ * - render: {boolean} - Whether to re-render the picker element
390
+ * default: true
391
+ * - autohide: {boolean} - Whether to hide the picker element after re-render
392
+ * Ignored when used with render: false
393
+ * default: config.autohide
394
+ */
395
+ setDate(...args) {
396
+ const dates = [...args];
397
+ const opts = {};
398
+ const lastArg = lastItemOf(args);
399
+ if (
400
+ typeof lastArg === 'object'
401
+ && !Array.isArray(lastArg)
402
+ && !(lastArg instanceof Date)
403
+ && lastArg
404
+ ) {
405
+ Object.assign(opts, dates.pop());
406
+ }
407
+
408
+ const inputDates = Array.isArray(dates[0]) ? dates[0] : dates;
409
+ setDate(this, inputDates, opts);
410
+ }
411
+
412
+ /**
413
+ * Update the selected date(s) with input field's value
414
+ * Not available on inline picker
415
+ *
416
+ * The input field will be refreshed with properly formatted date string.
417
+ *
418
+ * @param {Object} [options] - function options
419
+ * - autohide: {boolean} - whether to hide the picker element after refresh
420
+ * default: false
421
+ */
422
+ update(options = undefined) {
423
+ if (this.inline) {
424
+ return;
425
+ }
426
+
427
+ const opts = {clear: true, autohide: !!(options && options.autohide)};
428
+ const inputDates = stringToArray(this.inputField.value, this.config.dateDelimiter);
429
+ setDate(this, inputDates, opts);
430
+ }
431
+
432
+ /**
433
+ * Refresh the picker element and the associated input field
434
+ * @param {String} [target] - target item when refreshing one item only
435
+ * 'picker' or 'input'
436
+ * @param {Boolean} [forceRender] - whether to re-render the picker element
437
+ * regardless of its state instead of optimized refresh
438
+ */
439
+ refresh(target = undefined, forceRender = false) {
440
+ if (target && typeof target !== 'string') {
441
+ forceRender = target;
442
+ target = undefined;
443
+ }
444
+
445
+ let mode;
446
+ if (target === 'picker') {
447
+ mode = 2;
448
+ } else if (target === 'input') {
449
+ mode = 1;
450
+ } else {
451
+ mode = 3;
452
+ }
453
+ refreshUI(this, mode, !forceRender);
454
+ }
455
+
456
+ /**
457
+ * Enter edit mode
458
+ * Not available on inline picker or when the picker element is hidden
459
+ */
460
+ enterEditMode() {
461
+ if (this.inline || !this.picker.active || this.editMode) {
462
+ return;
463
+ }
464
+ this.editMode = true;
465
+ this.inputField.classList.add('in-edit', 'border-brand');
466
+ }
467
+
468
+ /**
469
+ * Exit from edit mode
470
+ * Not available on inline picker
471
+ * @param {Object} [options] - function options
472
+ * - update: {boolean} - whether to call update() after exiting
473
+ * If false, input field is revert to the existing selection
474
+ * default: false
475
+ */
476
+ exitEditMode(options = undefined) {
477
+ if (this.inline || !this.editMode) {
478
+ return;
479
+ }
480
+ const opts = Object.assign({update: false}, options);
481
+ delete this.editMode;
482
+ this.inputField.classList.remove('in-edit', 'border-brand');
483
+ if (opts.update) {
484
+ this.update(opts);
485
+ }
486
+ }
487
+ }
@@ -0,0 +1,5 @@
1
+ import Datepicker from './Datepicker.js';
2
+ import DateRangePicker from './DateRangePicker.js';
3
+
4
+ window.Datepicker = Datepicker;
5
+ window.DateRangePicker = DateRangePicker;
@@ -0,0 +1,48 @@
1
+ import {limitToRange} from '../lib/utils.js';
2
+ import {addMonths, addYears} from '../lib/date.js';
3
+
4
+ export function triggerDatepickerEvent(datepicker, type) {
5
+ const detail = {
6
+ date: datepicker.getDate(),
7
+ viewDate: new Date(datepicker.picker.viewDate),
8
+ viewId: datepicker.picker.currentView.id,
9
+ datepicker,
10
+ };
11
+ datepicker.element.dispatchEvent(new CustomEvent(type, {detail}));
12
+ }
13
+
14
+ // direction: -1 (to previous), 1 (to next)
15
+ export function goToPrevOrNext(datepicker, direction) {
16
+ const {minDate, maxDate} = datepicker.config;
17
+ const {currentView, viewDate} = datepicker.picker;
18
+ let newViewDate;
19
+ switch (currentView.id) {
20
+ case 0:
21
+ newViewDate = addMonths(viewDate, direction);
22
+ break;
23
+ case 1:
24
+ newViewDate = addYears(viewDate, direction);
25
+ break;
26
+ default:
27
+ newViewDate = addYears(viewDate, direction * currentView.navStep);
28
+ }
29
+ newViewDate = limitToRange(newViewDate, minDate, maxDate);
30
+ datepicker.picker.changeFocus(newViewDate).render();
31
+ }
32
+
33
+ export function switchView(datepicker) {
34
+ const viewId = datepicker.picker.currentView.id;
35
+ if (viewId === datepicker.config.maxView) {
36
+ return;
37
+ }
38
+ datepicker.picker.changeView(viewId + 1).render();
39
+ }
40
+
41
+ export function unfocus(datepicker) {
42
+ if (datepicker.config.updateOnBlur) {
43
+ datepicker.update({autohide: true});
44
+ } else {
45
+ datepicker.refresh('input');
46
+ datepicker.hide();
47
+ }
48
+ }