<template lang="pug">
.smart-calendar
.buttons
button.prev(@click="subtractMonth") ◀
button.next(@click="addMonth") ▶
p.title {{currentYear}}年 {{currentMonth}}月
table.table
thead.head
tr.row
td.col(v-for="dayOfWeek in weekIndexes", :class="headClass(dayOfWeek)") {{$_calendarable_weekNames[dayOfWeek]}}
tbody.body
tr.row(v-for="week in calendar")
td.col(
v-for="date in week",
:class="dateClass(date)",
@click="selecteDate(date)"
@mouseover="hoverDate(date)") {{date.day}}
</template>
<script lang="coffee">
import calendarable from '@scripts/mixins/calendarable.coffee'
export default
name: 'SmartCalendar'
introduction: "TODO"
token: """
<smart-calendar>
</smart-calendar>
"""
description: """
<p>TODO</p>
"""
mixins: [calendarable]
data: ->
weekForClassName: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
currentYear: undefined
currentMonth: undefined
selectedDates: []
isSelectingRange: false
hoverStartDate:
year: undefined
month: undefined
date: undefined
dayOfWeek: undefined
hoverdDate:
year: undefined
month: undefined
date: undefined
dayOfWeek: undefined
computed:
calendar: ->
currentMonthMoment = @createMoment()
prevMonthMoment = currentMonthMoment.clone().subtract(1, 'month')
nextMonthMoment = currentMonthMoment.clone().add(1, 'month')
prevMonthLastDay = prevMonthMoment.daysInMonth()
currentMonthLastDay = currentMonthMoment.daysInMonth()
currentMonthFirstDayOfWeek = @weekIndexes.indexOf(currentMonthMoment.day())
nextMonthFirstDayOfWeek = @weekIndexes.indexOf(nextMonthMoment.day())
prevMonthYear = prevMonthMoment.year()
currentMonthYear = currentMonthMoment.year()
nextMonthYear = nextMonthMoment.year()
prevMonth = prevMonthMoment.month() + 1
currentMonth = currentMonthMoment.month() + 1
nextMonth = nextMonthMoment.month() + 1
calendar = []
dayIdx = 1
for w in [0..5]
week = []
break if currentMonthLastDay < dayIdx
for d in [0..6]
if w == 0 and d < currentMonthFirstDayOfWeek
week[d] =
year: prevMonthYear
month: prevMonth
day: prevMonthLastDay - (currentMonthFirstDayOfWeek - d) + 1
dayOfWeek: @weekIndexes[d]
else if currentMonthLastDay < dayIdx
week[d] =
year: nextMonthYear
month: nextMonth
day: d - nextMonthFirstDayOfWeek + 1
dayOfWeek: @weekIndexes[d]
dayIdx++
else
week[d] =
year: currentMonthYear
month: currentMonth
day: dayIdx
dayOfWeek: @weekIndexes[d]
dayIdx++
calendar.push(week)
calendar
weekIndexes: ->
w = [0..6]
_.times @$_calendarable_startDayOfWeek, (_n) =>
tail = _.tail(w)
tail.push(_.head(w))
w = tail
w
methods:
createMoment: (year = @currentYear, month = @currentMonth, day = 1) -> moment({ year: year, month: month - 1, day: day})
createMomentByDate: (date) -> @createMoment(date.year, date.month, date.day)
createMomentRangeByDates: (date1, date2) ->
moment1 = @createMomentByDate(date1)
moment2 = @createMomentByDate(date2)
if moment1.isBefore(moment2)
moment.range(moment1, moment2)
else
moment.range(moment2, moment1)
toDateFromMoment: (moment) ->
{
year: moment.year()
month: moment.month() + 1
day: moment.date()
dayOfWeek: moment.day()
}
toDatesFromMomentRaange: (momentRange, ignoreDisable = true) ->
_(Array.from(momentRange.by('days')))
.map (moment) =>
@toDateFromMoment(moment) if ignoreDisable && ! @isDisable(moment)
.compact()
.value()
addMonth: ->
nextMonthMoment = @createMoment().add(1, 'month')
@currentYear = nextMonthMoment.year()
@currentMonth = nextMonthMoment.month() + 1
@$emit('update:month', @currentYear, @currentMonth)
subtractMonth: ->
prevMonthMoment = @createMoment().subtract(1, 'month')
@currentYear = prevMonthMoment.year()
@currentMonth = prevMonthMoment.month() + 1
@$emit('update:month', @currentYear, @currentMonth)
selecteDate: (date) ->
moment = @createMomentByDate(date)
unless @isDisable(moment)
switch @$_calendarable_selectMode
when 'single'
@selectedDates = [date]
when 'multiple'
if @includesDate(@selectedDates, date)
@selectedDates = _.filter @selectedDates, (selectedDate) => ! @isEqDate(selectedDate, date)
else
@selectedDates.push(date)
when 'range'
if @isSelectingRange
@isSelectingRange = false
hoverEndDate = @hoverdDate || @hoverStartDate
rangeMoment = @createMomentRangeByDates(@hoverStartDate, hoverEndDate)
@selectedDates = @toDatesFromMomentRaange(rangeMoment)
@hoverStartDate = undefined
@hoverdDate = undefined
else
@selectedDates = []
@hoverStartDate = date
@isSelectingRange = true
else
throw "error"
@$emit('update:date', @selectedDates)
hoverDate: (date) ->
if @$_calendarable_isRangeMode && @isSelectingRange
@hoverdDate = date
headClass: (dayOfWeek) ->
{
"-#{@weekForClassName[dayOfWeek]}": true
}
dateClass: (date) ->
moment = @createMomentByDate(date)
if @$_calendarable_isRangeMode && @isSelectingRange && @hoverStartDate && @hoverdDate
momentRange = @createMomentRangeByDates(@hoverStartDate, @hoverdDate)
result = {
'-selected': @includesDate(@selectedDates, date)
"-#{@weekForClassName[date.dayOfWeek]}": true
"-disable": @isDisable(moment)
"-inhover": momentRange && moment.within(momentRange)
"-hoverstart": momentRange && momentRange.start.isSame(moment)
"-hoverend": momentRange && momentRange.end.isSame(moment)
}
_.forEach @$_calendarable_classes, (func, className) => result[className] = true if func(moment)
result
isEqDate: (date1, date2) ->
date1.year == date2.year &&
date1.month == date2.month &&
date1.day == date2.day
includesDate: (dates, date) ->
_.reduce dates, (result, d) =>
result || @isEqDate(d, date)
, false
isDisable: (moment) ->
_.reduce @$_calendarable_disableDates, (result, func) =>
result || func(moment)
, false
created: ->
@currentYear = @$_calendarable_defaultYear
@currentMonth = @$_calendarable_defaultMonth
_.forEach @$_calendarable_defaultSelectedDates, (defaultSelectedDate) =>
m = moment(defaultSelectedDate, 'YYYY-MM-DD')
d = @toDateFromMoment(m)
@selectedDates.push(d)
mounted: -> null
</script>
<style lang="stylus" scoped>
.smart-calendar {
background-color: white; /* DEBUG */
color: black; /* DEBUG */
> .table {
> .head, > .body {
.row {
.col {
border: 1px solid black; /* DEBUG */
&.-sun {
background-color: #FF6666; /* DEBUG */
}
&.-sat {
background-color: #6666FF; /* DEBUG */
}
}
}
}
> .head {
.row {
.col {
}
}
}
> .body {
.row {
.col {
cursor: pointer;
&.-special { /* DEBUG */
&:before { content: "★" }
}
&.-inhover {
text-decoration: underline;
}
&.-hoverstart {
&:before { content: "【" } /* DEBUG */
}
&.-hoverend {
&:after { content: "】" } /* DEBUG */
}
&.-selected {
color: red; /* DEBUG */
}
&.-disable {
cursor: default;
background-color: gray; /* DEBUG */
}
}
}
}
}
}
</style>