@expressivecss/expressive 0.9.1 → 0.10.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 (188) hide show
  1. package/CHANGELOG.md +157 -1
  2. package/README.md +178 -25
  3. package/dist/css/expressive.css +365 -1222
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  8. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  9. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  10. package/dist/js/chunks/chunk-7MM4VJJO.mjs +509 -0
  11. package/dist/js/chunks/chunk-7MM4VJJO.mjs.map +7 -0
  12. package/dist/js/chunks/chunk-7UXHSYIA.mjs +183 -0
  13. package/dist/js/chunks/chunk-7UXHSYIA.mjs.map +7 -0
  14. package/dist/js/chunks/chunk-A3BJA7V4.mjs +28 -0
  15. package/dist/js/chunks/chunk-A3BJA7V4.mjs.map +7 -0
  16. package/dist/js/chunks/chunk-AOGFNMXS.mjs +691 -0
  17. package/dist/js/chunks/chunk-AOGFNMXS.mjs.map +7 -0
  18. package/dist/js/chunks/chunk-D4UZ4HFN.mjs +560 -0
  19. package/dist/js/chunks/chunk-D4UZ4HFN.mjs.map +7 -0
  20. package/dist/js/chunks/chunk-EP3CVKB5.mjs +88 -0
  21. package/dist/js/chunks/chunk-EP3CVKB5.mjs.map +7 -0
  22. package/dist/js/chunks/chunk-G576EP5O.mjs +459 -0
  23. package/dist/js/chunks/chunk-G576EP5O.mjs.map +7 -0
  24. package/dist/js/chunks/chunk-GY2Z5CJ2.mjs +320 -0
  25. package/dist/js/chunks/chunk-GY2Z5CJ2.mjs.map +7 -0
  26. package/dist/js/chunks/chunk-JKNPQQWY.mjs +191 -0
  27. package/dist/js/chunks/chunk-JKNPQQWY.mjs.map +7 -0
  28. package/dist/js/chunks/chunk-JNPIWFJT.mjs +365 -0
  29. package/dist/js/chunks/chunk-JNPIWFJT.mjs.map +7 -0
  30. package/dist/js/chunks/chunk-JODJAGFS.mjs +267 -0
  31. package/dist/js/chunks/chunk-JODJAGFS.mjs.map +7 -0
  32. package/dist/js/chunks/chunk-JR24UB4X.mjs +100 -0
  33. package/dist/js/chunks/chunk-JR24UB4X.mjs.map +7 -0
  34. package/dist/js/chunks/chunk-KR44BSYQ.mjs +55 -0
  35. package/dist/js/chunks/chunk-KR44BSYQ.mjs.map +7 -0
  36. package/dist/js/chunks/chunk-LZVBJ33S.mjs +102 -0
  37. package/dist/js/chunks/chunk-LZVBJ33S.mjs.map +7 -0
  38. package/dist/js/chunks/chunk-MISGHDUU.mjs +326 -0
  39. package/dist/js/chunks/chunk-MISGHDUU.mjs.map +7 -0
  40. package/dist/js/chunks/chunk-N3UHGN3N.mjs +175 -0
  41. package/dist/js/chunks/chunk-N3UHGN3N.mjs.map +7 -0
  42. package/dist/js/chunks/chunk-PB5L3EXF.mjs +7 -0
  43. package/dist/js/chunks/chunk-PB5L3EXF.mjs.map +7 -0
  44. package/dist/js/chunks/chunk-PIIAEYAJ.mjs +85 -0
  45. package/dist/js/chunks/chunk-PIIAEYAJ.mjs.map +7 -0
  46. package/dist/js/chunks/chunk-QSQ24FYT.mjs +59 -0
  47. package/dist/js/chunks/chunk-QSQ24FYT.mjs.map +7 -0
  48. package/dist/js/chunks/chunk-QYCYP443.mjs +1111 -0
  49. package/dist/js/chunks/chunk-QYCYP443.mjs.map +7 -0
  50. package/dist/js/chunks/chunk-SLFNPY5M.mjs +78 -0
  51. package/dist/js/chunks/chunk-SLFNPY5M.mjs.map +7 -0
  52. package/dist/js/chunks/chunk-TK6PX2GC.mjs +663 -0
  53. package/dist/js/chunks/chunk-TK6PX2GC.mjs.map +7 -0
  54. package/dist/js/chunks/chunk-TYM6U5DY.mjs +76 -0
  55. package/dist/js/chunks/chunk-TYM6U5DY.mjs.map +7 -0
  56. package/dist/js/chunks/chunk-TZZ24ZQS.mjs +154 -0
  57. package/dist/js/chunks/chunk-TZZ24ZQS.mjs.map +7 -0
  58. package/dist/js/chunks/chunk-VAS6JQ6P.mjs +94 -0
  59. package/dist/js/chunks/chunk-VAS6JQ6P.mjs.map +7 -0
  60. package/dist/js/chunks/chunk-WWPAPHMA.mjs +1 -0
  61. package/dist/js/chunks/chunk-WWPAPHMA.mjs.map +7 -0
  62. package/dist/js/chunks/chunk-XDB3G6RL.mjs +114 -0
  63. package/dist/js/chunks/chunk-XDB3G6RL.mjs.map +7 -0
  64. package/dist/js/chunks/chunk-XO5WX3TK.mjs +226 -0
  65. package/dist/js/chunks/chunk-XO5WX3TK.mjs.map +7 -0
  66. package/dist/js/chunks/chunk-YFXEJ7GR.mjs +286 -0
  67. package/dist/js/chunks/chunk-YFXEJ7GR.mjs.map +7 -0
  68. package/dist/js/chunks/chunk-ZIZVUWRL.mjs +120 -0
  69. package/dist/js/chunks/chunk-ZIZVUWRL.mjs.map +7 -0
  70. package/dist/js/chunks/chunk-ZZBZI2XN.mjs +338 -0
  71. package/dist/js/chunks/chunk-ZZBZI2XN.mjs.map +7 -0
  72. package/dist/js/expressive.cjs +747 -754
  73. package/dist/js/expressive.cjs.map +4 -4
  74. package/dist/js/expressive.js +747 -754
  75. package/dist/js/expressive.js.map +4 -4
  76. package/dist/js/expressive.min.js +11 -15
  77. package/dist/js/expressive.min.js.map +4 -4
  78. package/dist/js/expressive.mjs +83 -7490
  79. package/dist/js/expressive.mjs.map +4 -4
  80. package/dist/js/internal/behaviors/bottomSheets.mjs +9 -0
  81. package/dist/js/internal/behaviors/bottomSheets.mjs.map +7 -0
  82. package/dist/js/internal/behaviors/dialogs.mjs +8 -0
  83. package/dist/js/internal/behaviors/dialogs.mjs.map +7 -0
  84. package/dist/js/internal/behaviors/forms.mjs +8 -0
  85. package/dist/js/internal/behaviors/forms.mjs.map +7 -0
  86. package/dist/js/internal/behaviors/sheetDrag.mjs +8 -0
  87. package/dist/js/internal/behaviors/sheetDrag.mjs.map +7 -0
  88. package/dist/js/internal/behaviors/sideSheets.mjs +9 -0
  89. package/dist/js/internal/behaviors/sideSheets.mjs.map +7 -0
  90. package/dist/js/internal/components/appBar.mjs +8 -0
  91. package/dist/js/internal/components/appBar.mjs.map +7 -0
  92. package/dist/js/internal/components/autocomplete.mjs +10 -0
  93. package/dist/js/internal/components/autocomplete.mjs.map +7 -0
  94. package/dist/js/internal/components/buttonGroup.mjs +8 -0
  95. package/dist/js/internal/components/buttonGroup.mjs.map +7 -0
  96. package/dist/js/internal/components/buttons.mjs +9 -0
  97. package/dist/js/internal/components/buttons.mjs.map +7 -0
  98. package/dist/js/internal/components/cards.mjs +13 -0
  99. package/dist/js/internal/components/cards.mjs.map +7 -0
  100. package/dist/js/internal/components/carousel.mjs +9 -0
  101. package/dist/js/internal/components/carousel.mjs.map +7 -0
  102. package/dist/js/internal/components/characterCounter.mjs +8 -0
  103. package/dist/js/internal/components/characterCounter.mjs.map +7 -0
  104. package/dist/js/internal/components/chips.mjs +11 -0
  105. package/dist/js/internal/components/chips.mjs.map +7 -0
  106. package/dist/js/internal/components/datepicker.mjs +12 -0
  107. package/dist/js/internal/components/datepicker.mjs.map +7 -0
  108. package/dist/js/internal/components/expandingCard.mjs +11 -0
  109. package/dist/js/internal/components/expandingCard.mjs.map +7 -0
  110. package/dist/js/internal/components/index.mjs +88 -0
  111. package/dist/js/internal/components/index.mjs.map +7 -0
  112. package/dist/js/internal/components/lightbox.mjs +9 -0
  113. package/dist/js/internal/components/lightbox.mjs.map +7 -0
  114. package/dist/js/internal/components/menu.mjs +9 -0
  115. package/dist/js/internal/components/menu.mjs.map +7 -0
  116. package/dist/js/internal/components/navigationRail.mjs +8 -0
  117. package/dist/js/internal/components/navigationRail.mjs.map +7 -0
  118. package/dist/js/internal/components/registry.mjs +33 -0
  119. package/dist/js/internal/components/registry.mjs.map +7 -0
  120. package/dist/js/internal/components/scrollspy.mjs +8 -0
  121. package/dist/js/internal/components/scrollspy.mjs.map +7 -0
  122. package/dist/js/internal/components/select.mjs +10 -0
  123. package/dist/js/internal/components/select.mjs.map +7 -0
  124. package/dist/js/internal/components/slider.mjs +9 -0
  125. package/dist/js/internal/components/slider.mjs.map +7 -0
  126. package/dist/js/internal/components/snackbar.mjs +8 -0
  127. package/dist/js/internal/components/snackbar.mjs.map +7 -0
  128. package/dist/js/internal/components/tabs.mjs +10 -0
  129. package/dist/js/internal/components/tabs.mjs.map +7 -0
  130. package/dist/js/internal/components/timepicker.mjs +10 -0
  131. package/dist/js/internal/components/timepicker.mjs.map +7 -0
  132. package/dist/js/internal/components/tooltip.mjs +9 -0
  133. package/dist/js/internal/components/tooltip.mjs.map +7 -0
  134. package/dist/js/internal/core/component.mjs +7 -0
  135. package/dist/js/internal/core/component.mjs.map +7 -0
  136. package/dist/js/internal/core/utils.mjs +7 -0
  137. package/dist/js/internal/core/utils.mjs.map +7 -0
  138. package/dist/js/internal/plugins/dockedDisplayPlugin.mjs +8 -0
  139. package/dist/js/internal/plugins/dockedDisplayPlugin.mjs.map +7 -0
  140. package/dist/js/modular.mjs +113 -0
  141. package/dist/js/modular.mjs.map +7 -0
  142. package/dist/types/behaviors/sheetDrag.d.ts +1 -1
  143. package/dist/types/components/appBar.d.ts +1 -1
  144. package/dist/types/components/autocomplete.d.ts +9 -0
  145. package/dist/types/components/buttons.d.ts +4 -39
  146. package/dist/types/components/carousel.d.ts +10 -1
  147. package/dist/types/components/chips.d.ts +1 -0
  148. package/dist/types/components/datepicker.d.ts +7 -0
  149. package/dist/types/components/index.d.ts +0 -2
  150. package/dist/types/components/lightbox.d.ts +12 -0
  151. package/dist/types/components/menu.d.ts +3 -0
  152. package/dist/types/components/registry.d.ts +0 -4
  153. package/dist/types/components/select.d.ts +4 -0
  154. package/dist/types/components/slider.d.ts +2 -1
  155. package/dist/types/components/snackbar.d.ts +4 -2
  156. package/dist/types/components/tabs.d.ts +2 -1
  157. package/dist/types/components/timepicker.d.ts +5 -3
  158. package/dist/types/components/tooltip.d.ts +5 -3
  159. package/dist/types/index.d.ts +1 -8
  160. package/dist/types/modular.d.ts +28 -0
  161. package/package.json +16 -4
  162. package/src/sass/README.md +8 -0
  163. package/src/sass/components/_badges.scss +1 -6
  164. package/src/sass/components/_button-groups.scss +5 -1
  165. package/src/sass/components/_buttons.scss +8 -287
  166. package/src/sass/components/_chips.scss +17 -2
  167. package/src/sass/components/_dialog.scss +9 -4
  168. package/src/sass/components/_fab-menu.scss +11 -9
  169. package/src/sass/components/_icon-buttons.scss +0 -5
  170. package/src/sass/components/_index.scss +0 -5
  171. package/src/sass/components/_menu.scss +2 -2
  172. package/src/sass/components/_navigation-rail.scss +1 -1
  173. package/src/sass/components/_panes.scss +50 -78
  174. package/src/sass/components/_progress.scss +73 -13
  175. package/src/sass/components/_scrim.scss +4 -4
  176. package/src/sass/components/_side-sheet.scss +22 -12
  177. package/src/sass/components/_toolbar.scss +1 -4
  178. package/src/sass/components/_tooltip.scss +20 -2
  179. package/src/sass/components/forms/_input-fields.scss +5 -1
  180. package/src/sass/components/forms/_select.scss +1 -0
  181. package/src/sass/components/forms/_slider.scss +81 -25
  182. package/src/sass/custom.scss +38 -0
  183. package/dist/types/components/navigationDrawer.d.ts +0 -110
  184. package/src/sass/components/_banners.scss +0 -298
  185. package/src/sass/components/_bottom-app-bar.scss +0 -180
  186. package/src/sass/components/_floating-sheet.scss +0 -86
  187. package/src/sass/components/_navigation-drawer.scss +0 -476
  188. package/src/sass/components/_segmented-buttons.scss +0 -188
