This commit is contained in:
Nadar
2026-03-17 13:24:22 +03:00
commit 82e5ac9d81
554 changed files with 29637 additions and 0 deletions

View 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>