refactor(components): [date-picker] basic-date-table (#8095)

* Refactor `basic-date-table` to script setup.

Co-authored-by: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com>
This commit is contained in:
Jeremy
2022-06-05 21:03:24 +08:00
committed by GitHub
parent 6df6e49db0
commit 6cde29f559
3 changed files with 463 additions and 372 deletions

View File

@@ -21,17 +21,17 @@
</th>
</tr>
<tr
v-for="(row, key) in rows"
:key="key"
v-for="(row, rowKey) in rows"
:key="rowKey"
:class="[ns.e('row'), { current: isWeekActive(row[1]) }]"
>
<td
v-for="(cell, key_) in row"
:key="key_"
:ref="(el) => isSelectedCell(cell) && (currentCellRef = el)"
v-for="(cell, columnKey) in row"
:key="`${rowKey}.${columnKey}`"
:ref="(el) => { isSelectedCell(cell) && (currentCellRef = el as HTMLElement) }"
:class="getCellClasses(cell)"
:aria-current="cell.isCurrent ? 'date' : undefined"
:aria-selected="`${cell.isCurrent}`"
:aria-selected="cell.isCurrent"
:tabindex="isSelectedCell(cell) ? 0 : -1"
@focus="handleFocus"
>
@@ -42,407 +42,407 @@
</table>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, ref, watch } from 'vue'
<script lang="ts" setup>
import { computed, nextTick, ref, unref, watch } from 'vue'
import dayjs from 'dayjs'
import { useLocale, useNamespace } from '@element-plus/hooks'
import { castArray } from '@element-plus/utils'
import { basicDateTableProps } from '../props/basic-date-table'
import { buildPickerTable } from '../utils'
import ElDatePickerCell from './basic-cell-render'
import type { Dayjs } from 'dayjs'
import type { DateCell } from '../date-picker.type'
export default defineComponent({
components: {
ElDatePickerCell,
const props = defineProps(basicDateTableProps)
const emit = defineEmits(['changerange', 'pick', 'select'])
const ns = useNamespace('date-table')
const { t, lang } = useLocale()
const tbodyRef = ref<HTMLElement>()
const currentCellRef = ref<HTMLElement>()
// data
const lastRow = ref<number>()
const lastColumn = ref<number>()
const tableRows = ref<DateCell[][]>([[], [], [], [], [], []])
// todo better way to get Day.js locale object
const firstDayOfWeek = (props.date as any).$locale().weekStart || 7
const WEEKS_CONSTANT = props.date
.locale('en')
.localeData()
.weekdaysShort()
.map((_) => _.toLowerCase())
const offsetDay = computed(() => {
// Sunday 7(0), cal the left and right offset days, 3217654, such as Monday is -1, the is to adjust the position of the first two rows of dates
return firstDayOfWeek > 3 ? 7 - firstDayOfWeek : -firstDayOfWeek
})
const startDate = computed(() => {
const startDayOfMonth = props.date.startOf('month')
return startDayOfMonth.subtract(startDayOfMonth.day() || 7, 'day')
})
const WEEKS = computed(() => {
return WEEKS_CONSTANT.concat(WEEKS_CONSTANT).slice(
firstDayOfWeek,
firstDayOfWeek + 7
)
})
const hasCurrent = computed<boolean>(() => {
return rows.value.flat().some((row) => {
return row.isCurrent
})
})
const days = computed(() => {
const startOfMonth = props.date.startOf('month')
const startOfMonthDay = startOfMonth.day() || 7 // day of first day
const dateCountOfMonth = startOfMonth.daysInMonth()
const dateCountOfLastMonth = startOfMonth.subtract(1, 'month').daysInMonth()
return {
startOfMonthDay,
dateCountOfMonth,
dateCountOfLastMonth,
}
})
const selectedDate = computed(() => {
return props.selectionMode === 'dates'
? (castArray(props.parsedValue) as Dayjs[])
: ([] as Dayjs[])
})
// Return value indicates should the counter be incremented
const setDateText = (
cell: DateCell,
{
count,
rowIndex,
columnIndex,
}: {
count: number
rowIndex: number
columnIndex: number
}
): boolean => {
const { startOfMonthDay, dateCountOfMonth, dateCountOfLastMonth } =
unref(days)
const offset = unref(offsetDay)
if (rowIndex >= 0 && rowIndex <= 1) {
const numberOfDaysFromPreviousMonth =
startOfMonthDay + offset < 0
? 7 + startOfMonthDay + offset
: startOfMonthDay + offset
if (columnIndex + rowIndex * 7 >= numberOfDaysFromPreviousMonth) {
cell.text = count
return true
} else {
cell.text =
dateCountOfLastMonth -
(numberOfDaysFromPreviousMonth - (columnIndex % 7)) +
1 +
rowIndex * 7
cell.type = 'prev-month'
}
} else {
if (count <= dateCountOfMonth) {
cell.text = count
} else {
cell.text = count - dateCountOfMonth
cell.type = 'next-month'
}
return true
}
return false
}
const setCellMetadata = (
cell: DateCell,
{
columnIndex,
rowIndex,
}: {
columnIndex: number
rowIndex: number
},
props: basicDateTableProps,
emits: ['changerange', 'pick', 'select'],
expose: ['focus'],
setup(props, ctx) {
const ns = useNamespace('date-table')
count: number
) => {
const { disabledDate, cellClassName } = props
const _selectedDate = unref(selectedDate)
const shouldIncrement = setDateText(cell, { count, rowIndex, columnIndex })
const { t, lang } = useLocale()
const cellDate = cell.dayjs!.toDate()
cell.selected = _selectedDate.find(
(d) => d.valueOf() === cell.dayjs!.valueOf()
)
cell.isSelected = !!cell.selected
cell.isCurrent = isCurrent(cell)
cell.disabled = disabledDate?.(cellDate)
cell.customClass = cellClassName?.(cellDate)
return shouldIncrement
}
const tbodyRef = ref<HTMLElement>()
const currentCellRef = ref<HTMLElement>()
// data
const lastRow = ref(null)
const lastColumn = ref(null)
const tableRows = ref<DateCell[][]>([[], [], [], [], [], []])
const setRowMetadata = (row: DateCell[]) => {
if (props.selectionMode === 'week') {
const [start, end] = props.showWeekNumber ? [1, 7] : [0, 6]
const isActive = isWeekActive(row[start + 1])
row[start].inRange = isActive
row[start].start = isActive
row[end].inRange = isActive
row[end].end = isActive
}
}
// todo better way to get Day.js locale object
const firstDayOfWeek = (props.date as any).$locale().weekStart || 7
const WEEKS_CONSTANT = props.date
.locale('en')
.localeData()
.weekdaysShort()
.map((_) => _.toLowerCase())
const rows = computed(() => {
const { minDate, maxDate, rangeState, showWeekNumber } = props
const offsetDay = computed(() => {
// Sunday 7(0), cal the left and right offset days, 3217654, such as Monday is -1, the is to adjust the position of the first two rows of dates
return firstDayOfWeek > 3 ? 7 - firstDayOfWeek : -firstDayOfWeek
})
const offset = offsetDay.value
const rows_ = tableRows.value
const dateUnit = 'day'
let count = 1
const startDate = computed(() => {
const startDayOfMonth = props.date.startOf('month')
return startDayOfMonth.subtract(startDayOfMonth.day() || 7, 'day')
})
const WEEKS = computed(() => {
return WEEKS_CONSTANT.concat(WEEKS_CONSTANT).slice(
firstDayOfWeek,
firstDayOfWeek + 7
)
})
const hasCurrent = computed<boolean>(() => {
return rows.value.flat().some((row) => {
return row.isCurrent
})
})
const rows = computed(() => {
// TODO: refactory rows / getCellClasses
const startOfMonth = props.date.startOf('month')
const startOfMonthDay = startOfMonth.day() || 7 // day of first day
const dateCountOfMonth = startOfMonth.daysInMonth()
const dateCountOfLastMonth = startOfMonth
.subtract(1, 'month')
.daysInMonth()
const offset = offsetDay.value
const rows_ = tableRows.value
let count = 1
const selectedDate: Dayjs[] =
props.selectionMode === 'dates' ? castArray(props.parsedValue) : []
const calNow = dayjs().locale(lang.value).startOf('day')
for (let i = 0; i < 6; i++) {
const row = rows_[i]
if (props.showWeekNumber) {
if (!row[0]) {
row[0] = {
type: 'week',
text: startDate.value.add(i * 7 + 1, 'day').week(),
}
}
}
for (let j = 0; j < 7; j++) {
let cell = row[props.showWeekNumber ? j + 1 : j]
if (!cell) {
cell = {
row: i,
column: j,
type: 'normal',
inRange: false,
start: false,
end: false,
}
}
const index = i * 7 + j
const calTime = startDate.value.add(index - offset, 'day')
cell.dayjs = calTime
cell.date = calTime.toDate()
cell.timestamp = calTime.valueOf()
cell.type = 'normal'
const calEndDate =
props.rangeState.endDate ||
props.maxDate ||
(props.rangeState.selecting && props.minDate)
cell.inRange =
(props.minDate &&
calTime.isSameOrAfter(props.minDate, 'day') &&
calEndDate &&
calTime.isSameOrBefore(calEndDate, 'day')) ||
(props.minDate &&
calTime.isSameOrBefore(props.minDate, 'day') &&
calEndDate &&
calTime.isSameOrAfter(calEndDate, 'day'))
if (props.minDate?.isSameOrAfter(calEndDate)) {
cell.start = calEndDate && calTime.isSame(calEndDate, 'day')
cell.end = props.minDate && calTime.isSame(props.minDate, 'day')
} else {
cell.start = props.minDate && calTime.isSame(props.minDate, 'day')
cell.end = calEndDate && calTime.isSame(calEndDate, 'day')
}
const isToday = calTime.isSame(calNow, 'day')
if (isToday) {
cell.type = 'today'
}
if (i >= 0 && i <= 1) {
const numberOfDaysFromPreviousMonth =
startOfMonthDay + offset < 0
? 7 + startOfMonthDay + offset
: startOfMonthDay + offset
if (j + i * 7 >= numberOfDaysFromPreviousMonth) {
cell.text = count++
} else {
cell.text =
dateCountOfLastMonth -
(numberOfDaysFromPreviousMonth - (j % 7)) +
1 +
i * 7
cell.type = 'prev-month'
}
} else {
if (count <= dateCountOfMonth) {
cell.text = count++
} else {
cell.text = count++ - dateCountOfMonth
cell.type = 'next-month'
}
}
const cellDate = calTime.toDate()
cell.selected = selectedDate.find(
(_) => _.valueOf() === calTime.valueOf()
)
cell.isSelected = !!cell.selected
cell.isCurrent = isCurrent(cell)
cell.disabled = props.disabledDate && props.disabledDate(cellDate)
cell.customClass =
props.cellClassName && props.cellClassName(cellDate)
row[props.showWeekNumber ? j + 1 : j] = cell
}
if (props.selectionMode === 'week') {
const start = props.showWeekNumber ? 1 : 0
const end = props.showWeekNumber ? 7 : 6
const isActive = isWeekActive(row[start + 1])
row[start].inRange = isActive
row[start].start = isActive
row[end].inRange = isActive
row[end].end = isActive
if (showWeekNumber) {
for (let rowIndex = 0; rowIndex < 6; rowIndex++) {
if (!rows_[rowIndex][0]) {
rows_[rowIndex][0] = {
type: 'week',
text: startDate.value.add(rowIndex * 7 + 1, dateUnit).week(),
}
}
return rows_
})
}
}
watch(
() => props.date,
async () => {
if (tbodyRef.value?.contains(document.activeElement)) {
await nextTick()
currentCellRef.value?.focus()
}
buildPickerTable({ row: 6, column: 7 }, rows_, {
startDate: minDate,
columnIndexOffset: showWeekNumber ? 1 : 0,
nextEndDate:
rangeState.endDate ||
maxDate ||
(rangeState.selecting && minDate) ||
null,
now: dayjs().locale(unref(lang)).startOf(dateUnit),
unit: dateUnit,
relativeDateGetter: (idx: number) =>
startDate.value.add(idx - offset, dateUnit),
setCellMetadata: (...args) => {
if (setCellMetadata(...args, count)) {
count += 1
}
)
},
const focus = async () => {
setRowMetadata,
})
return rows_
})
watch(
() => props.date,
async () => {
if (tbodyRef.value?.contains(document.activeElement)) {
await nextTick()
currentCellRef.value?.focus()
}
}
)
const isCurrent = (cell): boolean => {
return (
props.selectionMode === 'date' &&
(cell.type === 'normal' || cell.type === 'today') &&
cellMatchesDate(cell, props.parsedValue)
)
const focus = async () => {
currentCellRef.value?.focus()
}
const isNormalDay = (type = '') => {
return ['normal', 'today'].includes(type)
}
const isCurrent = (cell: DateCell): boolean => {
return (
props.selectionMode === 'date' &&
isNormalDay(cell.type) &&
cellMatchesDate(cell, props.parsedValue as Dayjs)
)
}
const cellMatchesDate = (cell: DateCell, date: Dayjs) => {
if (!date) return false
return dayjs(date)
.locale(lang.value)
.isSame(props.date.date(Number(cell.text)), 'day')
}
const getCellClasses = (cell: DateCell) => {
const classes: string[] = []
if (isNormalDay(cell.type) && !cell.disabled) {
classes.push('available')
if (cell.type === 'today') {
classes.push('today')
}
} else {
classes.push(cell.type!)
}
if (isCurrent(cell)) {
classes.push('current')
}
if (
cell.inRange &&
(isNormalDay(cell.type) || props.selectionMode === 'week')
) {
classes.push('in-range')
if (cell.start) {
classes.push('start-date')
}
const cellMatchesDate = (cell, date) => {
if (!date) return false
return dayjs(date)
.locale(lang.value)
.isSame(props.date.date(Number(cell.text)), 'day')
if (cell.end) {
classes.push('end-date')
}
}
const getCellClasses = (cell) => {
const classes: string[] = []
if ((cell.type === 'normal' || cell.type === 'today') && !cell.disabled) {
classes.push('available')
if (cell.type === 'today') {
classes.push('today')
}
if (cell.disabled) {
classes.push('disabled')
}
if (cell.selected) {
classes.push('selected')
}
if (cell.customClass) {
classes.push(cell.customClass)
}
return classes.join(' ')
}
const getDateOfCell = (row: number, column: number) => {
const offsetFromStart =
row * 7 + (column - (props.showWeekNumber ? 1 : 0)) - offsetDay.value
return startDate.value.add(offsetFromStart, 'day')
}
const handleMouseMove = (event: MouseEvent) => {
if (!props.rangeState.selecting) return
let target = event.target as HTMLElement
if (target.tagName === 'SPAN') {
target = target.parentNode?.parentNode as HTMLElement
}
if (target.tagName === 'DIV') {
target = target.parentNode as HTMLElement
}
if (target.tagName !== 'TD') return
const row = (target.parentNode as HTMLTableRowElement).rowIndex - 1
const column = (target as HTMLTableCellElement).cellIndex
// can not select disabled date
if (rows.value[row][column].disabled) return
// only update rangeState when mouse moves to a new cell
// this avoids frequent Date object creation and improves performance
if (row !== lastRow.value || column !== lastColumn.value) {
lastRow.value = row
lastColumn.value = column
emit('changerange', {
selecting: true,
endDate: getDateOfCell(row, column),
})
}
}
const isSelectedCell = (cell: DateCell) => {
return (
(!hasCurrent.value && cell?.text === 1 && cell.type === 'normal') ||
cell.isCurrent
)
}
const handleFocus = (event: Event) => {
if (!hasCurrent.value && props.selectionMode === 'date') {
handlePickDate(event, true)
}
}
const handlePickDate = (event: Event, isKeyboardMovement = false) => {
const target = (event.target as HTMLElement).closest('td')
if (!target || target.tagName !== 'TD') return
const row = (target.parentNode as HTMLTableRowElement).rowIndex - 1
const column = (target as HTMLTableCellElement).cellIndex
const cell = rows.value[row][column]
if (cell.disabled || cell.type === 'week') return
const newDate = getDateOfCell(row, column)
if (props.selectionMode === 'range') {
if (!props.rangeState.selecting || !props.minDate) {
emit('pick', { minDate: newDate, maxDate: null })
emit('select', true)
} else {
if (newDate >= props.minDate) {
emit('pick', { minDate: props.minDate, maxDate: newDate })
} else {
classes.push(cell.type)
emit('pick', { minDate: newDate, maxDate: props.minDate })
}
if (isCurrent(cell)) {
classes.push('current')
}
if (
cell.inRange &&
(cell.type === 'normal' ||
cell.type === 'today' ||
props.selectionMode === 'week')
) {
classes.push('in-range')
if (cell.start) {
classes.push('start-date')
}
if (cell.end) {
classes.push('end-date')
}
}
if (cell.disabled) {
classes.push('disabled')
}
if (cell.selected) {
classes.push('selected')
}
if (cell.customClass) {
classes.push(cell.customClass)
}
return classes.join(' ')
emit('select', false)
}
} else if (props.selectionMode === 'date') {
emit('pick', newDate, isKeyboardMovement)
} else if (props.selectionMode === 'week') {
const weekNumber = newDate.week()
const value = `${newDate.year()}w${weekNumber}`
emit('pick', {
year: newDate.year(),
week: weekNumber,
value,
date: newDate.startOf('week'),
})
} else if (props.selectionMode === 'dates') {
const newValue = cell.selected
? castArray(props.parsedValue).filter(
(d) => d?.valueOf() !== newDate.valueOf()
)
: castArray(props.parsedValue).concat([newDate])
emit('pick', newValue)
}
}
const getDateOfCell = (row, column) => {
const offsetFromStart =
row * 7 + (column - (props.showWeekNumber ? 1 : 0)) - offsetDay.value
return startDate.value.add(offsetFromStart, 'day')
}
const isWeekActive = (cell: DateCell) => {
if (props.selectionMode !== 'week') return false
let newDate = props.date.startOf('day')
const handleMouseMove = (event) => {
if (!props.rangeState.selecting) return
if (cell.type === 'prev-month') {
newDate = newDate.subtract(1, 'month')
}
let target = event.target
if (target.tagName === 'SPAN') {
target = target.parentNode.parentNode
}
if (target.tagName === 'DIV') {
target = target.parentNode
}
if (target.tagName !== 'TD') return
if (cell.type === 'next-month') {
newDate = newDate.add(1, 'month')
}
const row = target.parentNode.rowIndex - 1
const column = target.cellIndex
newDate = newDate.date(Number.parseInt(cell.text as any, 10))
// can not select disabled date
if (rows.value[row][column].disabled) return
if (props.parsedValue && !Array.isArray(props.parsedValue)) {
const dayOffset = ((props.parsedValue.day() - firstDayOfWeek + 7) % 7) - 1
const weekDate = props.parsedValue.subtract(dayOffset, 'day')
return weekDate.isSame(newDate, 'day')
}
return false
}
// only update rangeState when mouse moves to a new cell
// this avoids frequent Date object creation and improves performance
if (row !== lastRow.value || column !== lastColumn.value) {
lastRow.value = row
lastColumn.value = column
ctx.emit('changerange', {
selecting: true,
endDate: getDateOfCell(row, column),
})
}
}
const isSelectedCell = (cell: DateCell) => {
return (
(!hasCurrent.value && cell?.text === 1 && cell.type === 'normal') ||
cell.isCurrent
)
}
const handleFocus = (event: Event) => {
if (!hasCurrent.value && props.selectionMode === 'date') {
handlePickDate(event, true)
}
}
const handlePickDate = (event: Event, isKeyboardMovement = false) => {
let target = event.target as HTMLElement
target = target?.closest('td')
if (!target || target.tagName !== 'TD') return
const row = (target.parentNode as HTMLTableRowElement).rowIndex - 1
const column = (target as HTMLTableCellElement).cellIndex
const cell = rows.value[row][column]
if (cell.disabled || cell.type === 'week') return
const newDate = getDateOfCell(row, column)
if (props.selectionMode === 'range') {
if (!props.rangeState.selecting || !props.minDate) {
ctx.emit('pick', { minDate: newDate, maxDate: null })
ctx.emit('select', true)
} else {
if (newDate >= props.minDate) {
ctx.emit('pick', { minDate: props.minDate, maxDate: newDate })
} else {
ctx.emit('pick', { minDate: newDate, maxDate: props.minDate })
}
ctx.emit('select', false)
}
} else if (props.selectionMode === 'date') {
ctx.emit('pick', newDate, isKeyboardMovement)
} else if (props.selectionMode === 'week') {
const weekNumber = newDate.week()
const value = `${newDate.year()}w${weekNumber}`
ctx.emit('pick', {
year: newDate.year(),
week: weekNumber,
value,
date: newDate.startOf('week'),
})
} else if (props.selectionMode === 'dates') {
const newValue = cell.selected
? castArray(props.parsedValue).filter(
(_) => _.valueOf() !== newDate.valueOf()
)
: castArray(props.parsedValue).concat([newDate])
ctx.emit('pick', newValue)
}
}
const isWeekActive = (cell) => {
if (props.selectionMode !== 'week') return false
let newDate = props.date.startOf('day')
if (cell.type === 'prev-month') {
newDate = newDate.subtract(1, 'month')
}
if (cell.type === 'next-month') {
newDate = newDate.add(1, 'month')
}
newDate = newDate.date(Number.parseInt(cell.text, 10))
if (props.parsedValue && !Array.isArray(props.parsedValue)) {
const dayOffset =
((props.parsedValue.day() - firstDayOfWeek + 7) % 7) - 1
const weekDate = props.parsedValue.subtract(dayOffset, 'day')
return weekDate.isSame(newDate, 'day')
}
return false
}
return {
ns,
tbodyRef,
currentCellRef,
handleMouseMove,
t,
hasCurrent,
rows,
isSelectedCell,
isWeekActive,
getCellClasses,
WEEKS,
handleFocus,
handlePickDate,
focus,
}
},
defineExpose({
/**
* @description focus on current cell
*/
focus,
})
</script>

View File

@@ -79,6 +79,7 @@ const lastRow = ref<number>()
const lastColumn = ref<number>()
const rows = computed<MonthCell[][]>(() => {
const rows = tableRows.value
const now = dayjs().locale(lang.value).startOf('month')
for (let i = 0; i < 3; i++) {

View File

@@ -1,6 +1,8 @@
import { default as dayjs, isDayjs } from 'dayjs'
import { isArray } from '@element-plus/utils'
import type { Dayjs } from 'dayjs'
import type { DateCell } from './date-picker.type'
type DayRange = [Dayjs | undefined, Dayjs | undefined]
@@ -40,3 +42,91 @@ export const getDefaultValue = (
start = start.locale(lang)
return [start, start.add(1, unit)]
}
type Dimension = {
row: number
column: number
}
type BuildPickerTableMetadata = {
startDate?: Dayjs | null
unit: 'month' | 'day'
columnIndexOffset: number
now: Dayjs
nextEndDate: Dayjs | null
relativeDateGetter: (index: number) => Dayjs
setCellMetadata?: (
cell: DateCell,
dimension: { rowIndex: number; columnIndex: number }
) => void
setRowMetadata?: (row: DateCell[]) => void
}
export const buildPickerTable = (
dimension: Dimension,
rows: DateCell[][],
{
columnIndexOffset,
startDate,
nextEndDate,
now,
unit,
relativeDateGetter,
setCellMetadata,
setRowMetadata,
}: BuildPickerTableMetadata
) => {
for (let rowIndex = 0; rowIndex < dimension.row; rowIndex++) {
const row = rows[rowIndex]
for (let columnIndex = 0; columnIndex < dimension.column; columnIndex++) {
let cell = row[columnIndex + columnIndexOffset]
if (!cell) {
cell = {
row: rowIndex,
column: columnIndex,
type: 'normal',
inRange: false,
start: false,
end: false,
}
}
const index = rowIndex * dimension.column + columnIndex
const nextStartDate = relativeDateGetter(index)
cell.dayjs = nextStartDate
cell.date = nextStartDate.toDate()
cell.timestamp = nextStartDate.valueOf()
cell.type = 'normal'
cell.inRange =
!!(
startDate &&
nextStartDate.isSameOrAfter(startDate, unit) &&
nextEndDate &&
nextStartDate.isSameOrBefore(nextEndDate, unit)
) ||
!!(
startDate &&
nextStartDate.isSameOrBefore(startDate, unit) &&
nextEndDate &&
nextStartDate.isSameOrAfter(nextEndDate, unit)
)
if (startDate?.isSameOrAfter(nextEndDate)) {
cell.start = !!nextEndDate && nextStartDate.isSame(nextEndDate, unit)
cell.end = startDate && nextStartDate.isSame(startDate, unit)
} else {
cell.start = !!startDate && nextStartDate.isSame(startDate, unit)
cell.end = !!nextEndDate && nextStartDate.isSame(nextEndDate, unit)
}
const isToday = nextStartDate.isSame(now, unit)
if (isToday) {
cell.type = 'today'
}
setCellMetadata?.(cell, { rowIndex, columnIndex })
row[columnIndex + columnIndexOffset] = cell
}
setRowMetadata?.(row)
}
}