kmaterialize 2.0.0 → 2.3.3-1.0.1

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 (156) 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/colors.module.css +74 -0
  118. package/sass/materialize.scss +51 -0
  119. package/sass/mixins.module.scss +168 -0
  120. package/sass/pulse.css +35 -0
  121. package/sass/theme.module.css +140 -0
  122. package/sass/tokens.module.css +272 -0
  123. package/sass/transitions.css +10 -0
  124. package/sass/typography.module.css +150 -0
  125. package/extras/noUiSlider/nouislider.css +0 -406
  126. package/extras/noUiSlider/nouislider.js +0 -2147
  127. package/extras/noUiSlider/nouislider.min.js +0 -1
  128. package/js/anime.min.js +0 -34
  129. package/js/autocomplete.js +0 -448
  130. package/js/buttons.js +0 -354
  131. package/js/cards.js +0 -40
  132. package/js/carousel.js +0 -717
  133. package/js/cash.js +0 -960
  134. package/js/characterCounter.js +0 -136
  135. package/js/chips.js +0 -481
  136. package/js/collapsible.js +0 -275
  137. package/js/component.js +0 -44
  138. package/js/datepicker.js +0 -975
  139. package/js/dropdown.js +0 -635
  140. package/js/forms.js +0 -275
  141. package/js/global.js +0 -444
  142. package/js/materialbox.js +0 -453
  143. package/js/modal.js +0 -382
  144. package/js/parallax.js +0 -138
  145. package/js/pushpin.js +0 -148
  146. package/js/range.js +0 -263
  147. package/js/scrollspy.js +0 -295
  148. package/js/select.js +0 -450
  149. package/js/sidenav.js +0 -580
  150. package/js/slider.js +0 -359
  151. package/js/tabs.js +0 -402
  152. package/js/tapTarget.js +0 -314
  153. package/js/timepicker.js +0 -647
  154. package/js/toasts.js +0 -310
  155. package/js/tooltip.js +0 -303
  156. 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
+ }