@ministryofjustice/frontend 4.0.1 → 5.1.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 (256) hide show
  1. package/govuk-prototype-kit.config.json +19 -4
  2. package/moj/_base.scss +2 -0
  3. package/moj/_base.scss.map +1 -0
  4. package/moj/all.bundle.js +3010 -0
  5. package/moj/all.bundle.js.map +1 -0
  6. package/moj/all.bundle.mjs +3293 -0
  7. package/moj/all.bundle.mjs.map +1 -0
  8. package/moj/all.mjs +17 -110
  9. package/moj/all.mjs.map +1 -1
  10. package/moj/all.scss +3 -0
  11. package/moj/all.scss.map +1 -0
  12. package/moj/common/index.mjs +57 -0
  13. package/moj/common/index.mjs.map +1 -0
  14. package/moj/common/moj-frontend-version.mjs +14 -0
  15. package/moj/common/moj-frontend-version.mjs.map +1 -0
  16. package/moj/components/_all.scss +2 -0
  17. package/moj/components/_all.scss.map +1 -0
  18. package/moj/components/action-bar/_action-bar.scss +2 -0
  19. package/moj/components/action-bar/_action-bar.scss.map +1 -0
  20. package/moj/components/add-another/_add-another.scss +2 -0
  21. package/moj/components/add-another/_add-another.scss.map +1 -0
  22. package/moj/components/add-another/add-another.bundle.js +157 -0
  23. package/moj/components/add-another/add-another.bundle.js.map +1 -0
  24. package/moj/components/add-another/add-another.bundle.mjs +271 -0
  25. package/moj/components/add-another/add-another.bundle.mjs.map +1 -0
  26. package/moj/components/add-another/add-another.mjs +135 -91
  27. package/moj/components/add-another/add-another.mjs.map +1 -1
  28. package/moj/components/alert/_alert.scss +4 -0
  29. package/moj/components/alert/_alert.scss.map +1 -0
  30. package/moj/components/alert/alert.bundle.js +254 -0
  31. package/moj/components/alert/alert.bundle.js.map +1 -0
  32. package/moj/components/alert/alert.bundle.mjs +490 -0
  33. package/moj/components/alert/alert.bundle.mjs.map +1 -0
  34. package/moj/components/alert/alert.mjs +97 -218
  35. package/moj/components/alert/alert.mjs.map +1 -1
  36. package/moj/components/alert/{alert.spec.helper.js → alert.spec.helper.bundle.js} +1 -1
  37. package/moj/components/alert/alert.spec.helper.bundle.js.map +1 -0
  38. package/moj/components/alert/alert.spec.helper.bundle.mjs +67 -0
  39. package/moj/components/alert/alert.spec.helper.bundle.mjs.map +1 -0
  40. package/moj/components/alert/alert.spec.helper.mjs.map +1 -1
  41. package/moj/components/badge/_badge.scss +2 -0
  42. package/moj/components/badge/_badge.scss.map +1 -0
  43. package/moj/components/banner/_banner.scss +2 -0
  44. package/moj/components/banner/_banner.scss.map +1 -0
  45. package/moj/components/button-menu/README.md +12 -6
  46. package/moj/components/button-menu/_button-menu.scss +4 -1
  47. package/moj/components/button-menu/_button-menu.scss.map +1 -0
  48. package/moj/components/button-menu/button-menu.bundle.js +270 -0
  49. package/moj/components/button-menu/button-menu.bundle.js.map +1 -0
  50. package/moj/components/button-menu/button-menu.bundle.mjs +506 -0
  51. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -0
  52. package/moj/components/button-menu/button-menu.mjs +214 -280
  53. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  54. package/moj/components/cookie-banner/_cookie-banner.scss +2 -0
  55. package/moj/components/cookie-banner/_cookie-banner.scss.map +1 -0
  56. package/moj/components/currency-input/_currency-input.scss +2 -0
  57. package/moj/components/currency-input/_currency-input.scss.map +1 -0
  58. package/moj/components/date-picker/_date-picker.scss +2 -0
  59. package/moj/components/date-picker/_date-picker.scss.map +1 -0
  60. package/moj/components/date-picker/date-picker.bundle.js +804 -0
  61. package/moj/components/date-picker/date-picker.bundle.js.map +1 -0
  62. package/moj/components/date-picker/date-picker.bundle.mjs +1040 -0
  63. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -0
  64. package/moj/components/date-picker/date-picker.mjs +663 -827
  65. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  66. package/moj/components/filter/_filter.scss +2 -0
  67. package/moj/components/filter/_filter.scss.map +1 -0
  68. package/moj/components/filter/template.njk +1 -1
  69. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +185 -0
  70. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -0
  71. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +421 -0
  72. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -0
  73. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +166 -81
  74. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  75. package/moj/components/form-validator/form-validator.bundle.js +288 -0
  76. package/moj/components/form-validator/form-validator.bundle.js.map +1 -0
  77. package/moj/components/form-validator/form-validator.bundle.mjs +524 -0
  78. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -0
  79. package/moj/components/form-validator/form-validator.mjs +226 -149
  80. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  81. package/moj/components/header/_header.scss +2 -0
  82. package/moj/components/header/_header.scss.map +1 -0
  83. package/moj/components/identity-bar/_identity-bar.scss +2 -0
  84. package/moj/components/identity-bar/_identity-bar.scss.map +1 -0
  85. package/moj/components/interruption-card/_interruption-card.scss +2 -0
  86. package/moj/components/interruption-card/_interruption-card.scss.map +1 -0
  87. package/moj/components/messages/_messages.scss +2 -0
  88. package/moj/components/messages/_messages.scss.map +1 -0
  89. package/moj/components/multi-file-upload/_multi-file-upload.scss +2 -0
  90. package/moj/components/multi-file-upload/_multi-file-upload.scss.map +1 -0
  91. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +397 -0
  92. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -0
  93. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +633 -0
  94. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -0
  95. package/moj/components/multi-file-upload/multi-file-upload.mjs +384 -213
  96. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  97. package/moj/components/multi-file-upload/template.njk +1 -1
  98. package/moj/components/multi-select/_multi-select.scss +2 -0
  99. package/moj/components/multi-select/_multi-select.scss.map +1 -0
  100. package/moj/components/multi-select/multi-select.bundle.js +143 -0
  101. package/moj/components/multi-select/multi-select.bundle.js.map +1 -0
  102. package/moj/components/multi-select/multi-select.bundle.mjs +379 -0
  103. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -0
  104. package/moj/components/multi-select/multi-select.mjs +123 -64
  105. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  106. package/moj/components/notification-badge/_notification-badge.scss +2 -0
  107. package/moj/components/notification-badge/_notification-badge.scss.map +1 -0
  108. package/moj/components/organisation-switcher/_organisation-switcher.scss +2 -0
  109. package/moj/components/organisation-switcher/_organisation-switcher.scss.map +1 -0
  110. package/moj/components/page-header-actions/_page-header-actions.scss +2 -0
  111. package/moj/components/page-header-actions/_page-header-actions.scss.map +1 -0
  112. package/moj/components/pagination/_pagination.scss +2 -2
  113. package/moj/components/pagination/_pagination.scss.map +1 -0
  114. package/moj/components/password-reveal/_password-reveal.scss +5 -1
  115. package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
  116. package/moj/components/password-reveal/password-reveal.bundle.js +52 -0
  117. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
  118. package/moj/components/password-reveal/password-reveal.bundle.mjs +166 -0
  119. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
  120. package/moj/components/password-reveal/password-reveal.mjs +39 -29
  121. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  122. package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
  123. package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
  124. package/moj/components/progress-bar/_progress-bar.scss +2 -0
  125. package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
  126. package/moj/components/rich-text-editor/README.md +16 -9
  127. package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
  128. package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
  129. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +210 -0
  130. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
  131. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +446 -0
  132. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
  133. package/moj/components/rich-text-editor/rich-text-editor.mjs +186 -140
  134. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  135. package/moj/components/search/_search.scss +2 -0
  136. package/moj/components/search/_search.scss.map +1 -0
  137. package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
  138. package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
  139. package/moj/components/search-toggle/search-toggle.bundle.js +122 -0
  140. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
  141. package/moj/components/search-toggle/search-toggle.bundle.mjs +358 -0
  142. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
  143. package/moj/components/search-toggle/search-toggle.mjs +104 -43
  144. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  145. package/moj/components/side-navigation/_side-navigation.scss +2 -0
  146. package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
  147. package/moj/components/sortable-table/_sortable-table.scss +2 -2
  148. package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
  149. package/moj/components/sortable-table/sortable-table.bundle.js +202 -0
  150. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
  151. package/moj/components/sortable-table/sortable-table.bundle.mjs +438 -0
  152. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
  153. package/moj/components/sortable-table/sortable-table.mjs +179 -122
  154. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  155. package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
  156. package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
  157. package/moj/components/tag/_tag.scss +2 -0
  158. package/moj/components/tag/_tag.scss.map +1 -0
  159. package/moj/components/task-list/_task-list.scss +2 -0
  160. package/moj/components/task-list/_task-list.scss.map +1 -0
  161. package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
  162. package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
  163. package/moj/components/timeline/_timeline.scss +2 -0
  164. package/moj/components/timeline/_timeline.scss.map +1 -0
  165. package/moj/core/_all.scss +3 -0
  166. package/moj/core/_all.scss.map +1 -0
  167. package/moj/core/_moj-frontend-properties.scss +7 -0
  168. package/moj/core/_moj-frontend-properties.scss.map +1 -0
  169. package/moj/filters/all.js +44 -22
  170. package/moj/filters/prototype-kit-13-filters.js +4 -3
  171. package/moj/helpers/_all.scss +2 -0
  172. package/moj/helpers/_all.scss.map +1 -0
  173. package/moj/helpers/_hidden.scss +2 -0
  174. package/moj/helpers/_hidden.scss.map +1 -0
  175. package/moj/helpers/_links.scss +2 -0
  176. package/moj/helpers/_links.scss.map +1 -0
  177. package/moj/helpers.bundle.js +140 -0
  178. package/moj/helpers.bundle.js.map +1 -0
  179. package/moj/helpers.bundle.mjs +128 -0
  180. package/moj/helpers.bundle.mjs.map +1 -0
  181. package/moj/helpers.mjs +50 -77
  182. package/moj/helpers.mjs.map +1 -1
  183. package/moj/init.js +11 -2
  184. package/moj/moj-frontend.min.css +1 -1
  185. package/moj/moj-frontend.min.css.map +1 -1
  186. package/moj/moj-frontend.min.js +1 -1
  187. package/moj/moj-frontend.min.js.map +1 -1
  188. package/moj/objects/_all.scss +2 -0
  189. package/moj/objects/_all.scss.map +1 -0
  190. package/moj/objects/_button-group.scss +2 -0
  191. package/moj/objects/_button-group.scss.map +1 -0
  192. package/moj/objects/_filter-layout.scss +2 -0
  193. package/moj/objects/_filter-layout.scss.map +1 -0
  194. package/moj/objects/_scrollable-pane.scss +2 -0
  195. package/moj/objects/_scrollable-pane.scss.map +1 -0
  196. package/moj/objects/_width-container.scss +2 -0
  197. package/moj/objects/_width-container.scss.map +1 -0
  198. package/moj/settings/_all.scss +2 -0
  199. package/moj/settings/_all.scss.map +1 -0
  200. package/moj/settings/_assets.scss +2 -0
  201. package/moj/settings/_assets.scss.map +1 -0
  202. package/moj/settings/_colours.scss +2 -0
  203. package/moj/settings/_colours.scss.map +1 -0
  204. package/moj/settings/_measurements.scss +2 -0
  205. package/moj/settings/_measurements.scss.map +1 -0
  206. package/moj/settings/_typography.scss +2 -0
  207. package/moj/settings/_typography.scss.map +1 -0
  208. package/moj/template.njk +13 -0
  209. package/moj/utilities/_all.scss +2 -0
  210. package/moj/utilities/_all.scss.map +1 -0
  211. package/moj/utilities/_hidden.scss +2 -0
  212. package/moj/utilities/_hidden.scss.map +1 -0
  213. package/moj/utilities/_width-container.scss +2 -0
  214. package/moj/utilities/_width-container.scss.map +1 -0
  215. package/moj/vendor/govuk-frontend/_base.scss +2 -0
  216. package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
  217. package/moj/vendor/govuk-frontend/_index.scss +2 -0
  218. package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
  219. package/package.json +5 -6
  220. package/moj/all.jquery.min.js +0 -1
  221. package/moj/all.jquery.min.js.map +0 -1
  222. package/moj/all.js +0 -2662
  223. package/moj/all.js.map +0 -1
  224. package/moj/components/add-another/add-another.js +0 -115
  225. package/moj/components/add-another/add-another.js.map +0 -1
  226. package/moj/components/alert/alert.js +0 -356
  227. package/moj/components/alert/alert.js.map +0 -1
  228. package/moj/components/alert/alert.spec.helper.js.map +0 -1
  229. package/moj/components/button-menu/button-menu.js +0 -338
  230. package/moj/components/button-menu/button-menu.js.map +0 -1
  231. package/moj/components/date-picker/date-picker.js +0 -970
  232. package/moj/components/date-picker/date-picker.js.map +0 -1
  233. package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
  234. package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
  235. package/moj/components/form-validator/form-validator.js +0 -205
  236. package/moj/components/form-validator/form-validator.js.map +0 -1
  237. package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
  238. package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
  239. package/moj/components/multi-select/multi-select.js +0 -86
  240. package/moj/components/multi-select/multi-select.js.map +0 -1
  241. package/moj/components/password-reveal/password-reveal.js +0 -44
  242. package/moj/components/password-reveal/password-reveal.js.map +0 -1
  243. package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
  244. package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
  245. package/moj/components/search-toggle/search-toggle.js +0 -63
  246. package/moj/components/search-toggle/search-toggle.js.map +0 -1
  247. package/moj/components/sortable-table/sortable-table.js +0 -147
  248. package/moj/components/sortable-table/sortable-table.js.map +0 -1
  249. package/moj/helpers.js +0 -200
  250. package/moj/helpers.js.map +0 -1
  251. package/moj/vendor/html5shiv.js +0 -326
  252. package/moj/vendor/jquery.js +0 -9300
  253. package/moj/version.js +0 -12
  254. package/moj/version.js.map +0 -1
  255. package/moj/version.mjs +0 -4
  256. package/moj/version.mjs.map +0 -1
