Calendar components are familiar to everyone, in some background projects, most will encounter. In actual projects, we usually choose a complete SET of UI framework, such as ElementUI commonly used in Vue, ANTD Design commonly used in React, etc. These component libraries will integrate the calendar component with complete functions. “Take the principle”, we can use it. But think carefully, the basic calendar component is mainly display, interaction is not complicated, so can we build a calendar component? Step by step, how do you write a calendar component
1. Core functions
A complete calendar display looks something like the one above. Contains three blocks:
- Month date;
- Last month date (red box);
- Start date of next month (blue box);
It is also ok to only display the date of the current month, but there will be blank areas on the calendar, which is not so beautiful. The usual practice is to use the days of the last month and the next month to make up the date. Therefore, we use a calendar table with 42 grids here, which can completely display the calendar information of any month. The difficult problems also revolve around these three, how to correctly render the block date calculation principle:
1) Get the day of the current month, calculate the day of the week of the 1st, and render the start position of the current month, and the end position is the day of the current month; 2) Supplement the days at the end of last month; The number of days at the end of the last month is calculated based on the number of days in the last month and the index of the first week of the current month: Number of days in the last month – Index of the first week of the current month + I (circular parameter) + 1; 3) The number of days from next month. You only need to know the remaining number of days in the current date table and fill it progressively from 1st.
Next we write out the calendar component step by step.
GenerateCalendar (Date: Date) : generateCalendar(Date: Date) : generateCalendar(Date: Date) : generateCalendar(Date: Date))
2. Calculation of month and date
There are two things to know when calculating the month and date: how many days is there in the month, and what day is the first day of the month? We use javascript’s built-in Date class: Date to manipulate almost anything related to a Date.
2.1 Days of month
We know that in a year there are 31 days in January, 3, 5, 7, 8, 10, December, 30 in April, 6, 9, and November, 29 February in leap years and 29 February in ordinary years. So we need two functions: isLeap(Year: number) and getDays(Year: Number, month: Year) to determine the exact number of days to give the date.
// Whether it is leap year
const isLeap = (year: number) = > {
return (year % 4= = =0 && year % 100! = =0) || year % 100= = =0;
};
// get a few days in xx months
const getDays = (year: number.month: number) :number= > {
const feb = isLeap(year) ? 29 : 28;
const daysPerMonth = [31, feb, 31.30.31.30.31.31.30.31.30.31];
return daysPerMonth[month];
};
Copy the code
Then we need to know what day the first of the month is, which we can get by using date.prototype.getDay (). Note: the API retrieves the week from Sunday, i.e. 0 means Sunday, and the rest increments accordingly.
The month we use here represents 42 cells of 6×7, which can display the month information completely. GenerateCalendar (date: date); generateCalendar(date: date);
export interface CalendarItem {
year: number;
month: number;
day: number;
isCurrentMonth: boolean;
}
// ...
export const generateCalendar = (date: Date) = > {
const currentYear = date.getFullYear();
const currentMonth = date.getMonth();
// Days of the month
const days = getDays(currentYear, currentMonth);
// What day is the first
const weekIndex = new Date(`${currentYear}.${currentMonth + 1}, 1 `).getDay();
const calendarTable: CalendarItem[] = [];
for (let i = 0; i < calendarGrid; i++) {
if (i < weekIndex) {
// Add the number of days last month
} else if (i >= days + weekIndex) {
// Add the number of days next month}}// Fill in the date of the month
for (let d = 1; d <= days; d++) {
calendarTable[weekIndex + d - 1] = {
year: currentYear,
month: currentMonth,
day: d,
isCurrentMonth: true}; }return calendarTable;
};
Copy the code
In CalendarItem, day indicates the date of the day, isCurrentMonth indicates whether the day is the date of the current month, because we have the dates of the last month and the next month in our table, and we need to perform secondary processing (gray processing) for dates that are not in the current month. IsCurrentMonth is required. We used a one-dimensional array: calendarTable to render the dates. Naturally, the middle part of the one-dimensional array is the date of the month; For example, if the first day of the month is Friday and the month has 30 days, the date of the month starts from the 5th + 1-1 (weekIndx + D-1) and continues until the 30th (days).
2.2 The number of days of last month and next month shall be supplemented
After the first step, the calendar is complete, but there will be a blank at the beginning and end of the table, which is not very beautiful. We can fill it with the days at the end of the last month and the days at the beginning of the next month. Last month and next month are determined according to the current month, note:
- If the current month is January, then last month is December of the previous year;
- If the current month is December, next month will be January of the following year.
- Other intermediate months, the month is normal ±1, the year is unchanged;
// Get the number of days next month/last month
const getNextOrLastMonthDays = (date: Date.type: 'next' | 'last') = > {
const month = date.getMonth();
const year = date.getFullYear();
if (type= = ='last') {
const lastMonth = month === 0 ? 11 : month - 1;
const lastYear = lastMonth === 11 ? year - 1 : year;
return {
year: lastYear,
month: lastMonth,
days: getDays(lastYear, lastMonth),
};
}
const nextMonth = month === 11 ? 0 : month + 1;
const nextYear = nextMonth === 0 ? year + 1 : year;
return {
year: nextYear,
month: nextMonth,
days: getDays(nextYear, nextMonth),
};
};
Copy the code
We then proceed to complete the generateCalendar function:
// ...
const calendarGrid = 42; // 7 * 6;
const generateCalendar = (date: Date) = > {
const currentYear = date.getFullYear();
const currentMonth = date.getMonth();
// Days of the month
const days = getDays(currentYear, currentMonth);
// Get the number of days at the end of last month and the number of days at the beginning of next month
const { days: lastMonthDays, year: lastMonthYear, month: lastMonth } = getNextOrLastMonthDays(date, 'last');
const { year: nextMonthYear, month: nextMonthMonth } = getNextOrLastMonthDays(date, 'next');
// What day is the first
const weekIndex = new Date(`${currentYear}.${currentMonth + 1}, 1 `).getDay();
// Displays the number of days in the next month at the end of the month
const trailDays = calendarGrid - weekIndex - days;
let trailVal = 0;
const calendarTable: CalendarItem[] = [];
for (let i = 0; i < calendarGrid; i++) {
if (i < weekIndex) {
// Add the number of days last month
calendarTable[i] = {
year: lastMonthYear,
month: lastMonth,
day: lastMonthDays - weekIndex + i + 1.isCurrentMonth: false}; }else if (i >= days + weekIndex) {
// Add the number of days next month
if (trailVal < trailDays) {
trailVal += 1;
}
calendarTable[i] = {
year: nextMonthYear,
month: nextMonthMonth,
day: trailVal,
isCurrentMonth: false}; }}// Fill in the date of the month
for (let d = 1; d <= days; d++) {
calendarTable[weekIndex + d - 1] = {
year: currentYear,
month: currentMonth,
day: d,
isCurrentMonth: true}; }return calendarTable;
};
Copy the code
To quote the principle again:
1) Get the day of the current month, calculate the day of the week of the 1st, and render the start position of the current month, and the end position is the day of the current month; 2) Supplement the days at the end of last month; The number of days at the end of the last month is calculated based on the number of days in the last month and the index of the first week of the current month: Number of days in the last month – Index of the first week of the current month + I (circular parameter) + 1; 3) The number of days from next month. You only need to know the remaining number of days in the current date table and fill it progressively from 1st.
This should become clearer with the code complementing the days of the month and month.
This completes the core function generateCalendar(Date: Date), which takes any date argument and returns complete information about the month in which the date is located. Next up is the presentation.
3. Calendar display
A basic calendar consists of a calendar header (week column) and date content. We want to write vue component, using Vue 3.2, combined with the setup syntax sugar, not too cool to write.
<template> <div class="calendar"> <! <div class="calendar-operate"> <! <div class="button-group"> <button class="button" @click="changeMonth('prev')"> < I class="icon ri-arrow-left-s-line"></i> </button> <button class="button" @click="changeMonth('next')"> <i class="icon ri-arrow-right-s-line"></i> </button> </div> <! <div class="calendar-operate__title">{{dateText}}</div> <! <button class="button" :disabled="isToday" @click="currentDate"> </button> </div> <! -- Number of days for loop render, <div class="calendar-header"> <span v-for="(item, index) in weekMapZh" :key="index" class="calendar-header__item" :class="{ gray: index === 0 || index === 6 }" >{{ item }}</span > </div> <! --> <div class="calendar-content" :data-month=" date.getmonth () + 1"> <div v-for="(item, index) in calendarTable" :key="index" class="calendar-content__item" :class="[{ light: !item.isCurrentMonth }, { active: isActive(item) }]" > {{ item.day }} </div> </div> </div> </template>Copy the code
First, we want to highlight the date of the day. For example, today is the 1st, we want to highlight the day in the calendar.
<script lang="ts" setup> // Const date = ref< date >(new date ()); const calendarTable = computed(() => generateCalendar(date.value)); / /... /** ** / const isActive = (item:) */ ** */ const isActive = (item:) CalendarItem) => { return isAllTrue([ item.day === date.value.getDate(), item.isCurrentMonth, item.month === new Date().getMonth(), item.year === new Date().getFullYear(), ]); }; </script>Copy the code
Second, if you want to have a month switch, that is, to last month or next month, there is a logic here, because generateCalendar(date: The Date function receives a Date parameter, so all operations on a Date will return the Date type, that is, change the Date parameter, and then use the calculation property to generate a new calendar table.
At this point, a basic, basic calendar with switch dates is complete 🎉🎉
Four,
A basic calendar component is completed step by step, and the function is not complicated. Here’s a simple callback:
- It is necessary to have a certain understanding of the Date class. At first, the reason why CALENDAR is a little complicated is that I am not familiar with the use of the Date class.
- Clear the logic and have a clear understanding of the product features to be implemented. Only when you know what to do can you think how to do it;
- Extensibility. We also need to consider the extensibility of components. Usually, we write more business components. Maybe the business components are only used in a few pages, but the presentation may be different, then we need to consider how to use less code, clear logic to make the components easy to use and later extension. The component code must also be maintainable (write comments of write comments, decouple of decouple ~) to qualify as a component.
Finally attached source address, welcome friends more advice, more star⭐️
Github.com/CiroLee/vue…