Demand: we often see some calendars in some travel, booking, hotel pages, these calendars have Gregorian calendar, lunar calendar, holidays, non-holidays (days off) mark, as well as product business information, such as ticket prices, tickets and so on. Now it’s time to build the wheel. First, the component should generate a basic calendar and hang some extended information on each day’s objects. It might look something like ⤵, or more complicated.
GitHub | NPM | Demo
Note: This is a UI independent component, all we need to do is how to build such a calendar object.
Design components
The presentation of a calendar is usually a table. We need to put the information of each day into the table to produce a complete calendar for a month or several months.
- Example Query the number of days in the current month
- Calculate how many days are left before the first day of the month (consider the starting point of the calendar, starting Monday or Sunday)
- If available, put in the days to skip
- Generate data for every day with data
- Put the data object in the month table
Example Query the number of days in a month
This function is very simple. It takes a parameter, date, and the variable number of days is February.
export function getMonthDays(date) {
const year = date.getFullYear()
const month = date.getMonth()
constleapYear = ! (year %4) && ((year % 100) | |! (year %400))
const days = [31, leapYear ? 29 : 28.31.30.31.30.31.31.30.31.30.31]
return days[month]
}
Copy the code
A leap in four years, a hundred years not leap, four hundred years to leap.
Please note that the date of the parameter here is likely to be new date (), for example, the date is 2018-08-31. When the user gets the normal data of this month, then directly set the month. If the user turns up to June, there will be problems, because there are no 31 days in June. In this case, the date will be July 1, which is different from the expected date. So be sure to set the date to 1 after the date operation.
const days = utils.getMonthDays(date)
date.setDate(1)
Copy the code
Create messages for the day
The daily information should contain some common attributes from the date object and some extended fields from the user. Here I’m using a class to construct this object.
class Day {
constructor(dateObj, extension = {}) {
this.year = dateObj.getFullYear()
this.month = dateObj.getMonth()
this.date = dateObj.getDate()
this.day = dateObj.getDay()
this.dateText = utils.getChinaStandard(dateObj)
this.past = this.toDay.getTime() > dateObj.getTime()
this.today = utils.getChinaStandard(new Date()) = = =this.dateText
this.timestamp = dateObj.getTime()
const _self = this
Object.keys(extension).forEach(key= > {
_self[key] = extension[key]
})
}
get toDay() {
const date = new Date()
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
date.setMilliseconds(0)
return date
}
}
Copy the code
Note:
- The past attribute may appear in a ticket scenario, and no ticket can be purchased for a past time
- Today is today
- DateText generates a date in string format
- The toDay() function is used to flatten hours and below
Generate a table for one month
This object is in the form of a two-dimensional array, and for several weeks of the month, each week’s array holds the objects of the day
The monthly method is designed as a static method to facilitate the caller to quickly build a data object for a particular month through kalendar.monthly (),
parameter
- Date Of the month
date
object - Mount needs to extend that information
- WeekStart Specifies the start day of a week. The default is Sunday
- UnifiedMount all dates require extended information
static monthly({date, mount = {}, weekStart = 0, unifiedMount = {}}) {
const monthTable = []
const days = utils.getMonthDays(date)
date.setDate(1)
const day = date.getDay()
let skip = 0
if(day ! == weekStart) skip = day - weekStartfor (let i = 0; i < days + skip; i += 7) {
const week = []
let num = 7
if(! i && skip) {for (let k = 0; k < skip; k++) week.push(null)
num -= skip
}
for (let j = 0; j < num; j++) {
const dateText = utils.getChinaStandard(date)
week.push(new Day(date, Object.assign({}, unifiedMount, mount[dateText])))
if (date.getDate() >= days) break
date.setDate(date.getDate() + 1)
}
monthTable.push(week)
}
return monthTable
}
Copy the code
The ticket price, or some default data, should be added to all unifiedMount dates
var unifiedMount = {
total: 1000.price: 550.bg: 'info'
}
Copy the code
Mount is date specific data, such as a day that is sold out and should be disabled for that day
var mount = {
'2018-08-30': {
total: 0.price: 550
},
'2018-08-31': {
total: 10.price: 750}},Copy the code
Build a long-term calendar object
In some mobile scenarios, it is often a one-time access for three months or longer, sliding display effect.
I returned the _create() function in the constructor of my Kalendar class. The _create() function generates information for many months.
class Kalendar {
constructor({start, end, unifiedMount = {}, mount = {}, weekStart = 0{} = {})this.startTime = start
this.endTime = end
this.unifiedMount = unifiedMount
this.mount = mount
this.weekStart = weekStart
return this._create()
}
......
_create() {
const {mount, weekStart, unifiedMount} = this
const table = {}
let count = (this.endDate.getFullYear() * 12 + this.endDate.getMonth() + 1)
- (this.startDate.getFullYear() * 12 + this.startDate.getMonth() + 1)
if (count < 0) return null
let idx = 0
do {
const date = this.startDate
date.setMonth(date.getMonth() + idx)
const monthTable = Kalendar.monthly({date, mount, weekStart, unifiedMount})
table[utils.getChinaStandard(date, true)] = monthTable
count--
idx++
} while (count > 0)
return table
}
static monthly({date, mount = {}, weekStart = 0, unifiedMount = {}}) { ...... }}Copy the code
Instead of generating a month of data, the constructor needs to set the start and end values. After calculating the month difference, the constructor calls the monthly function in a loop to avoid the startTime and endTime being the same month. While produces at least a month’s worth of data.
Package and release
For smaller volume, I used Rollup to build the component code and used the MIT protocol NPM Publish.
GitHub | NPM | Demo