initial
This commit is contained in:
216
layers/ui/components/calendar/index.vue
Normal file
216
layers/ui/components/calendar/index.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div :class="cn.b()">
|
||||
<div :class="cn.e('header')">
|
||||
<UiButton
|
||||
icon="chevron-left"
|
||||
size="small"
|
||||
type="ghost"
|
||||
color="secondary"
|
||||
@click="previousMonth"
|
||||
/>
|
||||
<p :class="cn.e('title')">
|
||||
{{ currentMonth }}
|
||||
</p>
|
||||
<UiButton
|
||||
icon="chevron-right"
|
||||
size="small"
|
||||
type="ghost"
|
||||
color="secondary"
|
||||
@click="nextMonth"
|
||||
/>
|
||||
</div>
|
||||
<div :class="cn.e('week-days')">
|
||||
<div
|
||||
v-for="dayOfWeek in daysOfWeek"
|
||||
:key="dayOfWeek"
|
||||
>
|
||||
{{ dayOfWeek }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="cn.e('days')">
|
||||
<div
|
||||
v-for="day in monthDays"
|
||||
:key="day.date"
|
||||
:class="getDayClasses(day)"
|
||||
@click="selectDay(day)"
|
||||
>
|
||||
{{ day.day }}
|
||||
</div>
|
||||
</div>
|
||||
<UiButton
|
||||
type="outlined"
|
||||
color="secondary"
|
||||
:class="cn.e('apply-button')"
|
||||
@click="apply"
|
||||
>
|
||||
{{ $t('apply') }}
|
||||
</UiButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// TODO: TypeScript
|
||||
import { computed, reactive, ref, toRef, watch } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import UiButton from '../button/index.vue'
|
||||
import useClassname from '../../composables/use-classname'
|
||||
|
||||
const props = defineProps({
|
||||
allowRangeSelection: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const cn = useClassname('ui-calendar')
|
||||
|
||||
const modelValue = toRef(props, 'modelValue')
|
||||
|
||||
const today = dayjs()
|
||||
const dateRange = reactive({
|
||||
start: null,
|
||||
end: null,
|
||||
})
|
||||
const currentDate = ref(today)
|
||||
const daysOfWeek = dayjs.weekdaysMin()
|
||||
|
||||
const currentMonth = computed(() => currentDate.value.format('MMMM, YYYY'))
|
||||
|
||||
const monthDays = computed(() => {
|
||||
const daysInMonth = currentDate.value.daysInMonth()
|
||||
const leadingDays = currentDate.value.startOf('month').day()
|
||||
|
||||
const days = []
|
||||
|
||||
for (let i = 0; i < leadingDays; i++) {
|
||||
const date = currentDate.value
|
||||
.subtract(1, 'month')
|
||||
.endOf('month')
|
||||
.subtract(leadingDays - i - 1, 'day')
|
||||
|
||||
days.push(date)
|
||||
}
|
||||
|
||||
for (let i = 0; i < daysInMonth; i++) {
|
||||
const date = currentDate.value.date(i + 1)
|
||||
|
||||
days.push(date)
|
||||
}
|
||||
|
||||
const num = 7 - (days.length % 7 || 7)
|
||||
|
||||
for (let i = 0; i < num; i++) {
|
||||
const date = currentDate.value.add(1, 'month').date(i + 1)
|
||||
|
||||
days.push(date)
|
||||
}
|
||||
|
||||
return days.map(formatDateObject)
|
||||
})
|
||||
|
||||
function formatDateObject(date) {
|
||||
return {
|
||||
raw: date,
|
||||
day: date.date(),
|
||||
month: date.get('month'),
|
||||
isToday: date.isToday(),
|
||||
isCurrentMonth: date.isSame(currentDate.value, 'month'),
|
||||
isPeriod:
|
||||
!!dateRange.start
|
||||
&& !!dateRange.end
|
||||
&& date.isBetween(dateRange.start, dateRange.end, 'day', '[]'),
|
||||
isPeriodStart: !!dateRange.start && date.isSame(dateRange.start, 'day'),
|
||||
isPeriodEnd: !!dateRange.end && date.isSame(dateRange.end, 'day'),
|
||||
}
|
||||
}
|
||||
|
||||
function nextMonth() {
|
||||
currentDate.value = currentDate.value.add(1, 'month')
|
||||
}
|
||||
function previousMonth() {
|
||||
currentDate.value = currentDate.value.subtract(1, 'month')
|
||||
}
|
||||
|
||||
function selectDay(day) {
|
||||
let { start, end } = dateRange
|
||||
|
||||
if (props.allowRangeSelection) {
|
||||
const isSameStartDay = day.raw.isSame(start, 'day')
|
||||
|
||||
if (isSameStartDay) {
|
||||
start = null
|
||||
end = null
|
||||
}
|
||||
else if (!start || end) {
|
||||
start = day.raw
|
||||
end = null
|
||||
}
|
||||
else {
|
||||
end = day.raw
|
||||
|
||||
if (dayjs(start).isAfter(dayjs(end), 'day'))
|
||||
[start, end] = [end, start]
|
||||
}
|
||||
}
|
||||
else {
|
||||
start = day.raw
|
||||
end = null
|
||||
}
|
||||
|
||||
dateRange.start = start
|
||||
dateRange.end = end
|
||||
}
|
||||
|
||||
watch(
|
||||
modelValue,
|
||||
(value) => {
|
||||
const [start, end] = value
|
||||
|
||||
dateRange.start = !!start && dayjs(start)
|
||||
dateRange.end = !!end && dayjs(end)
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function getDayClasses(day) {
|
||||
const classes = [cn.e('cell')]
|
||||
|
||||
if (day.isCurrentMonth)
|
||||
classes.push(cn.em('cell', 'current-month'))
|
||||
|
||||
if (day.isPeriod)
|
||||
classes.push(cn.em('cell', 'period'))
|
||||
|
||||
if (day.isPeriodStart)
|
||||
classes.push(cn.em('cell', 'period-start'))
|
||||
else if (day.isPeriodEnd)
|
||||
classes.push(cn.em('cell', 'period-end'))
|
||||
|
||||
if (day.isToday)
|
||||
classes.push(cn.em('cell', 'current-day'))
|
||||
|
||||
return classes
|
||||
}
|
||||
|
||||
function apply() {
|
||||
const value = []
|
||||
|
||||
if (dateRange.start)
|
||||
value.push(dateRange.start.startOf('day').valueOf())
|
||||
|
||||
if (dateRange.end)
|
||||
value.push(dateRange.end.startOf('day').valueOf())
|
||||
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use 'styles';
|
||||
</style>
|
||||
Reference in New Issue
Block a user