beyond-rails 0.0.139

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 (138) hide show
  1. checksums.yaml +7 -0
  2. data/src/font/icomoon.eot +0 -0
  3. data/src/font/icomoon.svg +125 -0
  4. data/src/font/icomoon.ttf +0 -0
  5. data/src/font/icomoon.woff +0 -0
  6. data/src/img/black-cat.svg +15 -0
  7. data/src/img/cart.svg +16 -0
  8. data/src/img/china-flag.svg +16 -0
  9. data/src/img/ecpay.svg +12 -0
  10. data/src/img/family-mart.svg +13 -0
  11. data/src/img/fb-messenger.svg +12 -0
  12. data/src/img/fb.svg +10 -0
  13. data/src/img/hct.svg +16 -0
  14. data/src/img/hi-life.svg +23 -0
  15. data/src/img/line.svg +14 -0
  16. data/src/img/ok-mart.svg +9 -0
  17. data/src/img/pelican.svg +33 -0
  18. data/src/img/seven-eleven.svg +13 -0
  19. data/src/img/smilepay.svg +13 -0
  20. data/src/img/taiwan-flag.svg +17 -0
  21. data/src/js/components/Alert.js +23 -0
  22. data/src/js/components/Autocomplete.js +110 -0
  23. data/src/js/components/AutocompleteMenu.js +88 -0
  24. data/src/js/components/Btn.js +41 -0
  25. data/src/js/components/Checkbox.js +24 -0
  26. data/src/js/components/DateInput.js +74 -0
  27. data/src/js/components/DateMenu.js +370 -0
  28. data/src/js/components/DateTimeRanger.js +436 -0
  29. data/src/js/components/Datepicker.js +250 -0
  30. data/src/js/components/DatepickerBtnArrow.js +18 -0
  31. data/src/js/components/Dropdown.js +137 -0
  32. data/src/js/components/Menu.js +43 -0
  33. data/src/js/components/Modal.js +76 -0
  34. data/src/js/components/Navbar.js +47 -0
  35. data/src/js/components/Radio.js +24 -0
  36. data/src/js/components/SearchDropdown.js +339 -0
  37. data/src/js/components/Sidebar.js +56 -0
  38. data/src/js/components/Tabbox.js +229 -0
  39. data/src/js/components/TimeInput.js +71 -0
  40. data/src/js/components/TimeMenu.js +117 -0
  41. data/src/js/components/Toast.js +47 -0
  42. data/src/js/components/ToastItem.js +62 -0
  43. data/src/js/components/Tooltip.js +94 -0
  44. data/src/js/consts/createdComponents.js +1 -0
  45. data/src/js/consts/index.js +5 -0
  46. data/src/js/helpers/bind.js +53 -0
  47. data/src/js/helpers/dateEq.js +5 -0
  48. data/src/js/helpers/dateGt.js +5 -0
  49. data/src/js/helpers/dateLt.js +5 -0
  50. data/src/js/helpers/docReady.js +10 -0
  51. data/src/js/helpers/getFloatedTargetPos.js +250 -0
  52. data/src/js/helpers/getKey.js +14 -0
  53. data/src/js/helpers/isTouchDevice.js +3 -0
  54. data/src/js/helpers/msToS.js +3 -0
  55. data/src/js/helpers/promisify.js +9 -0
  56. data/src/js/helpers/range.js +7 -0
  57. data/src/js/helpers/supportDom.js +46 -0
  58. data/src/js/helpers/toPixel.js +3 -0
  59. data/src/js/helpers/unbindAll.js +6 -0
  60. data/src/js/index.js +47 -0
  61. data/src/js/jquery/bindAlertFn.js +13 -0
  62. data/src/js/jquery/bindAutocompleteFn.js +13 -0
  63. data/src/js/jquery/bindBtnFn.js +17 -0
  64. data/src/js/jquery/bindCheckboxFn.js +13 -0
  65. data/src/js/jquery/bindDateTimeRangerFn.js +14 -0
  66. data/src/js/jquery/bindDatepickerFn.js +14 -0
  67. data/src/js/jquery/bindDropdownFn.js +14 -0
  68. data/src/js/jquery/bindMenuFn.js +13 -0
  69. data/src/js/jquery/bindModalFn.js +14 -0
  70. data/src/js/jquery/bindNavbarFn.js +13 -0
  71. data/src/js/jquery/bindRadioFn.js +13 -0
  72. data/src/js/jquery/bindSearchDropdownFn.js +14 -0
  73. data/src/js/jquery/bindSidebarFn.js +13 -0
  74. data/src/js/jquery/bindTabboxFn.js +13 -0
  75. data/src/js/jquery/bindToastFn.js +6 -0
  76. data/src/js/jquery/bindTooltipFn.js +13 -0
  77. data/src/js/jquery/index.js +52 -0
  78. data/src/js/polyfills/classList.js +263 -0
  79. data/src/js/polyfills/elementDataset.js +3 -0
  80. data/src/js/polyfills/nodeContains.js +17 -0
  81. data/src/js/polyfills/nodeHasAttribute.js +5 -0
  82. data/src/js/polyfills/nodeRemove.js +19 -0
  83. data/src/sass/_beyond-sprockets.scss +1 -0
  84. data/src/sass/_beyond.scss +50 -0
  85. data/src/sass/_main.scss +141 -0
  86. data/src/sass/abstracts/_mixins.scss +129 -0
  87. data/src/sass/abstracts/_placeholders.scss +43 -0
  88. data/src/sass/abstracts/_variables.scss +355 -0
  89. data/src/sass/base/_background.scss +10 -0
  90. data/src/sass/base/_typography.scss +183 -0
  91. data/src/sass/components/_alert.scss +50 -0
  92. data/src/sass/components/_autocomplete.scss +29 -0
  93. data/src/sass/components/_avatar.scss +28 -0
  94. data/src/sass/components/_badge.scss +29 -0
  95. data/src/sass/components/_breadcrumb.scss +17 -0
  96. data/src/sass/components/_btn-group.scss +19 -0
  97. data/src/sass/components/_btn.scss +172 -0
  98. data/src/sass/components/_card.scss +183 -0
  99. data/src/sass/components/_checkbox.scss +99 -0
  100. data/src/sass/components/_date-input.scss +28 -0
  101. data/src/sass/components/_date-menu.scss +85 -0
  102. data/src/sass/components/_date-time-ranger.scss +21 -0
  103. data/src/sass/components/_datepicker.scss +3 -0
  104. data/src/sass/components/_dropdown.scss +144 -0
  105. data/src/sass/components/_form.scss +383 -0
  106. data/src/sass/components/_icon.scss +371 -0
  107. data/src/sass/components/_input.scss +48 -0
  108. data/src/sass/components/_list.scss +23 -0
  109. data/src/sass/components/_modal.scss +72 -0
  110. data/src/sass/components/_nav.scss +75 -0
  111. data/src/sass/components/_navbar.scss +211 -0
  112. data/src/sass/components/_pagination.scss +64 -0
  113. data/src/sass/components/_radio.scss +71 -0
  114. data/src/sass/components/_search-dropdown.scss +28 -0
  115. data/src/sass/components/_select.scss +54 -0
  116. data/src/sass/components/_sidebar.scss +35 -0
  117. data/src/sass/components/_spinner.scss +79 -0
  118. data/src/sass/components/_tabbox.scss +83 -0
  119. data/src/sass/components/_table.scss +65 -0
  120. data/src/sass/components/_tag.scss +43 -0
  121. data/src/sass/components/_time-input.scss +28 -0
  122. data/src/sass/components/_time-menu.scss +24 -0
  123. data/src/sass/components/_toast.scss +51 -0
  124. data/src/sass/components/_tooltip.scss +10 -0
  125. data/src/sass/img/arrow-dropdown.svg +4 -0
  126. data/src/sass/img/arrow-select-ex.svg +18 -0
  127. data/src/sass/img/arrow-select.svg +18 -0
  128. data/src/sass/layout/_border-util.scss +36 -0
  129. data/src/sass/layout/_col.scss +90 -0
  130. data/src/sass/layout/_container.scss +44 -0
  131. data/src/sass/layout/_flex-util.scss +18 -0
  132. data/src/sass/layout/_offset-util.scss +20 -0
  133. data/src/sass/layout/_sizing-util.scss +14 -0
  134. data/src/sass/layout/_spacing-util.scss +9 -0
  135. data/src/sass/layout/_visibility-util.scss +25 -0
  136. data/src/sass/vendor/_normalize.scss +578 -0
  137. data/src/sass/vendor/_turbolink.scss +5 -0
  138. metadata +235 -0
