Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Calendar): add a button to collapse the month and time view (#2722)
- Loading branch information
Showing
28 changed files
with
769 additions
and
780 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,283 +1,136 @@ | ||
import React, { HTMLAttributes, useCallback, useMemo } from 'react'; | ||
import React, { useCallback } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import pick from 'lodash/pick'; | ||
import MonthDropdown from './MonthDropdown'; | ||
import TimeDropdown from './TimeDropdown'; | ||
import View from './View'; | ||
import Header, { HeaderProps } from './Header'; | ||
import { useClassNames, DateUtils, composeFunctions } from '../utils'; | ||
import { RsRefForwardingComponent, WithAsProps } from '../@types/common'; | ||
import Calendar from './CalendarContainer'; | ||
import { CalendarLocale } from '../locales'; | ||
import { CalendarProvider } from './CalendarContext'; | ||
|
||
export enum CalendarState { | ||
'DROP_TIME' = 'DROP_TIME', | ||
'DROP_MONTH' = 'DROP_MONTH' | ||
} | ||
|
||
export interface CalendarProps | ||
extends WithAsProps, | ||
Omit<HTMLAttributes<HTMLDivElement>, 'onSelect' | 'onChange' | 'onMouseMove'>, | ||
Omit<HeaderProps, 'onMoveForward' | 'onMoveBackward' | 'showDate' | 'showTime' | 'showMonth'> { | ||
/** The status of the calendar display: day, month, time. */ | ||
calendarState?: CalendarState; | ||
|
||
/** The panel render based on date range */ | ||
dateRange?: Date[]; | ||
|
||
/** Disabled date */ | ||
disabledDate?: (date: Date) => boolean; | ||
|
||
/** Disabled hours */ | ||
disabledHours?: (hour: number, date: Date) => boolean; | ||
|
||
/** Disabled minutes */ | ||
disabledMinutes?: (minute: number, date: Date) => boolean; | ||
|
||
/** Hidden seconds */ | ||
disabledSeconds?: (second: number, date: Date) => boolean; | ||
|
||
/** Format str */ | ||
format: string; | ||
|
||
/** Hidden hours */ | ||
hideHours?: (hour: number, date: Date) => boolean; | ||
|
||
/** Hidden minutes */ | ||
hideMinutes?: (minute: number, date: Date) => boolean; | ||
|
||
/** Hidden seconds */ | ||
hideSeconds?: (second: number, date: Date) => boolean; | ||
import Button from '../Button'; | ||
import { FormattedDate } from '../CustomProvider'; | ||
import { useClassNames, useCustom } from '../utils'; | ||
import { RsRefForwardingComponent, WithAsProps } from '../@types/common'; | ||
import useCalendarDate from './useCalendarDate'; | ||
|
||
/** The value that mouse hover on in range selection */ | ||
hoverRangeValue?: [Date, Date]; | ||
export interface CalendarProps extends WithAsProps { | ||
/** Controlled value */ | ||
value?: Date; | ||
|
||
/** Is it in the same month as today */ | ||
inSameMonth?: (date: Date) => boolean; | ||
/** Default value */ | ||
defaultValue?: Date; | ||
|
||
/** ISO 8601 standard, each calendar week begins on Monday and Sunday on the seventh day */ | ||
/** ISO 8601 standard, each calendar week begins on Monday and Sunday on the seventh day */ | ||
isoWeek?: boolean; | ||
|
||
/** Limit showing how many years in the future */ | ||
limitEndYear?: number; | ||
|
||
/** Custom locale */ | ||
locale: CalendarLocale; | ||
|
||
/** Callback after the date has changed */ | ||
onChangePageDate?: (nextPageDate: Date, event: React.MouseEvent) => void; | ||
|
||
/** Callback after the time has changed */ | ||
onChangePageTime?: (nextPageTime: Date, event: React.MouseEvent) => void; | ||
/** Display a compact calendar */ | ||
compact?: boolean; | ||
|
||
/** Callback after mouse enter other date cell */ | ||
onMouseMove?: (date: Date) => void; | ||
/** Show border */ | ||
bordered?: boolean; | ||
|
||
/** Switch to the callback triggered after the previous month. */ | ||
onMoveBackward?: (nextPageDate: Date) => void; | ||
/** Custom locale */ | ||
locale?: CalendarLocale; | ||
|
||
/** Switch to the callback triggered after the next month. */ | ||
onMoveForward?: (nextPageDate: Date) => void; | ||
/** Callback fired before the value changed */ | ||
onChange?: (date: Date) => void; | ||
|
||
/** Callback fired before the date selected */ | ||
onSelect?: (date: Date, event: React.MouseEvent) => void; | ||
|
||
/** Date displayed on the current page */ | ||
calendarDate: Date; | ||
onSelect?: (date: Date) => void; | ||
|
||
/** Custom rendering cell*/ | ||
/** Custom render calendar cells */ | ||
renderCell?: (date: Date) => React.ReactNode; | ||
|
||
/** Whether to show week numbers */ | ||
showWeekNumbers?: boolean; | ||
|
||
inline?: boolean; | ||
} | ||
|
||
const Calendar: RsRefForwardingComponent<'div', CalendarProps> = React.forwardRef( | ||
const CalendarPanel: RsRefForwardingComponent<typeof Calendar, CalendarProps> = React.forwardRef( | ||
(props: CalendarProps, ref) => { | ||
const { | ||
as: Component = 'div', | ||
as: Component = Calendar, | ||
bordered, | ||
className, | ||
classPrefix = 'calendar', | ||
calendarState, | ||
dateRange, | ||
disabledBackward, | ||
disabledDate, | ||
disabledForward, | ||
format, | ||
hoverRangeValue, | ||
inSameMonth, | ||
isoWeek = false, | ||
limitEndYear, | ||
locale, | ||
onChangePageDate, | ||
onChangePageTime, | ||
onMouseMove, | ||
onMoveBackward, | ||
onMoveForward, | ||
compact, | ||
defaultValue = new Date(), | ||
isoWeek, | ||
locale: overrideLocale, | ||
onChange, | ||
onSelect, | ||
onToggleMeridian, | ||
onToggleMonthDropdown, | ||
onToggleTimeDropdown, | ||
calendarDate, | ||
renderCell, | ||
renderTitle, | ||
renderToolbar, | ||
showMeridian, | ||
showWeekNumbers, | ||
inline, | ||
value, | ||
...rest | ||
} = props; | ||
const { withClassPrefix, merge } = useClassNames(classPrefix); | ||
const isDisabledDate = useCallback( | ||
(date: Date) => disabledDate?.(date) ?? false, | ||
[disabledDate] | ||
); | ||
const isTimeDisabled = (date: Date) => DateUtils.disabledTime(props, date); | ||
const handleMoveForward = useCallback(() => { | ||
onMoveForward?.(DateUtils.addMonths(calendarDate, 1)); | ||
}, [onMoveForward, calendarDate]); | ||
|
||
const handleMoveBackward = useCallback(() => { | ||
onMoveBackward?.(DateUtils.addMonths(calendarDate, -1)); | ||
}, [onMoveBackward, calendarDate]); | ||
const { calendarDate, setCalendarDate } = useCalendarDate(value, defaultValue); | ||
const { locale } = useCustom('Calendar', overrideLocale); | ||
|
||
const showDate = DateUtils.shouldDate(format); | ||
const showTime = DateUtils.shouldTime(format); | ||
const showMonth = DateUtils.shouldMonth(format); | ||
const handleChange = useCallback( | ||
(nextValue: Date) => { | ||
setCalendarDate(nextValue); | ||
onChange?.(nextValue); | ||
}, | ||
[setCalendarDate, onChange] | ||
); | ||
|
||
const onlyShowTime = showTime && !showDate && !showMonth; | ||
const onlyShowMonth = showMonth && !showDate && !showTime; | ||
const dropTime = calendarState === CalendarState.DROP_TIME || onlyShowTime; | ||
const dropMonth = calendarState === CalendarState.DROP_MONTH || onlyShowMonth; | ||
const handleClickToday = useCallback(() => { | ||
handleChange(new Date()); | ||
}, [handleChange]); | ||
|
||
const inSameThisMonthDate = useCallback( | ||
(date: Date) => | ||
composeFunctions( | ||
d => DateUtils.setDate(d, 1), | ||
d => DateUtils.isSameMonth(d, date) | ||
)(date), | ||
[] | ||
const handleSelect = useCallback( | ||
(nextValue: Date) => { | ||
onSelect?.(nextValue); | ||
handleChange(nextValue); | ||
}, | ||
[handleChange, onSelect] | ||
); | ||
|
||
const calendarClasses = merge( | ||
className, | ||
withClassPrefix({ | ||
'show-time-dropdown': dropTime, | ||
'show-month-dropdown': dropMonth, | ||
'show-week-numbers': showWeekNumbers | ||
}) | ||
); | ||
const timeDropdownProps = pick(rest, DateUtils.calendarOnlyProps); | ||
const contextValue = useMemo( | ||
() => ({ | ||
date: calendarDate, | ||
dateRange, | ||
disabledDate: isDisabledDate, | ||
format, | ||
hoverRangeValue, | ||
inSameMonth: inSameMonth ?? inSameThisMonthDate, | ||
isoWeek, | ||
locale, | ||
onChangePageDate, | ||
onChangePageTime, | ||
onMouseMove, | ||
onSelect, | ||
renderCell, | ||
showWeekNumbers, | ||
inline | ||
}), | ||
[ | ||
calendarDate, | ||
dateRange, | ||
format, | ||
hoverRangeValue, | ||
inSameMonth, | ||
inSameThisMonthDate, | ||
inline, | ||
isDisabledDate, | ||
isoWeek, | ||
locale, | ||
onChangePageDate, | ||
onChangePageTime, | ||
onMouseMove, | ||
onSelect, | ||
renderCell, | ||
showWeekNumbers | ||
] | ||
const { prefix, merge, withClassPrefix } = useClassNames(classPrefix); | ||
|
||
const renderToolbar = useCallback( | ||
() => ( | ||
<Button className={prefix('btn-today')} size="sm" onClick={handleClickToday}> | ||
{locale.today || 'Today'} | ||
</Button> | ||
), | ||
[handleClickToday, locale.today, prefix] | ||
); | ||
|
||
const customRenderCell = useCallback((date: Date) => renderCell?.(date), [renderCell]); | ||
|
||
const classes = merge(className, withClassPrefix('panel', { bordered, compact })); | ||
|
||
return ( | ||
<CalendarProvider value={contextValue}> | ||
<Component | ||
{...DateUtils.omitHideDisabledProps<Partial<CalendarProps>>(rest)} | ||
className={calendarClasses} | ||
ref={ref} | ||
> | ||
<Header | ||
showMonth={showMonth} | ||
showDate={showDate} | ||
showTime={showTime} | ||
showMeridian={showMeridian} | ||
disabledTime={isTimeDisabled} | ||
onMoveForward={handleMoveForward} | ||
onMoveBackward={handleMoveBackward} | ||
onToggleMonthDropdown={onToggleMonthDropdown} | ||
onToggleTimeDropdown={onToggleTimeDropdown} | ||
onToggleMeridian={onToggleMeridian} | ||
renderTitle={renderTitle} | ||
renderToolbar={renderToolbar} | ||
disabledBackward={disabledBackward} | ||
disabledForward={disabledForward} | ||
/> | ||
{showDate && <View />} | ||
{showMonth && ( | ||
<MonthDropdown | ||
show={dropMonth} | ||
limitEndYear={limitEndYear} | ||
disabledMonth={isDisabledDate} | ||
/> | ||
)} | ||
{showTime && ( | ||
<TimeDropdown {...timeDropdownProps} show={dropTime} showMeridian={showMeridian} /> | ||
)} | ||
</Component> | ||
</CalendarProvider> | ||
<Component | ||
{...rest} | ||
inline | ||
className={classes} | ||
ref={ref} | ||
isoWeek={isoWeek} | ||
format="yyyy-MM-dd" | ||
calendarDate={calendarDate} | ||
limitEndYear={1000} | ||
locale={locale} | ||
renderTitle={date => ( | ||
<FormattedDate date={date} formatStr={locale.formattedMonthPattern || 'MMMM yyyy'} /> | ||
)} | ||
renderToolbar={renderToolbar} | ||
renderCell={customRenderCell} | ||
onMoveForward={handleChange} | ||
onMoveBackward={handleChange} | ||
onChangePageDate={handleChange} | ||
onSelect={handleSelect} | ||
/> | ||
); | ||
} | ||
); | ||
|
||
Calendar.displayName = 'Calendar'; | ||
Calendar.propTypes = { | ||
calendarState: PropTypes.oneOf(Object.values(CalendarState)), | ||
className: PropTypes.string, | ||
classPrefix: PropTypes.string, | ||
disabledDate: PropTypes.func, | ||
disabledHours: PropTypes.func, | ||
disabledMinutes: PropTypes.func, | ||
disabledSeconds: PropTypes.func, | ||
format: PropTypes.string, | ||
hideHours: PropTypes.func, | ||
hideMinutes: PropTypes.func, | ||
hideSeconds: PropTypes.func, | ||
inSameMonth: PropTypes.func, | ||
CalendarPanel.displayName = 'CalendarPanel'; | ||
CalendarPanel.propTypes = { | ||
value: PropTypes.instanceOf(Date), | ||
defaultValue: PropTypes.instanceOf(Date), | ||
isoWeek: PropTypes.bool, | ||
limitEndYear: PropTypes.number, | ||
compact: PropTypes.bool, | ||
bordered: PropTypes.bool, | ||
locale: PropTypes.object, | ||
onChangePageDate: PropTypes.func, | ||
onChangePageTime: PropTypes.func, | ||
onMoveBackward: PropTypes.func, | ||
onMoveForward: PropTypes.func, | ||
className: PropTypes.string, | ||
classPrefix: PropTypes.string, | ||
onChange: PropTypes.func, | ||
onSelect: PropTypes.func, | ||
onToggleMeridian: PropTypes.func, | ||
onToggleMonthDropdown: PropTypes.func, | ||
onToggleTimeDropdown: PropTypes.func, | ||
calendarDate: PropTypes.instanceOf(Date), | ||
renderCell: PropTypes.func, | ||
renderTitle: PropTypes.func, | ||
renderToolbar: PropTypes.func, | ||
showMeridian: PropTypes.bool, | ||
showWeekNumbers: PropTypes.bool | ||
renderCell: PropTypes.func | ||
}; | ||
|
||
export default Calendar; | ||
export default CalendarPanel; |
Oops, something went wrong.
b49bfaf
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
rsuite-nextjs – ./docs
rsuite-nextjs-git-main-rsuite.vercel.app
rsuite.vercel.app
next.rsuitejs.com
rsuitejs.com
rsuite-nextjs-rsuite.vercel.app