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,905 @@
1
+ import { Utils } from '../../src/utils';
2
+ import { Component, BaseOptions, InitElements, MElement, I18nOptions } from '../../src/component';
3
+ import { DockedDisplayPlugin } from '../../src/dockedDisplayPlugin';
4
+ import { ModalDisplayPlugin } from '../../src/modalDisplayPlugin';
5
+
6
+ export type Views = 'hours' | 'minutes';
7
+
8
+ export interface TimepickerOptions extends BaseOptions {
9
+ /**
10
+ * Dial radius.
11
+ * @default 135
12
+ */
13
+ dialRadius: number;
14
+ /**
15
+ * Outer radius.
16
+ * @default 105
17
+ */
18
+ outerRadius: number;
19
+ /**
20
+ * Inner radius.
21
+ * @default 70
22
+ */
23
+ innerRadius: number;
24
+ /**
25
+ * Tick radius.
26
+ * @default 20
27
+ */
28
+ tickRadius: number;
29
+ /**
30
+ * Duration of the transition from/to the hours/minutes view.
31
+ * @default 350
32
+ */
33
+ duration: number;
34
+ /**
35
+ * Specify a DOM element OR selector for a DOM element to render
36
+ * the time picker in, by default it will be placed before the input.
37
+ * @default null
38
+ */
39
+ container: HTMLElement | string | null;
40
+ /**
41
+ * Show the clear button in the Timepicker.
42
+ * @default false
43
+ */
44
+ showClearBtn: boolean;
45
+ /**
46
+ * Autosubmit timepicker selection to input field
47
+ * @default true
48
+ */
49
+ // @todo this is only working on analog clock, should apply to hour/minute input fields and am/pm selector as well
50
+ autoSubmit: true;
51
+ /**
52
+ * Default time to set on the timepicker 'now' or '13:14'.
53
+ * @default 'now';
54
+ */
55
+ defaultTime: string;
56
+ /**
57
+ * Millisecond offset from the defaultTime.
58
+ * @default 0
59
+ */
60
+ fromNow: number;
61
+ /**
62
+ * Internationalization options.
63
+ */
64
+ i18n: Partial<I18nOptions>;
65
+ /**
66
+ * Use 12 hour AM/PM clock instead of 24 hour clock.
67
+ * @default true
68
+ */
69
+ twelveHour: boolean;
70
+ /**
71
+ * Vibrate device when dragging clock hand.
72
+ * @default true
73
+ */
74
+ vibrate: boolean;
75
+ /**
76
+ * Callback function when a time is selected.
77
+ * @default null
78
+ */
79
+ onSelect: (hour: number, minute: number) => void;
80
+ /**
81
+ * Callback function for interaction with input field.
82
+ * @default null
83
+ */
84
+ onInputInteraction: (() => void) | null;
85
+ /**
86
+ * Callback function for done.
87
+ * @default null
88
+ */
89
+ onDone: (() => void) | null;
90
+ /**
91
+ * Callback function for cancel.
92
+ * @default null
93
+ */
94
+ onCancel: (() => void) | null;
95
+ /**
96
+ * Display plugin
97
+ */
98
+ displayPlugin: string;
99
+ /**
100
+ * Configurable display plugin options
101
+ */
102
+ displayPluginOptions: object;
103
+ }
104
+
105
+ const _defaults: TimepickerOptions = {
106
+ dialRadius: 135,
107
+ outerRadius: 105,
108
+ innerRadius: 70,
109
+ tickRadius: 20,
110
+ duration: 350,
111
+ container: null,
112
+ defaultTime: 'now', // default time, 'now' or '13:14' e.g.
113
+ fromNow: 0, // Millisecond offset from the defaultTime
114
+ showClearBtn: false,
115
+ autoSubmit: true,
116
+ // internationalization
117
+ i18n: {
118
+ cancel: 'Cancel',
119
+ clear: 'Clear',
120
+ done: 'Ok'
121
+ },
122
+ twelveHour: true, // change to 12 hour AM/PM clock from 24 hour
123
+ vibrate: true, // vibrate the device when dragging clock hand
124
+ // Callbacks
125
+ onSelect: null,
126
+ onInputInteraction: null,
127
+ onDone: null,
128
+ onCancel: null,
129
+ displayPlugin: null,
130
+ displayPluginOptions: null
131
+ };
132
+
133
+ type Point = {
134
+ x: number;
135
+ y: number;
136
+ };
137
+
138
+ export class Timepicker extends Component<TimepickerOptions> {
139
+ declare el: HTMLInputElement;
140
+ id: string;
141
+ containerEl: HTMLElement;
142
+ plate: HTMLElement;
143
+ digitalClock: HTMLElement;
144
+ inputHours: HTMLInputElement;
145
+ inputMinutes: HTMLInputElement;
146
+ x0: number;
147
+ y0: number;
148
+ moved: boolean;
149
+ dx: number;
150
+ dy: number;
151
+ /**
152
+ * Current view on the timepicker.
153
+ * @default 'hours'
154
+ */
155
+ currentView: Views;
156
+ hand: SVGElement;
157
+ minutesView: HTMLElement;
158
+ hours: number;
159
+ minutes: number;
160
+ /** The selected time. */
161
+ time: string;
162
+ /**
163
+ * If the time is AM or PM on twelve-hour clock.
164
+ * @default 'PM'
165
+ */
166
+ amOrPm: 'AM' | 'PM';
167
+ static _template;
168
+ /** Vibrate device when dragging clock hand. */
169
+ vibrate: 'vibrate' | 'webkitVibrate' | null;
170
+ _canvas: HTMLElement;
171
+ hoursView: HTMLElement;
172
+ spanAmPm: HTMLSpanElement;
173
+ footer: HTMLElement;
174
+ private _amBtn: HTMLElement;
175
+ private _pmBtn: HTMLElement;
176
+ bg: Element;
177
+ bearing: Element;
178
+ g: Element;
179
+ toggleViewTimer: string | number | NodeJS.Timeout;
180
+ vibrateTimer: NodeJS.Timeout | number;
181
+ private displayPlugin: DockedDisplayPlugin | ModalDisplayPlugin;
182
+
183
+ constructor(el: HTMLInputElement, options: Partial<TimepickerOptions>) {
184
+ super(el, options, Timepicker);
185
+ this.el['M_Timepicker'] = this;
186
+ this.options = {
187
+ ...Timepicker.defaults,
188
+ ...options
189
+ };
190
+ this.id = Utils.guid();
191
+ this._insertHTMLIntoDOM();
192
+ this._setupVariables();
193
+ this._setupEventHandlers();
194
+ this._clockSetup();
195
+ if (this.options.displayPlugin) this._setupDisplayPlugin();
196
+ this._pickerSetup();
197
+ }
198
+
199
+ static get defaults(): TimepickerOptions {
200
+ return _defaults;
201
+ }
202
+
203
+ /**
204
+ * Initializes instance of Timepicker.
205
+ * @param el HTML element.
206
+ * @param options Component options.
207
+ */
208
+ static init(el: HTMLInputElement, options?: Partial<TimepickerOptions>): Timepicker;
209
+ /**
210
+ * Initializes instances of Timepicker.
211
+ * @param els HTML elements.
212
+ * @param options Component options.
213
+ */
214
+ static init(
215
+ els: InitElements<HTMLInputElement | MElement>,
216
+ options?: Partial<TimepickerOptions>
217
+ ): Timepicker[];
218
+ /**
219
+ * Initializes instances of Timepicker.
220
+ * @param els HTML elements.
221
+ * @param options Component options.
222
+ */
223
+ static init(
224
+ els: HTMLInputElement | InitElements<HTMLInputElement | MElement>,
225
+ options: Partial<TimepickerOptions> = {}
226
+ ): Timepicker | Timepicker[] {
227
+ return super.init(els, options, Timepicker);
228
+ }
229
+
230
+ static _addLeadingZero(num: number) {
231
+ return (num < 10 ? '0' : '') + num;
232
+ }
233
+
234
+ static _createSVGEl(name: string) {
235
+ const svgNS = 'http://www.w3.org/2000/svg';
236
+ return document.createElementNS(svgNS, name);
237
+ }
238
+
239
+ static _Pos(e: TouchEvent | MouseEvent): Point {
240
+ if (e.type.startsWith('touch') && (e as TouchEvent).targetTouches.length >= 1) {
241
+ return {
242
+ x: (e as TouchEvent).targetTouches[0].clientX,
243
+ y: (e as TouchEvent).targetTouches[0].clientY
244
+ };
245
+ }
246
+ // mouse event
247
+ return { x: (e as MouseEvent).clientX, y: (e as MouseEvent).clientY };
248
+ }
249
+
250
+ static getInstance(el: HTMLElement): Timepicker {
251
+ return el['M_Timepicker'];
252
+ }
253
+
254
+ destroy() {
255
+ this._removeEventHandlers();
256
+ this.containerEl.remove();
257
+ this.el['M_Timepicker'] = undefined;
258
+ }
259
+
260
+ _setupEventHandlers() {
261
+ this.el.addEventListener('click', this._handleInputClick);
262
+ // @todo allow input field to fill values from input field when container/modal opens
263
+ this.el.addEventListener('keydown', this._handleInputKeydown);
264
+ this.plate.addEventListener('mousedown', this._handleClockClickStart);
265
+ this.plate.addEventListener('touchstart', this._handleClockClickStart);
266
+ this.digitalClock.addEventListener('keyup', this._inputFromTextField);
267
+ this.inputHours.addEventListener('focus', () => this.showView('hours'));
268
+ this.inputHours.addEventListener('focusout', () => this.formatHours());
269
+ this.inputMinutes.addEventListener('focus', () => this.showView('minutes'));
270
+ this.inputMinutes.addEventListener('focusout', () => this.formatMinutes());
271
+ }
272
+
273
+ _removeEventHandlers() {
274
+ this.el.removeEventListener('click', this._handleInputClick);
275
+ this.el.removeEventListener('keydown', this._handleInputKeydown);
276
+ }
277
+
278
+ _handleInputClick = () => {
279
+ this.inputHours.focus();
280
+ if (typeof this.options.onInputInteraction === 'function')
281
+ this.options.onInputInteraction.call(this);
282
+ if (this.displayPlugin) this.displayPlugin.show();
283
+ };
284
+
285
+ _handleInputKeydown = (e: KeyboardEvent) => {
286
+ if (Utils.keys.ENTER.includes(e.key)) {
287
+ e.preventDefault();
288
+ this.inputHours.focus();
289
+ if (typeof this.options.onInputInteraction === 'function')
290
+ this.options.onInputInteraction.call(this);
291
+ if (this.displayPlugin) this.displayPlugin.show();
292
+ }
293
+ };
294
+
295
+ _handleTimeInputEnterKey = (e: KeyboardEvent) => {
296
+ if (Utils.keys.ENTER.includes(e.key)) {
297
+ e.preventDefault();
298
+ this._inputFromTextField();
299
+ }
300
+ };
301
+
302
+ _handleClockClickStart = (e) => {
303
+ e.preventDefault();
304
+ const clockPlateBR = this.plate.getBoundingClientRect();
305
+ const offset = { x: clockPlateBR.left, y: clockPlateBR.top };
306
+
307
+ this.x0 = offset.x + this.options.dialRadius;
308
+ this.y0 = offset.y + this.options.dialRadius;
309
+ this.moved = false;
310
+ const clickPos = Timepicker._Pos(e);
311
+ this.dx = clickPos.x - this.x0;
312
+ this.dy = clickPos.y - this.y0;
313
+
314
+ // Set clock hands
315
+ this.setHand(this.dx, this.dy, false);
316
+ // Mousemove on document
317
+ document.addEventListener('mousemove', this._handleDocumentClickMove);
318
+ document.addEventListener('touchmove', this._handleDocumentClickMove);
319
+ // Mouseup on document
320
+ document.addEventListener('mouseup', this._handleDocumentClickEnd);
321
+ document.addEventListener('touchend', this._handleDocumentClickEnd);
322
+ };
323
+
324
+ _handleDocumentClickMove = (e) => {
325
+ e.preventDefault();
326
+ const clickPos = Timepicker._Pos(e);
327
+ const x = clickPos.x - this.x0;
328
+ const y = clickPos.y - this.y0;
329
+ this.moved = true;
330
+ this.setHand(x, y, false);
331
+ };
332
+
333
+ _handleDocumentClickEnd = (e) => {
334
+ e.preventDefault();
335
+ document.removeEventListener('mouseup', this._handleDocumentClickEnd);
336
+ document.removeEventListener('touchend', this._handleDocumentClickEnd);
337
+ const clickPos = Timepicker._Pos(e);
338
+ const x = clickPos.x - this.x0;
339
+ const y = clickPos.y - this.y0;
340
+ if (this.moved && x === this.dx && y === this.dy) {
341
+ this.setHand(x, y);
342
+ }
343
+
344
+ if (this.currentView === 'hours') {
345
+ this.inputMinutes.focus();
346
+ this.showView('minutes', this.options.duration / 2);
347
+ } else {
348
+ // this.minutesView.classList.add('timepicker-dial-out');
349
+ setTimeout(() => {
350
+ if (this.options.autoSubmit) this.done();
351
+ }, this.options.duration / 2);
352
+ }
353
+
354
+ if (typeof this.options.onSelect === 'function') {
355
+ this.options.onSelect.call(this, this.hours, this.minutes);
356
+ }
357
+ // Unbind mousemove event
358
+ document.removeEventListener('mousemove', this._handleDocumentClickMove);
359
+ document.removeEventListener('touchmove', this._handleDocumentClickMove);
360
+ };
361
+
362
+ _insertHTMLIntoDOM() {
363
+ const template = document.createElement('template');
364
+ template.innerHTML = Timepicker._template.trim();
365
+
366
+ this.containerEl = <HTMLElement>template.content.firstChild;
367
+ this.containerEl.id = 'container-' + this.id;
368
+
369
+ // Append popover to input by default
370
+ const optEl = this.options.container;
371
+ const containerEl = optEl instanceof HTMLElement ? optEl : document.querySelector(optEl);
372
+
373
+ if (this.options.container && !!containerEl) {
374
+ containerEl.append(this.containerEl);
375
+ } else {
376
+ this.el.parentElement.appendChild(this.containerEl);
377
+ }
378
+ }
379
+
380
+ _setupVariables() {
381
+ this.currentView = 'hours';
382
+ this.vibrate = navigator.vibrate
383
+ ? 'vibrate'
384
+ : navigator['webkitVibrate']
385
+ ? 'webkitVibrate'
386
+ : null;
387
+ this._canvas = this.containerEl.querySelector('.timepicker-canvas');
388
+ this.plate = this.containerEl.querySelector('.timepicker-plate');
389
+ this.digitalClock = this.containerEl.querySelector('.timepicker-display-column');
390
+ this.hoursView = this.containerEl.querySelector('.timepicker-hours');
391
+ this.minutesView = this.containerEl.querySelector('.timepicker-minutes');
392
+ this.inputHours = this.containerEl.querySelector('.timepicker-input-hours');
393
+ this.inputMinutes = this.containerEl.querySelector('.timepicker-input-minutes');
394
+ this.spanAmPm = this.containerEl.querySelector('.timepicker-span-am-pm');
395
+ this.footer = this.containerEl.querySelector('.timepicker-footer');
396
+ this.amOrPm = 'PM';
397
+ }
398
+
399
+ /*private _createButton(text: string, visibility: string): HTMLButtonElement {
400
+ const button = document.createElement('button');
401
+ button.classList.add('btn', 'waves-effect', 'text');
402
+ button.style.visibility = visibility;
403
+ button.type = 'button';
404
+ button.tabIndex = -1;
405
+ button.innerText = text;
406
+ return button;
407
+ }*/
408
+
409
+ _pickerSetup() {
410
+ // clearButton.classList.add('timepicker-clear');
411
+ // clearButton.addEventListener('click', this.clear);
412
+ // this.footer.appendChild(clearButton);
413
+ if (this.options.showClearBtn) {
414
+ Utils.createButton(
415
+ this.footer,
416
+ this.options.i18n.clear,
417
+ ['timepicker-clear'],
418
+ true,
419
+ this.clear
420
+ );
421
+ }
422
+
423
+ if (!this.options.autoSubmit) {
424
+ /*const confirmationBtnsContainer = document.createElement('div');
425
+ confirmationBtnsContainer.classList.add('confirmation-btns');
426
+ this.footer.append(confirmationBtnsContainer);
427
+
428
+ const cancelButton = this._createButton(this.options.i18n.cancel, '');
429
+ cancelButton.classList.add('timepicker-close');
430
+ cancelButton.addEventListener('click', this.close);
431
+ confirmationBtnsContainer.appendChild(cancelButton);
432
+
433
+ const doneButton = this._createButton(this.options.i18n.done, '');
434
+ doneButton.classList.add('timepicker-close');
435
+ //doneButton.addEventListener('click', this._finishSelection);
436
+ confirmationBtnsContainer.appendChild(doneButton);*/
437
+ Utils.createConfirmationContainer(
438
+ this.footer,
439
+ this.options.i18n.done,
440
+ this.options.i18n.cancel,
441
+ this.confirm,
442
+ this.cancel
443
+ );
444
+ }
445
+
446
+ this._updateTimeFromInput();
447
+ this.showView('hours');
448
+ }
449
+
450
+ private _setupDisplayPlugin() {
451
+ if (this.options.displayPlugin === 'docked')
452
+ this.displayPlugin = DockedDisplayPlugin.init(
453
+ this.el,
454
+ this.containerEl,
455
+ this.options.displayPluginOptions
456
+ );
457
+ if (this.options.displayPlugin === 'modal') {
458
+ this.displayPlugin = ModalDisplayPlugin.init(this.el, this.containerEl, {
459
+ ...this.options.displayPluginOptions,
460
+ ...{ classList: ['timepicker-modal'] }
461
+ });
462
+ this.footer.remove();
463
+ this.footer = this.displayPlugin.footer;
464
+ }
465
+ }
466
+
467
+ _clockSetup() {
468
+ if (this.options.twelveHour) {
469
+ // AM Button
470
+ this._amBtn = document.createElement('div');
471
+ this._amBtn.classList.add('am-btn', 'btn');
472
+ this._amBtn.innerText = 'AM';
473
+ this._amBtn.tabIndex = 0;
474
+ this._amBtn.addEventListener('click', this._handleAmPmClick);
475
+ this._amBtn.addEventListener('keypress', this._handleAmPmKeypress);
476
+ this.spanAmPm.appendChild(this._amBtn);
477
+ // PM Button
478
+ this._pmBtn = document.createElement('div');
479
+ this._pmBtn.classList.add('pm-btn', 'btn');
480
+ this._pmBtn.innerText = 'PM';
481
+ this._pmBtn.tabIndex = 0;
482
+ this._pmBtn.addEventListener('click', this._handleAmPmClick);
483
+ this._pmBtn.addEventListener('keypress', this._handleAmPmKeypress);
484
+ this.spanAmPm.appendChild(this._pmBtn);
485
+ }
486
+ this._buildHoursView();
487
+ this._buildMinutesView();
488
+ this._buildSVGClock();
489
+ }
490
+
491
+ _buildSVGClock() {
492
+ // Draw clock hands and others
493
+ const dialRadius = this.options.dialRadius;
494
+ const tickRadius = this.options.tickRadius;
495
+ const diameter = dialRadius * 2;
496
+ const svg = Timepicker._createSVGEl('svg');
497
+ svg.setAttribute('class', 'timepicker-svg');
498
+ svg.setAttribute('width', diameter.toString());
499
+ svg.setAttribute('height', diameter.toString());
500
+ const g = Timepicker._createSVGEl('g');
501
+ g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
502
+ const bearing = Timepicker._createSVGEl('circle');
503
+ bearing.setAttribute('class', 'timepicker-canvas-bearing');
504
+ bearing.setAttribute('cx', '0');
505
+ bearing.setAttribute('cy', '0');
506
+ bearing.setAttribute('r', '4');
507
+ const hand = Timepicker._createSVGEl('line');
508
+ hand.setAttribute('x1', '0');
509
+ hand.setAttribute('y1', '0');
510
+ const bg = Timepicker._createSVGEl('circle');
511
+ bg.setAttribute('class', 'timepicker-canvas-bg');
512
+ bg.setAttribute('r', tickRadius.toString());
513
+ g.appendChild(hand);
514
+ g.appendChild(bg);
515
+ g.appendChild(bearing);
516
+ svg.appendChild(g);
517
+ this._canvas.appendChild(svg);
518
+ this.hand = hand;
519
+ this.bg = bg;
520
+ this.bearing = bearing;
521
+ this.g = g;
522
+ }
523
+
524
+ _buildHoursView() {
525
+ // const $tick = document.createElement('div');
526
+ // $tick.classList.add('timepicker-tick');
527
+ // Hours view
528
+ if (this.options.twelveHour) {
529
+ for (let i = 1; i < 13; i += 1) {
530
+ // const tick = <HTMLElement>$tick.cloneNode(true);
531
+ const radian = (i / 6) * Math.PI;
532
+ const radius = this.options.outerRadius;
533
+ this._buildHoursTick(i, radian, radius);
534
+ }
535
+ } else {
536
+ for (let i = 0; i < 24; i += 1) {
537
+ // const tick = <HTMLElement>$tick.cloneNode(true);
538
+ const radian = (i / 6) * Math.PI;
539
+ const inner = i > 0 && i < 13;
540
+ const radius = inner ? this.options.innerRadius : this.options.outerRadius;
541
+ this._buildHoursTick(i, radian, radius);
542
+ }
543
+ }
544
+ }
545
+
546
+ _buildHoursTick(i: number, radian: number, radius: number) {
547
+ const tick = document.createElement('div');
548
+ tick.classList.add('timepicker-tick');
549
+ tick.style.left =
550
+ this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px';
551
+ tick.style.top =
552
+ this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px';
553
+ tick.innerHTML = i === 0 ? '00' : i.toString();
554
+ this.hoursView.appendChild(tick);
555
+ }
556
+
557
+ _buildMinutesView() {
558
+ const _tick = document.createElement('div');
559
+ _tick.classList.add('timepicker-tick');
560
+ // Minutes view
561
+ for (let i = 0; i < 60; i += 5) {
562
+ const tick = <HTMLElement>_tick.cloneNode(true);
563
+ const radian = (i / 30) * Math.PI;
564
+ tick.style.left =
565
+ this.options.dialRadius +
566
+ Math.sin(radian) * this.options.outerRadius -
567
+ this.options.tickRadius +
568
+ 'px';
569
+ tick.style.top =
570
+ this.options.dialRadius -
571
+ Math.cos(radian) * this.options.outerRadius -
572
+ this.options.tickRadius +
573
+ 'px';
574
+ tick.innerHTML = Timepicker._addLeadingZero(i);
575
+ this.minutesView.appendChild(tick);
576
+ }
577
+ }
578
+
579
+ _handleAmPmClick = (e: MouseEvent) => {
580
+ this._handleAmPmInteraction(<HTMLElement>e.target);
581
+ };
582
+
583
+ _handleAmPmKeypress = (e: KeyboardEvent) => {
584
+ if (Utils.keys.ENTER.includes(e.key)) {
585
+ this._handleAmPmInteraction(<HTMLElement>e.target);
586
+ }
587
+ };
588
+
589
+ _handleAmPmInteraction = (e: HTMLElement) => {
590
+ this.amOrPm = e.classList.contains('am-btn') ? 'AM' : 'PM';
591
+ this._updateAmPmView();
592
+ };
593
+
594
+ _updateAmPmView() {
595
+ if (this.options.twelveHour) {
596
+ if (this.amOrPm === 'PM') {
597
+ this._amBtn.classList.remove('filled');
598
+ this._pmBtn.classList.add('filled');
599
+ } else if (this.amOrPm === 'AM') {
600
+ this._amBtn.classList.add('filled');
601
+ this._pmBtn.classList.remove('filled');
602
+ }
603
+ }
604
+ }
605
+
606
+ _updateTimeFromInput() {
607
+ // Get the time
608
+ let value = ((this.el.value || this.options.defaultTime || '') + '').split(':');
609
+ if (this.options.twelveHour && !(typeof value[1] === 'undefined')) {
610
+ if (value[1].toUpperCase().indexOf('AM') > 0) {
611
+ this.amOrPm = 'AM';
612
+ } else {
613
+ this.amOrPm = 'PM';
614
+ }
615
+ value[1] = value[1].replace('AM', '').replace('PM', '');
616
+ }
617
+ if (value[0] === 'now') {
618
+ const now = new Date(+new Date() + this.options.fromNow);
619
+ value = [now.getHours().toString(), now.getMinutes().toString()];
620
+ if (this.options.twelveHour) {
621
+ this.amOrPm = parseInt(value[0]) >= 12 && parseInt(value[0]) < 24 ? 'PM' : 'AM';
622
+ }
623
+ }
624
+ this.hours = +value[0] || 0;
625
+ this.minutes = +value[1] || 0;
626
+ this.inputHours.value = Timepicker._addLeadingZero(this.hours);
627
+ this.inputMinutes.value = Timepicker._addLeadingZero(this.minutes);
628
+
629
+ this._updateAmPmView();
630
+ }
631
+
632
+ /**
633
+ * Show hours or minutes view on timepicker.
634
+ * @param view The name of the view you want to switch to, 'hours' or 'minutes'.
635
+ * @param delay
636
+ */
637
+ showView = (view: Views, delay: number = null) => {
638
+ if (view === 'minutes' && getComputedStyle(this.hoursView).visibility === 'visible') {
639
+ // raiseCallback(this.options.beforeHourSelect);
640
+ }
641
+ const isHours = view === 'hours',
642
+ nextView = isHours ? this.hoursView : this.minutesView,
643
+ hideView = isHours ? this.minutesView : this.hoursView;
644
+ this.currentView = view;
645
+
646
+ /*if (isHours) {
647
+ this.inputHours.classList.add('text-primary');
648
+ this.inputMinutes.classList.remove('text-primary');
649
+ } else {
650
+ this.inputHours.classList.remove('text-primary');
651
+ this.inputMinutes.classList.add('text-primary');
652
+ }*/
653
+
654
+ // Transition view
655
+ hideView.classList.add('timepicker-dial-out');
656
+
657
+ nextView.style.visibility = 'visible';
658
+ nextView.classList.remove('timepicker-dial-out');
659
+
660
+ // Reset clock hand
661
+ this.resetClock(delay);
662
+ // After transitions ended
663
+ clearTimeout(this.toggleViewTimer);
664
+ this.toggleViewTimer = setTimeout(() => {
665
+ hideView.style.visibility = 'hidden';
666
+ }, this.options.duration);
667
+ };
668
+
669
+ resetClock(delay) {
670
+ const view = this.currentView,
671
+ value = this[view],
672
+ isHours = view === 'hours',
673
+ unit = Math.PI / (isHours ? 6 : 30),
674
+ radian = value * unit,
675
+ radius =
676
+ isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius,
677
+ x = Math.sin(radian) * radius,
678
+ y = -Math.cos(radian) * radius;
679
+
680
+ if (delay) {
681
+ this._canvas?.classList.add('timepicker-canvas-out');
682
+ setTimeout(() => {
683
+ this._canvas?.classList.remove('timepicker-canvas-out');
684
+ this.setHand(x, y);
685
+ }, delay);
686
+ } else {
687
+ this.setHand(x, y);
688
+ }
689
+ }
690
+
691
+ _inputFromTextField = () => {
692
+ const isHours = this.currentView === 'hours';
693
+ if (isHours && this.inputHours.value !== '') {
694
+ const value = parseInt(this.inputHours.value);
695
+ if (value > 0 && value < (this.options.twelveHour ? 13 : 24)) {
696
+ this.hours = value;
697
+ } else {
698
+ this.setHoursDefault();
699
+ }
700
+ this.drawClockFromTimeInput(this.hours, isHours);
701
+ } else if (!isHours && this.inputMinutes.value !== '') {
702
+ const value = parseInt(this.inputMinutes.value);
703
+ if (value >= 0 && value < 60) {
704
+ this.minutes = value;
705
+ } else {
706
+ this.minutes = new Date().getMinutes();
707
+ this.inputMinutes.value = this.minutes.toString();
708
+ }
709
+ this.drawClockFromTimeInput(this.minutes, isHours);
710
+ }
711
+ };
712
+
713
+ drawClockFromTimeInput(value, isHours) {
714
+ const unit = Math.PI / (isHours ? 6 : 30);
715
+ const radian = value * unit;
716
+ let radius;
717
+ if (this.options.twelveHour) {
718
+ radius = this.options.outerRadius;
719
+ } else {
720
+ radius =
721
+ isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius;
722
+ }
723
+ this.setClockAttributes(radian, radius);
724
+ }
725
+
726
+ setHand(x, y, roundBy5: boolean = false) {
727
+ const isHours = this.currentView === 'hours',
728
+ unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
729
+ z = Math.sqrt(x * x + y * y),
730
+ inner = isHours && z < (this.options.outerRadius + this.options.innerRadius) / 2;
731
+ let radian = Math.atan2(x, -y),
732
+ radius = inner ? this.options.innerRadius : this.options.outerRadius;
733
+
734
+ if (this.options.twelveHour) {
735
+ radius = this.options.outerRadius;
736
+ }
737
+
738
+ // Radian should in range [0, 2PI]
739
+ if (radian < 0) {
740
+ radian = Math.PI * 2 + radian;
741
+ }
742
+
743
+ // Get the round value
744
+ let value = Math.round(radian / unit);
745
+
746
+ // Get the round radian
747
+ radian = value * unit;
748
+
749
+ // Correct the hours or minutes
750
+ if (this.options.twelveHour) {
751
+ if (isHours) {
752
+ if (value === 0) value = 12;
753
+ } else {
754
+ if (roundBy5) value *= 5;
755
+ if (value === 60) value = 0;
756
+ }
757
+ } else {
758
+ if (isHours) {
759
+ if (value === 12) {
760
+ value = 0;
761
+ }
762
+ value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
763
+ } else {
764
+ if (roundBy5) {
765
+ value *= 5;
766
+ }
767
+ if (value === 60) {
768
+ value = 0;
769
+ }
770
+ }
771
+ }
772
+
773
+ // Once hours or minutes changed, vibrate the device
774
+ if (this[this.currentView] !== value) {
775
+ if (this.vibrate && this.options.vibrate) {
776
+ // Do not vibrate too frequently
777
+ if (!this.vibrateTimer) {
778
+ navigator[this.vibrate](10);
779
+ this.vibrateTimer = setTimeout(() => {
780
+ this.vibrateTimer = null;
781
+ }, 100);
782
+ }
783
+ }
784
+ }
785
+
786
+ this[this.currentView] = value;
787
+ if (isHours) {
788
+ this.inputHours.value = Timepicker._addLeadingZero(value);
789
+ } else {
790
+ this.inputMinutes.value = Timepicker._addLeadingZero(value);
791
+ }
792
+
793
+ // Set clock hand and others' position
794
+ this.setClockAttributes(radian, radius);
795
+ }
796
+
797
+ setClockAttributes(radian: number, radius: number) {
798
+ const cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
799
+ cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
800
+ cx2 = Math.sin(radian) * radius,
801
+ cy2 = -Math.cos(radian) * radius;
802
+ this.hand.setAttribute('x2', cx1.toString());
803
+ this.hand.setAttribute('y2', cy1.toString());
804
+ this.bg.setAttribute('cx', cx2.toString());
805
+ this.bg.setAttribute('cy', cy2.toString());
806
+ }
807
+
808
+ formatHours() {
809
+ if (this.inputHours.value == '') this.setHoursDefault();
810
+ this.inputHours.value = Timepicker._addLeadingZero(Number(this.inputHours.value));
811
+ }
812
+
813
+ formatMinutes() {
814
+ if (this.inputMinutes.value == '') this.minutes = new Date().getMinutes();
815
+ this.inputMinutes.value = Timepicker._addLeadingZero(Number(this.inputMinutes.value));
816
+ }
817
+
818
+ setHoursDefault() {
819
+ this.hours = new Date().getHours();
820
+ this.inputHours.value = (this.hours % (this.options.twelveHour ? 12 : 24)).toString();
821
+ }
822
+
823
+ done = (clearValue = null) => {
824
+ // Set input value
825
+ const last = this.el.value;
826
+ let value = clearValue
827
+ ? ''
828
+ : Timepicker._addLeadingZero(this.hours) + ':' + Timepicker._addLeadingZero(this.minutes);
829
+ this.time = value;
830
+ if (!clearValue && this.options.twelveHour) {
831
+ value = `${value} ${this.amOrPm}`;
832
+ }
833
+ this.el.value = value;
834
+ // Trigger change event
835
+ if (value !== last) {
836
+ this.el.dispatchEvent(
837
+ new Event('change', { bubbles: true, cancelable: true, composed: true })
838
+ );
839
+ }
840
+ };
841
+
842
+ confirm = () => {
843
+ this.done();
844
+ if (this.displayPlugin) this.displayPlugin.hide();
845
+ if (typeof this.options.onDone === 'function') this.options.onDone.call(this);
846
+ };
847
+
848
+ cancel = () => {
849
+ // not logical clearing the input field on cancel, since the end user might want to make use of the previously submitted value
850
+ // this.clear();
851
+ if (this.displayPlugin) this.displayPlugin.hide();
852
+ if (typeof this.options.onCancel === 'function') this.options.onCancel.call(this);
853
+ };
854
+
855
+ clear = () => {
856
+ // @todo should clear timepicker hour/minute input elems and reset analog clock, currently clears input el
857
+ this.done(true);
858
+ };
859
+
860
+ // deprecated
861
+ open() {
862
+ console.warn(
863
+ 'Timepicker.close() is deprecated. Remove this method and wrap in modal yourself.'
864
+ );
865
+ return this;
866
+ }
867
+
868
+ close() {
869
+ console.warn(
870
+ 'Timepicker.close() is deprecated. Remove this method and wrap in modal yourself.'
871
+ );
872
+ return this;
873
+ }
874
+
875
+ static {
876
+ Timepicker._template = `<div class="timepicker-container">
877
+ <div class="timepicker-digital-display">
878
+ <div class="timepicker-text-container">
879
+ <div class="timepicker-display-column timepicker-display-digital-clock">
880
+ <div class="timepicker-input-hours-wrapper">
881
+ <input type="text" maxlength="2" class="timepicker-input-hours text-primary" />
882
+ </div>
883
+ <div class="timepicker-input-divider-wrapper">
884
+ <span class="timepicker-input-divider">:</span>
885
+ </div>
886
+ <div class="timepicker-input-minutes-wrapper">
887
+ <input type="text" maxlength="2" class="timepicker-input-minutes" />
888
+ </div>
889
+ </div>
890
+ <div class="timepicker-display-column timepicker-display-am-pm">
891
+ <div class="timepicker-span-am-pm"></div>
892
+ </div>
893
+ </div>
894
+ </div>
895
+ <div class="timepicker-analog-display">
896
+ <div class="timepicker-plate">
897
+ <div class="timepicker-canvas"></div>
898
+ <div class="timepicker-dial timepicker-hours"></div>
899
+ <div class="timepicker-dial timepicker-minutes timepicker-dial-out"></div>
900
+ </div>
901
+ <div class="timepicker-footer"></div>
902
+ </div>
903
+ </div>`;
904
+ }
905
+ }