Last active
March 30, 2018 09:43
-
-
Save kb10uy/3f61d140b1ae191d0442e8a5809af15e to your computer and use it in GitHub Desktop.
Needs ts-loader and sass-loader.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="datetimepicker"> | |
<div class="view"> | |
<input type="text" class="preview" :value="previewText" readonly> | |
<button class="change-button" @click="openPicker">⏰</button> | |
</div> | |
<transition name="fade"> | |
<div class="picker" v-if="shown"> | |
<div class="date"> | |
<div class="month-selector"> | |
<button @click="moveMonth(-1)"><<</button> | |
<span class="current">{{ year }}年 {{ month + 1 }}月</span> | |
<button @click="moveMonth(1)">>></button> | |
</div> | |
<table class="calendar"> | |
<thead> | |
<tr> | |
<th class="sunday">日</th> | |
<th>月</th> | |
<th>火</th> | |
<th>水</th> | |
<th>木</th> | |
<th>金</th> | |
<th class="saturday">土</th> | |
</tr> | |
</thead> | |
<tbody> | |
<tr v-for="week in weeks" :key="week.reduce((a, c) => a + c)"> | |
<td v-for="d in week" :key="d" | |
@click.stop="pickDay(d)" | |
:class="{ today: d === day }"> | |
{{ d }} | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
<div class="time"> | |
<table> | |
<tbody> | |
<tr class="buttons"> | |
<td><button @click="moveHours(10)">+10</button></td> | |
<td></td> | |
<td><button @click="moveMinutes(10)">+10</button></td> | |
<td></td> | |
<td><button @click="moveSeconds(10)">+10</button></td> | |
</tr> | |
<tr class="buttons"> | |
<td><button @click="moveHours(1)">+1</button></td> | |
<td></td> | |
<td><button @click="moveMinutes(1)">+1</button></td> | |
<td></td> | |
<td><button @click="moveSeconds(1)">+1</button></td> | |
</tr> | |
<tr class="preview"> | |
<td>{{ hours.toString().padStart(2, '0') }}</td> | |
<td>:</td> | |
<td>{{ minutes.toString().padStart(2, '0') }}</td> | |
<td>:</td> | |
<td>{{ seconds.toString().padStart(2, '0') }}</td> | |
</tr> | |
<tr class="buttons"> | |
<td><button @click="moveHours(-1)">-1</button></td> | |
<td></td> | |
<td><button @click="moveMinutes(-1)">-1</button></td> | |
<td></td> | |
<td><button @click="moveSeconds(-1)">-1</button></td> | |
</tr> | |
<tr class="buttons"> | |
<td><button @click="moveHours(-10)">-10</button></td> | |
<td></td> | |
<td><button @click="moveMinutes(-10)">-10</button></td> | |
<td></td> | |
<td><button @click="moveSeconds(-10)">-10</button></td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</transition> | |
</div> | |
</template> | |
<script lang="ts"> | |
import Vue from 'vue'; | |
type DateTimePickerData = { | |
picked: Date; | |
shown: boolean; | |
weeks: number[][]; | |
}; | |
export default Vue.extend({ | |
data(): DateTimePickerData { | |
return { | |
picked: new Date(), | |
shown: false, | |
weeks: [], | |
}; | |
}, | |
computed: { | |
previewText(): string { | |
return this.picked.toLocaleString(); | |
}, | |
year(): number { | |
return this.picked.getFullYear(); | |
}, | |
month(): number { | |
return this.picked.getMonth(); | |
}, | |
day(): number { | |
return this.picked.getDate(); | |
}, | |
hours(): number { | |
return this.picked.getHours(); | |
}, | |
minutes(): number { | |
return this.picked.getMinutes(); | |
}, | |
seconds(): number { | |
return this.picked.getSeconds(); | |
}, | |
}, | |
watch: { | |
picked(newDate: Date, oldDate: Date) { | |
this.$emit('changed', newDate); | |
} | |
}, | |
methods: { | |
openPicker() { | |
this.buildMonth(); | |
this.shown = !this.shown; | |
}, | |
buildMonth() { | |
this.weeks = []; | |
const allDays = new Date(this.year, this.month + 1, 0).getDate(); | |
let dow = new Date(this.year, this.month, 1).getDay(); | |
let target: number[] = []; | |
for (let i = 0; i < allDays; i++) { | |
target[dow] = i + 1; | |
if (++dow === 7) { | |
dow = 0; | |
this.weeks.push(target); | |
target = []; | |
} | |
} | |
if (target.length !== 0) { | |
const fill = 7 - target.length; | |
for (let i = 0; i < fill; i++) target.push(undefined); | |
this.weeks.push(target); | |
} | |
}, | |
pickDay(d: number) { | |
if (!d) return; | |
this.picked = new Date(this.year, this.month, d, this.hours, this.minutes, this.seconds); | |
}, | |
moveMonth(n: number) { | |
this.picked = new Date(this.year, this.month + n, 1, this.hours, this.minutes, this.seconds); | |
this.buildMonth(); | |
}, | |
moveHours(n: number) { | |
const oldMonth = this.month; | |
this.picked = new Date(this.year, this.month, this.day, this.hours + n, this.minutes, this.seconds); | |
if (oldMonth != this.month) this.buildMonth(); | |
}, | |
moveMinutes(n: number) { | |
const oldMonth = this.month; | |
this.picked = new Date(this.year, this.month, this.day, this.hours, this.minutes + n, this.seconds); | |
if (oldMonth != this.month) this.buildMonth(); | |
}, | |
moveSeconds(n: number) { | |
const oldMonth = this.month; | |
this.picked = new Date(this.year, this.month, this.day, this.hours, this.minutes, this.seconds + n); | |
if (oldMonth != this.month) this.buildMonth(); | |
}, | |
}, | |
}); | |
</script> | |
<style lang="scss" scoped> | |
.datetimepicker { | |
position: relative; | |
margin: 8px 0px; | |
.view { | |
display: flex; | |
.preview { | |
font-size: 24px; | |
margin: 0px; | |
padding: 0px; | |
height: 28px; | |
} | |
.change-button { | |
font-size: 24px; | |
height: 32px; | |
margin: 0px; | |
} | |
} | |
.picker { | |
position: absolute; | |
display: flex; | |
justify-content: space-between; | |
flex-direction: row; | |
align-items: center; | |
top: 36px; | |
z-index: 100; | |
background-color: #eee; | |
border: solid 1px #666; | |
border-radius: 4px; | |
.date { | |
margin: 8px; | |
width: 240px; | |
.month-selector { | |
display: flex; | |
flex-direction: row; | |
.current { | |
flex-grow: 1; | |
text-align: center; | |
} | |
} | |
.calendar { | |
width: 100%; | |
border: solid 1px #aaa; | |
border-collapse: collapse; | |
text-align: center; | |
background-color: #fff; | |
td, | |
th { | |
border: solid 1px #aaa; | |
empty-cells: show; | |
cursor: pointer; | |
} | |
.sunday { | |
color: #f00; | |
} | |
.saturday { | |
color: #00f; | |
} | |
.today { | |
background-color: #faa; | |
} | |
} | |
} | |
.time { | |
text-align: center; | |
margin: 8px; | |
margin-left: 0px; | |
.buttons > td { | |
margin: 0; | |
padding: 0; | |
button { | |
width: 100%; | |
font-size: 12pt; | |
} | |
} | |
.preview { | |
font-size: 24pt; | |
line-height: 1.2em; | |
} | |
} | |
} | |
} | |
.fade-enter-active, .fade-leave-active { | |
transition: opacity 0.2s; | |
} | |
.fade-enter, .fade-leave-to { | |
opacity: 0; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment