kmaterialize 2.0.0 → 2.3.3-1.0.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 (150) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +55 -91
  3. package/components/appbar/_navbar.old.scss +223 -0
  4. package/components/appbar/appbar.css +235 -0
  5. package/components/appbar/appbar.mjs +54 -0
  6. package/components/appbar/appbar.test.mjs +9 -0
  7. package/components/atomic/atomic.mjs +19 -0
  8. package/components/atomic/component.mjs +67 -0
  9. package/components/badge/_badges.scss +75 -0
  10. package/components/badge/badge.mjs +11 -0
  11. package/components/breadcrumb/_breadcrumb.scss +27 -0
  12. package/components/button/_buttons.scss +352 -0
  13. package/components/button/button.mjs +11 -0
  14. package/components/button/buttons.stories.ts +150 -0
  15. package/components/button/buttons.ts +282 -0
  16. package/components/button/examples.iso.js +18 -0
  17. package/components/button/fabSpec.js +65 -0
  18. package/components/button/test-button.mjs +16 -0
  19. package/components/card/_cards.scss +229 -0
  20. package/components/card/card.mjs +10 -0
  21. package/components/card/cards.ts +185 -0
  22. package/components/card/cardsSpec.js +269 -0
  23. package/components/carousel/_carousel.scss +93 -0
  24. package/components/carousel/_slider.scss +119 -0
  25. package/components/carousel/carousel.ts +693 -0
  26. package/components/carousel/carouselSpec.js +61 -0
  27. package/components/carousel/slider.ts +431 -0
  28. package/components/carousel/sliderSpec.js +99 -0
  29. package/components/checkbox/_checkboxes.scss +281 -0
  30. package/components/checkbox/checkbox.mdx +24 -0
  31. package/components/checkbox/checkbox.stories.ts +120 -0
  32. package/components/chip/chip.mjs +37 -0
  33. package/components/chip/chip.test.mjs +10 -0
  34. package/components/chip/chips.css +126 -0
  35. package/components/chip/chips.mdx +33 -0
  36. package/components/chip/chips.ts +440 -0
  37. package/components/chip/chipsSpec.js +165 -0
  38. package/components/chip/readme.md +323 -0
  39. package/components/collapsible/_collapsible.scss +82 -0
  40. package/components/collapsible/collapsible.ts +230 -0
  41. package/components/collapsible/collapsibleSpec.js +240 -0
  42. package/components/datepicker/_datepicker.scss +268 -0
  43. package/components/datepicker/datepicker.ts +1450 -0
  44. package/components/datepicker/datepickerSpec.js +162 -0
  45. package/components/dialog/_materialbox.scss +42 -0
  46. package/components/dialog/_modal.scss +79 -0
  47. package/components/dialog/materialbox.ts +446 -0
  48. package/components/dialog/materialboxSpec.js +37 -0
  49. package/components/dialog/modal.ts +111 -0
  50. package/components/dialog/modalSpec.js +83 -0
  51. package/components/dialog/readme.md +5 -0
  52. package/components/divider/readme.md +15 -0
  53. package/components/dropdown/_dropdown.scss +83 -0
  54. package/components/dropdown/dropdown.ts +653 -0
  55. package/components/dropdown/dropdownSpec.js +108 -0
  56. package/components/list/_collection.scss +114 -0
  57. package/components/list/_list.scss +6 -0
  58. package/components/list/list.mjs +58 -0
  59. package/components/list/list.test.mjs +16 -0
  60. package/components/loading/loading.mjs +26 -0
  61. package/components/loading/loading.test.mjs +10 -0
  62. package/components/navigation-bar/_old +582 -0
  63. package/components/navigation-bar/navigation-bar.mdx +20 -0
  64. package/components/navigation-bar/navigation-bar.stories.ts +39 -0
  65. package/components/navigation-drawer/_sidenav.scss +247 -0
  66. package/components/navigation-drawer/sidenav.ts +546 -0
  67. package/components/navigation-drawer/sidenavSpec.js +124 -0
  68. package/components/pagination/_pagination.scss +47 -0
  69. package/components/progress/_preloader.scss +416 -0
  70. package/components/radio-button/_radio-buttons.scss +112 -0
  71. package/components/radio-button/radio-buttons.mdx +28 -0
  72. package/components/radio-button/radio-buttons.stories.ts +51 -0
  73. package/components/search/autocomplete.ts +620 -0
  74. package/components/search/autocompleteSpec.js +321 -0
  75. package/components/slider/_range.scss +157 -0
  76. package/components/slider/range.ts +216 -0
  77. package/components/snackbar/_toast.scss +68 -0
  78. package/components/snackbar/toastSpec.js +132 -0
  79. package/components/snackbar/toasts.ts +312 -0
  80. package/components/switch/_switches.scss +121 -0
  81. package/components/tabs/_tabs.scss +171 -0
  82. package/components/tabs/tabs.mjs +31 -0
  83. package/components/tabs/tabs.ts +358 -0
  84. package/components/tabs/tabsSpec.js +180 -0
  85. package/components/textfield/_file-input.scss +41 -0
  86. package/components/textfield/_input-fields.scss +368 -0
  87. package/components/textfield/_select.scss +104 -0
  88. package/components/textfield/formfields.css +311 -0
  89. package/components/textfield/forms.ts +182 -0
  90. package/components/textfield/formsSpec.js +142 -0
  91. package/components/textfield/select.ts +472 -0
  92. package/components/textfield/selectSpec.js +390 -0
  93. package/components/textfield/textfield.mjs +29 -0
  94. package/components/textfield/textfield.test.mjs +10 -0
  95. package/components/timepicker/_timepicker.scss +362 -0
  96. package/components/timepicker/timepicker.ts +905 -0
  97. package/components/tooltip/_tooltip.scss +40 -0
  98. package/components/tooltip/tooltip.ts +380 -0
  99. package/components/tooltip/tooltipSpec.js +145 -0
  100. package/dist/css/materialize.colors.min.css +6 -0
  101. package/dist/css/materialize.css +7578 -6326
  102. package/dist/css/materialize.min.css +6 -13
  103. package/dist/css/materialize.min.css.map +1 -0
  104. package/dist/js/materialize.cjs.js +8410 -0
  105. package/dist/js/materialize.d.ts +2625 -0
  106. package/dist/js/materialize.js +8415 -12374
  107. package/dist/js/materialize.min.js +6 -6
  108. package/dist/js/materialize.mjs +8382 -0
  109. package/package.json +82 -47
  110. package/sass/_colors.scss +417 -0
  111. package/sass/_global.scss +490 -0
  112. package/sass/_grid.scss +172 -0
  113. package/sass/_table_of_contents.scss +28 -0
  114. package/sass/_tapTarget.scss +102 -0
  115. package/sass/_typography.scss +58 -0
  116. package/sass/_variables.scss +56 -0
  117. package/sass/materialize.scss +51 -0
  118. package/sass/mixins.module.scss +168 -0
  119. package/extras/noUiSlider/nouislider.css +0 -406
  120. package/extras/noUiSlider/nouislider.js +0 -2147
  121. package/extras/noUiSlider/nouislider.min.js +0 -1
  122. package/js/anime.min.js +0 -34
  123. package/js/autocomplete.js +0 -448
  124. package/js/buttons.js +0 -354
  125. package/js/cards.js +0 -40
  126. package/js/carousel.js +0 -717
  127. package/js/cash.js +0 -960
  128. package/js/characterCounter.js +0 -136
  129. package/js/chips.js +0 -481
  130. package/js/collapsible.js +0 -275
  131. package/js/component.js +0 -44
  132. package/js/datepicker.js +0 -975
  133. package/js/dropdown.js +0 -635
  134. package/js/forms.js +0 -275
  135. package/js/global.js +0 -444
  136. package/js/materialbox.js +0 -453
  137. package/js/modal.js +0 -382
  138. package/js/parallax.js +0 -138
  139. package/js/pushpin.js +0 -148
  140. package/js/range.js +0 -263
  141. package/js/scrollspy.js +0 -295
  142. package/js/select.js +0 -450
  143. package/js/sidenav.js +0 -580
  144. package/js/slider.js +0 -359
  145. package/js/tabs.js +0 -402
  146. package/js/tapTarget.js +0 -314
  147. package/js/timepicker.js +0 -647
  148. package/js/toasts.js +0 -310
  149. package/js/tooltip.js +0 -303
  150. package/js/waves.js +0 -335
