Advertisement
Guest User

Untitled

a guest
Feb 18th, 2020
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.50 KB | None | 0 0
  1. <template>
  2. <div class="DateTimeSelector w-100">
  3. <WCard>
  4. <Scrollable
  5. class="DaysWrapper"
  6. horizontal
  7. >
  8. <div
  9. class="Days min-w-100 flex row-stretch-center position-relative"
  10. :style="{ width: daysWrapperWidth }"
  11. >
  12. <div class="position-absolute-fill">
  13. <div class="DayCircleArea h-100 position-relative">
  14. <div
  15. class="DayCircle position-absolute"
  16. :style="dayCircleStyle"
  17. />
  18. </div>
  19. </div>
  20. <div
  21. v-if="multiple"
  22. class="position-absolute-fill"
  23. >
  24. <div class="DayCircleArea h-100 position-relative">
  25. <div
  26. class="DayCircle position-absolute"
  27. :style="secondDayCircleStyle"
  28. />
  29. </div>
  30. </div>
  31. <a
  32. v-for="{ date, weekDay, dayInMonth, outOfRange } in days"
  33. :key="date"
  34. class="Day"
  35. :class="{ selected: getSelected(date), between: getStylesDatesBetween(date) && multiple, outOfRange }"
  36. @click.prevent="handleDates(outOfRange ? undefined : date)"
  37. >
  38. <WText
  39. class="WeekDay"
  40. weight="semi"
  41. align="center"
  42. >
  43. {{ weekDay }}
  44. </WText>
  45. <WText
  46. class="DayInMonth mb-0"
  47. weight="bold"
  48. align="center"
  49. >
  50. {{ dayInMonth }}
  51. </WText>
  52. </a>
  53. </div>
  54. </Scrollable>
  55. <div class="Hours flex row-stretch-center flex-wrap position-relative">
  56. <div class="position-absolute-fill">
  57. <div class="HourPillArea h-100 position-relative">
  58. <div
  59. class="HourPill position-absolute"
  60. :style="hourPillStyle"
  61. />
  62. </div>
  63. </div>
  64. <fragment
  65. v-for="(hour, index) in hours"
  66. :key="hour.value"
  67. >
  68. <a
  69. class="Hour"
  70. :class="{ selected: hour.value === selectedHour }"
  71. @click.prevent="selectHour(hour.value)"
  72. >
  73. <WText
  74. class="mb-0"
  75. weight="semi"
  76. align="center"
  77. >
  78. {{ hour.label }}
  79. </WText>
  80. </a>
  81. <div
  82. v-if="index === (hours.length / 2) - 1"
  83. class="HourLineBreak"
  84. />
  85. </fragment>
  86. </div>
  87. </WCard>
  88. </div>
  89. </template>
  90.  
  91. <script>
  92. import VueTypes from 'vue-types'
  93. import moment from 'moment'
  94. import range from 'lodash/range'
  95. import Scrollable from '../Scrollable/Scrollable.vue'
  96.  
  97. const UI_FORMAT_WEEKDAY = 'dd'
  98. const UI_FORMAT_DAY = 'D'
  99. const UI_FORMAT_HOUR = 'HH:mm'
  100.  
  101. export default {
  102. name: 'DateTimeSelector',
  103. components: {
  104. Scrollable,
  105. },
  106. props: {
  107. multiple: VueTypes.bool.def(false),
  108. dateStart: VueTypes.string.isRequired,
  109. dateEnd: VueTypes.string.isRequired,
  110. formatDate: VueTypes.string.def('YYYY-MM-DD'),
  111. formatHour: VueTypes.string.def('HH:mm'),
  112. value: VueTypes.oneOf([VueTypes.string, VueTypes.object]).optional,
  113. },
  114. data () {
  115. const arbitraryMoment = moment().minute(0)
  116. return {
  117. hours: range(0, 24).map((h) => {
  118. arbitraryMoment.hour(h)
  119. return {
  120. value: arbitraryMoment.format(this.formatHour),
  121. label: arbitraryMoment.format(UI_FORMAT_HOUR),
  122. }
  123. }),
  124. hoursInTwoRows: false,
  125. matchMediaForSmallScreens: undefined,
  126. selectedDates: {
  127. init: '',
  128. final: '',
  129. },
  130. }
  131. },
  132. computed: {
  133. formatValue () {
  134. if (this.value) {
  135. const {
  136. init: initialDate,
  137. final: finalDate,
  138. } = Object.keys(this.value).includes('init', 'final') ? this.value : { init: this.value }
  139. if (finalDate) return { initialDate, finalDate }
  140. return { initialDate }
  141. }
  142. return ''
  143. },
  144. momentStart () {
  145. return moment(this.dateStart)
  146. },
  147. momentEnd () {
  148. return moment(this.dateEnd)
  149. },
  150. momentValue () {
  151. return moment(this.formatValue.initialDate)
  152. },
  153. secondMomentValue () {
  154. return moment((this.formatValue.finalDate))
  155. },
  156. numDays () {
  157. return this.momentEnd.diff(this.momentStart, 'days') + 1 + 2
  158. },
  159. days () {
  160. return range(-1, this.numDays - 1).map((delta, index) => {
  161. const currentDate = this.momentStart.clone().add(delta, 'days')
  162. return {
  163. date: currentDate.format(this.formatDate),
  164. weekDay: currentDate.format(UI_FORMAT_WEEKDAY),
  165. dayInMonth: currentDate.format(UI_FORMAT_DAY),
  166. outOfRange: index === 0 || index === this.numDays - 1,
  167. }
  168. })
  169. },
  170. selectedInitialDate () {
  171. return this.momentValue.format(this.formatDate)
  172. },
  173. selectedFinalDate () {
  174. return this.secondMomentValue.format(this.formatDate)
  175. },
  176. selectedHour () {
  177. return this.momentValue.format(this.formatHour)
  178. },
  179. daysWrapperWidth () {
  180. return `${this.numDays * 40}px`
  181. },
  182. dayCircleStyle () {
  183. const deltaDays = this.momentValue.diff(this.momentStart, 'days') + 1
  184. return { left: this.getLeftByDay(deltaDays) }
  185. },
  186. secondDayCircleStyle () {
  187. const deltaDays = this.secondMomentValue.diff(this.momentStart, 'days') + 1
  188. return { left: this.getLeftByDay(deltaDays) }
  189. },
  190. hourPillStyle () {
  191. if (this.hoursInTwoRows) {
  192. const deltaHours = this.momentValue.hour()
  193. const left = `${Math.round(((deltaHours % 12) / (12 - 1)) * 10000) / 100}%`
  194. const top = `${deltaHours >= 12 ? 50 : 0}%`
  195. return { left, top }
  196. }
  197. const deltaHours = this.momentValue.hour()
  198. const left = `${Math.round((deltaHours / (24 - 1)) * 10000) / 100}%`
  199. return { left, top: 0 }
  200. },
  201. },
  202. mounted () {
  203. if (window && window.matchMedia) {
  204. this.matchMediaForSmallScreens = window.matchMedia('(max-width: 1024px)')
  205. this.matchMediaForSmallScreens.addListener(this.checkSmallScreens)
  206. this.hoursInTwoRows = this.matchMediaForSmallScreens.matches
  207. }
  208. },
  209. beforeDestroy () {
  210. this.matchMediaForSmallScreens && this.matchMediaForSmallScreens.removeListener(this.checkSmallScreens)
  211. },
  212. methods: {
  213. getStylesDatesBetween (date) {
  214. return (this.selectedInitialDate < date && date < this.selectedFinalDate) && this.selectedDates.final
  215. },
  216. getSelected (date) {
  217. return [this.selectedInitialDate, this.selectedFinalDate].includes(date)
  218. },
  219. getLeftByDay (deltaDays) {
  220. return `${Math.round((deltaDays / (this.numDays - 1)) * 10000) / 100}%`
  221. },
  222. handleDates (date) {
  223. if (this.multiple) {
  224. const [first, second] = Object.values(this.selectedDates)
  225. if (first && second) {
  226. this.selectedDates = {
  227. ...this.selectedDates,
  228. init: date,
  229. final: '',
  230. }
  231. } else if (first && second.includes('')) {
  232. this.selectedDates = {
  233. ...this.selectedDates,
  234. final: date,
  235. }
  236. } else {
  237. this.selectedDates = {
  238. ...this.selectedDates,
  239. init: date,
  240. }
  241. }
  242. return this.selectDateAndHour(this.selectedDates, this.selectedHour)
  243. }
  244. return this.selectDateAndHour(date, this.selectedHour)
  245. },
  246. selectHour (hour) {
  247. this.multiple ? this.selectDateAndHour(this.selectedDates, hour) : this.selectDateAndHour(this.selectedInitialDate, hour)
  248. },
  249. selectDateAndHour (date, hour) {
  250. const { init, final } = date
  251. if (hour && this.multiple) {
  252. this.$emit('input', init && final ? {
  253. init: moment(`${init} ${hour}`).toApiStandard(),
  254. final: moment(`${final} ${hour}`).toApiStandard(),
  255. } : { init: moment(`${init} ${hour}`).toApiStandard() })
  256. } else {
  257. this.$emit('input', moment(`${date} ${hour}`).toApiStandard())
  258. }
  259. },
  260. checkSmallScreens (e) {
  261. this.hoursInTwoRows = e.matches
  262. },
  263. },
  264. }
  265. </script>
  266.  
  267. <style scoped lang="scss">
  268. $daySize: 25px;
  269. $hourWidth: 36px;
  270. $hourHeight: 18px;
  271. $transitionDuration: 0.3s;
  272.  
  273. $selectedColor: $primary-medium;
  274. $hoverColor: lighten($selectedColor, 35%);
  275.  
  276. .DateTimeSelector {
  277. padding-top: 30px;
  278. }
  279.  
  280. .DaysWrapper {
  281. margin-top: -35px;
  282. }
  283.  
  284. .Days .between {
  285. .WText.DayInMonth {
  286. background: $hoverColor;
  287. opacity: 0.4;
  288. color: white;
  289. }
  290. }
  291.  
  292. .DayCircleArea {
  293. margin-right: $daySize;
  294. }
  295. .DayCircle {
  296. bottom: 0;
  297. width: $daySize;
  298. height: $daySize;
  299. border-radius: $daySize;
  300. background: $selectedColor;
  301. transition: all $transitionDuration ease-in-out;
  302. }
  303.  
  304. .Day {
  305. width: $daySize;
  306. min-width: $daySize;
  307. z-index: 1;
  308.  
  309. &.outOfRange {
  310. pointer-events: none;
  311. }
  312. }
  313. .WText.WeekDay {
  314. width: 100%;
  315. margin-bottom: 22px;
  316. font-size: 12px;
  317. opacity: 0.3;
  318. transition: opacity $transitionDuration ease-in-out;
  319.  
  320. .Day.selected > & {
  321. opacity: 1;
  322. }
  323. }
  324. .WText.DayInMonth {
  325. line-height: $daySize;
  326. border-radius: $daySize;
  327. font-size: 14px;
  328. color: $dark;
  329. transition: all $transitionDuration ease-in-out;
  330.  
  331. .Day.selected > & {
  332. color: white;
  333. }
  334. .Day.outOfRange > & {
  335. color: $gray-medium;
  336. }
  337. .Day:not(.selected):hover > & {
  338. background: $hoverColor;
  339. }
  340. }
  341.  
  342. .Hours {
  343. margin-top: 20px;
  344. }
  345. .HourPillArea {
  346. margin-right: $hourWidth;
  347. }
  348. .HourPill {
  349. top: 0;
  350. width: $hourWidth + 8px;
  351. height: $hourHeight + 4px;
  352. margin: -2px -4px;
  353. border-radius: $hourHeight;
  354. background: $selectedColor;
  355. transition: all $transitionDuration ease-in-out;
  356. }
  357. .Hour {
  358. width: $hourWidth;
  359. min-width: $hourWidth;
  360. height: $hourHeight;
  361. z-index: 1;
  362.  
  363. .WText {
  364. font-size: 11px;
  365. line-height: $hourHeight;
  366. border-radius: $hourHeight;
  367. color: $dark;
  368. opacity: 0.85;
  369. transition: all $transitionDuration ease-in-out;
  370. }
  371. &:nth-child(12):after {
  372. content: '';
  373. flex-basis: 100%;
  374. }
  375. &LineBreak {
  376. flex-basis: 100%;
  377. @media screen and (min-width: 1024px) {
  378. display: none;
  379. }
  380. }
  381.  
  382. &.selected .WText {
  383. color: white;
  384. font-size: 12px;
  385. opacity: 1;
  386. }
  387. &:not(.selected):hover .WText {
  388. background: $hoverColor;
  389. }
  390. }
  391. </style>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement