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