Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pickers] Fix toolbar on the new range pickers #6989

Merged
merged 14 commits into from
Dec 5, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(
reduceAnimations,
onMonthChange,
defaultCalendarMonth,
currentDatePosition: currentDatePositionProp,
onCurrentDatePositionChange,
rangePosition: rangePositionProps,
onRangePositionChange,
calendars,
components,
componentsProps,
Expand All @@ -173,16 +173,16 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(
state: 'value',
});

const [currentDatePosition, setCurrentDatePosition] = useControlled<DateRangePosition>({
controlled: currentDatePositionProp,
const [rangePosition, setRangePosition] = useControlled<DateRangePosition>({
controlled: rangePositionProps,
default: 'start',
name: 'DateRangeCalendar',
state: 'currentDatePosition',
state: 'rangePosition',
});

const handleDatePositionChange = useEventCallback((position: DateRangePosition) => {
if (currentDatePosition !== position) {
setCurrentDatePosition(position);
if (rangePosition !== position) {
setRangePosition(position);
}
});

Expand All @@ -196,14 +196,14 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(
newDate,
utils,
range: value,
currentlySelectingRangeEnd: currentDatePosition,
rangePosition,
allowRangeFlip,
});

setCurrentDatePosition(nextSelection);
onCurrentDatePositionChange?.(nextSelection);
setRangePosition(nextSelection);
onRangePositionChange?.(nextSelection);

const isFullRangeSelected = currentDatePosition === 'end' && isRangeValid(utils, newRange);
const isFullRangeSelected = rangePosition === 'end' && isRangeValid(utils, newRange);

setValue(newRange);
onChange?.(newRange, isFullRangeSelected ? 'finish' : 'partial');
Expand Down Expand Up @@ -242,18 +242,17 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(
utils,
range: valueDayRange,
newDate: rangeDragDay,
currentlySelectingRangeEnd: currentDatePosition,
rangePosition,
allowRangeFlip: true,
}).newRange;
return newRange[0] !== null && newRange[1] !== null
? [utils.startOfDay(newRange[0]), utils.endOfDay(newRange[1])]
: newRange;
}, [currentDatePosition, rangeDragDay, utils, valueDayRange]);
}, [rangePosition, rangeDragDay, utils, valueDayRange]);

const wrappedShouldDisableDate =
shouldDisableDate &&
((dayToTest: TDate) =>
shouldDisableDate?.(dayToTest, draggingDatePosition || currentDatePosition));
((dayToTest: TDate) => shouldDisableDate?.(dayToTest, draggingDatePosition || rangePosition));

