1<template>
2 <div class="p-6.5 px-5 rounded-7 border border-outline-gray-2">
3 <div class="mb-6.5 flex justify-between items-center">
4 <div class="ms-1">
5 <Popover v-if="startYear !== endYear">
6 <template #trigger>
7 <Button
8 class="flex items-center gap-2 text-xl-semibold cursor-pointer select-none"
9 variant="ghost"
10 :label="currentYear + ''"
11 icon-right="lucide-chevron-down"
12 />
13 </template>
14 <template #default="{ close: closePopover }">
15 <div class="w-24">
16 <div ref="yearsContainer" class="max-h-60 overflow-y-auto py-1">
17 <div
18 v-for="year in yearsList"
19 :key="year"
20 ref="yearItems"
21 class="cursor-pointer px-3 py-1.5 text-sm hover:bg-surface-gray-2 flex items-center justify-between"
22 @click="onYearChange(closePopover, year)"
23 >
24 {{ year }}
25 <LucideCheck class="size-4" v-if="year === currentYear" />
26 </div>
27 </div>
28 </div>
29 </template>
30 </Popover>
31 <div
32 v-else
33 class="flex items-center gap-2 px-2 text-xl-semibold select-none"
34 >
35 {{ startYear }}
36 </div>
37 </div>
38 <div class="flex gap-2 items-center">
39 <Button
40 variant="ghost"
41 icon="lucide-chevron-left"
42 :disabled="visibleMonths === 'first-half'"
43 @click="visibleMonths = 'first-half'"
44 />
45 <Button variant="ghost" label="Today" @click="goToToday()" />
46 <Button
47 variant="ghost"
48 icon="lucide-chevron-right"
49 :disabled="visibleMonths === 'second-half'"
50 @click="visibleMonths = 'second-half'"
51 />
52 </div>
53 </div>
54 <div
55 class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5"
56 v-if="visibleMonths === 'first-half'"
57 >
58 <HLCalender
59 v-for="month in months.slice(0, 6)"
60 :key="month"
61 :year="currentYear"
62 :month="month"
63 :holidays="holidayData.holidays"
64 />
65 </div>
66 <div
67 class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 justify-between gap-5"
68 v-else
69 >
70 <HLCalender
71 v-for="month in months.slice(6, 12)"
72 :key="month"
73 :year="currentYear"
74 :month="month"
75 :holidays="holidayData.holidays"
76 />
77 </div>
78 <div class="flex gap-2 items-center w-full justify-center mt-8">
79 <div
80 :class="[
81 'size-1.5 rounded-full cursor-pointer',
82 {
83 'bg-surface-gray-10': visibleMonths === 'first-half',
84 'bg-surface-gray-4': visibleMonths === 'second-half',
85 },
86 ]"
87 @click="visibleMonths = 'first-half'"
88 />
89 <div
90 :class="[
91 'size-1.5 rounded-full cursor-pointer',
92 {
93 'bg-surface-gray-10': visibleMonths === 'second-half',
94 'bg-surface-gray-4': visibleMonths === 'first-half',
95 },
96 ]"
97 @click="visibleMonths = 'second-half'"
98 />
99 </div>
100 </div>
101</template>
102<script setup lang="ts">
103import LucideCheck from "~icons/lucide/check";
104import { computed, ref, watch } from "vue";
105import HLCalender from "./HLCalender.vue";
106import { holidayData } from "@/stores/holidayList";
107import { Button, dayjs, Popover } from "frappe-ui";
108
109const visibleMonths = ref<"first-half" | "second-half">("first-half");
110const months = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
111const currentYear = ref(dayjs().year());
112
113const startYear = ref(dayjs(holidayData.value.from_date || dayjs()).year());
114const endYear = ref(dayjs(holidayData.value.to_date || dayjs()).year());
115
116const yearsList = computed(() => {
117 const yearList = [];
118 for (let year = startYear.value; year <= endYear.value; year++) {
119 yearList.push(year);
120 }
121 return yearList;
122});
123
124const onYearChange = (closePopover: () => void, year: number) => {
125 currentYear.value = year;
126 if (year === dayjs(holidayData.value.from_date).year()) {
127 if (dayjs(holidayData.value.from_date).month() >= 6) {
128 visibleMonths.value = "second-half";
129 }
130 } else if (year === dayjs(holidayData.value.to_date).year()) {
131 if (dayjs(holidayData.value.to_date).month() >= 6) {
132 visibleMonths.value = "first-half";
133 }
134 } else {
135 visibleMonths.value = "first-half";
136 }
137 closePopover();
138};
139
140const goToToday = () => {
141 const today = dayjs();
142 currentYear.value = today.year();
143 visibleMonths.value = today.month() >= 6 ? "second-half" : "first-half";
144};
145
146watch(
147 () => [holidayData.value.from_date, holidayData.value.to_date],
148 ([fromDate, toDate]) => {
149 fromDate = dayjs(fromDate || dayjs());
150 toDate = dayjs(toDate || dayjs());
151 startYear.value = fromDate.year();
152 endYear.value = toDate.year();
153 currentYear.value = fromDate.year();
154 visibleMonths.value = fromDate.month() >= 6 ? "second-half" : "first-half";
155 },
156 // The list is loaded before this view mounts, so a change-only watch never fires.
157 { immediate: true }
158);
159</script>
160