@@ -0,0 +1,1111 @@
1
+ import {
2
+ DockedDisplayPlugin
3
+ } from "./chunk-SLFNPY5M.mjs";
4
+ import {
5
+ FormSelect
6
+ } from "./chunk-G576EP5O.mjs";
7
+ import {
8
+ Utils
9
+ } from "./chunk-MISGHDUU.mjs";
10
+ import {
11
+ Component
12
+ } from "./chunk-QSQ24FYT.mjs";
13
+
14
+ // src/ts/components/datepicker.ts
15
+ var _defaults = {
16
+ // the default output format for the input field value
17
+ format: "mmm dd, yyyy",
18
+ // Used to create date object from current input string
19
+ parse: null,
20
+ // The initial condition if the datepicker is based on date range
21
+ isDateRange: false,
22
+ // The selector of the user specified date range end element
23
+ dateRangeEndEl: null,
24
+ // The initial condition if the datepicker is based on multiple date selection
25
+ isMultipleSelection: false,
26
+ // The initial date to view when first opened
27
+ defaultDate: null,
28
+ // The initial end date to view when first opened
29
+ defaultEndDate: null,
30
+ // Make the `defaultDate` the initial selected value
31
+ setDefaultDate: false,
32
+ // Make the `defaultEndDate` the initial selected value
33
+ setDefaultEndDate: false,
34
+ disableWeekends: false,
35
+ disableDayFn: null,
36
+ // First day of week (0: Sunday, 1: Monday etc)
37
+ firstDay: 0,
38
+ // The earliest date that can be selected
39
+ minDate: null,
40
+ // Thelatest date that can be selected
41
+ maxDate: null,
42
+ // Number of years either side, or array of upper/lower range
43
+ yearRange: 10,
44
+ // used internally (don't config outside)
45
+ minYear: 0,
46
+ maxYear: 9999,
47
+ minMonth: void 0,
48
+ maxMonth: void 0,
49
+ startRange: null,
50
+ endRange: null,
51
+ isRTL: false,
52
+ yearRangeReverse: false,
53
+ // Render the month after year in the calendar title
54
+ showMonthAfterYear: false,
55
+ // Render days of the calendar grid that fall in the next or previous month
56
+ showDaysInNextAndPreviousMonths: false,
57
+ // Specify if docked picker is in open state by default
58
+ openByDefault: false,
59
+ // Specify a DOM element to render the calendar in
60
+ container: null,
61
+ // Show clear button
62
+ showClearBtn: false,
63
+ // Autosubmit
64
+ autoSubmit: true,
65
+ // internationalization
66
+ i18n: {
67
+ cancel: "Cancel",
68
+ clear: "Clear",
69
+ done: "Ok",
70
+ previousMonth: "\u2039",
71
+ nextMonth: "\u203A",
72
+ months: [
73
+ "January",
74
+ "February",
75
+ "March",
76
+ "April",
77
+ "May",
78
+ "June",
79
+ "July",
80
+ "August",
81
+ "September",
82
+ "October",
83
+ "November",
84
+ "December"
85
+ ],
86
+ monthsShort: [
87
+ "Jan",
88
+ "Feb",
89
+ "Mar",
90
+ "Apr",
91
+ "May",
92
+ "Jun",
93
+ "Jul",
94
+ "Aug",
95
+ "Sep",
96
+ "Oct",
97
+ "Nov",
98
+ "Dec"
99
+ ],
100
+ weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
101
+ weekdaysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
102
+ weekdaysAbbrev: ["S", "M", "T", "W", "T", "F", "S"]
103
+ },
104
+ // events array
105
+ events: [],
106
+ // callback function
107
+ onSelect: null,
108
+ onDraw: null,
109
+ onInputInteraction: null,
110
+ displayPlugin: null,
111
+ displayPluginOptions: null,
112
+ onConfirm: null,
113
+ onCancel: null
114
+ };
115
+ var _Datepicker = class _Datepicker extends Component {
116
+ constructor(el, options) {
117
+ super(el, options, _Datepicker);
118
+ this.multiple = false;
119
+ /** Nesting depth of {@link _batchDraws}; > 0 means draws are being collected. */
120
+ this._drawDepth = 0;
121
+ /** Whether a draw was requested while batching. */
122
+ this._drawDirty = false;
123
+ /** Shape of the currently rendered controls; see {@link _controlsCacheKey}. */
124
+ this._controlsKey = null;
125
+ /** id linking the controls element to the table's aria-labelledby. */
126
+ this._randId = null;
127
+ this._focusedDate = null;
128
+ this._handleInputClick = (e) => {
129
+ if (e.type == "date") {
130
+ e.preventDefault();
131
+ }
132
+ this._batchDraws(() => {
133
+ this.setDateFromInput(e.target);
134
+ this.draw();
135
+ this.gotoDate(e.target === this.el ? this.date : this.endDate);
136
+ });
137
+ if (this.displayPlugin) this.displayPlugin.show();
138
+ if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
139
+ };
140
+ this._handleInputKeydown = (e) => {
141
+ if (e.key === Utils.keys.ENTER) {
142
+ e.preventDefault();
143
+ this._batchDraws(() => {
144
+ this.setDateFromInput(e.target);
145
+ this._focusedDate = e.target === this.endDateEl ? this.endDate : this.date;
146
+ this.draw();
147
+ });
148
+ if (this.displayPlugin) this.displayPlugin.show();
149
+ this._syncCalendarFocus(true);
150
+ if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
151
+ }
152
+ };
153
+ this._handleCalendarFocus = (event) => {
154
+ const button = event.target;
155
+ if (!button.matches(".datepicker-day-button")) return;
156
+ this._focusedDate = this._dayDate(button);
157
+ this._syncCalendarFocus();
158
+ };
159
+ this._handleCalendarKeydown = (event) => {
160
+ const button = event.target;
161
+ if (!button.matches(".datepicker-day-button") || event.altKey || event.ctrlKey || event.metaKey) return;
162
+ const date = this._dayDate(button);
163
+ const weekday = (date.getDay() - this.options.firstDay + 7) % 7;
164
+ const offsets = {
165
+ ArrowLeft: this.options.isRTL ? 1 : -1,
166
+ ArrowRight: this.options.isRTL ? -1 : 1,
167
+ ArrowUp: -7,
168
+ ArrowDown: 7,
169
+ Home: -weekday,
170
+ End: 6 - weekday
171
+ };
172
+ if (Object.hasOwn(offsets, event.key)) date.setDate(date.getDate() + offsets[event.key]);
173
+ else if (event.key === "PageUp" || event.key === "PageDown") {
174
+ const day = date.getDate();
175
+ date.setDate(1);
176
+ date.setMonth(date.getMonth() + (event.key === "PageUp" ? -1 : 1) * (event.shiftKey ? 12 : 1));
177
+ const last = new Date(date.getTime());
178
+ last.setMonth(last.getMonth() + 1, 0);
179
+ date.setDate(Math.min(day, last.getDate()));
180
+ } else return;
181
+ event.preventDefault();
182
+ const { minDate, maxDate, minYear, maxYear, minMonth, maxMonth } = this.options;
183
+ if (minDate && date < minDate) date.setTime(minDate.getTime());
184
+ if (maxDate && date > maxDate) date.setTime(maxDate.getTime());
185
+ if (date.getFullYear() < minYear || date.getFullYear() > maxYear || date.getFullYear() === minYear && date.getMonth() < (minMonth ?? 0) || date.getFullYear() === maxYear && date.getMonth() > (maxMonth ?? 11)) return;
186
+ this._focusedDate = date;
187
+ this.gotoDate(date);
188
+ };
189
+ this._handleCalendarClick = (e) => {
190
+ const target = e.target;
191
+ if (target.classList.contains("is-disabled")) return;
192
+ this._batchDraws(() => {
193
+ if (target.classList.contains("datepicker-day-button") && !target.classList.contains("is-empty") && !target.parentElement.classList.contains("is-disabled") && !target.parentElement.classList.contains("is-selection-disabled")) {
194
+ const selectedDate = new Date(
195
+ e.target.getAttribute("data-year"),
196
+ e.target.getAttribute("data-month"),
197
+ e.target.getAttribute("data-day")
198
+ );
199
+ if (!this.multiple || this.multiple && this.options.isMultipleSelection) {
200
+ this.setDate(selectedDate);
201
+ }
202
+ if (this.options.isDateRange) {
203
+ this._handleDateRangeCalendarClick(selectedDate);
204
+ }
205
+ if (this.options.autoSubmit) this._finishSelection();
206
+ } else if (target.closest(".month-prev")) {
207
+ this.prevMonth();
208
+ } else if (target.closest(".month-next")) {
209
+ this.nextMonth();
210
+ }
211
+ });
212
+ };
213
+ this._handleDateRangeCalendarClick = (date) => {
214
+ if (this.endDate == null || !_Datepicker._compareDates(date, this.endDate)) {
215
+ if (_Datepicker._isDate(this.date) && _Datepicker._comparePastDate(date, this.date)) {
216
+ return;
217
+ }
218
+ this.setDate(date, false, _Datepicker._isDate(this.date));
219
+ return;
220
+ }
221
+ this._clearDates();
222
+ this.draw();
223
+ };
224
+ this._handleClearClick = () => {
225
+ this._clearDates();
226
+ this.setInputValues();
227
+ };
228
+ this._clearDates = () => {
229
+ this.date = null;
230
+ this.endDate = null;
231
+ this.dates?.splice(0);
232
+ this.draw();
233
+ };
234
+ this._handleMonthChange = (e) => {
235
+ this.gotoMonth(e.target.value);
236
+ };
237
+ this._handleYearChange = (e) => {
238
+ this.gotoYear(e.target.value);
239
+ };
240
+ this._handleInputChange = (e) => {
241
+ let date;
242
+ const el = e.target;
243
+ if (e["detail"]?.firedBy === this) return;
244
+ if (el == this.endDateEl && !this.date) return;
245
+ if (this.options.parse) {
246
+ date = this.options.parse(
247
+ e.target.value,
248
+ typeof this.options.format === "function" ? this.options.format(new Date(this.el.value)) : this.options.format
249
+ );
250
+ } else {
251
+ date = new Date(Date.parse(e.target.value));
252
+ }
253
+ if (_Datepicker._isDate(date)) {
254
+ if (el == this.endDateEl && date < this.date) return this.setInputValue(el, this.endDate);
255
+ this.setDate(date, false, el == this.endDateEl, true);
256
+ if (e.type == "date") {
257
+ this.setDataDate(e, date);
258
+ this.setInputValues();
259
+ }
260
+ }
261
+ };
262
+ this._finishSelection = () => {
263
+ this.setInputValues();
264
+ };
265
+ this._confirm = () => {
266
+ this._finishSelection();
267
+ if (typeof this.options.onConfirm === "function") this.options.onConfirm.call(this);
268
+ };
269
+ this._cancel = () => {
270
+ if (typeof this.options.onCancel === "function") this.options.onCancel.call(this);
271
+ };
272
+ this.el["Expressive_Datepicker"] = this;
273
+ this.options = {
274
+ ..._Datepicker.defaults,
275
+ ...options
276
+ };
277
+ if (!!options && options.hasOwnProperty("i18n") && typeof options.i18n === "object") {
278
+ this.options.i18n = { ..._Datepicker.defaults.i18n, ...options.i18n };
279
+ }
280
+ if (this.options.minDate) this.options.minDate.setHours(0, 0, 0, 0);
281
+ if (this.options.maxDate) this.options.maxDate.setHours(0, 0, 0, 0);
282
+ this.id = Utils.guid();
283
+ if (this.options.isMultipleSelection) {
284
+ this.multiple = true;
285
+ this.dates = [];
286
+ this.dateEls = [el];
287
+ }
288
+ this._setupVariables();
289
+ this._insertHTMLIntoDOM();
290
+ this._setupEventHandlers();
291
+ if (!this.options.defaultDate) {
292
+ this.options.defaultDate = new Date(Date.parse(this.el.value));
293
+ }
294
+ const defDate = this.options.defaultDate;
295
+ if (_Datepicker._isDate(defDate)) {
296
+ if (this.options.setDefaultDate) {
297
+ this.setDate(defDate, true);
298
+ this.setInputValue(this.el, defDate);
299
+ } else {
300
+ this.gotoDate(defDate);
301
+ }
302
+ } else {
303
+ this.gotoDate(/* @__PURE__ */ new Date());
304
+ }
305
+ if (this.options.isDateRange) {
306
+ this.multiple = true;
307
+ const defEndDate = this.options.defaultEndDate;
308
+ if (_Datepicker._isDate(defEndDate)) {
309
+ if (this.options.setDefaultEndDate) {
310
+ this.setDate(defEndDate, true, true);
311
+ this.setInputValue(this.endDateEl, defEndDate);
312
+ }
313
+ }
314
+ }
315
+ if (this.options.displayPlugin) {
316
+ if (this.options.displayPlugin === "docked") this.displayPlugin = DockedDisplayPlugin.init(this.el, this.containerEl, this.options.displayPluginOptions);
317
+ }
318
+ }
319
+ static get defaults() {
320
+ return _defaults;
321
+ }
322
+ /**
323
+ * Initializes instances of Datepicker.
324
+ * @param els HTML elements.
325
+ * @param options Component options.
326
+ */
327
+ static init(els, options = {}) {
328
+ return super.init(els, options, _Datepicker);
329
+ }
330
+ /**
331
+ * Escape a value for interpolation into the calendar markup.
332
+ *
333
+ * `draw()` assembles the calendar as an HTML string, so every value that is
334
+ * not a number this class computed itself has to go through here. In
335
+ * practice that means the i18n strings: month and weekday names are the one
336
+ * part of a datepicker that routinely comes from a translation file, and a
337
+ * translation file is as trustworthy as whatever wrote it.
338
+ */
339
+ static _escape(value) {
340
+ return String(value ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
341
+ }
342
+ static _isDate(obj) {
343
+ return /Date/.test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
344
+ }
345
+ static _isWeekend(date) {
346
+ const day = date.getDay();
347
+ return day === 0 || day === 6;
348
+ }
349
+ /**
350
+ * @deprecated as this function has no effect without any return statement or global parameter setter.
351
+ */
352
+ static _setToStartOfDay(date) {
353
+ if (_Datepicker._isDate(date)) date.setHours(0, 0, 0, 0);
354
+ }
355
+ static _getDaysInMonth(year, month) {
356
+ return [31, _Datepicker._isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
357
+ }
358
+ static _isLeapYear(year) {
359
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
360
+ }
361
+ static _compareDates(a, b) {
362
+ return a.getTime() === b.getTime();
363
+ }
364
+ static _compareWithinRange(day, date, dateEnd) {
365
+ return day.getTime() > date.getTime() && day.getTime() < dateEnd.getTime();
366
+ }
367
+ static _comparePastDate(a, b) {
368
+ return a.getTime() < b.getTime();
369
+ }
370
+ static getInstance(el) {
371
+ return el["Expressive_Datepicker"];
372
+ }
373
+ destroy() {
374
+ this._removeEventHandlers();
375
+ this.dateEls?.slice(1).forEach((input) => this._removeDateInput(input));
376
+ if (!this.options.dateRangeEndEl && this.endDateEl) this._removeDateInput(this.endDateEl);
377
+ this.displayPlugin?.destroy();
378
+ this.containerEl.remove();
379
+ this.destroySelects();
380
+ this.el["Expressive_Datepicker"] = void 0;
381
+ }
382
+ destroySelects() {
383
+ const oldYearSelect = this.calendarEl.querySelector(".orig-select-year");
384
+ if (oldYearSelect) {
385
+ FormSelect.getInstance(oldYearSelect).destroy();
386
+ }
387
+ const oldMonthSelect = this.calendarEl.querySelector(".orig-select-month");
388
+ if (oldMonthSelect) {
389
+ FormSelect.getInstance(oldMonthSelect).destroy();
390
+ }
391
+ }
392
+ _insertHTMLIntoDOM() {
393
+ if (this.el.type == "date") {
394
+ this.el.classList.add("datepicker-date-input");
395
+ }
396
+ this._renderDateInputFormat(this.el);
397
+ if (this.options.isDateRange) {
398
+ this.containerEl.classList.add("daterange");
399
+ if (!this.options.dateRangeEndEl) {
400
+ this.endDateEl = this.createDateInput();
401
+ this.endDateEl.classList.add("datepicker-end-date");
402
+ } else {
403
+ this.endDateEl = document.querySelector(this.options.dateRangeEndEl);
404
+ if (!this.endDateEl) {
405
+ console.warn("Specified date range end input element in dateRangeEndEl not found");
406
+ } else {
407
+ this._renderDateInputFormat(this.endDateEl);
408
+ }
409
+ }
410
+ }
411
+ Utils.createButton(this.footer, this.options.i18n.clear, ["datepicker-clear"], this.options.showClearBtn, this._handleClearClick);
412
+ if (!this.options.autoSubmit) {
413
+ Utils.createConfirmationContainer(this.footer, this.options.i18n.done, this.options.i18n.cancel, this._confirm, this._cancel);
414
+ }
415
+ if (this.options.container) {
416
+ const optEl = this.options.container;
417
+ this.options.container = optEl instanceof HTMLElement ? optEl : document.querySelector(optEl);
418
+ this.options.container.append(this.containerEl);
419
+ } else {
420
+ const appendTo = !this.endDateEl ? this.el : this.endDateEl;
421
+ if (!this.options.openByDefault) this.containerEl.setAttribute("style", "display: none; visibility: hidden;");
422
+ appendTo.parentElement.after(this.containerEl);
423
+ }
424
+ }
425
+ /**
426
+ * Renders the date input format into the optional `.datepicker-format` hint
427
+ * next to the input, if the consumer provided one.
428
+ */
429
+ _renderDateInputFormat(el) {
430
+ const formatEl = el.parentElement?.querySelector(".datepicker-format");
431
+ if (!formatEl) return;
432
+ formatEl.textContent = typeof this.options.format === "function" ? "" : String(this.options.format);
433
+ }
434
+ /**
435
+ * Gets a string representation of the given date.
436
+ */
437
+ toString(date = this.date, format = null) {
438
+ format = format || this.options.format;
439
+ if (typeof format === "function") return format(date);
440
+ if (!_Datepicker._isDate(date)) return "";
441
+ return this.formatDate(date, format);
442
+ }
443
+ /**
444
+ * Returns the formatted date.
445
+ */
446
+ formatDate(date, format) {
447
+ const formatArray = format.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g);
448
+ return formatArray.map((label) => this.formats[label] ? this.formats[label](date) : label).join("");
449
+ }
450
+ /**
451
+ * Sets date from input field.
452
+ */
453
+ setDateFromInput(el) {
454
+ const date = new Date(Date.parse(el.value));
455
+ this.setDate(date, false, el == this.endDateEl, true);
456
+ }
457
+ /**
458
+ * Set a date on the datepicker.
459
+ * @param date Date to set on the datepicker.
460
+ * @param preventOnSelect Undocumented as of 5 March 2018.
461
+ * @param isEndDate
462
+ * @param fromUserInput
463
+ */
464
+ setDate(date = null, preventOnSelect = false, isEndDate = false, fromUserInput = false) {
465
+ const selectedDate = this.validateDate(date);
466
+ if (!selectedDate) {
467
+ return;
468
+ }
469
+ _Datepicker._setToStartOfDay(selectedDate);
470
+ if (!this.options.isMultipleSelection) this.setSingleDate(selectedDate, isEndDate);
471
+ else if (!fromUserInput) this.setMultiDate(selectedDate);
472
+ this.gotoDate(selectedDate);
473
+ if (!preventOnSelect && typeof this.options.onSelect === "function") {
474
+ this.options.onSelect.call(this, selectedDate);
475
+ }
476
+ }
477
+ validateDate(date) {
478
+ if (!date) {
479
+ this._renderDateDisplay(date);
480
+ return this.draw();
481
+ }
482
+ if (typeof date === "string") {
483
+ date = new Date(Date.parse(date));
484
+ }
485
+ if (!_Datepicker._isDate(date)) {
486
+ return;
487
+ }
488
+ const min = this.options.minDate, max = this.options.maxDate;
489
+ if (_Datepicker._isDate(min) && date < min) {
490
+ date = min;
491
+ } else if (_Datepicker._isDate(max) && date > max) {
492
+ date = max;
493
+ }
494
+ return new Date(date.getTime());
495
+ }
496
+ /**
497
+ * Set a single date on the datepicker.
498
+ * @param date Date to set on the datepicker.
499
+ * @param isEndDate
500
+ */
501
+ setSingleDate(date, isEndDate) {
502
+ if (!isEndDate) {
503
+ this.date = date;
504
+ } else if (isEndDate) {
505
+ this.endDate = date;
506
+ }
507
+ }
508
+ /**
509
+ * Set a multi date on the datepicker.
510
+ * @param date Date to set on the datepicker.
511
+ */
512
+ setMultiDate(date) {
513
+ const selectedDate = this.dates?.find((item) => {
514
+ return item.getTime() === date.getTime() ? item : false;
515
+ });
516
+ if (!selectedDate) {
517
+ this.dates.push(date);
518
+ } else {
519
+ this.dates.splice(this.dates.indexOf(selectedDate), 1);
520
+ }
521
+ this.dates.sort((a, b) => a.getTime() < b.getTime() ? -1 : 0);
522
+ }
523
+ /**
524
+ * Sets the data-date attribute on the date input field
525
+ */
526
+ setDataDate(el, date) {
527
+ el.setAttribute("data-date", this.toString(date));
528
+ }
529
+ /**
530
+ * Sets dates on the input values.
531
+ */
532
+ setInputValues() {
533
+ if (!this.options?.isMultipleSelection) {
534
+ this.setInputValue(this.el, this.date);
535
+ if (this.options?.isDateRange) {
536
+ this.setInputValue(this.endDateEl, this.endDate);
537
+ }
538
+ return;
539
+ }
540
+ this.setMultipleSelectionInputValues();
541
+ }
542
+ setMultipleSelectionInputValues() {
543
+ const count = Math.max(1, this.dates.length);
544
+ for (const input of this.dateEls.splice(count)) this._removeDateInput(input);
545
+ while (this.dateEls.length < count) this.dateEls.push(this.createDateInput());
546
+ this.dateEls.forEach((input, index) => this.setInputValue(input, this.dates[index] ?? null));
547
+ }
548
+ /**
549
+ * Sets given date as the input value on the given element.
550
+ */
551
+ setInputValue(el, date) {
552
+ if (!el) return;
553
+ if (!_Datepicker._isDate(date)) {
554
+ el.value = "";
555
+ if (el.type === "date") el.setAttribute("data-date", "");
556
+ } else if (el.type == "date") {
557
+ this.setDataDate(el, date);
558
+ el.value = this.formatDate(date, "yyyy-mm-dd");
559
+ } else {
560
+ el.value = this.toString(date);
561
+ }
562
+ this.el.dispatchEvent(
563
+ new CustomEvent("change", {
564
+ bubbles: true,
565
+ cancelable: true,
566
+ composed: true,
567
+ detail: { firedBy: this }
568
+ })
569
+ );
570
+ }
571
+ /**
572
+ * Renders the date in the modal head section.
573
+ */
574
+ _renderDateDisplay(date, endDate = null) {
575
+ const displayDate = _Datepicker._isDate(date) ? date : /* @__PURE__ */ new Date();
576
+ if (!this.options.isDateRange) {
577
+ this.dateTextEl.textContent = this.formatDate(displayDate, "ddd, mmm d");
578
+ } else {
579
+ const displayEndDate = _Datepicker._isDate(endDate) ? endDate : /* @__PURE__ */ new Date();
580
+ this.dateTextEl.textContent = `${this.formatDate(displayDate, "mmm d")} - ${this.formatDate(displayEndDate, "mmm d")}`;
581
+ }
582
+ }
583
+ /**
584
+ * Change date view to a specific date on the datepicker.
585
+ * @param date Date to show on the datepicker.
586
+ */
587
+ gotoDate(date) {
588
+ let newCalendar = true;
589
+ if (!_Datepicker._isDate(date)) {
590
+ return;
591
+ }
592
+ if (this.calendars) {
593
+ const firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1), lastVisibleDate = new Date(
594
+ this.calendars[this.calendars.length - 1].year,
595
+ this.calendars[this.calendars.length - 1].month,
596
+ 1
597
+ ), visibleDate = date.getTime();
598
+ lastVisibleDate.setMonth(lastVisibleDate.getMonth() + 1);
599
+ lastVisibleDate.setDate(lastVisibleDate.getDate() - 1);
600
+ newCalendar = visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate;
601
+ }
602
+ if (newCalendar) {
603
+ this.calendars = [
604
+ {
605
+ month: date.getMonth(),
606
+ year: date.getFullYear()
607
+ }
608
+ ];
609
+ }
610
+ this.adjustCalendars();
611
+ }
612
+ adjustCalendars() {
613
+ this.calendars[0] = this.adjustCalendar(this.calendars[0]);
614
+ this.draw();
615
+ }
616
+ adjustCalendar(calendar) {
617
+ if (calendar.month < 0) {
618
+ calendar.year -= Math.ceil(Math.abs(calendar.month) / 12);
619
+ calendar.month += 12;
620
+ }
621
+ if (calendar.month > 11) {
622
+ calendar.year += Math.floor(Math.abs(calendar.month) / 12);
623
+ calendar.month -= 12;
624
+ }
625
+ return calendar;
626
+ }
627
+ nextMonth() {
628
+ this.calendars[0].month++;
629
+ this.adjustCalendars();
630
+ }
631
+ prevMonth() {
632
+ this.calendars[0].month--;
633
+ this.adjustCalendars();
634
+ }
635
+ render(year, month, randId) {
636
+ const now = /* @__PURE__ */ new Date(), days = _Datepicker._getDaysInMonth(year, month), data = [];
637
+ let before = new Date(year, month, 1).getDay(), row = [];
638
+ _Datepicker._setToStartOfDay(now);
639
+ if (this.options.firstDay > 0) {
640
+ before -= this.options.firstDay;
641
+ if (before < 0) {
642
+ before += 7;
643
+ }
644
+ }
645
+ const previousMonth = month === 0 ? 11 : month - 1, nextMonth = month === 11 ? 0 : month + 1, yearOfPreviousMonth = month === 0 ? year - 1 : year, yearOfNextMonth = month === 11 ? year + 1 : year, daysInPreviousMonth = _Datepicker._getDaysInMonth(yearOfPreviousMonth, previousMonth);
646
+ let cells = days + before, after = cells;
647
+ while (after > 7) {
648
+ after -= 7;
649
+ }
650
+ cells += 7 - after;
651
+ let isWeekSelected = false;
652
+ for (let i = 0, r = 0; i < cells; i++) {
653
+ const day = new Date(year, month, 1 + (i - before)), isToday = _Datepicker._compareDates(day, now), hasEvent = this.options.events.indexOf(day.toDateString()) !== -1, isEmpty = i < before || i >= days + before, isStartRange = this.options.startRange && _Datepicker._compareDates(this.options.startRange, day), isEndRange = this.options.endRange && _Datepicker._compareDates(this.options.endRange, day), isInRange = this.options.startRange && this.options.endRange && this.options.startRange < day && day < this.options.endRange, isDisabled = this.options.minDate && day < this.options.minDate || this.options.maxDate && day > this.options.maxDate || this.options.disableWeekends && _Datepicker._isWeekend(day) || this.options.disableDayFn && this.options.disableDayFn(day), isDateRangeStart = this.options.isDateRange && this.date && this.endDate && _Datepicker._compareDates(this.date, day), isDateRangeEnd = this.options.isDateRange && this.endDate && _Datepicker._compareDates(this.endDate, day), isDateRange = this.options.isDateRange && _Datepicker._isDate(this.endDate) && _Datepicker._compareWithinRange(day, this.date, this.endDate);
654
+ let dayNumber = 1 + (i - before), monthNumber = month, yearNumber = year;
655
+ let isSelected = false;
656
+ if (_Datepicker._isDate(this.date)) {
657
+ isSelected = _Datepicker._compareDates(day, this.date);
658
+ }
659
+ if (!isSelected && _Datepicker._isDate(this.endDate)) {
660
+ isSelected = _Datepicker._compareDates(day, this.endDate);
661
+ }
662
+ if (this.options.isMultipleSelection && this.dates?.some((item) => item.getTime() === day.getTime())) {
663
+ isSelected = true;
664
+ }
665
+ if (isEmpty) {
666
+ if (i < before) {
667
+ dayNumber = daysInPreviousMonth + dayNumber;
668
+ monthNumber = previousMonth;
669
+ yearNumber = yearOfPreviousMonth;
670
+ } else {
671
+ dayNumber = dayNumber - days;
672
+ monthNumber = nextMonth;
673
+ yearNumber = yearOfNextMonth;
674
+ }
675
+ }
676
+ const dayConfig = {
677
+ day: dayNumber,
678
+ month: monthNumber,
679
+ year: yearNumber,
680
+ hasEvent,
681
+ isSelected,
682
+ isToday,
683
+ isDisabled,
684
+ isEmpty,
685
+ isStartRange,
686
+ isEndRange,
687
+ isInRange,
688
+ showDaysInNextAndPreviousMonths: this.options.showDaysInNextAndPreviousMonths,
689
+ isDateRangeStart,
690
+ isDateRangeEnd,
691
+ isDateRange
692
+ };
693
+ row.push(this.renderDay(dayConfig));
694
+ if (++r === 7) {
695
+ data.push(this.renderRow(row, this.options.isRTL, isWeekSelected));
696
+ row = [];
697
+ r = 0;
698
+ isWeekSelected = false;
699
+ }
700
+ }
701
+ return this.renderTable(this.options, data, randId);
702
+ }
703
+ renderDay(opts) {
704
+ const classMap = {
705
+ isDisabled: "is-disabled",
706
+ isToday: "is-today",
707
+ isSelected: "is-selected",
708
+ hasEvent: "has-event",
709
+ isInRange: "is-inrange",
710
+ isStartRange: "is-startrange",
711
+ isEndRange: "is-endrange",
712
+ isDateRangeStart: "is-daterange-start",
713
+ isDateRangeEnd: "is-daterange-end",
714
+ isDateRange: "is-daterange"
715
+ }, ariaSelected = !(["isSelected", "isDateRange"].filter((prop) => !!(opts.hasOwnProperty(prop) && opts[prop] === true)).length === 0), arr = ["datepicker-day"];
716
+ if (opts.isEmpty) {
717
+ if (opts.showDaysInNextAndPreviousMonths) {
718
+ arr.push("is-outside-current-month");
719
+ arr.push("is-selection-disabled");
720
+ } else {
721
+ return '<td class="is-empty"></td>';
722
+ }
723
+ }
724
+ for (const [property, className] of Object.entries(classMap)) {
725
+ if (opts.hasOwnProperty(property) && opts[property]) {
726
+ arr.push(className);
727
+ }
728
+ }
729
+ const day = _Datepicker._escape(opts.day);
730
+ const month = _Datepicker._escape(opts.month);
731
+ const year = _Datepicker._escape(opts.year);
732
+ return `<td data-day="${day}" class="${arr.join(" ")}" aria-selected="${ariaSelected}"><button class="datepicker-day-button" type="button" data-year="${year}" data-month="${month}" data-day="${day}">${day}</button></td>`;
733
+ }
734
+ /** Assemble calendar cells; reject unsupported elements and attributes. */
735
+ renderRow(days, isRTL, isRowSelected) {
736
+ return '<tr class="datepicker-row' + (isRowSelected ? " is-selected" : "") + '">' + _Datepicker._calendarMarkup((isRTL ? days.reverse() : days).join(""), "tr") + "</tr>";
737
+ }
738
+ /** Assemble calendar rows; reject unsupported elements and attributes. */
739
+ renderTable(opts, data, randId) {
740
+ return '<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' + _Datepicker._escape(randId) + '">' + this.renderHead(opts) + this.renderBody(data) + "</table></div>";
741
+ }
742
+ renderHead(opts) {
743
+ const arr = [];
744
+ let i;
745
+ for (i = 0; i < 7; i++) {
746
+ const full = _Datepicker._escape(this.renderDayName(opts, i));
747
+ const abbrev = _Datepicker._escape(this.renderDayName(opts, i, true));
748
+ arr.push(`<th scope="col"><abbr title="${full}">${abbrev}</abbr></th>`);
749
+ }
750
+ return "<thead><tr>" + (opts.isRTL ? arr.reverse() : arr).join("") + "</tr></thead>";
751
+ }
752
+ /** Wrap calendar rows after validating and encoding their contents. */
753
+ renderBody(rows) {
754
+ return "<tbody>" + _Datepicker._calendarMarkup(rows.join(""), "tbody") + "</tbody>";
755
+ }
756
+ static _calendarMarkup(html, parent) {
757
+ const template = document.createElement("template");
758
+ template.innerHTML = html;
759
+ const attributes = {
760
+ tr: ["class"],
761
+ td: ["class", "data-day", "aria-selected"],
762
+ button: ["class", "type", "data-year", "data-month", "data-day", "aria-label", "disabled", "tabindex"]
763
+ };
764
+ const render = (node, container) => {
765
+ if (node.nodeType === Node.TEXT_NODE) {
766
+ if (container === "tbody" || container === "tr") {
767
+ if (node.textContent.trim()) throw new TypeError("Unsupported calendar row content");
768
+ }
769
+ return _Datepicker._escape(node.textContent);
770
+ }
771
+ if (!(node instanceof HTMLElement)) throw new TypeError("Unsupported calendar row content");
772
+ const tag = node.localName === "tr" ? "tr" : node.localName === "td" ? "td" : node.localName === "button" ? "button" : null;
773
+ if (!tag || !(container === "tbody" && tag === "tr" || container === "tr" && tag === "td" || container === "td" && tag === "button")) {
774
+ throw new TypeError("Unsupported calendar row element");
775
+ }
776
+ if ([...node.attributes].some((attr) => !attributes[tag].includes(attr.name))) {
777
+ throw new TypeError("Unsupported calendar row attribute");
778
+ }
779
+ if (tag === "button" && node.getAttribute("type") !== "button") {
780
+ throw new TypeError('Calendar day buttons require type="button"');
781
+ }
782
+ const attrs = attributes[tag].filter((name) => node.hasAttribute(name)).map((name) => ` ${name}="${_Datepicker._escape(node.getAttribute(name))}"`).join("");
783
+ return `<${tag}${attrs}>${[...node.childNodes].map((child) => render(child, tag)).join("")}</${tag}>`;
784
+ };
785
+ return [...template.content.childNodes].map((node) => render(node, parent)).join("");
786
+ }
787
+ /**
788
+ * Whether each month arrow is usable at this position in the allowed range.
789
+ *
790
+ * Depends on the month, which is deliberately *not* part of the controls
791
+ * cache key - so the reuse path has to recompute and reapply it.
792
+ */
793
+ _arrowState(year, month) {
794
+ const opts = this.options;
795
+ const isMinYear = year === opts.minYear;
796
+ const isMaxYear = year === opts.maxYear;
797
+ return {
798
+ prev: !(isMinYear && (month === 0 || opts.minMonth >= month)),
799
+ next: !(isMaxYear && (month === 11 || opts.maxMonth <= month))
800
+ };
801
+ }
802
+ /**
803
+ * Identifies the *shape* of the controls - everything that changes which
804
+ * options the month and year selects contain, as opposed to which one is
805
+ * picked. While this is stable the two FormSelects can be left alone.
806
+ *
807
+ * `year` is in the key because a numeric `yearRange` centres the year list
808
+ * on the displayed year, so paging across a year boundary does change it.
809
+ */
810
+ _controlsCacheKey(year) {
811
+ const o = this.options;
812
+ return [
813
+ year,
814
+ o.minYear,
815
+ o.maxYear,
816
+ o.minMonth,
817
+ o.maxMonth,
818
+ Array.isArray(o.yearRange) ? o.yearRange.join("-") : o.yearRange,
819
+ o.yearRangeReverse,
820
+ o.showMonthAfterYear
821
+ ].join("|");
822
+ }
823
+ renderTitle(instance, c, year, month, refYear, randId) {
824
+ const opts = this.options, isMinYear = year === opts.minYear, isMaxYear = year === opts.maxYear;
825
+ let i, j, arr = [], html = '<div id="' + _Datepicker._escape(randId) + '" class="datepicker-controls" role="heading" aria-live="assertive">';
826
+ const { prev, next } = this._arrowState(year, month);
827
+ for (i = 0; i < 12; i++) {
828
+ arr.push(
829
+ '<option value="' + (year === refYear ? i - c : 12 + i - c) + '"' + (i === month ? ' selected="selected"' : "") + (isMinYear && i < opts.minMonth || isMaxYear && i > opts.maxMonth ? 'disabled="disabled"' : "") + ">" + _Datepicker._escape(opts.i18n.months[i]) + "</option>"
830
+ );
831
+ }
832
+ const monthHtml = '<select class="datepicker-select orig-select-month" tabindex="-1">' + arr.join("") + "</select>";
833
+ if (Array.isArray(opts.yearRange)) {
834
+ i = opts.yearRange[0];
835
+ j = opts.yearRange[1] + 1;
836
+ } else {
837
+ i = year - opts.yearRange;
838
+ j = 1 + year + opts.yearRange;
839
+ }
840
+ for (arr = []; i < j && i <= opts.maxYear; i++) {
841
+ if (i >= opts.minYear) {
842
+ arr.push(`<option value="${i}" ${i === year ? 'selected="selected"' : ""}>${i}</option>`);
843
+ }
844
+ }
845
+ if (opts.yearRangeReverse) arr.reverse();
846
+ const yearHtml = `<select class="datepicker-select orig-select-year" tabindex="-1">${arr.join("")}</select>`;
847
+ const leftArrow = '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/><path d="M0-.5h24v24H0z" fill="none"/></svg>';
848
+ html += `<button class="month-prev${prev ? "" : " is-disabled"}" type="button">${leftArrow}</button>`;
849
+ html += '<div class="selects-container">';
850
+ if (opts.showMonthAfterYear) {
851
+ html += yearHtml + monthHtml;
852
+ } else {
853
+ html += monthHtml + yearHtml;
854
+ }
855
+ html += "</div>";
856
+ const rightArrow = '<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/><path d="M0-.25h24v24H0z" fill="none"/></svg>';
857
+ html += `<button class="month-next${next ? "" : " is-disabled"}" type="button">${rightArrow}</button>`;
858
+ return html += "</div>";
859
+ }
860
+ /**
861
+ * Run `fn`, collapsing every draw it triggers into a single one at the end.
862
+ *
863
+ * A single input click reaches draw() three times - once through
864
+ * setDateFromInput -> setDate -> gotoDate, once directly, and once through
865
+ * the trailing gotoDate - and each one destroys two FormSelect instances,
866
+ * reparses the calendar and builds two fresh FormSelects. The three calls
867
+ * cannot simply be deleted: the direct draw() is what covers an empty input
868
+ * (setDate bails before drawing when the value does not parse), and the
869
+ * trailing gotoDate re-centres on a date validateDate may have clamped to
870
+ * minDate/maxDate. So they are collected instead of removed.
871
+ *
872
+ * Deliberately synchronous: the constructor and callers read calendarEl
873
+ * immediately after init(), which a microtask-deferred draw would leave
874
+ * empty.
875
+ */
876
+ _batchDraws(fn) {
877
+ this._drawDepth++;
878
+ try {
879
+ fn();
880
+ } finally {
881
+ this._drawDepth--;
882
+ if (this._drawDepth === 0 && this._drawDirty) {
883
+ this._drawDirty = false;
884
+ this.draw();
885
+ }
886
+ }
887
+ }
888
+ // refresh HTML
889
+ draw() {
890
+ if (this._drawDepth > 0) {
891
+ this._drawDirty = true;
892
+ return;
893
+ }
894
+ const active = this.calendarEl.getRootNode().activeElement;
895
+ const restoreDayFocus = active?.matches(".datepicker-day-button") && this.calendarEl.contains(active);
896
+ const restoreArrow = this.calendarEl.contains(active) && active?.matches(".month-prev, .month-next") ? active.classList.contains("month-prev") ? ".month-prev" : ".month-next" : null;
897
+ const opts = this.options, minYear = opts.minYear, maxYear = opts.maxYear, minMonth = opts.minMonth, maxMonth = opts.maxMonth;
898
+ if (this._y <= minYear) {
899
+ this._y = minYear;
900
+ if (!isNaN(minMonth) && this._m < minMonth) {
901
+ this._m = minMonth;
902
+ }
903
+ }
904
+ if (this._y >= maxYear) {
905
+ this._y = maxYear;
906
+ if (!isNaN(maxMonth) && this._m > maxMonth) {
907
+ this._m = maxMonth;
908
+ }
909
+ }
910
+ const year = this.calendars[0].year;
911
+ const month = this.calendars[0].month;
912
+ if (!this.options.isDateRange) {
913
+ this._renderDateDisplay(this.date);
914
+ } else {
915
+ this._renderDateDisplay(this.date, this.endDate);
916
+ }
917
+ const key = this._controlsCacheKey(year);
918
+ if (key === this._controlsKey && this.calendarEl.querySelector(".datepicker-controls")) {
919
+ this._reuseControls(year, month);
920
+ } else {
921
+ this._controlsKey = key;
922
+ this._randId = "datepicker-title-" + Utils.guid();
923
+ this._rebuildControls(year, month);
924
+ }
925
+ this._renderCalendarTable(year, month);
926
+ this._syncCalendarFocus(!!restoreDayFocus);
927
+ if (restoreArrow) this.calendarEl.querySelector(restoreArrow)?.focus();
928
+ if (typeof this.options.onDraw === "function") {
929
+ this.options.onDraw.call(this);
930
+ }
931
+ }
932
+ /** Tear down the two FormSelects and render the controls from scratch. */
933
+ _rebuildControls(year, month) {
934
+ this.destroySelects();
935
+ this.calendarEl.innerHTML = this.renderTitle(this, 0, year, month, year, this._randId);
936
+ const yearSelect = this.calendarEl.querySelector(".orig-select-year");
937
+ const monthSelect = this.calendarEl.querySelector(".orig-select-month");
938
+ FormSelect.init(yearSelect, {
939
+ classes: "select-year",
940
+ menuOptions: { container: Utils.portalRoot(this.el), constrainWidth: false }
941
+ });
942
+ FormSelect.init(monthSelect, {
943
+ classes: "select-month",
944
+ menuOptions: { container: Utils.portalRoot(this.el), constrainWidth: false }
945
+ });
946
+ yearSelect.addEventListener("change", this._handleYearChange);
947
+ monthSelect.addEventListener("change", this._handleMonthChange);
948
+ }
949
+ /**
950
+ * Keep the existing controls and bring them up to date: the selected month,
951
+ * and the arrow states, which depend on the month rather than on the key.
952
+ */
953
+ _reuseControls(year, month) {
954
+ const monthSelect = this.calendarEl.querySelector(".orig-select-month");
955
+ if (monthSelect && monthSelect.value !== month.toString()) {
956
+ monthSelect.value = month.toString();
957
+ FormSelect.getInstance(monthSelect)?.refresh();
958
+ }
959
+ const { prev, next } = this._arrowState(year, month);
960
+ this.calendarEl.querySelector(".month-prev")?.classList.toggle("is-disabled", !prev);
961
+ this.calendarEl.querySelector(".month-next")?.classList.toggle("is-disabled", !next);
962
+ }
963
+ /** Replace just the day grid, leaving the controls in place. */
964
+ _renderCalendarTable(year, month) {
965
+ const html = this.render(year, month, this._randId);
966
+ const existing = this.calendarEl.querySelector(".datepicker-table-wrapper");
967
+ if (existing) existing.outerHTML = html;
968
+ else this.calendarEl.insertAdjacentHTML("beforeend", html);
969
+ }
970
+ _setupEventHandlers() {
971
+ this.el.addEventListener("click", this._handleInputClick);
972
+ this.el.addEventListener("keydown", this._handleInputKeydown);
973
+ this.el.addEventListener("change", this._handleInputChange);
974
+ this.endDateEl?.addEventListener("click", this._handleInputClick);
975
+ this.endDateEl?.addEventListener("keydown", this._handleInputKeydown);
976
+ this.endDateEl?.addEventListener("change", this._handleInputChange);
977
+ this.calendarEl.addEventListener("click", this._handleCalendarClick);
978
+ this.calendarEl.addEventListener("keydown", this._handleCalendarKeydown);
979
+ this.calendarEl.addEventListener("focusin", this._handleCalendarFocus);
980
+ }
981
+ _setupVariables() {
982
+ const template = document.createElement("template");
983
+ template.innerHTML = _Datepicker._template.trim();
984
+ this.containerEl = template.content.firstChild;
985
+ this.calendarEl = this.containerEl.querySelector(".datepicker-calendar");
986
+ this.yearTextEl = this.containerEl.querySelector(".year-text");
987
+ this.dateTextEl = this.containerEl.querySelector(".date-text");
988
+ this.footer = this.containerEl.querySelector(".datepicker-footer");
989
+ this.formats = {
990
+ d: (date) => {
991
+ return date.getDate();
992
+ },
993
+ dd: (date) => {
994
+ const d = date.getDate();
995
+ return (d < 10 ? "0" : "") + d;
996
+ },
997
+ ddd: (date) => {
998
+ return this.options.i18n.weekdaysShort[date.getDay()];
999
+ },
1000
+ dddd: (date) => {
1001
+ return this.options.i18n.weekdays[date.getDay()];
1002
+ },
1003
+ m: (date) => {
1004
+ return date.getMonth() + 1;
1005
+ },
1006
+ mm: (date) => {
1007
+ const m = date.getMonth() + 1;
1008
+ return (m < 10 ? "0" : "") + m;
1009
+ },
1010
+ mmm: (date) => {
1011
+ return this.options.i18n.monthsShort[date.getMonth()];
1012
+ },
1013
+ mmmm: (date) => {
1014
+ return this.options.i18n.months[date.getMonth()];
1015
+ },
1016
+ yy: (date) => {
1017
+ return ("" + date.getFullYear()).slice(2);
1018
+ },
1019
+ yyyy: (date) => {
1020
+ return date.getFullYear();
1021
+ }
1022
+ };
1023
+ }
1024
+ _removeEventHandlers() {
1025
+ for (const input of this.dateEls ?? [this.el]) this._removeInputEventHandlers(input);
1026
+ this.calendarEl.removeEventListener("click", this._handleCalendarClick);
1027
+ this.calendarEl.removeEventListener("keydown", this._handleCalendarKeydown);
1028
+ this.calendarEl.removeEventListener("focusin", this._handleCalendarFocus);
1029
+ if (this.options.isDateRange && this.endDateEl) {
1030
+ this._removeInputEventHandlers(this.endDateEl);
1031
+ }
1032
+ }
1033
+ _removeInputEventHandlers(input) {
1034
+ input.removeEventListener("click", this._handleInputClick);
1035
+ input.removeEventListener("keydown", this._handleInputKeydown);
1036
+ input.removeEventListener("change", this._handleInputChange);
1037
+ }
1038
+ _removeDateInput(input) {
1039
+ const focused = input.getRootNode().activeElement === input;
1040
+ this._removeInputEventHandlers(input);
1041
+ input.remove();
1042
+ if (focused && document.activeElement === document.body) this.el.focus();
1043
+ }
1044
+ _dayDate(button) {
1045
+ const date = /* @__PURE__ */ new Date(0);
1046
+ date.setFullYear(Number(button.dataset.year), Number(button.dataset.month), Number(button.dataset.day));
1047
+ _Datepicker._setToStartOfDay(date);
1048
+ return date;
1049
+ }
1050
+ _syncCalendarFocus(restore = false) {
1051
+ const buttons = [...this.calendarEl.querySelectorAll(".datepicker-day-button")];
1052
+ const current = buttons.filter((button) => !button.parentElement.classList.contains("is-selection-disabled"));
1053
+ const preferred = this._focusedDate || this.date || /* @__PURE__ */ new Date();
1054
+ const target = current.find((button) => _Datepicker._compareDates(this._dayDate(button), preferred)) || current.find((button) => button.parentElement.classList.contains("is-selected")) || current.find((button) => !button.parentElement.classList.contains("is-disabled")) || current[0];
1055
+ for (const button of buttons) {
1056
+ button.tabIndex = button === target ? 0 : -1;
1057
+ button.setAttribute("aria-disabled", String(button.parentElement.matches(".is-disabled, .is-selection-disabled")));
1058
+ }
1059
+ if (target) {
1060
+ this._focusedDate = this._dayDate(target);
1061
+ if (restore) target.focus();
1062
+ }
1063
+ }
1064
+ // change view to a specific month (zero-index, e.g. 0: January)
1065
+ gotoMonth(month) {
1066
+ if (!isNaN(month)) {
1067
+ this.calendars[0].month = parseInt(month, 10);
1068
+ this.adjustCalendars();
1069
+ }
1070
+ }
1071
+ // change view to a specific full year (e.g. "2012")
1072
+ gotoYear(year) {
1073
+ if (!isNaN(year)) {
1074
+ this.calendars[0].year = parseInt(year, 10);
1075
+ this.adjustCalendars();
1076
+ }
1077
+ }
1078
+ renderDayName(opts, day, abbr = false) {
1079
+ day += opts.firstDay;
1080
+ while (day >= 7) {
1081
+ day -= 7;
1082
+ }
1083
+ return abbr ? opts.i18n.weekdaysAbbrev[day] : opts.i18n.weekdays[day];
1084
+ }
1085
+ createDateInput() {
1086
+ const dateInput = this.el.cloneNode(true);
1087
+ dateInput.removeAttribute("id");
1088
+ dateInput.addEventListener("click", this._handleInputClick);
1089
+ dateInput.addEventListener("keydown", this._handleInputKeydown);
1090
+ dateInput.addEventListener("change", this._handleInputChange);
1091
+ this.el.parentElement.appendChild(dateInput);
1092
+ return dateInput;
1093
+ }
1094
+ };
1095
+ _Datepicker._template = `
1096
+ <div class="datepicker-container">
1097
+ <div class="datepicker-date-display">
1098
+ <span class="year-text"></span>
1099
+ <span class="date-text"></span>
1100
+ </div>
1101
+ <div class="datepicker-calendar-container">
1102
+ <div class="datepicker-calendar"></div>
1103
+ <div class="datepicker-footer"></div>
1104
+ </div>
1105
+ </div>`;
1106
+ var Datepicker = _Datepicker;
1107
+
1108
+ export {
1109
+ Datepicker
1110
+ };
1111
+ //# sourceMappingURL=chunk-QYCYP443.mjs.map