storj/web/satellite/src/components/common/VDatePicker.vue
NikolaiYurchenko fc105af0e5 web/satellite: user select text restricted
Change-Id: If3692d55e48255c95b7722c5a574060c84fdf502
2020-02-26 11:13:56 +00:00

568 lines
14 KiB
Vue

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
<template>
<div class="cov-vue-date">
<div class="datepickbox">
<input type="text" title="input date" class="cov-datepicker" readonly="readonly" :style="option.inputStyle ? option.inputStyle : {}" />
</div>
<div class="datepicker-overlay" v-if="isChecking" @click.self="dismiss" :style="{'background' : option.overlayOpacity? 'rgba(0,0,0,'+option.overlayOpacity+')' : 'rgba(0,0,0,0.5)'}">
<div class="cov-date-body" :style="{'background-color': option.color ? option.color.header : '#3f51b5'}">
<div class="cov-date-monthly">
<div class="cov-date-previous" @click="onPreviousMonthClick">«</div>
<div class="cov-date-caption" :style="{'color': option.color ? option.color.headerText : '#fff'}">
<span class="year-selection" @click="showYear">{{selectedDateState.year}}</span>
<span class="month-selection" @click="showMonth">{{displayedMonth}}</span>
</div>
<div class="cov-date-next" @click="onNextMonthClick">»</div>
</div>
<div class="cov-date-box" v-if="isDaysChoiceShown">
<div class="cov-picker-box">
<div class="week">
<ul class="week-ul">
<li class="week-li" v-for="week in daysInWeek" :key="week">{{week}}</li>
</ul>
</div>
<div class="day" v-for="(day, index) in daysToShow" :key="index" @click="checkDay(day)" :class="{'checked':day.checked,'unavailable':day.unavailable,'passive-day': !(day.inMonth), 'today': day.today}" :style="day.checked ? (option.color && option.color.checkedDay ? { background: option.color.checkedDay } : { background: '#2683FF' }) : {}">{{day.value}}</div>
</div>
</div>
<div class="cov-date-box list-box" v-if="isYearChoiceShown">
<div class="cov-picker-box date-list" id="yearList">
<div class="date-item year" v-for="yearItem in years" :key="yearItem" @click="setYear(yearItem)">{{yearItem}}</div>
</div>
</div>
<div class="cov-date-box list-box" v-if="isMonthChoiceShown">
<div class="cov-picker-box date-list">
<div class="date-item month" v-for="monthItem in monthsNames" :key="monthItem" @click="setMonth(monthItem)">{{monthItem}}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import {
DateGenerator,
DateStamp,
DayAction,
DayItem,
DisplayedType,
Options,
} from '@/utils/datepicker';
@Component
export default class VDatePicker extends Vue {
@Prop({default: () => new Options()})
private option: Options;
@Prop({default: () => false})
private isSundayFirst: boolean;
private readonly MAX_DAYS_SELECTED: number = 2;
public selectedDays: Date[] = [];
private showType: number = DisplayedType.Day;
private dateGenerator: DateGenerator = new DateGenerator();
// daysInWeek contains days names abbreviations
public readonly daysInWeek: string[] = [];
public readonly monthsNames: string[] = [];
// years contains years numbers available to choose
public readonly years: number[] = [];
// isChecking indicates when calendar is shown
public isChecking: boolean = false;
public displayedMonth: string;
// daysToShow contains days of selected month with a few extra days from adjacent months
public daysToShow: DayItem[] = [];
// Combination of selected year, month and day
public selectedDateState: DateStamp = new DateStamp(0, 0, 0);
public constructor() {
super();
this.daysInWeek = this.isSundayFirst ? this.option.sundayFirstWeek : this.option.mondayFirstWeek;
this.monthsNames = this.option.month;
this.displayedMonth = this.monthsNames[0];
this.years = this.dateGenerator.populateYears();
}
/**
* computed value that indicates should days view be shown
*/
public get isDaysChoiceShown(): boolean {
return this.showType === DisplayedType.Day;
}
/**
* computed value that indicates should month choice view be shown
*/
public get isMonthChoiceShown(): boolean {
return this.showType === DisplayedType.Month;
}
/**
* computed value that indicates should year choice view be shown
*/
public get isYearChoiceShown(): boolean {
return this.showType === DisplayedType.Year;
}
/**
* onPreviousMonthClick set previous month
*/
public onPreviousMonthClick(): void {
this.nextMonth(DayAction.Previous);
}
/**
* onNextMonthClick set next month
*/
public onNextMonthClick(): void {
this.nextMonth(DayAction.Next);
}
/**
* checkDay toggles checked property of day object
*
* @param day represent day object to check/uncheck
*/
public checkDay(day: DayItem): void {
if (day.unavailable || !day.value) {
return;
}
if (!day.inMonth) {
this.nextMonth(day.action);
return;
}
if (day.checked) {
day.checked = false;
this.selectedDays.splice(this.selectedDays.indexOf(day.moment), 1);
return;
}
if (this.selectedDays.length < this.MAX_DAYS_SELECTED) {
this.selectedDays.push(day.moment);
day.checked = true;
}
if (this.selectedDays.length === this.MAX_DAYS_SELECTED) {
this.submitSelectedDays();
}
}
/**
* setYear selects chosen year
*
* @param year
*/
public setYear(year): void {
this.populateDays(new Date(year, this.selectedDateState.month, 1));
}
/**
* setYear selects chosen month
*
* @param month
*/
public setMonth(month: string): void {
const monthIndex = this.monthsNames.indexOf(month);
this.populateDays(new Date(this.selectedDateState.year, monthIndex, 1));
}
/**
* dismiss closes popup and clears values
*/
public dismiss(): void {
if (!this.option.dismissible) {
return;
}
this.selectedDays = [];
this.isChecking = false;
}
/**
* showCheck used for external popup opening
*/
public showCheck(): void {
this.populateDays();
this.isChecking = true;
}
/**
* showYear used for opening choose year view
*/
public showYear(): void {
this.showType = DisplayedType.Year;
}
/**
* showMonth used for opening choose month view
*/
public showMonth(): void {
this.showType = DisplayedType.Month;
}
/**
* nextMonth set month depends on day action (next or previous)
*
* @param action represents next or previous type of action
*/
private nextMonth(action: DayAction): void {
const currentMoment = new Date(this.selectedDateState.year, this.selectedDateState.month, 1);
const currentMonth = currentMoment.getMonth();
const now = new Date();
switch (action) {
case DayAction.Next:
if (currentMonth === now.getMonth() && currentMoment.getFullYear() === now.getFullYear()) {
return;
}
currentMoment.setMonth(currentMonth + 1);
break;
case DayAction.Previous:
currentMoment.setMonth(currentMonth - 1);
break;
}
this.populateDays(currentMoment);
}
/**
* submitSelectedDays emits function to receive selected dates externally and then clears state
*/
private submitSelectedDays(): void {
this.$emit('change', this.selectedDays);
this.isChecking = false;
this.selectedDays = [];
}
/**
* populateDays used for populating date items into calendars depending on selected date
*
* @param date represents Date which is used to create current date items to show
*/
private populateDays(date: Date = new Date()): void {
this.selectedDateState.fromDate(date);
this.showType = DisplayedType.Day;
this.displayedMonth = this.monthsNames[this.selectedDateState.month];
this.daysToShow = this.dateGenerator.populateDays(this.selectedDateState, this.isSundayFirst);
}
}
</script>
<style scoped lang="scss">
.datepicker-overlay {
position: fixed;
width: 100%;
height: 100%;
z-index: 998;
top: 0;
left: 0;
overflow: hidden;
-webkit-animation: fadein 0.5s;
/* Safari, Chrome and Opera > 12.1 */
-moz-animation: fadein 0.5s;
/* Internet Explorer */
-o-animation: fadein 0.5s;
/* Opera < 12.1 */
animation: fadein 0.5s;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Firefox < 16 */
@-moz-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Safari, Chrome and Opera > 12.1 */
@-webkit-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Internet Explorer */
@-ms-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Opera < 12.1 */
@-o-keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.cov-date-body {
background: #3f51b5;
overflow: hidden;
font-size: 16px;
font-weight: 400;
position: fixed;
display: block;
width: 400px;
max-width: 100%;
z-index: 999;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2);
font-family: 'font_medium', sans-serif;
}
.cov-picker-box {
background: #fff;
display: inline-block;
padding: 25px;
box-sizing: border-box !important;
-moz-box-sizing: border-box !important;
-webkit-box-sizing: border-box !important;
width: 400px;
max-width: 100%;
height: 280px;
text-align: start !important;
}
.day {
width: 14.2857143%;
display: inline-block;
text-align: center;
cursor: pointer;
height: 34px;
padding: 0;
line-height: 34px;
color: #000;
background: #fff;
vertical-align: middle;
}
.week-ul {
margin: 0 0 8px;
padding: 0;
list-style: none;
}
.week-li {
width: 14.2%;
display: inline-block;
text-align: center;
background: transparent;
color: #000;
font-weight: bold;
}
.passive-day {
color: #bbb;
}
.checked {
background: #2683ff;
color: #fff !important;
}
.unavailable {
color: #ccc;
cursor: not-allowed;
}
.cov-date-monthly {
height: 50px;
}
.cov-date-monthly > div {
display: inline-block;
padding: 0;
margin: 0;
vertical-align: middle;
color: #fff;
height: 50px;
float: left;
text-align: center;
}
.cov-date-previous,
.cov-date-next {
position: relative;
width: 20% !important;
text-indent: -300px;
overflow: hidden;
color: #fff;
}
.cov-date-caption {
width: 60%;
padding: 10px 0 !important;
box-sizing: border-box;
font-size: 18px;
font-family: 'font_medium', sans-serif;
line-height: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.month-selection,
.year-selection {
padding: 0 3px;
}
.cov-date-previous:hover,
.cov-date-next:hover {
background: rgba(255, 255, 255, 0.1);
}
.day:hover {
background: #eaeaea;
}
.unavailable:hover {
background: none;
}
.cov-date-next:before,
.cov-date-previous:before {
width: 20px;
height: 2px;
text-align: center;
position: absolute;
background: #fff;
top: 50%;
margin-top: -7px;
margin-left: -7px;
left: 50%;
line-height: 0;
content: '';
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
transform: rotate(45deg);
}
.cov-date-next:after,
.cov-date-previous:after {
width: 20px;
height: 2px;
text-align: center;
position: absolute;
background: #fff;
margin-top: 6px;
margin-left: -7px;
top: 50%;
left: 50%;
line-height: 0;
content: '';
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.cov-date-previous:after {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
transform: rotate(45deg);
}
.cov-date-previous:before {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.date-item {
text-align: center;
font-size: 20px;
padding: 10px 0;
cursor: pointer;
}
.date-item:hover {
background: #e0e0e0;
}
.date-list {
overflow: auto;
vertical-align: top;
padding: 0;
}
.cov-vue-date {
display: inline-block;
color: #5d5d5d;
}
.watch-box {
height: 100%;
overflow: hidden;
}
::-webkit-scrollbar {
width: 2px;
}
::-webkit-scrollbar-track {
background: #e3e3e3;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 2px;
}
.cov-date-box {
font-family: 'font_medium', sans-serif;
}
.today {
background: lightblue;
color: white;
}
</style>