index.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. import baseComponent from '../helpers/baseComponent'
  2. import classNames from '../helpers/libs/classNames'
  3. import locales from './locales/index'
  4. import { props } from './props'
  5. const DATETIME = 'datetime'
  6. const DATE = 'date'
  7. const TIME = 'time'
  8. const MONTH = 'month'
  9. const YEAR = 'year'
  10. const ONE_DAY = 24 * 60 * 60 * 1000
  11. function fomartArray(min, max, step = 1) {
  12. let i = min
  13. let result = []
  14. while (i <= max) {
  15. result.push(i)
  16. i+=step
  17. }
  18. return result
  19. }
  20. function getDaysInMonth(date) {
  21. return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
  22. }
  23. function pad(n) {
  24. return n < 10 ? `0${n}` : n + ''
  25. }
  26. function cloneDate(date) {
  27. return new Date(+date)
  28. }
  29. function setMonth(date, month) {
  30. date.setDate(Math.min(date.getDate(), getDaysInMonth(new Date(date.getFullYear(), month))))
  31. date.setMonth(month)
  32. }
  33. function valueToDate(value, props = {}) {
  34. if (!Array.isArray(value)) {
  35. if (typeof value === 'string') {
  36. value = value.replace(/\-/g, '/')
  37. }
  38. if (!isNaN(Number(value))) {
  39. value = Number(value)
  40. }
  41. return new Date(value)
  42. }
  43. const { mode, use12Hours } = props
  44. const now = new Date()
  45. const year = now.getFullYear()
  46. const month = now.getMonth()
  47. const day = now.getDate()
  48. const newValue = value.map((v) => Number(v))
  49. if (use12Hours && [DATETIME, TIME].includes(mode)) {
  50. const hourIndex = mode === DATETIME ? 3 : 0
  51. const ampmIndex = newValue.length - 1
  52. const ampm = Number(newValue[ampmIndex])
  53. let nhour = Number(newValue[hourIndex])
  54. if (ampm === 1) {
  55. if (nhour <= 12) {
  56. nhour += 12
  57. }
  58. nhour = nhour >= 24 ? 0 : nhour
  59. } else {
  60. if (nhour === 0) {
  61. nhour = 12
  62. }
  63. if (nhour > 12) {
  64. nhour -= 12
  65. }
  66. nhour = nhour >= 12 ? 0 : nhour
  67. }
  68. newValue.splice(hourIndex, 1, nhour)
  69. newValue.splice(ampmIndex, 1)
  70. }
  71. if (mode === TIME) {
  72. newValue.unshift(day)
  73. newValue.unshift(month)
  74. newValue.unshift(year)
  75. } else if (mode === MONTH) {
  76. newValue.push(day)
  77. } else if (mode === YEAR) {
  78. newValue.push(month)
  79. newValue.push(day)
  80. }
  81. while (newValue.length <= 6) {
  82. newValue.push(0)
  83. }
  84. return new Date(...newValue)
  85. }
  86. baseComponent({
  87. properties: props,
  88. data: {
  89. inputValue: [],
  90. options: [],
  91. },
  92. observers: {
  93. inputValue() {
  94. this.updatedCols()
  95. },
  96. value(value) {
  97. this.setValue(value)
  98. },
  99. ['mode, minuteStep, use12Hours, minDate, maxDate, minHour, maxHour, minMinute, maxMinute, lang']() {
  100. this.setValue(this.data.inputValue)
  101. },
  102. },
  103. methods: {
  104. getDefaultMinDate() {
  105. if (!this.defaultMinDate) {
  106. this.defaultMinDate = new Date(2000, 1, 1, 0, 0, 0)
  107. }
  108. return this.defaultMinDate
  109. },
  110. getDefaultMaxDate() {
  111. if (!this.defaultMaxDate) {
  112. this.defaultMaxDate = new Date(2030, 1, 1, 23, 59, 59)
  113. }
  114. return this.defaultMaxDate
  115. },
  116. getMinDate() {
  117. return this.data.minDate ? valueToDate(this.data.minDate, this.data) : this.getDefaultMinDate()
  118. },
  119. getMaxDate() {
  120. return this.data.maxDate ? valueToDate(this.data.maxDate, this.data) : this.getDefaultMaxDate()
  121. },
  122. getDateMember(type = 'min', member = 'year') {
  123. const methods = {
  124. min: 'getMinDate',
  125. max: 'getMaxDate',
  126. year: 'getFullYear',
  127. month: 'getMonth',
  128. day: 'getDate',
  129. hour: 'getHours',
  130. minute: 'getMinutes',
  131. }
  132. return this[methods[type]]()[methods[member]]()
  133. },
  134. getDisplayHour(rawHour) {
  135. // 12 hour am (midnight 00:00) -> 12 hour pm (noon 12:00) -> 12 hour am (midnight 00:00)
  136. if (this.data.use12Hours) {
  137. if (rawHour === 0) {
  138. rawHour = 12
  139. }
  140. if (rawHour > 12) {
  141. rawHour -= 12
  142. }
  143. return rawHour
  144. }
  145. return rawHour
  146. },
  147. setHours(date, hour) {
  148. if (this.data.use12Hours) {
  149. const dh = date.getHours()
  150. let nhour = hour
  151. nhour = dh >= 12 ? hour + 12 : hour
  152. nhour = nhour >= 24 ? 0 : nhour // Make sure no more than one day
  153. date.setHours(nhour)
  154. } else {
  155. date.setHours(hour)
  156. }
  157. },
  158. setAmPm(date, index) {
  159. if (index === 0) {
  160. date.setTime(+date - ONE_DAY / 2)
  161. } else {
  162. date.setTime(+date + ONE_DAY / 2)
  163. }
  164. },
  165. getNewDate(values, index) {
  166. const value = parseInt(values[index], 10)
  167. const { mode } = this.data
  168. let newValue = cloneDate(this.getDate())
  169. if (mode === DATETIME || mode === DATE || mode === YEAR || mode === MONTH) {
  170. switch (index) {
  171. case 0:
  172. newValue.setFullYear(value)
  173. break
  174. case 1:
  175. setMonth(newValue, value)
  176. break
  177. case 2:
  178. newValue.setDate(value)
  179. break
  180. case 3:
  181. this.setHours(newValue, value)
  182. break
  183. case 4:
  184. newValue.setMinutes(value)
  185. break
  186. case 5:
  187. this.setAmPm(newValue, value)
  188. break
  189. default:
  190. break
  191. }
  192. } else if (mode === TIME) {
  193. switch (index) {
  194. case 0:
  195. this.setHours(newValue, value)
  196. break
  197. case 1:
  198. newValue.setMinutes(value)
  199. break
  200. case 2:
  201. this.setAmPm(newValue, value)
  202. break
  203. default:
  204. break
  205. }
  206. }
  207. return this.clipDate(newValue)
  208. },
  209. clipDate(date) {
  210. const { mode } = this.data
  211. const minDate = this.getMinDate()
  212. const maxDate = this.getMaxDate()
  213. if (mode === DATETIME) {
  214. if (date < minDate) {
  215. return cloneDate(minDate)
  216. }
  217. if (date > maxDate) {
  218. return cloneDate(maxDate)
  219. }
  220. } else if (mode === DATE || mode === YEAR || mode === MONTH) {
  221. if (+date + ONE_DAY <= minDate) {
  222. return cloneDate(minDate)
  223. }
  224. if (date >= +maxDate + ONE_DAY) {
  225. return cloneDate(maxDate)
  226. }
  227. } else if (mode === TIME) {
  228. const maxHour = maxDate.getHours()
  229. const maxMinutes = maxDate.getMinutes()
  230. const minHour = minDate.getHours()
  231. const minMinutes = minDate.getMinutes()
  232. const hour = date.getHours()
  233. const minutes = date.getMinutes()
  234. if (hour < minHour || hour === minHour && minutes < minMinutes) {
  235. return cloneDate(minDate)
  236. }
  237. if (hour > maxHour || hour === maxHour && minutes > maxMinutes) {
  238. return cloneDate(maxDate)
  239. }
  240. }
  241. return date
  242. },
  243. getDate(d) {
  244. const date = d ? d : this.data.value
  245. return this.clipDate(date ? valueToDate(date, this.data) : this.getMinDate())
  246. },
  247. getDateData(date) {
  248. const { mode, lang } = this.data
  249. const locale = locales[lang]
  250. const selYear = date.getFullYear()
  251. const selMonth = date.getMonth()
  252. const minDateYear = this.getDateMember('min', 'year')
  253. const maxDateYear = this.getDateMember('max', 'year')
  254. const minDateMonth = this.getDateMember('min', 'month')
  255. const maxDateMonth = this.getDateMember('max', 'month')
  256. const minDateDay = this.getDateMember('min', 'day')
  257. const maxDateDay = this.getDateMember('max', 'day')
  258. const years = fomartArray(minDateYear, maxDateYear).map((i) => ({
  259. value: i + '',
  260. label: i + locale.year + '',
  261. }))
  262. if (mode === YEAR) {
  263. return [years]
  264. }
  265. const minMonth = minDateYear === selYear ? minDateMonth : 0
  266. const maxMonth = maxDateYear === selYear ? maxDateMonth : 11
  267. const months = fomartArray(minMonth, maxMonth).map((i) => ({
  268. value: i + '',
  269. label: i + 1 + locale.month + '',
  270. }))
  271. if (mode === MONTH) {
  272. return [years, months]
  273. }
  274. const minDay = minDateYear === selYear && minDateMonth === selMonth ? minDateDay : 1
  275. const maxDay = maxDateYear === selYear && maxDateMonth === selMonth ? maxDateDay : getDaysInMonth(date)
  276. const days = fomartArray(minDay, maxDay).map((i) => ({
  277. value: i + '',
  278. label: i + locale.day + '',
  279. }))
  280. return [years, months, days]
  281. },
  282. getTimeData(date) {
  283. let { minHour, maxHour, minMinute, maxMinute } = this.data
  284. const { mode, minuteStep, use12Hours, lang } = this.data
  285. const locale = locales[lang]
  286. const minDateMinute = this.getDateMember('min', 'minute')
  287. const maxDateMinute = this.getDateMember('max', 'minute')
  288. const minDateHour = this.getDateMember('min', 'hour')
  289. const maxDateHour = this.getDateMember('max', 'hour')
  290. const hour = date.getHours()
  291. if (mode === DATETIME) {
  292. const year = date.getFullYear()
  293. const month = date.getMonth()
  294. const day = date.getDate()
  295. const minDateYear = this.getDateMember('min', 'year')
  296. const maxDateYear = this.getDateMember('max', 'year')
  297. const minDateMonth = this.getDateMember('min', 'month')
  298. const maxDateMonth = this.getDateMember('max', 'month')
  299. const minDateDay = this.getDateMember('min', 'day')
  300. const maxDateDay = this.getDateMember('max', 'day')
  301. if (minDateYear === year && minDateMonth === month && minDateDay === day) {
  302. minHour = minDateHour
  303. if (minDateHour === hour) {
  304. minMinute = minDateMinute
  305. }
  306. }
  307. if (maxDateYear === year && maxDateMonth === month && maxDateDay === day) {
  308. maxHour = maxDateHour
  309. if (maxDateHour === hour) {
  310. maxMinute = maxDateMinute
  311. }
  312. }
  313. } else {
  314. minHour = minDateHour
  315. if (minDateHour === hour) {
  316. minMinute = minDateMinute
  317. }
  318. maxHour = maxDateHour
  319. if (maxDateHour === hour) {
  320. maxMinute = maxDateMinute
  321. }
  322. }
  323. let hours = []
  324. if (minHour === 0 && maxHour === 0 || minHour !== 0 && maxHour !== 0) {
  325. minHour = this.getDisplayHour(minHour)
  326. } else if (minHour === 0 && use12Hours) {
  327. minHour = 1
  328. hours.push({
  329. value: '0',
  330. label: locale.hour ? '12' + locale.hour : '12',
  331. })
  332. }
  333. maxHour = this.getDisplayHour(maxHour)
  334. hours = [...hours, ...fomartArray(minHour, maxHour).map((i) => ({
  335. value: i + '',
  336. label: locale.hour ? i + locale.hour + '' : pad(i),
  337. }))]
  338. const minutes = []
  339. const selMinute = date.getMinutes()
  340. for (let i = minMinute; i <= maxMinute; i += minuteStep) {
  341. minutes.push({
  342. value: i + '',
  343. label: locale.minute ? i + locale.minute + '' : pad(i),
  344. })
  345. if (selMinute > i && selMinute < i + minuteStep) {
  346. minutes.push({
  347. value: selMinute + '',
  348. label: locale.minute ? selMinute + locale.minute + '' : pad(selMinute),
  349. })
  350. }
  351. }
  352. const ampm = [{ value: '0', label: locale.am }, { value: '1', label: locale.pm }]
  353. return [hours, minutes].concat(use12Hours ? [ampm] : [])
  354. },
  355. getValueCols(d) {
  356. const { mode, use12Hours } = this.data
  357. const date = this.getDate(d)
  358. let cols = []
  359. let value = []
  360. if (mode === YEAR) {
  361. return {
  362. cols: this.getDateData(date),
  363. value: [date.getFullYear() + ''],
  364. }
  365. }
  366. if (mode === MONTH) {
  367. return {
  368. cols: this.getDateData(date),
  369. value: [date.getFullYear() + '', date.getMonth() + ''],
  370. }
  371. }
  372. if (mode === DATETIME || mode === DATE) {
  373. cols = this.getDateData(date)
  374. value = [date.getFullYear() + '', date.getMonth() + '', date.getDate() + '']
  375. }
  376. if (mode === DATETIME || mode === TIME) {
  377. cols = cols.concat(this.getTimeData(date))
  378. const hour = date.getHours()
  379. const selMinute = date.getMinutes()
  380. let dtValue = [hour + '', selMinute + '']
  381. let nhour = hour
  382. if (use12Hours) {
  383. nhour = hour === 0 ? 12 : (hour > 12 ? hour - 12 : hour)
  384. dtValue = [nhour + '', selMinute + '', (hour >= 12 ? 1 : 0) + '']
  385. }
  386. value = value.concat(dtValue)
  387. }
  388. return {
  389. value,
  390. cols,
  391. }
  392. },
  393. onValueChange(e) {
  394. const { value, index } = e.detail
  395. const newDate = this.getNewDate(value, index)
  396. const { value: newValue, cols: newCols } = this.getValueCols(newDate)
  397. const values = this.getValue(newValue, newCols)
  398. this.triggerEvent('valueChange', { ...e.detail, ...values, date: +newDate })
  399. },
  400. updatedCols() {
  401. const { cols } = this.getValueCols()
  402. this.setData({ cols })
  403. },
  404. updated(inputValue) {
  405. if (this.data.inputValue !== inputValue) {
  406. this.setData({
  407. inputValue,
  408. })
  409. }
  410. },
  411. setValue(value = this.data.inputValue) {
  412. const { value: inputValue } = this.getValueCols()
  413. this.updated(inputValue)
  414. },
  415. getValue(value = this.data.inputValue, cols = this.data.cols) {
  416. this.picker = this.picker || this.querySelector('#wux-picker')
  417. return {
  418. ...this.picker.getValue(value, cols),
  419. date: +this.getDate(),
  420. }
  421. },
  422. },
  423. attached() {
  424. this.setValue(this.data.value)
  425. },
  426. })