* Copyright (c) 2022 - present TinyVue Authors.
* Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import { range as rangeDate, getDayCountOfMonth, nextDate, toDate1, hasClass, DATEPICKER } from '@opentiny/utils'
import { arrayFindIndex, coerceTruthyValueToArray, arrayFind } from '../date-table'
export const datesInMonth = (year, month) => {
const numOfDays = getDayCountOfMonth(year, month)
const firstDay = new Date(year, month, 1)
return rangeDate(numOfDays).map((n) => nextDate(firstDay, n))
}
export const clearDate = (date) => new Date(date.getFullYear(), date.getMonth())
export const getMonthTimestamp = (time) => {
if (typeof time === 'number' || typeof time === 'string') {
return clearDate(new Date(time)).getTime()
} else if (time instanceof Date) {
return clearDate(time).getTime()
}
return NaN
}
export const getRows =
({ props, state, vm }) =>
() => {
const tableRows = state.tableRows
const disabledDate = state.disabledDate
const selectedDate = []
const now = getMonthTimestamp(new Date())
const { date, minDate, maxDate } = props
for (let i = 0; i < 3; i++) {
const row = tableRows[i]
for (let j = 0; j < 4; j++) {
let cell = row[j]
if (!cell) {
cell = {
type: DATEPICKER.Normal,
row: i,
column: j,
inRange: false,
start: false,
end: false
}
}
cell.type = DATEPICKER.Normal
const index = i * 4 + j
const time = new Date(date.getFullYear(), index).getTime()
cell.start = minDate && time === getMonthTimestamp(minDate)
cell.end = maxDate && time === getMonthTimestamp(maxDate)
cell.inRange = time >= getMonthTimestamp(minDate) && time <= getMonthTimestamp(maxDate)
const isToday = time === now
if (isToday) {
cell.type = DATEPICKER.Today
}
cell.text = index
let cellDate = new Date(time)
cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate)
cell.selected = arrayFind(selectedDate, (selectDate) => selectDate.getTime() === cellDate.getTime())
vm.$set(row, j, cell)
}
}
return tableRows
}
export const markRange =
({ props, state }) =>
(minDate, maxDate) => {
minDate = getMonthTimestamp(minDate)
maxDate = getMonthTimestamp(maxDate)
if (minDate > maxDate) {
;[minDate, maxDate] = [maxDate, minDate]
}
const rows = state.rows
for (let i = 0, len = rows.length; i < len; i++) {
const row = rows[i]
for (let j = 0, l = row.length; j < l; j++) {
const cell = row[j]
const index = i * 4 + j
const time = new Date(props.date.getFullYear(), index).getTime()
cell.start = minDate && time === minDate
cell.end = maxDate && time === maxDate
cell.inRange = minDate && time >= minDate && time <= maxDate
}
}
}
export const watchDate =
({ api, props }) =>
(value, oldvalue) => {
if (getMonthTimestamp(value) !== getMonthTimestamp(oldvalue)) {
api.markRange(props.minDate, props.maxDate)
}
}
const getTarget = (event) => {
let target = event.target
if (target.tagName === 'A') {
target = target.parentNode.parentNode
}
if (target.tagName === 'DIV') {
target = target.parentNode
}
if (target.tagName !== 'TD') {
return
}
return target
}
export const handleMouseMove =
({ api, emit, props, state }) =>
(event) => {
if (!props.rangeState.selecting) {
return
}
const target = getTarget(event)
if (!target) {
return
}
const row = target.parentNode.rowIndex
const colu = target.cellIndex
if (state.rows[row][colu].disabled) {
return
}
if (row !== state.lastRow || colu !== state.lastColumn) {
state.lastColumn = colu
state.lastRow = row
emit('changerange', {
maxDate: props.maxDate,
minDate: props.minDate,
rangeState: {
selecting: true,
endDate: api.getMonthOfCell(row * 4 + colu)
}
})
}
}
export const handleMonthTableClick =
({ api, emit, props }) =>
(event) => {
if (props.readonly) {
return
}
const target = getTarget(event)
if (!target) {
return
}
if (hasClass(target, 'disabled')) {
return
}
const column = target.cellIndex
const row = target.parentNode.rowIndex
const month = row * 4 + column
const newDate = api.getMonthOfCell(month)
if (props.selectionMode === DATEPICKER.Range) {
if (props.rangeState.selecting) {
if (newDate < props.minDate) {
emit('pick', { minDate: newDate, maxDate: props.minDate })
} else {
emit('pick', { minDate: props.minDate, maxDate: newDate })
}
props.rangeState.selecting = false
} else {
emit('pick', { minDate: newDate, maxDate: null })
props.rangeState.selecting = true
}
} else {
emit('pick', month)
}
}
export const cellMatchesDate = (props) => (cell, date) => {
const value = new Date(date)
return props.date.getFullYear() === value.getFullYear() && Number(cell.text) === value.getMonth()
}
export const getCellStyle =
({ api, props }) =>
(cell) => {
const style = {}
const year = props.date.getFullYear()
const today = new Date()
const month = cell.text
const defaultValue = props.defaultValue
? Array.isArray(props.defaultValue)
? props.defaultValue
: [props.defaultValue]
: []
style.disabled =
typeof props.disabledDate === 'function' ? datesInMonth(year, month).every(props.disabledDate) : false
const execDate = typeof props.value === 'object' ? props.value : toDate1(props.value)
style.current =
arrayFindIndex(
coerceTruthyValueToArray(execDate),
(date) => date.getFullYear() === year && date.getMonth() === month
) >= 0
style.today = today.getFullYear() === year && today.getMonth() === month
style.default = defaultValue.some((date) => api.cellMatchesDate(cell, date))
if (cell.inRange) {
style[DATEPICKER.InRange] = true
if (cell.start) {
style[DATEPICKER.StartDate] = true
}
if (cell.end) {
style[DATEPICKER.EndDate] = true
}
}
return style
}
export const getMonthOfCell = (props) => (month) => new Date(props.date.getFullYear(), month, 1)