@@ -1,970 +0,0 @@
1
- (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MOJFrontend = global.MOJFrontend || {}));
5
- })(this, (function (exports) { 'use strict';
6
-
7
- /**
8
- * Date picker config
9
- *
10
- * @typedef {object} DatePickerConfig
11
- * @property {string} [excludedDates] - Dates that cannot be selected
12
- * @property {string} [excludedDays] - Days that cannot be selected
13
- * @property {boolean} [leadingZeroes] - Whether to add leading zeroes when populating the field
14
- * @property {string} [minDate] - The earliest available date
15
- * @property {string} [maxDate] - The latest available date
16
- * @property {string} [weekStartDay] - First day of the week in calendar view
17
- */
18
-
19
- /**
20
- * @param {HTMLElement} $module - HTML element
21
- * @param {DatePickerConfig} config - config object
22
- * @class
23
- */
24
- function DatePicker($module, config = {}) {
25
- if (!$module) {
26
- return this
27
- }
28
-
29
- const schema = Object.freeze({
30
- properties: {
31
- excludedDates: { type: 'string' },
32
- excludedDays: { type: 'string' },
33
- leadingZeros: { type: 'string' },
34
- maxDate: { type: 'string' },
35
- minDate: { type: 'string' },
36
- weekStartDay: { type: 'string' }
37
- }
38
- });
39
-
40
- const defaults = {
41
- leadingZeros: false,
42
- weekStartDay: 'monday'
43
- };
44
-
45
- // data attributes override JS config, which overrides defaults
46
- this.config = this.mergeConfigs(
47
- defaults,
48
- config,
49
- this.parseDataset(schema, $module.dataset)
50
- );
51
-
52
- this.dayLabels = [
53
- 'Monday',
54
- 'Tuesday',
55
- 'Wednesday',
56
- 'Thursday',
57
- 'Friday',
58
- 'Saturday',
59
- 'Sunday'
60
- ];
61
-
62
- this.monthLabels = [
63
- 'January',
64
- 'February',
65
- 'March',
66
- 'April',
67
- 'May',
68
- 'June',
69
- 'July',
70
- 'August',
71
- 'September',
72
- 'October',
73
- 'November',
74
- 'December'
75
- ];
76
-
77
- this.currentDate = new Date();
78
- this.currentDate.setHours(0, 0, 0, 0);
79
- this.calendarDays = [];
80
- this.excludedDates = [];
81
- this.excludedDays = [];
82
-
83
- this.buttonClass = 'moj-datepicker__button';
84
- this.selectedDayButtonClass = 'moj-datepicker__button--selected';
85
- this.currentDayButtonClass = 'moj-datepicker__button--current';
86
- this.todayButtonClass = 'moj-datepicker__button--today';
87
-
88
- this.$module = $module;
89
- this.$input = $module.querySelector('.moj-js-datepicker-input');
90
- }
91
-
92
- DatePicker.prototype.init = function () {
93
- // Check that required elements are present
94
- if (!this.$input) {
95
- return
96
- }
97
- if (this.$module.dataset.initialized) {
98
- return
99
- }
100
-
101
- this.setOptions();
102
- this.initControls();
103
- this.$module.setAttribute('data-initialized', 'true');
104
- };
105
-
106
- DatePicker.prototype.initControls = function () {
107
- this.id = `datepicker-${this.$input.id}`;
108
-
109
- this.$dialog = this.createDialog();
110
- this.createCalendarHeaders();
111
-
112
- const $componentWrapper = document.createElement('div');
113
- const $inputWrapper = document.createElement('div');
114
- $componentWrapper.classList.add('moj-datepicker__wrapper');
115
- $inputWrapper.classList.add('govuk-input__wrapper');
116
-
117
- this.$input.parentNode.insertBefore($componentWrapper, this.$input);
118
- $componentWrapper.appendChild($inputWrapper);
119
- $inputWrapper.appendChild(this.$input);
120
-
121
- $inputWrapper.insertAdjacentHTML('beforeend', this.toggleTemplate());
122
- $componentWrapper.insertAdjacentElement('beforeend', this.$dialog);
123
-
124
- this.$calendarButton = this.$module.querySelector('.moj-js-datepicker-toggle');
125
- this.$dialogTitle = this.$dialog.querySelector(
126
- '.moj-js-datepicker-month-year'
127
- );
128
-
129
- this.createCalendar();
130
-
131
- this.$prevMonthButton = this.$dialog.querySelector(
132
- '.moj-js-datepicker-prev-month'
133
- );
134
- this.$prevYearButton = this.$dialog.querySelector(
135
- '.moj-js-datepicker-prev-year'
136
- );
137
- this.$nextMonthButton = this.$dialog.querySelector(
138
- '.moj-js-datepicker-next-month'
139
- );
140
- this.$nextYearButton = this.$dialog.querySelector(
141
- '.moj-js-datepicker-next-year'
142
- );
143
- this.$cancelButton = this.$dialog.querySelector('.moj-js-datepicker-cancel');
144
- this.$okButton = this.$dialog.querySelector('.moj-js-datepicker-ok');
145
-
146
- // add event listeners
147
- this.$prevMonthButton.addEventListener('click', (event) =>
148
- this.focusPreviousMonth(event, false)
149
- );
150
- this.$prevYearButton.addEventListener('click', (event) =>
151
- this.focusPreviousYear(event, false)
152
- );
153
- this.$nextMonthButton.addEventListener('click', (event) =>
154
- this.focusNextMonth(event, false)
155
- );
156
- this.$nextYearButton.addEventListener('click', (event) =>
157
- this.focusNextYear(event, false)
158
- );
159
- this.$cancelButton.addEventListener('click', (event) => {
160
- event.preventDefault();
161
- this.closeDialog(event);
162
- });
163
- this.$okButton.addEventListener('click', () => {
164
- this.selectDate(this.currentDate);
165
- });
166
-
167
- const dialogButtons = this.$dialog.querySelectorAll(
168
- 'button:not([disabled="true"])'
169
- );
170
- // eslint-disable-next-line prefer-destructuring
171
- this.$firstButtonInDialog = dialogButtons[0];
172
- this.$lastButtonInDialog = dialogButtons[dialogButtons.length - 1];
173
- this.$firstButtonInDialog.addEventListener('keydown', (event) =>
174
- this.firstButtonKeydown(event)
175
- );
176
- this.$lastButtonInDialog.addEventListener('keydown', (event) =>
177
- this.lastButtonKeydown(event)
178
- );
179
-
180
- this.$calendarButton.addEventListener('click', (event) =>
181
- this.toggleDialog(event)
182
- );
183
-
184
- this.$dialog.addEventListener('keydown', (event) => {
185
- if (event.key === 'Escape') {
186
- this.closeDialog();
187
- event.preventDefault();
188
- event.stopPropagation();
189
- }
190
- });
191
-
192
- document.body.addEventListener('mouseup', (event) =>
193
- this.backgroundClick(event)
194
- );
195
-
196
- // populates calendar with initial dates, avoids Wave errors about null buttons
197
- this.updateCalendar();
198
- };
199
-
200
- DatePicker.prototype.createDialog = function () {
201
- const titleId = `datepicker-title-${this.$input.id}`;
202
- const $dialog = document.createElement('div');
203
-
204
- $dialog.id = this.id;
205
- $dialog.setAttribute('class', 'moj-datepicker__dialog');
206
- $dialog.setAttribute('role', 'dialog');
207
- $dialog.setAttribute('aria-modal', 'true');
208
- $dialog.setAttribute('aria-labelledby', titleId);
209
- $dialog.innerHTML = this.dialogTemplate(titleId);
210
- $dialog.hidden = true;
211
-
212
- return $dialog
213
- };
214
-
215
- DatePicker.prototype.createCalendar = function () {
216
- const $tbody = this.$dialog.querySelector('tbody');
217
- let dayCount = 0;
218
- for (let i = 0; i < 6; i++) {
219
- // create row
220
- const $row = $tbody.insertRow(i);
221
-
222
- for (let j = 0; j < 7; j++) {
223
- // create cell (day)
224
- const $cell = document.createElement('td');
225
- const $dateButton = document.createElement('button');
226
-
227
- $cell.appendChild($dateButton);
228
- $row.appendChild($cell);
229
-
230
- const calendarDay = new DSCalendarDay($dateButton, dayCount, i, j, this);
231
- calendarDay.init();
232
- this.calendarDays.push(calendarDay);
233
- dayCount++;
234
- }
235
- }
236
- };
237
-
238
- DatePicker.prototype.toggleTemplate = function () {
239
- return `<button class="moj-datepicker__toggle moj-js-datepicker-toggle" type="button" aria-haspopup="dialog" aria-controls="${this.id}" aria-expanded="false">
240
- <span class="govuk-visually-hidden">Choose date</span>
241
- <svg width="32" height="24" focusable="false" class="moj-datepicker-icon" aria-hidden="true" role="img" viewBox="0 0 22 22">
242
- <path
243
- fill="currentColor"
244
- fill-rule="evenodd"
245
- clip-rule="evenodd"
246
- d="M16.1333 2.93333H5.86668V4.4C5.86668 5.21002 5.21003 5.86667 4.40002 5.86667C3.59 5.86667 2.93335 5.21002 2.93335 4.4V2.93333H2C0.895431 2.93333 0 3.82877 0 4.93334V19.2667C0 20.3712 0.89543 21.2667 2 21.2667H20C21.1046 21.2667 22 20.3712 22 19.2667V4.93333C22 3.82876 21.1046 2.93333 20 2.93333H19.0667V4.4C19.0667 5.21002 18.41 5.86667 17.6 5.86667C16.79 5.86667 16.1333 5.21002 16.1333 4.4V2.93333ZM20.5333 8.06667H1.46665V18.8C1.46665 19.3523 1.91436 19.8 2.46665 19.8H19.5333C20.0856 19.8 20.5333 19.3523 20.5333 18.8V8.06667Z"
247
- ></path>
248
- <rect x="3.66669" width="1.46667" height="5.13333" rx="0.733333" fill="currentColor"></rect>
249
- <rect x="16.8667" width="1.46667" height="5.13333" rx="0.733333" fill="currentColor"></rect>
250
- </svg>
251
- </button>`
252
- };
253
-
254
- /**
255
- * HTML template for calendar dialog
256
- *
257
- * @param {string} [titleId] - Id attribute for dialog title
258
- * @returns {string}
259
- */
260
- DatePicker.prototype.dialogTemplate = function (titleId) {
261
- return `<div class="moj-datepicker__dialog-header">
262
- <div class="moj-datepicker__dialog-navbuttons">
263
- <button class="moj-datepicker__button moj-js-datepicker-prev-year">
264
- <span class="govuk-visually-hidden">Previous year</span>
265
- <svg width="44" height="40" viewBox="0 0 44 40" fill="none" fill="none" focusable="false" aria-hidden="true" role="img">
266
- <path fill-rule="evenodd" clip-rule="evenodd" d="M23.1643 20L28.9572 14.2071L27.5429 12.7929L20.3358 20L27.5429 27.2071L28.9572 25.7929L23.1643 20Z" fill="currentColor"/>
267
- <path fill-rule="evenodd" clip-rule="evenodd" d="M17.1643 20L22.9572 14.2071L21.5429 12.7929L14.3358 20L21.5429 27.2071L22.9572 25.7929L17.1643 20Z" fill="currentColor"/>
268
- </svg>
269
- </button>
270
-
271
- <button class="moj-datepicker__button moj-js-datepicker-prev-month">
272
- <span class="govuk-visually-hidden">Previous month</span>
273
- <svg width="44" height="40" viewBox="0 0 44 40" fill="none" focusable="false" aria-hidden="true" role="img">
274
- <path fill-rule="evenodd" clip-rule="evenodd" d="M20.5729 20L25.7865 14.2071L24.5137 12.7929L18.0273 20L24.5137 27.2071L25.7865 25.7929L20.5729 20Z" fill="currentColor"/>
275
- </svg>
276
- </button>
277
- </div>
278
-
279
- <h2 id="${titleId}" class="moj-datepicker__dialog-title moj-js-datepicker-month-year" aria-live="polite">June 2020</h2>
280
-
281
- <div class="moj-datepicker__dialog-navbuttons">
282
- <button class="moj-datepicker__button moj-js-datepicker-next-month">
283
- <span class="govuk-visually-hidden">Next month</span>
284
- <svg width="44" height="40" viewBox="0 0 44 40" fill="none" focusable="false" aria-hidden="true" role="img">
285
- <path fill-rule="evenodd" clip-rule="evenodd" d="M23.4271 20L18.2135 14.2071L19.4863 12.7929L25.9727 20L19.4863 27.2071L18.2135 25.7929L23.4271 20Z" fill="currentColor"/>
286
- </svg>
287
- </button>
288
-
289
- <button class="moj-datepicker__button moj-js-datepicker-next-year">
290
- <span class="govuk-visually-hidden">Next year</span>
291
- <svg width="44" height="40" viewBox="0 0 44 40" fill="none" fill="none" focusable="false" aria-hidden="true" role="img">
292
- <path fill-rule="evenodd" clip-rule="evenodd" d="M20.8357 20L15.0428 14.2071L16.4571 12.7929L23.6642 20L16.4571 27.2071L15.0428 25.7929L20.8357 20Z" fill="currentColor"/>
293
- <path fill-rule="evenodd" clip-rule="evenodd" d="M26.8357 20L21.0428 14.2071L22.4571 12.7929L29.6642 20L22.4571 27.2071L21.0428 25.7929L26.8357 20Z" fill="currentColor"/>
294
- </svg>
295
- </button>
296
- </div>
297
- </div>
298
-
299
- <table class="moj-datepicker__calendar moj-js-datepicker-grid" role="grid" aria-labelledby="${titleId}">
300
- <thead>
301
- <tr></tr>
302
- </thead>
303
-
304
- <tbody></tbody>
305
- </table>
306
-
307
- <div class="govuk-button-group">
308
- <button type="button" class="govuk-button moj-js-datepicker-ok">Select</button>
309
- <button type="button" class="govuk-button govuk-button--secondary moj-js-datepicker-cancel">Close</button>
310
- </div>`
311
- };
312
-
313
- DatePicker.prototype.createCalendarHeaders = function () {
314
- this.dayLabels.forEach((day) => {
315
- const html = `<th scope="col"><span aria-hidden="true">${day.substring(0, 3)}</span><span class="govuk-visually-hidden">${day}</span></th>`;
316
- const $headerRow = this.$dialog.querySelector('thead > tr');
317
- $headerRow.insertAdjacentHTML('beforeend', html);
318
- });
319
- };
320
-
321
- /**
322
- * Pads given number with leading zeros
323
- *
324
- * @param {number} value - The value to be padded
325
- * @param {number} length - The length in characters of the output
326
- * @returns {string}
327
- */
328
- DatePicker.prototype.leadingZeros = function (value, length = 2) {
329
- let ret = value.toString();
330
-
331
- while (ret.length < length) {
332
- ret = `0${ret}`;
333
- }
334
-
335
- return ret
336
- };
337
-
338
- DatePicker.prototype.setOptions = function () {
339
- this.setMinAndMaxDatesOnCalendar();
340
- this.setExcludedDates();
341
- this.setExcludedDays();
342
- this.setLeadingZeros();
343
- this.setWeekStartDay();
344
- };
345
-
346
- DatePicker.prototype.setMinAndMaxDatesOnCalendar = function () {
347
- if (this.config.minDate) {
348
- this.minDate = this.formattedDateFromString(this.config.minDate, null);
349
- if (this.minDate && this.currentDate < this.minDate) {
350
- this.currentDate = this.minDate;
351
- }
352
- }
353
-
354
- if (this.config.maxDate) {
355
- this.maxDate = this.formattedDateFromString(this.config.maxDate, null);
356
- if (this.maxDate && this.currentDate > this.maxDate) {
357
- this.currentDate = this.maxDate;
358
- }
359
- }
360
- };
361
-
362
- DatePicker.prototype.setExcludedDates = function () {
363
- if (this.config.excludedDates) {
364
- this.excludedDates = this.config.excludedDates
365
- .replace(/\s+/, ' ')
366
- .split(' ')
367
- .map((item) => {
368
- return item.includes('-')
369
- ? this.parseDateRangeString(item)
370
- : this.formattedDateFromString(item)
371
- })
372
- .flat()
373
- .filter((item) => item);
374
- }
375
- };
376
-
377
- /*
378
- * Parses a daterange string into an array of dates
379
- * @param {String} datestring - A daterange string in the format "dd/mm/yyyy-dd/mm/yyyy"
380
- * @returns {Date[]}
381
- */
382
- DatePicker.prototype.parseDateRangeString = function (datestring) {
383
- const dates = [];
384
- const [startDate, endDate] = datestring
385
- .split('-')
386
- .map((d) => this.formattedDateFromString(d, null));
387
-
388
- if (startDate && endDate) {
389
- const date = new Date(startDate.getTime());
390
- /* eslint-disable no-unmodified-loop-condition */
391
- while (date <= endDate) {
392
- dates.push(new Date(date));
393
- date.setDate(date.getDate() + 1);
394
- }
395
- /* eslint-enable no-unmodified-loop-condition */
396
- }
397
- return dates
398
- };
399
-
400
- DatePicker.prototype.setExcludedDays = function () {
401
- if (this.config.excludedDays) {
402
- // lowercase and arrange dayLabels to put indexOf sunday == 0 for comparison
403
- // with getDay() function
404
- const weekDays = this.dayLabels.map((item) => item.toLowerCase());
405
- if (this.config.weekStartDay === 'monday') {
406
- weekDays.unshift(weekDays.pop());
407
- }
408
-
409
- this.excludedDays = this.config.excludedDays
410
- .replace(/\s+/, ' ')
411
- .toLowerCase()
412
- .split(' ')
413
- .map((item) => weekDays.indexOf(item))
414
- .filter((item) => item !== -1);
415
- }
416
- };
417
-
418
- DatePicker.prototype.setLeadingZeros = function () {
419
- if (typeof this.config.leadingZeros !== 'boolean') {
420
- if (this.config.leadingZeros.toLowerCase() === 'true') {
421
- this.config.leadingZeros = true;
422
- return
423
- }
424
- if (this.config.leadingZeros.toLowerCase() === 'false') {
425
- this.config.leadingZeros = false;
426
- }
427
- }
428
- };
429
-
430
- DatePicker.prototype.setWeekStartDay = function () {
431
- const weekStartDayParam = this.config.weekStartDay;
432
- if (weekStartDayParam && weekStartDayParam.toLowerCase() === 'sunday') {
433
- this.config.weekStartDay = 'sunday';
434
- // Rotate dayLabels array to put Sunday as the first item
435
- this.dayLabels.unshift(this.dayLabels.pop());
436
- } else {
437
- this.config.weekStartDay = 'monday';
438
- }
439
- };
440
-
441
- /**
442
- * Determine if a date is selecteable
443
- *
444
- * @param {Date} date - the date to check
445
- * @returns {boolean}
446
- */
447
- DatePicker.prototype.isExcludedDate = function (date) {
448
- // This comparison does not work correctly - it will exclude the mindate itself
449
- // see: https://github.com/ministryofjustice/moj-frontend/issues/923
450
- if (this.minDate && this.minDate > date) {
451
- return true
452
- }
453
-
454
- // This comparison works as expected - the maxdate will not be excluded
455
- if (this.maxDate && this.maxDate < date) {
456
- return true
457
- }
458
-
459
- for (const excludedDate of this.excludedDates) {
460
- if (date.toDateString() === excludedDate.toDateString()) {
461
- return true
462
- }
463
- }
464
-
465
- if (this.excludedDays.includes(date.getDay())) {
466
- return true
467
- }
468
-
469
- return false
470
- };
471
-
472
- /**
473
- * Get a Date object from a string
474
- *
475
- * @param {string} dateString - string in the format d/m/yyyy dd/mm/yyyy
476
- * @param {Date} fallback - date object to return if formatting fails
477
- * @returns {Date}
478
- */
479
- DatePicker.prototype.formattedDateFromString = function (
480
- dateString,
481
- fallback = new Date()
482
- ) {
483
- let formattedDate = null;
484
- // Accepts d/m/yyyy and dd/mm/yyyy
485
- const dateFormatPattern = /(\d{1,2})([-/,. ])(\d{1,2})\2(\d{4})/;
486
-
487
- if (!dateFormatPattern.test(dateString)) return fallback
488
-
489
- const match = dateString.match(dateFormatPattern);
490
- const day = match[1];
491
- const month = match[3];
492
- const year = match[4];
493
-
494
- formattedDate = new Date(`${year}-${month}-${day}`);
495
- if (formattedDate instanceof Date && !isNaN(formattedDate)) {
496
- return formattedDate
497
- }
498
- return fallback
499
- };
500
-
501
- /**
502
- * Get a formatted date string from a Date object
503
- *
504
- * @param {Date} date - date to format to a string
505
- * @returns {string}
506
- */
507
- DatePicker.prototype.formattedDateFromDate = function (date) {
508
- if (this.config.leadingZeros) {
509
- return `${this.leadingZeros(date.getDate())}/${this.leadingZeros(date.getMonth() + 1)}/${date.getFullYear()}`
510
- }
511
-
512
- return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`
513
- };
514
-
515
- /**
516
- * Get a human readable date in the format Monday 2 March 2024
517
- *
518
- * @param {Date} date - date to format
519
- * @returns {string}
520
- */
521
- DatePicker.prototype.formattedDateHuman = function (date) {
522
- return `${this.dayLabels[(date.getDay() + 6) % 7]} ${date.getDate()} ${this.monthLabels[date.getMonth()]} ${date.getFullYear()}`
523
- };
524
-
525
- DatePicker.prototype.backgroundClick = function (event) {
526
- if (
527
- this.isOpen() &&
528
- !this.$dialog.contains(event.target) &&
529
- !this.$input.contains(event.target) &&
530
- !this.$calendarButton.contains(event.target)
531
- ) {
532
- event.preventDefault();
533
- this.closeDialog();
534
- }
535
- };
536
-
537
- DatePicker.prototype.firstButtonKeydown = function (event) {
538
- if (event.key === 'Tab' && event.shiftKey) {
539
- this.$lastButtonInDialog.focus();
540
- event.preventDefault();
541
- }
542
- };
543
-
544
- DatePicker.prototype.lastButtonKeydown = function (event) {
545
- if (event.key === 'Tab' && !event.shiftKey) {
546
- this.$firstButtonInDialog.focus();
547
- event.preventDefault();
548
- }
549
- };
550
-
551
- // render calendar
552
- DatePicker.prototype.updateCalendar = function () {
553
- this.$dialogTitle.innerHTML = `${this.monthLabels[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
554
-
555
- const day = this.currentDate;
556
- const firstOfMonth = new Date(day.getFullYear(), day.getMonth(), 1);
557
- let dayOfWeek;
558
-
559
- if (this.config.weekStartDay === 'monday') {
560
- dayOfWeek = firstOfMonth.getDay() === 0 ? 6 : firstOfMonth.getDay() - 1; // Change logic to make Monday first day of week, i.e. 0
561
- } else {
562
- dayOfWeek = firstOfMonth.getDay();
563
- }
564
-
565
- firstOfMonth.setDate(firstOfMonth.getDate() - dayOfWeek);
566
-
567
- const thisDay = new Date(firstOfMonth);
568
-
569
- // loop through our days
570
- for (let i = 0; i < this.calendarDays.length; i++) {
571
- const hidden = thisDay.getMonth() !== day.getMonth();
572
- const disabled = this.isExcludedDate(thisDay);
573
-
574
- this.calendarDays[i].update(thisDay, hidden, disabled);
575
-
576
- thisDay.setDate(thisDay.getDate() + 1);
577
- }
578
- };
579
-
580
- DatePicker.prototype.setCurrentDate = function (focus = true) {
581
- const { currentDate } = this;
582
- this.calendarDays.forEach((calendarDay) => {
583
- calendarDay.button.classList.add('moj-datepicker__button');
584
- calendarDay.button.classList.add('moj-datepicker__calendar-day');
585
- calendarDay.button.setAttribute('tabindex', -1);
586
- calendarDay.button.classList.remove(this.selectedDayButtonClass);
587
- const calendarDayDate = calendarDay.date;
588
- calendarDayDate.setHours(0, 0, 0, 0);
589
-
590
- const today = new Date();
591
- today.setHours(0, 0, 0, 0);
592
-
593
- if (
594
- calendarDayDate.getTime() ===
595
- currentDate.getTime() /* && !calendarDay.button.disabled */
596
- ) {
597
- if (focus) {
598
- calendarDay.button.setAttribute('tabindex', 0);
599
- calendarDay.button.focus();
600
- calendarDay.button.classList.add(this.selectedDayButtonClass);
601
- }
602
- }
603
-
604
- if (
605
- this.inputDate &&
606
- calendarDayDate.getTime() === this.inputDate.getTime()
607
- ) {
608
- calendarDay.button.classList.add(this.currentDayButtonClass);
609
- calendarDay.button.setAttribute('aria-current', 'date');
610
- } else {
611
- calendarDay.button.classList.remove(this.currentDayButtonClass);
612
- calendarDay.button.removeAttribute('aria-current');
613
- }
614
-
615
- if (calendarDayDate.getTime() === today.getTime()) {
616
- calendarDay.button.classList.add(this.todayButtonClass);
617
- } else {
618
- calendarDay.button.classList.remove(this.todayButtonClass);
619
- }
620
- });
621
-
622
- // if no date is tab-able, make the first non-disabled date tab-able
623
- if (!focus) {
624
- const enabledDays = this.calendarDays.filter((calendarDay) => {
625
- return (
626
- window.getComputedStyle(calendarDay.button).display === 'block' &&
627
- !calendarDay.button.disabled
628
- )
629
- });
630
-
631
- enabledDays[0].button.setAttribute('tabindex', 0);
632
-
633
- this.currentDate = enabledDays[0].date;
634
- }
635
- };
636
-
637
- DatePicker.prototype.selectDate = function (date) {
638
- if (this.isExcludedDate(date)) {
639
- return
640
- }
641
-
642
- this.$calendarButton.querySelector('span').innerText =
643
- `Choose date. Selected date is ${this.formattedDateHuman(date)}`;
644
- this.$input.value = this.formattedDateFromDate(date);
645
-
646
- const changeEvent = new Event('change', { bubbles: true, cancelable: true });
647
- this.$input.dispatchEvent(changeEvent);
648
-
649
- this.closeDialog();
650
- };
651
-
652
- DatePicker.prototype.isOpen = function () {
653
- return this.$dialog.classList.contains('moj-datepicker__dialog--open')
654
- };
655
-
656
- DatePicker.prototype.toggleDialog = function (event) {
657
- event.preventDefault();
658
- if (this.isOpen()) {
659
- this.closeDialog();
660
- } else {
661
- this.setMinAndMaxDatesOnCalendar();
662
- this.openDialog();
663
- }
664
- };
665
-
666
- DatePicker.prototype.openDialog = function () {
667
- this.$dialog.hidden = false;
668
- this.$dialog.classList.add('moj-datepicker__dialog--open');
669
- this.$calendarButton.setAttribute('aria-expanded', 'true');
670
-
671
- // position the dialog
672
- // if input is wider than dialog pin it to the right
673
- if (this.$input.offsetWidth > this.$dialog.offsetWidth) {
674
- this.$dialog.style.right = `0px`;
675
- }
676
- this.$dialog.style.top = `${this.$input.offsetHeight + 3}px`;
677
-
678
- // get the date from the input element
679
- this.inputDate = this.formattedDateFromString(this.$input.value);
680
- this.currentDate = this.inputDate;
681
- this.currentDate.setHours(0, 0, 0, 0);
682
-
683
- this.updateCalendar();
684
- this.setCurrentDate();
685
- };
686
-
687
- DatePicker.prototype.closeDialog = function () {
688
- this.$dialog.hidden = true;
689
- this.$dialog.classList.remove('moj-datepicker__dialog--open');
690
- this.$calendarButton.setAttribute('aria-expanded', 'false');
691
- this.$calendarButton.focus();
692
- };
693
-
694
- DatePicker.prototype.goToDate = function (date, focus) {
695
- const current = this.currentDate;
696
- this.currentDate = date;
697
-
698
- if (
699
- current.getMonth() !== this.currentDate.getMonth() ||
700
- current.getFullYear() !== this.currentDate.getFullYear()
701
- ) {
702
- this.updateCalendar();
703
- }
704
-
705
- this.setCurrentDate(focus);
706
- };
707
-
708
- // day navigation
709
- DatePicker.prototype.focusNextDay = function () {
710
- const date = new Date(this.currentDate);
711
- date.setDate(date.getDate() + 1);
712
- this.goToDate(date);
713
- };
714
-
715
- DatePicker.prototype.focusPreviousDay = function () {
716
- const date = new Date(this.currentDate);
717
- date.setDate(date.getDate() - 1);
718
- this.goToDate(date);
719
- };
720
-
721
- // week navigation
722
- DatePicker.prototype.focusNextWeek = function () {
723
- const date = new Date(this.currentDate);
724
- date.setDate(date.getDate() + 7);
725
- this.goToDate(date);
726
- };
727
-
728
- DatePicker.prototype.focusPreviousWeek = function () {
729
- const date = new Date(this.currentDate);
730
- date.setDate(date.getDate() - 7);
731
- this.goToDate(date);
732
- };
733
-
734
- DatePicker.prototype.focusFirstDayOfWeek = function () {
735
- const date = new Date(this.currentDate);
736
- const firstDayOfWeekIndex = this.config.weekStartDay === 'sunday' ? 0 : 1;
737
- const dayOfWeek = date.getDay();
738
- const diff =
739
- dayOfWeek >= firstDayOfWeekIndex
740
- ? dayOfWeek - firstDayOfWeekIndex
741
- : 6 - dayOfWeek;
742
-
743
- date.setDate(date.getDate() - diff);
744
- date.setHours(0, 0, 0, 0);
745
-
746
- this.goToDate(date);
747
- };
748
-
749
- DatePicker.prototype.focusLastDayOfWeek = function () {
750
- const date = new Date(this.currentDate);
751
- const lastDayOfWeekIndex = this.config.weekStartDay === 'sunday' ? 6 : 0;
752
- const dayOfWeek = date.getDay();
753
- const diff =
754
- dayOfWeek <= lastDayOfWeekIndex
755
- ? lastDayOfWeekIndex - dayOfWeek
756
- : 7 - dayOfWeek;
757
-
758
- date.setDate(date.getDate() + diff);
759
- date.setHours(0, 0, 0, 0);
760
-
761
- this.goToDate(date);
762
- };
763
-
764
- // month navigation
765
- DatePicker.prototype.focusNextMonth = function (event, focus = true) {
766
- event.preventDefault();
767
- const date = new Date(this.currentDate);
768
- date.setMonth(date.getMonth() + 1, 1);
769
- this.goToDate(date, focus);
770
- };
771
-
772
- DatePicker.prototype.focusPreviousMonth = function (event, focus = true) {
773
- event.preventDefault();
774
- const date = new Date(this.currentDate);
775
- date.setMonth(date.getMonth() - 1, 1);
776
- this.goToDate(date, focus);
777
- };
778
-
779
- // year navigation
780
- DatePicker.prototype.focusNextYear = function (event, focus = true) {
781
- event.preventDefault();
782
- const date = new Date(this.currentDate);
783
- date.setFullYear(date.getFullYear() + 1, date.getMonth(), 1);
784
- this.goToDate(date, focus);
785
- };
786
-
787
- DatePicker.prototype.focusPreviousYear = function (event, focus = true) {
788
- event.preventDefault();
789
- const date = new Date(this.currentDate);
790
- date.setFullYear(date.getFullYear() - 1, date.getMonth(), 1);
791
- this.goToDate(date, focus);
792
- };
793
-
794
- /**
795
- * Parse dataset
796
- *
797
- * @param {Schema} schema - Component class
798
- * @param {DOMStringMap} dataset - HTML element dataset
799
- * @returns {object} Normalised dataset
800
- */
801
- DatePicker.prototype.parseDataset = function (schema, dataset) {
802
- const parsed = {};
803
-
804
- for (const [field, ,] of Object.entries(schema.properties)) {
805
- if (field in dataset) {
806
- parsed[field] = dataset[field];
807
- }
808
- }
809
-
810
- return parsed
811
- };
812
-
813
- /**
814
- * Config merging function
815
- *
816
- * Takes any number of objects and combines them together, with
817
- * greatest priority on the LAST item passed in.
818
- *
819
- * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
820
- * @returns {{ [key: string]: unknown }} A merged config object
821
- */
822
- DatePicker.prototype.mergeConfigs = function (...configObjects) {
823
- const formattedConfigObject = {};
824
-
825
- // Loop through each of the passed objects
826
- for (const configObject of configObjects) {
827
- for (const key of Object.keys(configObject)) {
828
- const option = formattedConfigObject[key];
829
- const override = configObject[key];
830
-
831
- // Push their keys one-by-one into formattedConfigObject. Any duplicate
832
- // keys with object values will be merged, otherwise the new value will
833
- // override the existing value.
834
- if (typeof option === 'object' && typeof override === 'object') {
835
- // @ts-expect-error Index signature for type 'string' is missing
836
- formattedConfigObject[key] = this.mergeConfigs(option, override);
837
- } else {
838
- formattedConfigObject[key] = override;
839
- }
840
- }
841
- }
842
-
843
- return formattedConfigObject
844
- };
845
-
846
- /**
847
- *
848
- * @param {HTMLElement} button
849
- * @param {number} index
850
- * @param {number} row
851
- * @param {number} column
852
- * @param {DatePicker} picker
853
- * @class
854
- */
855
- function DSCalendarDay(button, index, row, column, picker) {
856
- this.index = index;
857
- this.row = row;
858
- this.column = column;
859
- this.button = button;
860
- this.picker = picker;
861
-
862
- this.date = new Date();
863
- }
864
-
865
- DSCalendarDay.prototype.init = function () {
866
- this.button.addEventListener('keydown', this.keyPress.bind(this));
867
- this.button.addEventListener('click', this.click.bind(this));
868
- };
869
-
870
- /**
871
- * @param {Date} day - the Date for the calendar day
872
- * @param {boolean} hidden - visibility of the day
873
- * @param {boolean} disabled - is the day selectable or excluded
874
- */
875
- DSCalendarDay.prototype.update = function (day, hidden, disabled) {
876
- const label = day.getDate();
877
- let accessibleLabel = this.picker.formattedDateHuman(day);
878
-
879
- if (disabled) {
880
- this.button.setAttribute('aria-disabled', true);
881
- accessibleLabel = `Excluded date, ${accessibleLabel}`;
882
- } else {
883
- this.button.removeAttribute('aria-disabled');
884
- }
885
-
886
- if (hidden) {
887
- this.button.style.display = 'none';
888
- } else {
889
- this.button.style.display = 'block';
890
- }
891
- this.button.setAttribute(
892
- 'data-testid',
893
- this.picker.formattedDateFromDate(day)
894
- );
895
-
896
- this.button.innerHTML = `<span class="govuk-visually-hidden">${accessibleLabel}</span><span aria-hidden="true">${label}</span>`;
897
- this.date = new Date(day);
898
- };
899
-
900
- DSCalendarDay.prototype.click = function (event) {
901
- this.picker.goToDate(this.date);
902
- this.picker.selectDate(this.date);
903
-
904
- event.stopPropagation();
905
- event.preventDefault();
906
- };
907
-
908
- DSCalendarDay.prototype.keyPress = function (event) {
909
- let calendarNavKey = true;
910
-
911
- switch (event.key) {
912
- case 'ArrowLeft':
913
- this.picker.focusPreviousDay();
914
- break
915
- case 'ArrowRight':
916
- this.picker.focusNextDay();
917
- break
918
- case 'ArrowUp':
919
- this.picker.focusPreviousWeek();
920
- break
921
- case 'ArrowDown':
922
- this.picker.focusNextWeek();
923
- break
924
- case 'Home':
925
- this.picker.focusFirstDayOfWeek();
926
- break
927
- case 'End':
928
- this.picker.focusLastDayOfWeek();
929
- break
930
- case 'PageUp':
931
- // eslint-disable-next-line no-unused-expressions
932
- event.shiftKey
933
- ? this.picker.focusPreviousYear(event)
934
- : this.picker.focusPreviousMonth(event);
935
- break
936
- case 'PageDown':
937
- // eslint-disable-next-line no-unused-expressions
938
- event.shiftKey
939
- ? this.picker.focusNextYear(event)
940
- : this.picker.focusNextMonth(event);
941
- break
942
- default:
943
- calendarNavKey = false;
944
- break
945
- }
946
-
947
- if (calendarNavKey) {
948
- event.preventDefault();
949
- event.stopPropagation();
950
- }
951
- };
952
-
953
- /**
954
- * Schema for component config
955
- *
956
- * @typedef {object} Schema
957
- * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
958
- */
959
-
960
- /**
961
- * Schema property for component config
962
- *
963
- * @typedef {object} SchemaProperty
964
- * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
965
- */
966
-
967
- exports.DatePicker = DatePicker;
968
-
969
- }));
970
- //# sourceMappingURL=date-picker.js.map