@@ -0,0 +1,436 @@
1
+ import endOfDay from 'date-fns/endOfDay'
2
+ import parse from 'date-fns/parse'
3
+ import set from 'date-fns/set'
4
+ import noop from 'lodash.noop'
5
+ import startOfDay from 'date-fns/startOfDay'
6
+ import getHours from 'date-fns/getHours'
7
+ import getMinutes from 'date-fns/getMinutes'
8
+ import getSeconds from 'date-fns/getSeconds'
9
+ import DateInput from './DateInput'
10
+ import TimeInput from './TimeInput'
11
+ import DateMenu from './DateMenu'
12
+ import TimeMenu from './TimeMenu'
13
+ import DatepickerBtnArrow from './DatepickerBtnArrow'
14
+ import dateGt from '../helpers/dateGt'
15
+ import dateLt from '../helpers/dateLt'
16
+ import supportDom from '../helpers/supportDom'
17
+ import dateToTimestamp from '@superlanding/datetotimestamp'
18
+ import timestampToDate from '@superlanding/timestamptodate'
19
+
20
+ @supportDom
21
+ export default class DateTimeRanger {
22
+
23
+ constructor(dom, options = {}) {
24
+ this.dom = dom
25
+ this.options = options
26
+ this.options.change = options.change || noop
27
+ this.options.useMouseOver = ('useMouseOver' in options) ?
28
+ options.useMouseOver : true
29
+
30
+ this.lastTriggered = null
31
+ this.nextDate = null
32
+ this.focused = false
33
+ this.inputDateStartSet = false
34
+ this.inputDateEndSet = false
35
+ this.init()
36
+ }
37
+
38
+ init() {
39
+ const { dom } = this
40
+ const { startAt, endAt } = this.options
41
+
42
+ this.startDate = startAt ? new Date(startAt * 1000) : startOfDay(new Date())
43
+ this.endDate = endAt ? new Date(endAt * 1000) : endOfDay(this.startDate)
44
+
45
+ this.currentDate = this.startDate
46
+
47
+ this.inputDateStart = new DateInput(
48
+ dom.querySelector('[data-date-start]'),
49
+ this.startDate,
50
+ this.options
51
+ )
52
+
53
+ this.inputTimeStart = new TimeInput(
54
+ dom.querySelector('[data-time-start]'),
55
+ this.startDate,
56
+ this.options
57
+ )
58
+
59
+ this.btnArrow = new DatepickerBtnArrow(
60
+ dom.querySelector('[data-btn-arrow]')
61
+ )
62
+
63
+ this.inputDateEnd = new DateInput(
64
+ dom.querySelector('[data-date-end]'),
65
+ this.endDate,
66
+ this.options
67
+ )
68
+
69
+ this.inputTimeEnd = new TimeInput(
70
+ dom.querySelector('[data-time-end]'),
71
+ this.endDate,
72
+ this.options
73
+ )
74
+
75
+ this.dateMenu = new DateMenu({
76
+ date: this.currentDate,
77
+ startDate: this.startDate,
78
+ endDate: this.endDate,
79
+ options: this.options
80
+ })
81
+ this.timeMenu = new TimeMenu()
82
+
83
+ this.addEvents()
84
+ }
85
+
86
+ setTimestamps(startAt, endAt) {
87
+ return this.setDates(
88
+ timestampToDate(startAt),
89
+ timestampToDate(endAt)
90
+ )
91
+ }
92
+
93
+ setDates(startDate, endDate) {
94
+ if (dateGt(startDate, endDate)) {
95
+ throw new Error('Start date cannot be greater than end date.')
96
+ }
97
+ this.startDate = startDate
98
+ this.endDate = endDate
99
+ this.inputDateStart.setDate(this.startDate)
100
+ this.inputTimeStart.setDate(this.startDate)
101
+ this.inputDateEnd.setDate(this.endDate)
102
+ this.inputTimeEnd.setDate(this.endDate)
103
+ }
104
+
105
+ clearInputStatus() {
106
+ this.inputDateStart.clearStatus()
107
+ this.inputTimeStart.clearStatus()
108
+ this.inputDateEnd.clearStatus()
109
+ this.inputTimeEnd.clearStatus()
110
+ }
111
+
112
+ clearTimeInputStatus() {
113
+ this.inputTimeStart.clearStatus()
114
+ this.inputTimeEnd.clearStatus()
115
+ }
116
+
117
+ handleDateInputFocus(input) {
118
+ this.focused = true
119
+ this.inputDateStartSet = false
120
+ this.inputDateEndSet = false
121
+ this.clearTimeInputStatus()
122
+ this.inputDateStart.setActive(true)
123
+ this.inputDateEnd.setActive(true)
124
+ this.dateMenu.setDate({
125
+ date: this.startDate,
126
+ startDate: this.startDate,
127
+ endDate: this.endDate
128
+ })
129
+ this.dateMenu.show(this.dom)
130
+ this.timeMenu.hide()
131
+ }
132
+
133
+ handleTimeInputFocus(input) {
134
+ this.focused = true
135
+ this.clearInputStatus()
136
+ input.setActive(true)
137
+ this.lastTriggered = input
138
+ this.dateMenu.hide()
139
+ this.timeMenu.show({
140
+ src: this.dom,
141
+ date: input.date,
142
+ step: parseInt(input.dom.dataset.step, 10) || 30
143
+ })
144
+ }
145
+
146
+ handleDateInputKeyUp({ event, input, date, isStart }) {
147
+
148
+ const res = parse(event.target.value, input.datePattern, date)
149
+ this.nextDate = null
150
+
151
+ if (res.toString() === 'Invalid Date') {
152
+ return input.setDanger(true)
153
+ }
154
+ if (isStart && dateGt(startOfDay(res), startOfDay(this.endDate))) {
155
+ return input.setDanger(true)
156
+ }
157
+ if ((! isStart) && dateLt(startOfDay(res), startOfDay(this.startDate))) {
158
+ return input.setDanger(true)
159
+ }
160
+ input.setDanger(false)
161
+ this.nextDate = res
162
+ }
163
+
164
+ handleTimeInputKeyUp({ event, input, date, isStart }) {
165
+ const res = parse(event.target.value, input.timePattern, date)
166
+ this.nextDate = null
167
+
168
+ if (res.toString() === 'Invalid Date') {
169
+ return input.setDanger(true)
170
+ }
171
+ if (isStart && dateGt(startOfDay(res), startOfDay(this.endDate))) {
172
+ return input.setDanger(true)
173
+ }
174
+ if ((! isStart) && dateLt(startOfDay(res), startOfDay(this.startDate))) {
175
+ return input.setDanger(true)
176
+ }
177
+ input.setDanger(false)
178
+ this.nextDate = res
179
+ }
180
+
181
+ handleDateInputBlur({ input, isStart }) {
182
+ const dateProp = isStart ? 'startDate' : 'endDate'
183
+ const oldDate = this[dateProp]
184
+ const { nextDate } = this
185
+
186
+ if (nextDate) {
187
+ this[dateProp] = nextDate
188
+ input.setDate(nextDate)
189
+ this.dateMenu.setDate({ [dateProp]: nextDate })
190
+ this.nextDate = null
191
+ }
192
+ else {
193
+ input.setDate(oldDate)
194
+ }
195
+ }
196
+
197
+ handleTimeInputBlur({ input, isStart }) {
198
+ const dateProp = isStart ? 'startDate' : 'endDate'
199
+ const oldDate = this[dateProp]
200
+ const { nextDate } = this
201
+
202
+ if (nextDate) {
203
+ this[dateProp] = nextDate
204
+ input.setDate(nextDate)
205
+ this.nextDate = null
206
+ }
207
+ else {
208
+ input.setDate(oldDate)
209
+ }
210
+ }
211
+
212
+ addDateInputEvents() {
213
+ this.inputDateStart.on('focus', () => this.handleDateInputFocus(this.inputDateStart))
214
+ this.inputDateStart.on('keyup', event => {
215
+ return this.handleDateInputKeyUp({
216
+ event,
217
+ input: this.inputDateStart,
218
+ date: this.startDate,
219
+ isStart: true
220
+ })
221
+ })
222
+ this.inputDateStart.on('blur', () => {
223
+ return this.handleDateInputBlur({
224
+ input: this.inputDateStart,
225
+ isStart: true
226
+ })
227
+ })
228
+
229
+ this.inputDateEnd.on('focus', () => this.handleDateInputFocus(this.inputDateEnd))
230
+ this.inputDateEnd.on('keyup', event => {
231
+ return this.handleDateInputKeyUp({
232
+ event,
233
+ input: this.inputDateEnd,
234
+ date: this.endDate,
235
+ isStart: false
236
+ })
237
+ })
238
+ this.inputDateEnd.on('blur', () => {
239
+ return this.handleDateInputBlur({
240
+ input: this.inputDateEnd,
241
+ isStart: false
242
+ })
243
+ })
244
+ }
245
+
246
+ addTimeInputEvents() {
247
+ this.inputTimeStart.on('focus', () => this.handleTimeInputFocus(this.inputTimeStart))
248
+ this.inputTimeStart.on('keyup', event => {
249
+ return this.handleTimeInputKeyUp({
250
+ event,
251
+ input: this.inputTimeStart,
252
+ date: this.startDate,
253
+ isStart: true
254
+ })
255
+ })
256
+ this.inputTimeStart.on('blur', event => {
257
+ return this.handleTimeInputBlur({
258
+ input: this.inputTimeStart,
259
+ isStart: true
260
+ })
261
+ })
262
+
263
+ this.inputTimeEnd.on('focus', () => this.handleTimeInputFocus(this.inputTimeEnd))
264
+ this.inputTimeEnd.on('keyup', event => {
265
+ return this.handleTimeInputKeyUp({
266
+ event,
267
+ input: this.inputTimeEnd,
268
+ date: this.startDate,
269
+ isStart: false
270
+ })
271
+ })
272
+ this.inputTimeEnd.on('blur', event => {
273
+ return this.handleTimeInputBlur({
274
+ input: this.inputTimeEnd,
275
+ isStart: false
276
+ })
277
+ })
278
+ }
279
+
280
+ switchDates() {
281
+ const oldStartDate = this.startDate
282
+ const oldEndDate = this.endDate;
283
+ [this.startDate, this.endDate] = [this.endDate, this.startDate]
284
+
285
+ // keeps the time
286
+ this.startDate = set(this.startDate, {
287
+ hours: getHours(oldStartDate),
288
+ minutes: getMinutes(oldStartDate),
289
+ seconds: getSeconds(oldStartDate)
290
+ })
291
+ this.endDate = set(this.endDate, {
292
+ hours: getHours(oldEndDate),
293
+ minutes: getMinutes(oldEndDate),
294
+ seconds: getSeconds(oldEndDate)
295
+ })
296
+ this.inputDateStart.setDate(this.startDate)
297
+ this.inputDateEnd.setDate(this.endDate)
298
+ }
299
+
300
+ emitChange() {
301
+ const { startDate, endDate } = this
302
+ this.options.change({
303
+ startDate,
304
+ endDate,
305
+ startAt: dateToTimestamp(startDate),
306
+ endAt: dateToTimestamp(endDate)
307
+ })
308
+ }
309
+
310
+ addMenuEvents() {
311
+ this.dateMenu.on('td-mouseover', (event, res) => {
312
+ if (this.dateMenu.startDate && (! this.dateMenu.endDate)) {
313
+ this.dateMenu.setHoveredCell(res)
314
+ }
315
+ })
316
+ this.dateMenu.on('td-click', (event, res) => {
317
+ event.stopPropagation()
318
+ event.preventDefault()
319
+
320
+ if (this.inputDateStartSet && this.inputDateEndSet) {
321
+ this.inputDateStartSet = false
322
+ this.inputDateEndSet = false
323
+ }
324
+
325
+ const { year, month, date } = res
326
+
327
+ if ((! this.inputDateStartSet) && (! this.inputDateEndSet)) {
328
+ this.dateMenu.setDate({ startDate: null, endDate: null })
329
+ const nextStartDate = set(this.startDate, { year, month, date })
330
+ this.startDate = nextStartDate
331
+ this.inputDateStart.setDate(nextStartDate)
332
+ this.inputDateStartSet = true
333
+ return this.dateMenu.setDate({
334
+ startDate: this.startDate
335
+ })
336
+ }
337
+
338
+ if (this.inputDateStartSet && (! this.inputDateEndSet)) {
339
+ this.endDate = set(this.endDate, { year, month, date })
340
+
341
+ // switch if next endDate is prior to startDate
342
+ if (dateLt(startOfDay(this.endDate), startOfDay(this.startDate))) {
343
+ this.switchDates()
344
+ }
345
+ this.inputDateStart.setDate(this.startDate)
346
+ this.inputDateEnd.setDate(this.endDate)
347
+
348
+ this.inputDateEndSet = true
349
+ this.dateMenu.setDate({
350
+ startDate: this.startDate,
351
+ endDate: this.endDate
352
+ })
353
+ return this.emitChange()
354
+ }
355
+ })
356
+
357
+ this.timeMenu.on('click', (event, res) => {
358
+ const nextDate = set(this.lastTriggered.date, { hours: res.hour, minutes: res.minute })
359
+ if (this.lastTriggered === this.inputTimeStart) {
360
+ this.startDate = nextDate
361
+ }
362
+ if (this.lastTriggered === this.inputTimeEnd) {
363
+ this.endDate = nextDate
364
+ }
365
+ this.lastTriggered.setDate(nextDate)
366
+ this.timeMenu.hide()
367
+ this.clearInputStatus()
368
+ this.emitChange()
369
+ })
370
+ }
371
+
372
+ addBtnArrowEvents() {
373
+ this.btnArrow.on('click', () => {
374
+ this.inputDateStart.focus()
375
+ this.handleDateInputFocus(this.inputDateStart)
376
+ })
377
+ }
378
+
379
+ addEvents() {
380
+
381
+ this.addDateInputEvents()
382
+ this.addTimeInputEvents()
383
+ this.addMenuEvents()
384
+ this.addBtnArrowEvents()
385
+
386
+ this.addEvent(document, 'click', event => {
387
+ const { dom, dateMenu, timeMenu } = this
388
+ const { target } = event
389
+ const dateMenuDom = dateMenu.dom
390
+ const timeMenuDom = timeMenu.dom
391
+
392
+ if (this.focused) {
393
+ this.focused = false
394
+ return
395
+ }
396
+ if ((! dateMenu.isVisible) && (! timeMenu.isVisible)) {
397
+ return
398
+ }
399
+ if (dom.contains(target)) {
400
+ return
401
+ }
402
+
403
+ if (dateMenuDom.contains(target)) {
404
+ return
405
+ }
406
+ if (dateMenuDom === target) {
407
+ return
408
+ }
409
+
410
+ if (timeMenuDom.contains(target)) {
411
+ return
412
+ }
413
+ if (timeMenuDom === target) {
414
+ return
415
+ }
416
+
417
+ this.clearInputStatus()
418
+
419
+ if (dateLt(startOfDay(this.endDate), startOfDay(this.startDate))) {
420
+ this.switchDates()
421
+ }
422
+ dateMenu.hide()
423
+ timeMenu.hide()
424
+ })
425
+ }
426
+
427
+ destroy() {
428
+ this.inputDateStart.destroy()
429
+ this.inputTimeStart.destroy()
430
+ this.inputDateEnd.destroy()
431
+ this.inputTimeEnd.destroy()
432
+ this.dateMenu.destroy()
433
+ this.timeMenu.destroy()
434
+ this.btnArrow.destroy()
435
+ }
436
+ }
@@ -0,0 +1,250 @@
1
+ import parse from 'date-fns/parse'
2
+ import set from 'date-fns/set'
3
+ import noop from 'lodash.noop'
4
+ import toDate from 'date-fns/toDate'
5
+ import DateInput from './DateInput'
6
+ import TimeInput from './TimeInput'
7
+ import DateMenu from './DateMenu'
8
+ import TimeMenu from './TimeMenu'
9
+ import supportDom from '../helpers/supportDom'
10
+ import { DEFAULT_TIMEZONE } from '../consts'
11
+ import dateToTimestamp from '@superlanding/datetotimestamp'
12
+ import timestampToDate from '@superlanding/timestamptodate'
13
+
14
+ @supportDom
15
+ export default class Datepicker {
16
+
17
+ constructor(dom, timestamp, options = {}) {
18
+ this.dom = dom
19
+ this.options = options
20
+ this.options.change = options.change || noop
21
+ this.tz = options.tz || DEFAULT_TIMEZONE
22
+
23
+ this.date = (timestamp === null) ? null : timestampToDate(timestamp)
24
+ this.menuDate = (timestamp === null) ? toDate(new Date()) : toDate(this.date)
25
+ this.focused = false
26
+ this.nextDate = null
27
+ this.init()
28
+ }
29
+
30
+ init() {
31
+ const { dom } = this
32
+ this.dateInput = new DateInput(
33
+ dom.querySelector('[data-date]'),
34
+ this.date,
35
+ this.options
36
+ )
37
+ this.dateMenu = new DateMenu({
38
+ date: this.menuDate,
39
+ options: Object.assign({}, this.options, { useSingleMenu: true })
40
+ })
41
+
42
+ const timeInput = dom.querySelector('[data-time]')
43
+ if (timeInput) {
44
+ this.timeInput = new TimeInput(
45
+ timeInput,
46
+ this.date,
47
+ this.options
48
+ )
49
+ this.timeMenu = new TimeMenu()
50
+ }
51
+ this.addEvents()
52
+ }
53
+
54
+ clearInputStatus() {
55
+ this.dateInput.clearStatus()
56
+ this.timeInput && this.timeInput.clearStatus()
57
+ }
58
+
59
+ handleDateInputFocus() {
60
+ this.focused = true
61
+ this.clearInputStatus()
62
+ this.dateInput.setActive(true)
63
+ this.dateMenu.setDate({ date: this.menuDate })
64
+ this.dateMenu.show(this.dom)
65
+ this.timeMenu && this.timeMenu.hide()
66
+ }
67
+
68
+ setTimestamp(timestamp) {
69
+ return this.setDate(timestampToDate(timestamp))
70
+ }
71
+
72
+ setDate(date) {
73
+ this.date = date
74
+ this.dateInput.setDate(date)
75
+ this.dateMenu.setDate({ startDate: date })
76
+ }
77
+
78
+ handleDateInputKeyUp(event) {
79
+ const { date, dateInput } = this
80
+ const { value } = event.target
81
+
82
+ if ((! dateInput.required) && (value === '')) {
83
+ this.date = null
84
+ this.nextDate = null
85
+ return
86
+ }
87
+
88
+ const res = parse(value, dateInput.datePattern, date)
89
+ this.nextDate = null
90
+ if (res.toString() === 'Invalid Date') {
91
+ return dateInput.setDanger(true)
92
+ }
93
+ dateInput.setDanger(false)
94
+ this.nextDate = res
95
+ }
96
+
97
+ handleDateInputBlur() {
98
+ const { nextDate, date, dateInput } = this
99
+
100
+ if (date === null) {
101
+ dateInput.setDate(null)
102
+ this.timeInput && this.timeInput.setDate(null)
103
+ }
104
+ else if (nextDate) {
105
+ this.date = nextDate
106
+ dateInput.setDate(nextDate)
107
+ this.dateMenu.setDate({ startDate: nextDate })
108
+ this.nextDate = null
109
+ }
110
+ else {
111
+ dateInput.setDate(date)
112
+ }
113
+ }
114
+
115
+ handleTimeInputFocus() {
116
+ const { timeInput } = this
117
+ this.focused = true
118
+ this.clearInputStatus()
119
+ timeInput.setActive(true)
120
+ this.dateMenu.hide()
121
+ this.timeMenu.show({ src: this.dom, date: timeInput.date })
122
+ }
123
+
124
+ handleTimeInputKeyUp(event) {
125
+ const { date, timeInput } = this
126
+ const { value } = event.target
127
+
128
+ if ((! timeInput.required) && (value === '')) {
129
+ this.nextDate = null
130
+ return
131
+ }
132
+ const res = parse(event.target.value, timeInput.timePattern, date)
133
+ this.nextDate = null
134
+
135
+ if (res.toString() === 'Invalid Date') {
136
+ return timeInput.setDanger(true)
137
+ }
138
+ timeInput.setDanger(false)
139
+ this.nextDate = res
140
+ }
141
+
142
+ handleTimeInputBlur() {
143
+ const { nextDate, date, timeInput } = this
144
+
145
+ if (nextDate) {
146
+ this.date = nextDate
147
+ timeInput.setDate(nextDate)
148
+ this.nextDate = null
149
+ }
150
+ else if (date) {
151
+ timeInput.setDate(date)
152
+ }
153
+ }
154
+
155
+ addDateInputEvents() {
156
+ this.dateInput.on('focus', () => this.handleDateInputFocus())
157
+ this.dateInput.on('keyup', event => this.handleDateInputKeyUp(event))
158
+ this.dateInput.on('blur', () => this.handleDateInputBlur())
159
+ }
160
+
161
+ addTimeInputEvents() {
162
+ this.timeInput.on('focus', () => this.handleTimeInputFocus())
163
+ this.timeInput.on('keyup', event => this.handleTimeInputKeyUp(event))
164
+ this.timeInput.on('blur', () => this.handleTimeInputBlur())
165
+ }
166
+
167
+ emitChange() {
168
+ const { date } = this
169
+ this.options.change({
170
+ date,
171
+ timestamp: dateToTimestamp(date)
172
+ })
173
+ }
174
+
175
+ addEvents() {
176
+
177
+ this.addDateInputEvents()
178
+
179
+ this.dateMenu.on('td-click', (event, res) => {
180
+ event.stopPropagation()
181
+ event.preventDefault()
182
+
183
+ const { year, month, date } = res
184
+ this.date = set(this.date || new Date(), { year, month, date })
185
+ this.dateInput.setDate(this.date)
186
+ this.dateMenu.setDate({ startDate: this.date })
187
+ this.dateInput.setActive(false)
188
+ this.dateMenu.hide()
189
+ this.emitChange()
190
+ })
191
+
192
+ if (this.timeInput) {
193
+ this.addTimeInputEvents()
194
+
195
+ this.timeMenu.on('click', (event, res) => {
196
+ if (this.date === null) {
197
+ this.date = set(new Date(), { hours: res.hour, minutes: res.minute })
198
+ this.dateInput.setDate(this.date)
199
+ }
200
+ else {
201
+ this.date = set(this.date, { hours: res.hour, minutes: res.minute })
202
+ }
203
+ this.timeInput.setDate(this.date)
204
+ this.timeMenu.hide()
205
+ this.clearInputStatus()
206
+ this.emitChange()
207
+ })
208
+ }
209
+
210
+ this.addEvent(document, 'click', event => {
211
+ const { dom, dateMenu, timeMenu } = this
212
+ const { target } = event
213
+ const dateMenuDom = dateMenu.dom
214
+ const timeMenuDom = timeMenu ? timeMenu.dom : null
215
+
216
+ if (this.focused) {
217
+ this.focused = false
218
+ return
219
+ }
220
+ if ((! dateMenu.isVisible) && (timeMenu && (! timeMenu.isVisible))) {
221
+ return
222
+ }
223
+ if (dom.contains(target)) {
224
+ return
225
+ }
226
+ if (dateMenuDom.contains(target)) {
227
+ return
228
+ }
229
+ if (dateMenuDom === target) {
230
+ return
231
+ }
232
+ if (timeMenuDom && timeMenuDom.contains(target)) {
233
+ return
234
+ }
235
+ if (timeMenuDom && (timeMenuDom === target)) {
236
+ return
237
+ }
238
+ this.clearInputStatus()
239
+ dateMenu.hide()
240
+ timeMenu && timeMenu.hide()
241
+ })
242
+ }
243
+
244
+ destroy() {
245
+ this.dateInput.destroy()
246
+ this.dateMenu.destroy()
247
+ this.timeInput && this.timeInput.destroy()
248
+ this.timeMenu && this.timeMenu.destroy()
249
+ }
250
+ }