@vtex/admin-ui
Version:
> VTEX admin component library
105 lines (88 loc) • 2.87 kB
text/typescript
import { useRef, useEffect, useCallback } from 'react'
import { Clickable } from 'reakit/Clickable'
import { createComponent, useElement } from '@vtex/admin-ui-react'
import { callAllHandlers, ensureFocus } from '@vtex/admin-ui-util'
import { useForkRef } from '@vtex/admin-ui-hooks'
import { isSameDay } from 'date-fns'
import { useDateFormatter } from '../i18n'
import * as style from './calendar.style'
import type { CalendarStateReturn } from './calendar-state'
export const CalendarCellButton = createComponent<
typeof Clickable,
CalendarCellButtonOptions
>((props) => {
const {
date,
state: {
isDisabled: isDisabledOption,
month,
isInvalidDateRange,
dateValue,
focusedDate,
isFocused: isFocusedOption,
selectDate,
setFocusedDate,
},
disabled: htmlDisabled,
onClick: htmlOnClick,
onFocus: htmlOnFocus,
ref: htmlRef,
...htmlProps
} = props
const ref = useRef<HTMLElement>(null)
const isCurrentMonth = date.getMonth() === month
const isDisabled =
isDisabledOption || !isCurrentMonth || isInvalidDateRange(date)
const disabled = htmlDisabled || isDisabled
const isSelected = dateValue ? isSameDay(date, dateValue) : false
const isFocused =
isFocusedOption && focusedDate && isSameDay(date, focusedDate)
const isToday = isSameDay(date, new Date())
// Focus the button in the DOM when the state updates.
useEffect(() => {
if (isFocused && ref.current) {
ensureFocus(ref.current)
}
}, [date, focusedDate, isFocused, ref])
const onClick = useCallback(() => {
if (disabled) return
selectDate(date)
setFocusedDate(date)
}, [date, disabled, selectDate, setFocusedDate])
const onFocus = useCallback(() => {
if (disabled) return
setFocusedDate(date)
}, [date, disabled, setFocusedDate])
const dateFormatter = useDateFormatter({
weekday: 'long',
day: 'numeric',
month: 'long',
year: 'numeric',
})
// aria-label should be localize Day of week, Month, Day and Year without Time.
function getAriaLabel() {
const dateLabel = dateFormatter.format(date)
const isTodayLabel = isToday ? 'Today, ' : ''
const isSelectedLabel = isSelected ? ' selected' : ''
return `${isTodayLabel}${dateLabel}${isSelectedLabel}`
}
return useElement(Clickable, {
baseStyle: style.calendarCellButton,
ref: useForkRef(ref, htmlRef),
children: useDateFormatter({ day: 'numeric' }).format(date),
'aria-label': getAriaLabel(),
disabled,
tabIndex: !disabled
? isSameDay(date, focusedDate ?? new Date())
? 0
: -1
: undefined,
onClick: callAllHandlers(htmlOnClick, onClick),
onFocus: callAllHandlers(htmlOnFocus, onFocus),
...htmlProps,
})
})
export type CalendarCellButtonOptions = {
date: Date
state: CalendarStateReturn
}