const {
calendarState,
Expand All @@ -276,16 +275,15 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(

const prevValue = React.useRef<DateRange<TDate> | null>(null);
React.useEffect(() => {
const date = currentDatePosition === 'start' ? value[0] : value[1];
const date = rangePosition === 'start' ? value[0] : value[1];
if (!date || !utils.isValid(date)) {
return;
}

const prevDate =
currentDatePosition === 'start' ? prevValue.current?.[0] : prevValue.current?.[1];
const prevDate = rangePosition === 'start' ? prevValue.current?.[0] : prevValue.current?.[1];
prevValue.current = value;

// The current date did not change, this call comes either from a `currentlySelectingRangeEnd` change or a change in the other date.
// The current date did not change, this call comes either from a `rangePosition` change or a change in the other date.
// In both cases, we don't want to change the visible month(s).
if (disableAutoMonthSwitching && prevDate && utils.isEqual(prevDate, date)) {
return;
Expand All @@ -301,14 +299,14 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(
requestedMonthNumber > currentMonthNumber + displayingMonthRange
) {
const newMonth =
currentDatePosition === 'start'
rangePosition === 'start'
? date
: // If need to focus end, scroll to the state when "end" is displaying in the last calendar
utils.addMonths(date, -displayingMonthRange);

changeMonth(newMonth);
}
}, [currentDatePosition, value]); // eslint-disable-line
}, [rangePosition, value]); // eslint-disable-line

const selectNextMonth = React.useCallback(() => {
changeMonth(utils.getNextMonth(calendarState.currentMonth));
Expand Down Expand Up @@ -358,7 +356,7 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar<TDate>(
utils,
range: valueDayRange,
newDate: rangePreviewDay,
currentlySelectingRangeEnd: currentDatePosition,
rangePosition,
});

const handlePreviewDayChange = (newPreviewRequest: TDate) => {
Expand Down Expand Up @@ -516,7 +514,6 @@ DateRangeCalendar.propTypes = {
* @default {}
*/
componentsProps: PropTypes.object,
currentDatePosition: PropTypes.oneOf(['end', 'start']),
/**
* Formats the day of week displayed in the calendar header.
* @param {string} day The day of week provided by the adapter's method `getWeekdays`.
Expand Down Expand Up @@ -594,14 +591,15 @@ DateRangeCalendar.propTypes = {
* @param {PickerSelectionState | undefined} selectionState Indicates if the date range selection is complete.
*/
onChange: PropTypes.func,
onCurrentDatePositionChange: PropTypes.func,
/**
* Callback firing on month change @DateIOType.
* @template TDate
* @param {TDate} month The new month.
* @returns {void|Promise} -
*/
onMonthChange: PropTypes.func,
onRangePositionChange: PropTypes.func,
rangePosition: PropTypes.oneOf(['end', 'start']),
/**
* Make picker read only.
* @default false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
PickersCalendarHeaderSlotsComponent,
PickersCalendarHeaderSlotsComponentsProps,
} from '@mui/x-date-pickers/internals';
import { DateRange, DayRangeValidationProps } from '../internal/models';
import { DateRange, RangePositionProps, DayRangeValidationProps } from '../internal/models';
import { DateRangeCalendarClasses } from './dateRangeCalendarClasses';
import { DateRangePickerDayProps } from '../DateRangePickerDay';

Expand All @@ -39,7 +39,8 @@ export interface DateRangeCalendarSlotsComponentsProps<TDate>
export interface DateRangeCalendarProps<TDate>
extends ExportedDayCalendarProps<TDate>,
BaseDateValidationProps<TDate>,
DayRangeValidationProps<TDate> {
DayRangeValidationProps<TDate>,
Partial<RangePositionProps> {
/**
* The selected value.
* Used when the component is controlled.
Expand Down Expand Up @@ -105,8 +106,6 @@ export interface DateRangeCalendarProps<TDate>
* @returns {void|Promise} -
*/
onMonthChange?: (month: TDate) => void | Promise<void>;
currentDatePosition?: DateRangePosition;
onCurrentDatePositionChange?: (newPosition: DateRangePosition) => void;
/**
* The number of calendars to render.
* @default 2
Expand Down Expand Up @@ -136,6 +135,6 @@ export type ExportedDateRangeCalendarProps<TDate> = Omit<
| 'classes'
| 'components'
| 'componentsProps'
| 'currentDatePosition'
| 'onCurrentDatePositionChange'
| 'rangePosition'
| 'onRangePositionChange'
>;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
onSpaceOrEnter,
useLocaleText,
} from '@mui/x-date-pickers/internals';
import { CurrentlySelectingRangeEndProps, DateRange } from '../internal/models/range';
import { RangePositionProps, DateRange } from '../internal/models/range';
import { DateRangeValidationError } from '../internal/hooks/validation/useDateRangeValidation';
import {
DateRangePickerInputClasses,
Expand Down Expand Up @@ -73,7 +73,7 @@ export interface DateRangePickerInputProps<TDate>
DateInputProps<TDate>,
keyof ExportedDateRangePickerInputProps<TDate> | 'value' | 'validationError'
>,
CurrentlySelectingRangeEndProps {
RangePositionProps {
validationError: DateRangeValidationError;
value: DateRange<TDate>;
classes?: Partial<DateRangePickerInputClasses>;
Expand All @@ -93,7 +93,8 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
): JSX.Element {
const props = useThemeProps({ props: inProps, name: 'MuiDateRangePickerInput' });
const {
currentlySelectingRangeEnd,
rangePosition,
onRangePositionChange,
disableOpenPicker,
onBlur,
onChange,
Expand All @@ -103,7 +104,6 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
value: [start, end],
readOnly,
renderInput,
setCurrentlySelectingRangeEnd,
TextFieldProps,
validationError: [startValidationError, endValidationError],
className,
Expand All @@ -121,12 +121,12 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
return;
}

if (currentlySelectingRangeEnd === 'start') {
if (rangePosition === 'start') {
startRef.current?.focus();
} else if (currentlySelectingRangeEnd === 'end') {
} else if (rangePosition === 'end') {
endRef.current?.focus();
}
}, [currentlySelectingRangeEnd, open]);
}, [rangePosition, open]);

// TODO: rethink this approach. We do not need to wait for calendar to be updated to rerender input (looks like freezing)
// TODO: so simply break 1 react's commit phase in 2 (first for input and second for calendars) by executing onChange in the next tick
Expand All @@ -148,8 +148,8 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
) => {
event.stopPropagation();
if (setCurrentlySelectingRangeEnd) {
setCurrentlySelectingRangeEnd('start');
if (onRangePositionChange) {
onRangePositionChange('start');
}
if (!readOnly && !disableOpenPicker) {
openPicker();
Expand All @@ -160,23 +160,23 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>,
) => {
event.stopPropagation();
if (setCurrentlySelectingRangeEnd) {
setCurrentlySelectingRangeEnd('end');
if (onRangePositionChange) {
onRangePositionChange('end');
}
if (!readOnly && !disableOpenPicker) {
openPicker();
}
};

const focusOnRangeEnd = () => {
if (open && setCurrentlySelectingRangeEnd) {
setCurrentlySelectingRangeEnd('end');
if (open && onRangePositionChange) {
onRangePositionChange('end');
}
};

const focusOnRangeStart = () => {
if (open && setCurrentlySelectingRangeEnd) {
setCurrentlySelectingRangeEnd('start');
if (open && onRangePositionChange) {
onRangePositionChange('start');
}
};
const startInputProps = useMaskedInput({
Expand All @@ -189,7 +189,7 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
TextFieldProps: {
...TextFieldProps,
inputRef: startRef,
focused: open ? currentlySelectingRangeEnd === 'start' : undefined,
focused: open ? rangePosition === 'start' : undefined,
// registering `onClick` listener on the root element as well to correctly handle cases where user is clicking on `label`
// which has `pointer-events: none` and due to DOM structure the `input` does not catch the click event
...(!readOnly && !other.disabled && { onClick: openRangeStartSelection }),
Expand All @@ -212,7 +212,7 @@ export const DateRangePickerInput = React.forwardRef(function DateRangePickerInp
TextFieldProps: {
...TextFieldProps,
inputRef: endRef,
focused: open ? currentlySelectingRangeEnd === 'end' : undefined,
focused: open ? rangePosition === 'end' : undefined,
// registering `onClick` listener on the root element as well to correctly handle cases where user is clicking on `label`
// which has `pointer-events: none` and due to DOM structure the `input` does not catch the click event
...(!readOnly && !other.disabled && { onClick: openRangeEndSelection }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
useLocaleText,
ExportedBaseToolbarProps,
} from '@mui/x-date-pickers/internals';
import { DateRange, CurrentlySelectingRangeEndProps } from '../internal/models';
import { DateRange, RangePositionProps } from '../internal/models';
import {
DateRangePickerToolbarClasses,
getDateRangePickerToolbarUtilityClass,
Expand All @@ -32,7 +32,7 @@ export interface DateRangePickerToolbarProps<TDate>
BaseToolbarProps<DateRange<TDate>, 'day'>,
'views' | 'view' | 'onViewChange' | 'onChange' | 'isLandscape'
>,
CurrentlySelectingRangeEndProps {
RangePositionProps {
classes?: Partial<DateRangePickerToolbarClasses>;
}

Expand Down Expand Up @@ -69,11 +69,11 @@ export const DateRangePickerToolbar = React.forwardRef(function DateRangePickerT
const props = useThemeProps({ props: inProps, name: 'MuiDateRangePickerToolbar' });

const {
currentlySelectingRangeEnd,
value: [start, end],
isMobileKeyboardViewOpen,
setCurrentlySelectingRangeEnd,
toggleMobileKeyboardView,
rangePosition,
onRangePositionChange,
toolbarFormat,
} = props;

Expand Down Expand Up @@ -104,15 +104,15 @@ export const DateRangePickerToolbar = React.forwardRef(function DateRangePickerT
<PickersToolbarButton
variant={start !== null ? 'h5' : 'h6'}
value={startDateValue}
selected={currentlySelectingRangeEnd === 'start'}
onClick={() => setCurrentlySelectingRangeEnd('start')}
selected={rangePosition === 'start'}
onClick={() => onRangePositionChange('start')}
/>
<Typography variant="h5">&nbsp;{'–'}&nbsp;</Typography>
<PickersToolbarButton
variant={end !== null ? 'h5' : 'h6'}
value={endDateValue}
selected={currentlySelectingRangeEnd === 'end'}
onClick={() => setCurrentlySelectingRangeEnd('end')}
selected={rangePosition === 'end'}
onClick={() => onRangePositionChange('end')}
/>
</DateRangePickerToolbarContainer>
</DateRangePickerToolbarRoot>
Expand Down
Loading