@@ -0,0 +1,1450 @@
1
+ import { Utils } from '../../src/utils';
2
+ import { BaseOptions, Component, I18nOptions, InitElements, MElement } from '../../src/component';
3
+ import { FormSelect } from '../textfield/select';
4
+ import { DockedDisplayPlugin } from '../../src/dockedDisplayPlugin';
5
+ import { ModalDisplayPlugin } from '../../src/modalDisplayPlugin';
6
+
7
+ export interface DateI18nOptions extends I18nOptions {
8
+ previousMonth: string;
9
+ nextMonth: string;
10
+ months: string[];
11
+ monthsShort: string[];
12
+ weekdays: string[];
13
+ weekdaysShort: string[];
14
+ weekdaysAbbrev: string[];
15
+ }
16
+
17
+ export interface DatepickerOptions extends BaseOptions {
18
+ /**
19
+ * The date output format for the input field value
20
+ * or a function taking the date and outputting the
21
+ * formatted date string.
22
+ * @default 'mmm dd, yyyy'
23
+ */
24
+ format: string | ((d: Date) => string);
25
+ /**
26
+ * Used to create date object from current input string.
27
+ * @default null
28
+ */
29
+ parse: ((value: string, format: string) => Date) | null;
30
+ /**
31
+ * The initial condition if the datepicker is based on date range.
32
+ * @default false
33
+ */
34
+ isDateRange: boolean;
35
+ /**
36
+ * The selector of the user specified date range end element
37
+ */
38
+ dateRangeEndEl: string | null;
39
+ /**
40
+ * The initial condition if the datepicker is based on multiple date selection.
41
+ * @default false
42
+ */
43
+ isMultipleSelection: boolean;
44
+ /**
45
+ * The initial date to view when first opened.
46
+ * @default null
47
+ */
48
+ defaultDate: Date | null;
49
+ /**
50
+ * The initial end date to view when first opened.
51
+ * @default null
52
+ */
53
+ defaultEndDate: Date | null;
54
+ /**
55
+ * Make the `defaultDate` the initial selected value.
56
+ * @default false
57
+ */
58
+ setDefaultDate: boolean;
59
+ /**
60
+ * Make the `defaultEndDate` the initial selected value.
61
+ * @default false
62
+ */
63
+ setDefaultEndDate: boolean;
64
+ /**
65
+ * Prevent selection of any date on the weekend.
66
+ * @default false
67
+ */
68
+ disableWeekends: boolean;
69
+ /**
70
+ * Custom function to disable certain days.
71
+ * @default null
72
+ */
73
+ disableDayFn: ((day: Date) => boolean) | null;
74
+ /**
75
+ * First day of week (0: Sunday, 1: Monday etc).
76
+ * @default 0
77
+ */
78
+ firstDay: number;
79
+ /**
80
+ * The earliest date that can be selected.
81
+ * @default null
82
+ */
83
+ minDate: Date | null;
84
+ /**
85
+ * The latest date that can be selected.
86
+ * @default null
87
+ */
88
+ maxDate: Date | null;
89
+ /**
90
+ * Number of years either side, or array of upper/lower range.
91
+ * @default 10
92
+ */
93
+ yearRange: number | number[];
94
+ /**
95
+ * Sort year range in reverse order.
96
+ * @default false
97
+ */
98
+ yearRangeReverse: boolean;
99
+ /**
100
+ * Changes Datepicker to RTL.
101
+ * @default false
102
+ */
103
+ isRTL: boolean;
104
+ /**
105
+ * Show month after year in Datepicker title.
106
+ * @default false
107
+ */
108
+ showMonthAfterYear: boolean;
109
+ /**
110
+ * Render days of the calendar grid that fall in the next
111
+ * or previous month.
112
+ * @default false
113
+ */
114
+ showDaysInNextAndPreviousMonths: boolean;
115
+ /**
116
+ * Specify if the docked datepicker is in open state by default
117
+ */
118
+ openByDefault: boolean;
119
+ /**
120
+ * Specify a DOM element OR selector for a DOM element to render
121
+ * the calendar in, by default it will be placed before the input.
122
+ * @default null
123
+ */
124
+ container: HTMLElement | string | null;
125
+ /**
126
+ * Show the clear button in the datepicker.
127
+ * @default false
128
+ */
129
+ showClearBtn: boolean;
130
+ /**
131
+ * Autosubmit calendar day select to input field
132
+ * @default false
133
+ */
134
+ autoSubmit: true;
135
+ /**
136
+ * Internationalization options.
137
+ */
138
+ i18n: Partial<DateI18nOptions>;
139
+ /**
140
+ * An array of string returned by `Date.toDateString()`,
141
+ * indicating there are events in the specified days.
142
+ * @default []
143
+ */
144
+ events: string[];
145
+ /**
146
+ * Callback function when date is selected,
147
+ * first parameter is the newly selected date.
148
+ * @default null
149
+ */
150
+ onSelect: ((selectedDate: Date) => void) | null;
151
+ /**
152
+ * Callback function when Datepicker is closed.
153
+ * @default null
154
+ */
155
+ //onClose: (() => void) | null; // TODO: Remove
156
+ /**
157
+ * Callback function when Datepicker HTML is refreshed.
158
+ * @default null
159
+ */
160
+ onDraw: (() => void) | null;
161
+ /**
162
+ * Callback function for interaction with input field.
163
+ * @default null
164
+ */
165
+ onInputInteraction: (() => void) | null;
166
+ /**
167
+ * Callback function for interaction with confirm button.
168
+ * @default null
169
+ */
170
+ onConfirm: (() => void) | null;
171
+ /**
172
+ * Callback function for interaction with close button.
173
+ * @default null
174
+ */
175
+ onCancel: (() => void) | null;
176
+
177
+ /** Field used for internal calculations DO NOT CHANGE IT */
178
+ minYear?: number;
179
+ /** Field used for internal calculations DO NOT CHANGE IT */
180
+ maxYear?: number;
181
+ /** Field used for internal calculations DO NOT CHANGE IT */
182
+ minMonth?: number;
183
+ /** Field used for internal calculations DO NOT CHANGE IT */
184
+ maxMonth?: number;
185
+ /** Field used for internal calculations DO NOT CHANGE IT */
186
+ startRange?: Date;
187
+ /** Field used for internal calculations DO NOT CHANGE IT */
188
+ endRange?: Date;
189
+ /**
190
+ * Display plugin
191
+ */
192
+ displayPlugin: string;
193
+ /**
194
+ * Configurable display plugin options
195
+ */
196
+ displayPluginOptions: object;
197
+ }
198
+
199
+ const _defaults: DatepickerOptions = {
200
+ // the default output format for the input field value
201
+ format: 'mmm dd, yyyy',
202
+ // Used to create date object from current input string
203
+ parse: null,
204
+ // The initial condition if the datepicker is based on date range
205
+ isDateRange: false,
206
+ // The selector of the user specified date range end element
207
+ dateRangeEndEl: null,
208
+ // The initial condition if the datepicker is based on multiple date selection
209
+ isMultipleSelection: false,
210
+ // The initial date to view when first opened
211
+ defaultDate: null,
212
+ // The initial end date to view when first opened
213
+ defaultEndDate: null,
214
+ // Make the `defaultDate` the initial selected value
215
+ setDefaultDate: false,
216
+ // Make the `defaultEndDate` the initial selected value
217
+ setDefaultEndDate: false,
218
+ disableWeekends: false,
219
+ disableDayFn: null,
220
+ // First day of week (0: Sunday, 1: Monday etc)
221
+ firstDay: 0,
222
+ // The earliest date that can be selected
223
+ minDate: null,
224
+ // Thelatest date that can be selected
225
+ maxDate: null,
226
+ // Number of years either side, or array of upper/lower range
227
+ yearRange: 10,
228
+ // used internally (don't config outside)
229
+ minYear: 0,
230
+ maxYear: 9999,
231
+ minMonth: undefined,
232
+ maxMonth: undefined,
233
+ startRange: null,
234
+ endRange: null,
235
+ isRTL: false,
236
+ yearRangeReverse: false,
237
+ // Render the month after year in the calendar title
238
+ showMonthAfterYear: false,
239
+ // Render days of the calendar grid that fall in the next or previous month
240
+ showDaysInNextAndPreviousMonths: false,
241
+ // Specify if docked picker is in open state by default
242
+ openByDefault: false,
243
+ // Specify a DOM element to render the calendar in
244
+ container: null,
245
+ // Show clear button
246
+ showClearBtn: false,
247
+ // Autosubmit
248
+ autoSubmit: true,
249
+ // internationalization
250
+ i18n: {
251
+ cancel: 'Cancel',
252
+ clear: 'Clear',
253
+ done: 'Ok',
254
+ previousMonth: '‹',
255
+ nextMonth: '›',
256
+ months: [
257
+ 'January',
258
+ 'February',
259
+ 'March',
260
+ 'April',
261
+ 'May',
262
+ 'June',
263
+ 'July',
264
+ 'August',
265
+ 'September',
266
+ 'October',
267
+ 'November',
268
+ 'December'
269
+ ],
270
+ monthsShort: [
271
+ 'Jan',
272
+ 'Feb',
273
+ 'Mar',
274
+ 'Apr',
275
+ 'May',
276
+ 'Jun',
277
+ 'Jul',
278
+ 'Aug',
279
+ 'Sep',
280
+ 'Oct',
281
+ 'Nov',
282
+ 'Dec'
283
+ ],
284
+ weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
285
+ weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
286
+ weekdaysAbbrev: ['S', 'M', 'T', 'W', 'T', 'F', 'S']
287
+ },
288
+ // events array
289
+ events: [],
290
+ // callback function
291
+ onSelect: null,
292
+ onDraw: null,
293
+ onInputInteraction: null,
294
+ displayPlugin: null,
295
+ displayPluginOptions: null,
296
+ onConfirm: null,
297
+ onCancel: null
298
+ };
299
+
300
+ export class Datepicker extends Component<DatepickerOptions> {
301
+ declare el: HTMLInputElement;
302
+ id: string;
303
+ multiple: boolean = false;
304
+ calendarEl: HTMLElement;
305
+
306
+ /** CLEAR button instance. */
307
+ // clearBtn: HTMLElement;
308
+ /** DONE button instance */
309
+ /*doneBtn: HTMLElement;
310
+ cancelBtn: HTMLElement;*/
311
+
312
+ containerEl: HTMLElement;
313
+ yearTextEl: HTMLElement;
314
+ dateTextEl: HTMLElement;
315
+ endDateEl: HTMLInputElement;
316
+ dateEls: HTMLInputElement[];
317
+ /** The selected Date. */
318
+ date: Date;
319
+ endDate: null | Date;
320
+ dates: Date[];
321
+ formats: object;
322
+ calendars: [{ month: number; year: number }];
323
+ private _y: number;
324
+ private _m: number;
325
+ private displayPlugin: DockedDisplayPlugin | ModalDisplayPlugin;
326
+ private footer: HTMLElement;
327
+ static _template: string;
328
+
329
+ constructor(el: HTMLInputElement, options: Partial<DatepickerOptions>) {
330
+ super(el, options, Datepicker);
331
+ this.el['M_Datepicker'] = this;
332
+
333
+ this.options = {
334
+ ...Datepicker.defaults,
335
+ ...options
336
+ };
337
+
338
+ // make sure i18n defaults are not lost when only few i18n option properties are passed
339
+ if (!!options && options.hasOwnProperty('i18n') && typeof options.i18n === 'object') {
340
+ this.options.i18n = { ...Datepicker.defaults.i18n, ...options.i18n };
341
+ }
342
+
343
+ // Remove time component from minDate and maxDate options
344
+ if (this.options.minDate) this.options.minDate.setHours(0, 0, 0, 0);
345
+ if (this.options.maxDate) this.options.maxDate.setHours(0, 0, 0, 0);
346
+
347
+ this.id = Utils.guid();
348
+
349
+ this._setupVariables();
350
+ this._insertHTMLIntoDOM();
351
+ this._setupEventHandlers();
352
+ if (this.options.displayPlugin) this._setupDisplayPlugin();
353
+ this._pickerSetup();
354
+ }
355
+
356
+ static get defaults() {
357
+ return _defaults;
358
+ }
359
+
360
+ /**
361
+ * Initializes instance of Datepicker.
362
+ * @param el HTML element.
363
+ * @param options Component options.
364
+ */
365
+ static init(el: HTMLInputElement, options?: Partial<DatepickerOptions>): Datepicker;
366
+ /**
367
+ * Initializes instances of Datepicker.
368
+ * @param els HTML elements.
369
+ * @param options Component options.
370
+ */
371
+ static init(
372
+ els: InitElements<HTMLInputElement | MElement>,
373
+ options?: Partial<DatepickerOptions>
374
+ ): Datepicker[];
375
+ /**
376
+ * Initializes instances of Datepicker.
377
+ * @param els HTML elements.
378
+ * @param options Component options.
379
+ */
380
+ static init(
381
+ els: HTMLInputElement | InitElements<HTMLInputElement | MElement>,
382
+ options: Partial<DatepickerOptions> = {}
383
+ ): Datepicker | Datepicker[] {
384
+ return super.init(els, options, Datepicker);
385
+ }
386
+
387
+ static _isDate(obj) {
388
+ return /Date/.test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
389
+ }
390
+
391
+ static _isWeekend(date) {
392
+ const day = date.getDay();
393
+ return day === 0 || day === 6;
394
+ }
395
+
396
+ /**
397
+ * @deprecated as this function has no effect without any return statement or global parameter setter.
398
+ */
399
+ static _setToStartOfDay(date) {
400
+ if (Datepicker._isDate(date)) date.setHours(0, 0, 0, 0);
401
+ }
402
+
403
+ static _getDaysInMonth(year, month) {
404
+ return [31, Datepicker._isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][
405
+ month
406
+ ];
407
+ }
408
+
409
+ static _isLeapYear(year) {
410
+ // solution by Matti Virkkunen: http://stackoverflow.com/a/4881951
411
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
412
+ }
413
+
414
+ static _compareDates(a, b) {
415
+ // weak date comparison (use setToStartOfDay(date) to ensure correct result)
416
+ return a.getTime() === b.getTime();
417
+ }
418
+
419
+ static _compareWithinRange(day: Date, date: Date, dateEnd: Date) {
420
+ return day.getTime() > date.getTime() && day.getTime() < dateEnd.getTime();
421
+ }
422
+
423
+ static _comparePastDate(a: Date, b: Date) {
424
+ return a.getTime() < b.getTime();
425
+ }
426
+
427
+ static getInstance(el: HTMLElement): Datepicker {
428
+ return el['M_Datepicker'];
429
+ }
430
+
431
+ destroy() {
432
+ this._removeEventHandlers();
433
+ this.containerEl.remove();
434
+ this.destroySelects();
435
+ this.el['M_Datepicker'] = undefined;
436
+ }
437
+
438
+ destroySelects() {
439
+ const oldYearSelect = this.calendarEl.querySelector('.orig-select-year');
440
+ if (oldYearSelect) {
441
+ FormSelect.getInstance(oldYearSelect as HTMLElement).destroy();
442
+ }
443
+ const oldMonthSelect = this.calendarEl.querySelector('.orig-select-month');
444
+ if (oldMonthSelect) {
445
+ FormSelect.getInstance(oldMonthSelect as HTMLElement).destroy();
446
+ }
447
+ }
448
+
449
+ _insertHTMLIntoDOM() {
450
+ // HTML5 input date field support
451
+ if (this.el.type == 'date') {
452
+ this.el.classList.add('datepicker-date-input');
453
+ }
454
+
455
+ if (!this.el.parentElement.querySelector('.datepicker-format') === null) {
456
+ this._renderDateInputFormat(this.el);
457
+ }
458
+
459
+ if (this.options.isDateRange) {
460
+ this.containerEl.classList.add('daterange');
461
+ if (!this.options.dateRangeEndEl) {
462
+ this.endDateEl = this.createDateInput();
463
+ this.endDateEl.classList.add('datepicker-end-date');
464
+ } else if (
465
+ (document.querySelector(this.options.dateRangeEndEl) as HTMLInputElement) === undefined
466
+ ) {
467
+ console.warn('Specified date range end input element in dateRangeEndEl not found');
468
+ } else {
469
+ this.endDateEl = document.querySelector(this.options.dateRangeEndEl) as HTMLInputElement;
470
+ if (!this.endDateEl.parentElement.querySelector('.datepicker-format') === null) {
471
+ this._renderDateInputFormat(this.endDateEl);
472
+ }
473
+ }
474
+ }
475
+
476
+ Utils.createButton(
477
+ this.footer,
478
+ this.options.i18n.clear,
479
+ ['datepicker-clear'],
480
+ this.options.showClearBtn,
481
+ this._handleClearClick
482
+ );
483
+
484
+ if (!this.options.autoSubmit) {
485
+ Utils.createConfirmationContainer(
486
+ this.footer,
487
+ this.options.i18n.done,
488
+ this.options.i18n.cancel,
489
+ this._confirm,
490
+ this._cancel
491
+ );
492
+ }
493
+
494
+ if (this.options.container) {
495
+ const optEl = this.options.container;
496
+ this.options.container =
497
+ optEl instanceof HTMLElement ? optEl : (document.querySelector(optEl) as HTMLElement);
498
+ this.options.container.append(this.containerEl);
499
+ } else {
500
+ const appendTo = !this.endDateEl ? this.el : this.endDateEl;
501
+ appendTo.parentElement.after(this.containerEl);
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Renders the date input format
507
+ */
508
+ _renderDateInputFormat(el: HTMLInputElement) {
509
+ el.parentElement.querySelector('.datepicker-format').innerHTML = this.options.format.toString();
510
+ }
511
+
512
+ /**
513
+ * Gets a string representation of the given date.
514
+ */
515
+ toString(date: Date = this.date, format: string | ((d: Date) => string) = null): string {
516
+ format = format || this.options.format;
517
+ if (typeof format === 'function') return format(date);
518
+ if (!Datepicker._isDate(date)) return '';
519
+ // String Format
520
+ return this.formatDate(date, format);
521
+ }
522
+
523
+ /**
524
+ * Returns the formatted date.
525
+ */
526
+ formatDate(date: Date, format: string) {
527
+ const formatArray = format.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g);
528
+ return formatArray
529
+ .map((label) => (this.formats[label] ? this.formats[label](date) : label))
530
+ .join('');
531
+ }
532
+
533
+ /**
534
+ * Sets date from input field.
535
+ */
536
+ setDateFromInput(el: HTMLInputElement) {
537
+ const date = new Date(Date.parse(el.value));
538
+ this.setDate(date, false, el == this.endDateEl, true);
539
+ }
540
+
541
+ /**
542
+ * Set a date on the datepicker.
543
+ * @param date Date to set on the datepicker.
544
+ * @param preventOnSelect Undocumented as of 5 March 2018.
545
+ * @param isEndDate
546
+ * @param fromUserInput
547
+ */
548
+ setDate(
549
+ date: Date = null,
550
+ preventOnSelect: boolean = false,
551
+ isEndDate: boolean = false,
552
+ fromUserInput: boolean = false
553
+ ) {
554
+ const selectedDate = this.validateDate(date);
555
+ if (!selectedDate) {
556
+ return;
557
+ }
558
+ if (!this.options.isMultipleSelection) this.setSingleDate(selectedDate, isEndDate);
559
+ else if (!fromUserInput) this.setMultiDate(selectedDate);
560
+ Datepicker._setToStartOfDay(selectedDate);
561
+ this.gotoDate(selectedDate);
562
+ if (!preventOnSelect && typeof this.options.onSelect === 'function') {
563
+ this.options.onSelect.call(this, selectedDate);
564
+ }
565
+ }
566
+
567
+ validateDate(date: Date) {
568
+ if (!date) {
569
+ this._renderDateDisplay(date);
570
+ return this.draw();
571
+ }
572
+ if (typeof date === 'string') {
573
+ date = new Date(Date.parse(date));
574
+ }
575
+ if (!Datepicker._isDate(date)) {
576
+ return;
577
+ }
578
+ const min = this.options.minDate,
579
+ max = this.options.maxDate;
580
+ if (Datepicker._isDate(min) && date < min) {
581
+ date = min;
582
+ } else if (Datepicker._isDate(max) && date > max) {
583
+ date = max;
584
+ }
585
+ return new Date(date.getTime());
586
+ }
587
+
588
+ /**
589
+ * Set a single date on the datepicker.
590
+ * @param date Date to set on the datepicker.
591
+ * @param isEndDate
592
+ */
593
+ setSingleDate(date: Date, isEndDate: boolean) {
594
+ if (!isEndDate) {
595
+ this.date = date;
596
+ } else if (isEndDate) {
597
+ this.endDate = date;
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Set a multi date on the datepicker.
603
+ * @param date Date to set on the datepicker.
604
+ */
605
+ setMultiDate(date: Date) {
606
+ const selectedDate = this.dates?.find((item) => {
607
+ return item.getTime() === date.getTime() ? item : false;
608
+ });
609
+ if (!selectedDate) {
610
+ this.dates.push(date);
611
+ } else {
612
+ this.dates.splice(this.dates.indexOf(selectedDate), 1);
613
+ }
614
+ this.dates.sort((a: Date, b: Date) => (a.getTime() < b.getTime() ? -1 : 0));
615
+ }
616
+
617
+ /**
618
+ * Sets the data-date attribute on the date input field
619
+ */
620
+ setDataDate(el, date) {
621
+ el.setAttribute('data-date', this.toString(date));
622
+ }
623
+
624
+ /**
625
+ * Sets dates on the input values.
626
+ */
627
+ setInputValues() {
628
+ if (!this.options?.isMultipleSelection) {
629
+ this.setInputValue(this.el, this.date);
630
+ if (this.options?.isDateRange) {
631
+ this.setInputValue(this.endDateEl, this.endDate);
632
+ }
633
+ return;
634
+ }
635
+ this.setMultipleSelectionInputValues();
636
+ }
637
+
638
+ setMultipleSelectionInputValues() {
639
+ const dateElsArr = Array.from(this.dateEls).filter((el, index) => {
640
+ if (index > this.dates.length - 1) return el;
641
+ });
642
+ dateElsArr.forEach((el) => {
643
+ el.remove();
644
+ });
645
+ this.dates.forEach((date: Date, index: number) => {
646
+ if (Array.from(this.dateEls)[index]) {
647
+ this.setInputValue(this.dateEls[index], date);
648
+ return;
649
+ }
650
+
651
+ const dateEl = this.createDateInput();
652
+ this.setInputValue(dateEl, date);
653
+ this.dateEls.push(dateEl);
654
+ });
655
+ }
656
+
657
+ /**
658
+ * Sets given date as the input value on the given element.
659
+ */
660
+ setInputValue(el, date) {
661
+ if (el.type == 'date') {
662
+ this.setDataDate(el, date);
663
+ el.value = this.formatDate(date, 'yyyy-mm-dd');
664
+ } else {
665
+ el.value = this.toString(date);
666
+ }
667
+ this.el.dispatchEvent(
668
+ new CustomEvent('change', {
669
+ bubbles: true,
670
+ cancelable: true,
671
+ composed: true,
672
+ detail: { firedBy: this }
673
+ })
674
+ );
675
+ }
676
+
677
+ /**
678
+ * Display plugin setup.
679
+ */
680
+ _setupDisplayPlugin() {
681
+ const displayPluginOptions = {
682
+ ...this.options.displayPluginOptions,
683
+ ...{
684
+ onOpen: () => {
685
+ document.querySelectorAll('.select-dropdown').forEach((e: HTMLInputElement) => {
686
+ e.tabIndex = 0;
687
+ });
688
+ },
689
+ onClose: () => {
690
+ document.querySelectorAll('.select-dropdown').forEach((e: HTMLInputElement) => {
691
+ e.tabIndex = -1;
692
+ });
693
+ }
694
+ }
695
+ };
696
+
697
+ if (this.options.displayPlugin === 'docked')
698
+ this.displayPlugin = DockedDisplayPlugin.init(
699
+ this.el,
700
+ this.containerEl,
701
+ displayPluginOptions
702
+ );
703
+ if (this.options.displayPlugin === 'modal') {
704
+ this.displayPlugin = ModalDisplayPlugin.init(this.el, this.containerEl, {
705
+ ...displayPluginOptions,
706
+ ...{ classList: ['datepicker-modal'] }
707
+ });
708
+ this.footer.remove();
709
+ this.footer = this.displayPlugin.footer;
710
+ }
711
+ if (this.options.openByDefault) this.displayPlugin.show();
712
+ }
713
+
714
+ /**
715
+ * Renders the date in the modal head section.
716
+ */
717
+ _renderDateDisplay(date: Date, endDate: Date = null) {
718
+ const displayDate = Datepicker._isDate(date) ? date : new Date();
719
+ // this.yearTextEl.innerHTML = displayDate.getFullYear().toString();
720
+ // @todo should we include an option for date formatting by component options?
721
+ if (!this.options.isDateRange) {
722
+ this.dateTextEl.innerHTML = this.formatDate(displayDate, 'ddd, mmm d');
723
+ } else {
724
+ const displayEndDate = Datepicker._isDate(endDate) ? endDate : new Date();
725
+ this.dateTextEl.innerHTML = `${this.formatDate(displayDate, 'mmm d')} - ${this.formatDate(
726
+ displayEndDate,
727
+ 'mmm d'
728
+ )}`;
729
+ }
730
+ }
731
+
732
+ /**
733
+ * Change date view to a specific date on the datepicker.
734
+ * @param date Date to show on the datepicker.
735
+ */
736
+ gotoDate(date: Date) {
737
+ let newCalendar = true;
738
+ if (!Datepicker._isDate(date)) {
739
+ return;
740
+ }
741
+ if (this.calendars) {
742
+ const firstVisibleDate = new Date(this.calendars[0].year, this.calendars[0].month, 1),
743
+ lastVisibleDate = new Date(
744
+ this.calendars[this.calendars.length - 1].year,
745
+ this.calendars[this.calendars.length - 1].month,
746
+ 1
747
+ ),
748
+ visibleDate = date.getTime();
749
+ // get the end of the month
750
+ lastVisibleDate.setMonth(lastVisibleDate.getMonth() + 1);
751
+ lastVisibleDate.setDate(lastVisibleDate.getDate() - 1);
752
+ newCalendar =
753
+ visibleDate < firstVisibleDate.getTime() || lastVisibleDate.getTime() < visibleDate;
754
+ }
755
+ if (newCalendar) {
756
+ this.calendars = [
757
+ {
758
+ month: date.getMonth(),
759
+ year: date.getFullYear()
760
+ }
761
+ ];
762
+ }
763
+ this.adjustCalendars();
764
+ }
765
+
766
+ adjustCalendars() {
767
+ this.calendars[0] = this.adjustCalendar(this.calendars[0]);
768
+ this.draw();
769
+ }
770
+
771
+ adjustCalendar(calendar) {
772
+ if (calendar.month < 0) {
773
+ calendar.year -= Math.ceil(Math.abs(calendar.month) / 12);
774
+ calendar.month += 12;
775
+ }
776
+ if (calendar.month > 11) {
777
+ calendar.year += Math.floor(Math.abs(calendar.month) / 12);
778
+ calendar.month -= 12;
779
+ }
780
+ return calendar;
781
+ }
782
+
783
+ nextMonth() {
784
+ this.calendars[0].month++;
785
+ this.adjustCalendars();
786
+ }
787
+
788
+ prevMonth() {
789
+ this.calendars[0].month--;
790
+ this.adjustCalendars();
791
+ }
792
+
793
+ render(year, month, randId) {
794
+ const now = new Date(),
795
+ days = Datepicker._getDaysInMonth(year, month),
796
+ data = [];
797
+ let before = new Date(year, month, 1).getDay(),
798
+ row = [];
799
+ Datepicker._setToStartOfDay(now);
800
+ if (this.options.firstDay > 0) {
801
+ before -= this.options.firstDay;
802
+ if (before < 0) {
803
+ before += 7;
804
+ }
805
+ }
806
+ const previousMonth = month === 0 ? 11 : month - 1,
807
+ nextMonth = month === 11 ? 0 : month + 1,
808
+ yearOfPreviousMonth = month === 0 ? year - 1 : year,
809
+ yearOfNextMonth = month === 11 ? year + 1 : year,
810
+ daysInPreviousMonth = Datepicker._getDaysInMonth(yearOfPreviousMonth, previousMonth);
811
+ let cells = days + before,
812
+ after = cells;
813
+ while (after > 7) {
814
+ after -= 7;
815
+ }
816
+ cells += 7 - after;
817
+ let isWeekSelected = false;
818
+ for (let i = 0, r = 0; i < cells; i++) {
819
+ const day = new Date(year, month, 1 + (i - before)),
820
+ isToday = Datepicker._compareDates(day, now),
821
+ hasEvent = this.options.events.indexOf(day.toDateString()) !== -1,
822
+ isEmpty = i < before || i >= days + before,
823
+ isStartRange =
824
+ this.options.startRange && Datepicker._compareDates(this.options.startRange, day),
825
+ isEndRange = this.options.endRange && Datepicker._compareDates(this.options.endRange, day),
826
+ isInRange =
827
+ this.options.startRange &&
828
+ this.options.endRange &&
829
+ this.options.startRange < day &&
830
+ day < this.options.endRange,
831
+ isDisabled =
832
+ (this.options.minDate && day < this.options.minDate) ||
833
+ (this.options.maxDate && day > this.options.maxDate) ||
834
+ (this.options.disableWeekends && Datepicker._isWeekend(day)) ||
835
+ (this.options.disableDayFn && this.options.disableDayFn(day)),
836
+ isDateRangeStart =
837
+ this.options.isDateRange &&
838
+ this.date &&
839
+ this.endDate &&
840
+ Datepicker._compareDates(this.date, day),
841
+ isDateRangeEnd =
842
+ this.options.isDateRange && this.endDate && Datepicker._compareDates(this.endDate, day),
843
+ isDateRange =
844
+ this.options.isDateRange &&
845
+ Datepicker._isDate(this.endDate) &&
846
+ Datepicker._compareWithinRange(day, this.date, this.endDate);
847
+ let dayNumber = 1 + (i - before),
848
+ monthNumber = month,
849
+ yearNumber = year;
850
+
851
+ let isSelected = false;
852
+ if (Datepicker._isDate(this.date)) {
853
+ isSelected = Datepicker._compareDates(day, this.date);
854
+ }
855
+
856
+ if (!isSelected && Datepicker._isDate(this.endDate)) {
857
+ isSelected = Datepicker._compareDates(day, this.endDate);
858
+ }
859
+
860
+ if (
861
+ this.options.isMultipleSelection &&
862
+ this.dates?.some((item) => item.getTime() === day.getTime())
863
+ ) {
864
+ isSelected = true;
865
+ }
866
+
867
+ if (isEmpty) {
868
+ if (i < before) {
869
+ dayNumber = daysInPreviousMonth + dayNumber;
870
+ monthNumber = previousMonth;
871
+ yearNumber = yearOfPreviousMonth;
872
+ } else {
873
+ dayNumber = dayNumber - days;
874
+ monthNumber = nextMonth;
875
+ yearNumber = yearOfNextMonth;
876
+ }
877
+ }
878
+
879
+ const dayConfig = {
880
+ day: dayNumber,
881
+ month: monthNumber,
882
+ year: yearNumber,
883
+ hasEvent: hasEvent,
884
+ isSelected: isSelected,
885
+ isToday: isToday,
886
+ isDisabled: isDisabled,
887
+ isEmpty: isEmpty,
888
+ isStartRange: isStartRange,
889
+ isEndRange: isEndRange,
890
+ isInRange: isInRange,
891
+ showDaysInNextAndPreviousMonths: this.options.showDaysInNextAndPreviousMonths,
892
+ isDateRangeStart: isDateRangeStart,
893
+ isDateRangeEnd: isDateRangeEnd,
894
+ isDateRange: isDateRange
895
+ };
896
+
897
+ row.push(this.renderDay(dayConfig));
898
+
899
+ if (++r === 7) {
900
+ data.push(this.renderRow(row, this.options.isRTL, isWeekSelected));
901
+ row = [];
902
+ r = 0;
903
+ isWeekSelected = false;
904
+ }
905
+ }
906
+ return this.renderTable(this.options, data, randId);
907
+ }
908
+
909
+ renderDay(opts) {
910
+ const classMap = {
911
+ isDisabled: 'is-disabled',
912
+ isToday: 'is-today',
913
+ isSelected: 'is-selected',
914
+ hasEvent: 'has-event',
915
+ isInRange: 'is-inrange',
916
+ isStartRange: 'is-startrange',
917
+ isEndRange: 'is-endrange',
918
+ isDateRangeStart: 'is-daterange-start',
919
+ isDateRangeEnd: 'is-daterange-end',
920
+ isDateRange: 'is-daterange'
921
+ },
922
+ ariaSelected = !(
923
+ ['isSelected', 'isDateRange'].filter(
924
+ (prop) => !!(opts.hasOwnProperty(prop) && opts[prop] === true)
925
+ ).length === 0
926
+ ),
927
+ arr = ['datepicker-day'];
928
+
929
+ if (opts.isEmpty) {
930
+ if (opts.showDaysInNextAndPreviousMonths) {
931
+ arr.push('is-outside-current-month');
932
+ arr.push('is-selection-disabled');
933
+ } else {
934
+ return '<td class="is-empty"></td>';
935
+ }
936
+ }
937
+
938
+ for (const [property, className] of Object.entries(classMap)) {
939
+ if (opts.hasOwnProperty(property) && opts[property]) {
940
+ arr.push(className);
941
+ }
942
+ }
943
+
944
+ return (
945
+ `<td data-day="${opts.day}" class="${arr.join(' ')}" aria-selected="${ariaSelected}">` +
946
+ `<button class="datepicker-day-button" type="button" data-year="${opts.year}" data-month="${opts.month}" data-day="${opts.day}">${opts.day}</button>` +
947
+ '</td>'
948
+ );
949
+ }
950
+
951
+ renderRow(days, isRTL, isRowSelected) {
952
+ return (
953
+ '<tr class="datepicker-row' +
954
+ (isRowSelected ? ' is-selected' : '') +
955
+ '">' +
956
+ (isRTL ? days.reverse() : days).join('') +
957
+ '</tr>'
958
+ );
959
+ }
960
+
961
+ renderTable(opts, data, randId) {
962
+ return (
963
+ '<div class="datepicker-table-wrapper"><table cellpadding="0" cellspacing="0" class="datepicker-table" role="grid" aria-labelledby="' +
964
+ randId +
965
+ '">' +
966
+ this.renderHead(opts) +
967
+ this.renderBody(data) +
968
+ '</table></div>'
969
+ );
970
+ }
971
+
972
+ renderHead(opts) {
973
+ const arr = [];
974
+ let i;
975
+ for (i = 0; i < 7; i++) {
976
+ arr.push(
977
+ `<th scope="col"><abbr title="${this.renderDayName(opts, i)}">${this.renderDayName(
978
+ opts,
979
+ i,
980
+ true
981
+ )}</abbr></th>`
982
+ );
983
+ }
984
+ return '<thead><tr>' + (opts.isRTL ? arr.reverse() : arr).join('') + '</tr></thead>';
985
+ }
986
+
987
+ renderBody(rows) {
988
+ return '<tbody>' + rows.join('') + '</tbody>';
989
+ }
990
+
991
+ renderTitle(instance, c, year, month, refYear, randId) {
992
+ const opts = this.options,
993
+ isMinYear = year === opts.minYear,
994
+ isMaxYear = year === opts.maxYear;
995
+ let i,
996
+ j,
997
+ arr = [],
998
+ html =
999
+ '<div id="' +
1000
+ randId +
1001
+ '" class="datepicker-controls" role="heading" aria-live="assertive">',
1002
+ prev = true,
1003
+ next = true;
1004
+
1005
+ for (i = 0; i < 12; i++) {
1006
+ arr.push(
1007
+ '<option value="' +
1008
+ (year === refYear ? i - c : 12 + i - c) +
1009
+ '"' +
1010
+ (i === month ? ' selected="selected"' : '') +
1011
+ ((isMinYear && i < opts.minMonth) || (isMaxYear && i > opts.maxMonth)
1012
+ ? 'disabled="disabled"'
1013
+ : '') +
1014
+ '>' +
1015
+ opts.i18n.months[i] +
1016
+ '</option>'
1017
+ );
1018
+ }
1019
+
1020
+ const monthHtml =
1021
+ '<select class="datepicker-select orig-select-month" tabindex="-1">' +
1022
+ arr.join('') +
1023
+ '</select>';
1024
+
1025
+ if (Array.isArray(opts.yearRange)) {
1026
+ i = opts.yearRange[0];
1027
+ j = opts.yearRange[1] + 1;
1028
+ } else {
1029
+ i = year - opts.yearRange;
1030
+ j = 1 + year + opts.yearRange;
1031
+ }
1032
+
1033
+ for (arr = []; i < j && i <= opts.maxYear; i++) {
1034
+ if (i >= opts.minYear) {
1035
+ arr.push(`<option value="${i}" ${i === year ? 'selected="selected"' : ''}>${i}</option>`);
1036
+ }
1037
+ }
1038
+ if (opts.yearRangeReverse) arr.reverse();
1039
+
1040
+ const yearHtml = `<select class="datepicker-select orig-select-year" tabindex="-1">${arr.join(
1041
+ ''
1042
+ )}</select>`;
1043
+
1044
+ const leftArrow =
1045
+ '<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>';
1046
+ html += `<button class="month-prev${
1047
+ prev ? '' : ' is-disabled'
1048
+ // @todo remove button class and add scss mixin, current implementation temporary for focus states, @see https://github.com/materializecss/materialize/issues/566
1049
+ } btn" type="button">${leftArrow}</button>`;
1050
+
1051
+ html += '<div class="selects-container">';
1052
+ if (opts.showMonthAfterYear) {
1053
+ html += yearHtml + monthHtml;
1054
+ } else {
1055
+ html += monthHtml + yearHtml;
1056
+ }
1057
+ html += '</div>';
1058
+
1059
+ if (isMinYear && (month === 0 || opts.minMonth >= month)) {
1060
+ prev = false;
1061
+ }
1062
+
1063
+ if (isMaxYear && (month === 11 || opts.maxMonth <= month)) {
1064
+ next = false;
1065
+ }
1066
+
1067
+ const rightArrow =
1068
+ '<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>';
1069
+ html += `<button class="month-next${
1070
+ next ? '' : ' is-disabled'
1071
+ // @todo remove button class and add scss mixin, current implementation temporary for focus states, @see https://github.com/materializecss/materialize/issues/566
1072
+ } btn" type="button">${rightArrow}</button>`;
1073
+
1074
+ return (html += '</div>');
1075
+ }
1076
+
1077
+ // refresh HTML
1078
+ draw() {
1079
+ const opts = this.options,
1080
+ minYear = opts.minYear,
1081
+ maxYear = opts.maxYear,
1082
+ minMonth = opts.minMonth,
1083
+ maxMonth = opts.maxMonth;
1084
+ let html = '';
1085
+
1086
+ if (this._y <= minYear) {
1087
+ this._y = minYear;
1088
+ if (!isNaN(minMonth) && this._m < minMonth) {
1089
+ this._m = minMonth;
1090
+ }
1091
+ }
1092
+ if (this._y >= maxYear) {
1093
+ this._y = maxYear;
1094
+ if (!isNaN(maxMonth) && this._m > maxMonth) {
1095
+ this._m = maxMonth;
1096
+ }
1097
+ }
1098
+
1099
+ const randId =
1100
+ 'datepicker-title-' +
1101
+ Math.random()
1102
+ .toString(36)
1103
+ .replace(/[^a-z]+/g, '')
1104
+ .substr(0, 2);
1105
+
1106
+ for (let c = 0; c < 1; c++) {
1107
+ if (!this.options.isDateRange) {
1108
+ this._renderDateDisplay(this.date);
1109
+ } else {
1110
+ this._renderDateDisplay(this.date, this.endDate);
1111
+ }
1112
+ html +=
1113
+ this.renderTitle(
1114
+ this,
1115
+ c,
1116
+ this.calendars[c].year,
1117
+ this.calendars[c].month,
1118
+ this.calendars[0].year,
1119
+ randId
1120
+ ) + this.render(this.calendars[c].year, this.calendars[c].month, randId);
1121
+ }
1122
+
1123
+ this.destroySelects();
1124
+
1125
+ this.calendarEl.innerHTML = html;
1126
+
1127
+ // Init Materialize Select
1128
+ const yearSelect = this.calendarEl.querySelector('.orig-select-year') as HTMLSelectElement;
1129
+ const monthSelect = this.calendarEl.querySelector('.orig-select-month') as HTMLSelectElement;
1130
+ // @todo fix accessibility @see https://github.com/materializecss/materialize/issues/522
1131
+ FormSelect.init(yearSelect, {
1132
+ classes: 'select-year',
1133
+ dropdownOptions: { container: document.body, constrainWidth: false }
1134
+ });
1135
+ FormSelect.init(monthSelect, {
1136
+ classes: 'select-month',
1137
+ dropdownOptions: { container: document.body, constrainWidth: false }
1138
+ });
1139
+
1140
+ // Add change handlers for select
1141
+ yearSelect.addEventListener('change', this._handleYearChange);
1142
+ monthSelect.addEventListener('change', this._handleMonthChange);
1143
+
1144
+ if (typeof this.options.onDraw === 'function') {
1145
+ this.options.onDraw.call(this);
1146
+ }
1147
+ }
1148
+
1149
+ _setupEventHandlers() {
1150
+ this.el.addEventListener('click', this._handleInputClick);
1151
+ this.el.addEventListener('keydown', this._handleInputKeydown);
1152
+ this.el.addEventListener('change', this._handleInputChange);
1153
+ this.calendarEl.addEventListener('click', this._handleCalendarClick);
1154
+ }
1155
+
1156
+ _setupVariables() {
1157
+ const template = document.createElement('template');
1158
+ template.innerHTML = Datepicker._template.trim();
1159
+
1160
+ this.containerEl = <HTMLElement>template.content.firstChild;
1161
+
1162
+ this.calendarEl = this.containerEl.querySelector('.datepicker-calendar');
1163
+ this.yearTextEl = this.containerEl.querySelector('.year-text');
1164
+ this.dateTextEl = this.containerEl.querySelector('.date-text');
1165
+ this.footer = this.containerEl.querySelector('.datepicker-footer');
1166
+
1167
+ this.formats = {
1168
+ d: (date: Date) => {
1169
+ return date.getDate();
1170
+ },
1171
+ dd: (date: Date) => {
1172
+ const d = date.getDate();
1173
+ return (d < 10 ? '0' : '') + d;
1174
+ },
1175
+ ddd: (date: Date) => {
1176
+ return this.options.i18n.weekdaysShort[date.getDay()];
1177
+ },
1178
+ dddd: (date: Date) => {
1179
+ return this.options.i18n.weekdays[date.getDay()];
1180
+ },
1181
+ m: (date: Date) => {
1182
+ return date.getMonth() + 1;
1183
+ },
1184
+ mm: (date: Date) => {
1185
+ const m = date.getMonth() + 1;
1186
+ return (m < 10 ? '0' : '') + m;
1187
+ },
1188
+ mmm: (date: Date) => {
1189
+ return this.options.i18n.monthsShort[date.getMonth()];
1190
+ },
1191
+ mmmm: (date: Date) => {
1192
+ return this.options.i18n.months[date.getMonth()];
1193
+ },
1194
+ yy: (date: Date) => {
1195
+ return ('' + date.getFullYear()).slice(2);
1196
+ },
1197
+ yyyy: (date: Date) => {
1198
+ return date.getFullYear();
1199
+ }
1200
+ };
1201
+ }
1202
+
1203
+ _pickerSetup() {
1204
+ if (!this.options.defaultDate) {
1205
+ this.options.defaultDate = new Date(Date.parse(this.el.value));
1206
+ }
1207
+
1208
+ const defDate = this.options.defaultDate;
1209
+ if (Datepicker._isDate(defDate)) {
1210
+ if (this.options.setDefaultDate) {
1211
+ this.setDate(defDate, true);
1212
+ this.setInputValue(this.el, defDate);
1213
+ } else {
1214
+ this.gotoDate(defDate);
1215
+ }
1216
+ } else {
1217
+ this.gotoDate(new Date());
1218
+ }
1219
+
1220
+ if (this.options.isDateRange) {
1221
+ this.multiple = true;
1222
+ const defEndDate = this.options.defaultEndDate;
1223
+ if (Datepicker._isDate(defEndDate)) {
1224
+ if (this.options.setDefaultEndDate) {
1225
+ this.setDate(defEndDate, true, true);
1226
+ this.setInputValue(this.endDateEl, defEndDate);
1227
+ }
1228
+ }
1229
+ }
1230
+
1231
+ if (this.options.isMultipleSelection) {
1232
+ this.multiple = true;
1233
+ this.dates = [];
1234
+ this.dateEls = [];
1235
+ this.dateEls.push(this.el);
1236
+ }
1237
+ }
1238
+
1239
+ _removeEventHandlers() {
1240
+ this.el.removeEventListener('click', this._handleInputClick);
1241
+ this.el.removeEventListener('keydown', this._handleInputKeydown);
1242
+ this.el.removeEventListener('change', this._handleInputChange);
1243
+ this.calendarEl.removeEventListener('click', this._handleCalendarClick);
1244
+ if (this.options.isDateRange) {
1245
+ this.endDateEl.removeEventListener('click', this._handleInputClick);
1246
+ this.endDateEl.removeEventListener('keypress', this._handleInputKeydown);
1247
+ this.endDateEl.removeEventListener('change', this._handleInputChange);
1248
+ }
1249
+ }
1250
+
1251
+ _handleInputClick = (e) => {
1252
+ // Prevents default browser datepicker modal rendering
1253
+ if (e.type == 'date') {
1254
+ e.preventDefault();
1255
+ }
1256
+ this.setDateFromInput(e.target as HTMLInputElement);
1257
+ this.draw();
1258
+ this.gotoDate(<HTMLElement>e.target === this.el ? this.date : this.endDate);
1259
+ if (this.displayPlugin) this.displayPlugin.show();
1260
+ if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
1261
+ };
1262
+
1263
+ _handleInputKeydown = (e: KeyboardEvent) => {
1264
+ if (Utils.keys.ENTER.includes(e.key)) {
1265
+ e.preventDefault();
1266
+ this.setDateFromInput(e.target as HTMLInputElement);
1267
+ this.draw();
1268
+ if (this.displayPlugin) this.displayPlugin.show();
1269
+ if (this.options.onInputInteraction) this.options.onInputInteraction.call(this);
1270
+ }
1271
+ };
1272
+
1273
+ _handleCalendarClick = (e) => {
1274
+ const target = <HTMLElement>e.target;
1275
+ if (!target.classList.contains('is-disabled')) {
1276
+ if (
1277
+ target.classList.contains('datepicker-day-button') &&
1278
+ !target.classList.contains('is-empty') &&
1279
+ !target.parentElement.classList.contains('is-disabled')
1280
+ ) {
1281
+ const selectedDate = new Date(
1282
+ e.target.getAttribute('data-year'),
1283
+ e.target.getAttribute('data-month'),
1284
+ e.target.getAttribute('data-day')
1285
+ );
1286
+
1287
+ if (!this.multiple || (this.multiple && this.options.isMultipleSelection)) {
1288
+ this.setDate(selectedDate);
1289
+ }
1290
+
1291
+ if (this.options.isDateRange) {
1292
+ const confirmAfterSelection = Datepicker._isDate(this.date) && this.options.autoSubmit;
1293
+ this._handleDateRangeCalendarClick(selectedDate);
1294
+
1295
+ if (confirmAfterSelection) {
1296
+ this._confirm();
1297
+ }
1298
+ }
1299
+
1300
+ if (!this.options.isDateRange && this.options.autoSubmit) this._confirm();
1301
+ } else if (target.closest('.month-prev')) {
1302
+ this.prevMonth();
1303
+ } else if (target.closest('.month-next')) {
1304
+ this.nextMonth();
1305
+ }
1306
+ }
1307
+ };
1308
+
1309
+ _handleDateRangeCalendarClick = (date: Date) => {
1310
+ if (this.endDate == null || !Datepicker._compareDates(date, this.endDate)) {
1311
+ if (Datepicker._isDate(this.date) && Datepicker._comparePastDate(date, this.date)) {
1312
+ return;
1313
+ }
1314
+
1315
+ this.setDate(date, true, Datepicker._isDate(this.date));
1316
+ return;
1317
+ }
1318
+
1319
+ this._clearDates();
1320
+ this.draw();
1321
+ };
1322
+
1323
+ _handleClearClick = () => {
1324
+ this._clearDates();
1325
+ this.setInputValues();
1326
+ };
1327
+
1328
+ _clearDates = () => {
1329
+ this.date = null;
1330
+ this.endDate = null;
1331
+ this.draw();
1332
+ };
1333
+
1334
+ _handleMonthChange = (e) => {
1335
+ this.gotoMonth(e.target.value);
1336
+ };
1337
+
1338
+ _handleYearChange = (e) => {
1339
+ this.gotoYear(e.target.value);
1340
+ };
1341
+
1342
+ // change view to a specific month (zero-index, e.g. 0: January)
1343
+ gotoMonth(month) {
1344
+ if (!isNaN(month)) {
1345
+ this.calendars[0].month = parseInt(month, 10);
1346
+ this.adjustCalendars();
1347
+ }
1348
+ }
1349
+
1350
+ // change view to a specific full year (e.g. "2012")
1351
+ gotoYear(year) {
1352
+ if (!isNaN(year)) {
1353
+ this.calendars[0].year = parseInt(year, 10);
1354
+ this.adjustCalendars();
1355
+ }
1356
+ }
1357
+
1358
+ _handleInputChange = (e: Event) => {
1359
+ let date;
1360
+ const el = e.target as HTMLElement;
1361
+ // Prevent change event from being fired when triggered by the plugin
1362
+ if (e['detail']?.firedBy === this) return;
1363
+ // Prevent change event from being fired if an end date is set without a start date
1364
+ if (el == this.endDateEl && !this.date) return;
1365
+ if (this.options.parse) {
1366
+ date = this.options.parse(
1367
+ (e.target as HTMLInputElement).value,
1368
+ typeof this.options.format === 'function'
1369
+ ? this.options.format(new Date(this.el.value))
1370
+ : this.options.format
1371
+ );
1372
+ } else {
1373
+ date = new Date(Date.parse((e.target as HTMLInputElement).value));
1374
+ }
1375
+ if (Datepicker._isDate(date)) {
1376
+ this.setDate(date, false, el == this.endDateEl, true);
1377
+ if (e.type == 'date') {
1378
+ this.setDataDate(e, date);
1379
+ this.setInputValues();
1380
+ }
1381
+ }
1382
+ };
1383
+
1384
+ renderDayName(opts, day, abbr: boolean = false) {
1385
+ day += opts.firstDay;
1386
+ while (day >= 7) {
1387
+ day -= 7;
1388
+ }
1389
+ return abbr ? opts.i18n.weekdaysAbbrev[day] : opts.i18n.weekdays[day];
1390
+ }
1391
+
1392
+ createDateInput() {
1393
+ const dateInput = <HTMLInputElement>this.el.cloneNode(true);
1394
+ dateInput.addEventListener('click', this._handleInputClick);
1395
+ dateInput.addEventListener('keypress', this._handleInputKeydown);
1396
+ dateInput.addEventListener('change', this._handleInputChange);
1397
+ this.el.parentElement.appendChild(dateInput);
1398
+ return dateInput;
1399
+ }
1400
+
1401
+ // Set input value to the selected date and close Datepicker
1402
+ _finishSelection = () => {
1403
+ this.setInputValues();
1404
+ // Commented out because of function deprecations
1405
+ // this.close();
1406
+ };
1407
+
1408
+ _confirm = () => {
1409
+ this._finishSelection();
1410
+ if (this.displayPlugin) this.displayPlugin.hide();
1411
+ if (typeof this.options.onConfirm === 'function') this.options.onConfirm.call(this);
1412
+ };
1413
+
1414
+ _cancel = () => {
1415
+ if (this.displayPlugin) this.displayPlugin.hide();
1416
+ if (typeof this.options.onCancel === 'function') this.options.onCancel.call(this);
1417
+ };
1418
+
1419
+ // deprecated
1420
+ open() {
1421
+ console.warn('Datepicker.open() is deprecated. Remove this method and wrap in modal yourself.');
1422
+ return this;
1423
+ }
1424
+ close() {
1425
+ console.warn(
1426
+ 'Datepicker.close() is deprecated. Remove this method and wrap in modal yourself.'
1427
+ );
1428
+ return this;
1429
+ }
1430
+
1431
+ static {
1432
+ Datepicker._template = `
1433
+ <div class="datepicker-container">
1434
+ <div class="datepicker-date-display">
1435
+ <span class="year-text"></span>
1436
+ <span class="date-text"></span>
1437
+ </div>
1438
+ <div class="datepicker-calendar-container">
1439
+ <div class="datepicker-calendar"></div>
1440
+ <div class="datepicker-footer">
1441
+ <!--<button class="btn-flat datepicker-clear waves-effect" style="visibility: hidden;" type="button"></button>
1442
+ <div class="confirmation-btns">
1443
+ <button class="btn-flat datepicker-cancel waves-effect" type="button"></button>
1444
+ <button class="btn-flat datepicker-done waves-effect" type="button"></button>
1445
+ </div>-->
1446
+ </div>
1447
+ </div>
1448
+ </div>`;
1449
+ }
1450
+ }