From 1e92a72b61db8ff596a68ddf51c1997269251e56 Mon Sep 17 00:00:00 2001 From: flavien Date: Fri, 24 Jan 2025 08:07:30 +0100 Subject: [PATCH] [pickers] Use context to remove props on --- .../date-picker/examplesConfig.styling.tsx | 2 +- .../pages/x/api/date-pickers/date-picker.json | 2 +- .../x/api/date-pickers/date-range-picker.json | 2 +- .../x/api/date-pickers/date-time-picker.json | 2 +- .../date-pickers/date-time-range-picker.json | 2 +- .../api/date-pickers/desktop-date-picker.json | 2 +- .../desktop-date-range-picker.json | 2 +- .../desktop-date-time-picker.json | 2 +- .../desktop-date-time-range-picker.json | 2 +- .../api/date-pickers/desktop-time-picker.json | 2 +- .../pages/x/api/date-pickers/time-picker.json | 2 +- .../DateRangeCalendar/DateRangeCalendar.tsx | 6 +- .../useDesktopRangePicker.tsx | 13 +-- .../useDesktopRangePicker.types.ts | 8 +- .../src/DateCalendar/DateCalendar.tsx | 6 +- .../PickerPopper.tsx} | 96 +++++++++---------- .../components/PickerPopper/index.ts | 5 + .../PickerPopper/pickerPopperClasses.ts | 17 ++++ .../internals/components/PickerProvider.tsx | 12 ++- .../components/pickersPopperClasses.ts | 19 ---- .../useDesktopPicker/useDesktopPicker.tsx | 17 +--- .../useDesktopPicker.types.ts | 9 +- .../src/internals/hooks/usePicker/index.ts | 7 +- .../internals/hooks/usePicker/usePicker.ts | 5 +- .../hooks/usePicker/usePicker.types.ts | 7 +- .../hooks/usePicker/usePickerProvider.ts | 22 ++++- .../hooks/usePicker/usePickerViews.tsx | 28 ++++-- ...ceAnimations.ts => useReduceAnimations.ts} | 9 +- .../x-date-pickers/src/internals/index.ts | 15 ++- .../src/themeAugmentation/components.d.ts | 6 +- .../src/themeAugmentation/overrides.d.ts | 4 +- .../src/themeAugmentation/props.d.ts | 4 +- .../themeAugmentation.spec.ts | 10 +- 33 files changed, 185 insertions(+), 162 deletions(-) rename packages/x-date-pickers/src/internals/components/{PickersPopper.tsx => PickerPopper/PickerPopper.tsx} (85%) create mode 100644 packages/x-date-pickers/src/internals/components/PickerPopper/index.ts create mode 100644 packages/x-date-pickers/src/internals/components/PickerPopper/pickerPopperClasses.ts delete mode 100644 packages/x-date-pickers/src/internals/components/pickersPopperClasses.ts rename packages/x-date-pickers/src/internals/hooks/{useDefaultReduceAnimations.ts => useReduceAnimations.ts} (83%) diff --git a/docs/data/date-pickers/date-picker/examplesConfig.styling.tsx b/docs/data/date-pickers/date-picker/examplesConfig.styling.tsx index 0e1a6e4e8c4e1..0dcfa75edfdf0 100644 --- a/docs/data/date-pickers/date-picker/examplesConfig.styling.tsx +++ b/docs/data/date-pickers/date-picker/examplesConfig.styling.tsx @@ -272,7 +272,7 @@ export const datePickerExamples: PickersSubcomponentType = { slots: ['root'], moreInformation: , }, - PickersPopper: { + PickerPopper: { examples: { customTheme: { type: 'success', diff --git a/docs/pages/x/api/date-pickers/date-picker.json b/docs/pages/x/api/date-pickers/date-picker.json index e27298dacae1d..6675d1b94e4f3 100644 --- a/docs/pages/x/api/date-pickers/date-picker.json +++ b/docs/pages/x/api/date-pickers/date-picker.json @@ -232,7 +232,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/date-range-picker.json b/docs/pages/x/api/date-pickers/date-range-picker.json index 9ee6734f43842..2c73f0d253d68 100644 --- a/docs/pages/x/api/date-pickers/date-range-picker.json +++ b/docs/pages/x/api/date-pickers/date-range-picker.json @@ -194,7 +194,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/date-time-picker.json b/docs/pages/x/api/date-pickers/date-time-picker.json index 4f43b0337b63c..55f19a6faa1a4 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker.json +++ b/docs/pages/x/api/date-pickers/date-time-picker.json @@ -257,7 +257,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/date-time-range-picker.json b/docs/pages/x/api/date-pickers/date-time-range-picker.json index 7487f6b6418ea..3031cef327313 100644 --- a/docs/pages/x/api/date-pickers/date-time-range-picker.json +++ b/docs/pages/x/api/date-pickers/date-time-range-picker.json @@ -242,7 +242,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/desktop-date-picker.json b/docs/pages/x/api/date-pickers/desktop-date-picker.json index 22c0208dbcf29..e63dcf9485c05 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-picker.json @@ -225,7 +225,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/desktop-date-range-picker.json b/docs/pages/x/api/date-pickers/desktop-date-range-picker.json index 76ac2bcbf93d6..a8d2718e436a5 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-range-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-range-picker.json @@ -187,7 +187,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json index 2b4133cb7a1ba..3f138c31c104f 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json @@ -253,7 +253,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json b/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json index 0dfa110d2cff3..544fbea695dc6 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-time-range-picker.json @@ -238,7 +238,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/desktop-time-picker.json b/docs/pages/x/api/date-pickers/desktop-time-picker.json index 828c2fe408438..3007fb1b8b7a0 100644 --- a/docs/pages/x/api/date-pickers/desktop-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-time-picker.json @@ -167,7 +167,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/docs/pages/x/api/date-pickers/time-picker.json b/docs/pages/x/api/date-pickers/time-picker.json index af9663e29bc31..4b95d5c0450be 100644 --- a/docs/pages/x/api/date-pickers/time-picker.json +++ b/docs/pages/x/api/date-pickers/time-picker.json @@ -171,7 +171,7 @@ { "name": "desktopPaper", "description": "Custom component for the paper rendered inside the desktop picker's Popper.", - "default": "PickersPopperPaper", + "default": "PickerPopperPaper", "class": null }, { diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx index 5377d307f330f..def8246aa717b 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx @@ -16,7 +16,7 @@ import { DayCalendar, DayCalendarSlots, DayCalendarSlotProps, - useDefaultReduceAnimations, + useReduceAnimations, useCalendarState, useDefaultDates, useUtils, @@ -113,17 +113,17 @@ function useDateRangeCalendarDefaultizedProps( ): DateRangeCalendarDefaultizedProps { const utils = useUtils(); const defaultDates = useDefaultDates(); - const defaultReduceAnimations = useDefaultReduceAnimations(); const themeProps = useThemeProps({ props, name, }); + const reduceAnimations = useReduceAnimations(themeProps.reduceAnimations); return { ...themeProps, renderLoading: themeProps.renderLoading ?? (() => ...), - reduceAnimations: themeProps.reduceAnimations ?? defaultReduceAnimations, + reduceAnimations, loading: props.loading ?? false, disablePast: props.disablePast ?? false, disableFuture: props.disableFuture ?? false, diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx index 1b6a0c70a0802..49504376f9bd6 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.tsx @@ -6,7 +6,7 @@ import { executeInTheNextEventLoopTick, getActiveElement, usePicker, - PickersPopper, + PickerPopper, DateOrTimeViewWithMeridiem, PickerProvider, PickerValue, @@ -55,7 +55,6 @@ export const useDesktopRangePicker = < autoFocus, disableOpenPicker, localeText, - reduceAnimations, } = props; const fieldContainerRef = React.useRef(null); @@ -80,7 +79,7 @@ export const useDesktopRangePicker = < fieldRef = endFieldRef; } - const { providerProps, renderCurrentView, shouldRestoreFocus, ownerState } = usePicker< + const { providerProps, renderCurrentView, ownerState } = usePicker< PickerRangeValue, TView, TExternalProps @@ -185,21 +184,17 @@ export const useDesktopRangePicker = < - {renderCurrentView()} - + diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts index 06494f9d27b4c..02026727ba11b 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts @@ -1,6 +1,6 @@ import { - PickersPopperSlots, - PickersPopperSlotProps, + PickerPopperSlots, + PickerPopperSlotProps, UsePickerViewsProps, DateOrTimeViewWithMeridiem, } from '@mui/x-date-pickers/internals'; @@ -12,11 +12,11 @@ import { UseRangePickerSlots, } from '../models/useRangePicker'; -export interface UseDesktopRangePickerSlots extends UseRangePickerSlots, PickersPopperSlots {} +export interface UseDesktopRangePickerSlots extends UseRangePickerSlots, PickerPopperSlots {} export interface UseDesktopRangePickerSlotProps extends UseRangePickerSlotProps, - PickersPopperSlotProps {} + PickerPopperSlotProps {} export interface DesktopRangeOnlyPickerProps extends RangeOnlyPickerProps { /** diff --git a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx index beba17c77429a..4445ec018cb4f 100644 --- a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx @@ -24,7 +24,7 @@ import { mergeDateAndTime, } from '../internals/utils/date-utils'; import { PickerViewRoot } from '../internals/components/PickerViewRoot'; -import { useDefaultReduceAnimations } from '../internals/hooks/useDefaultReduceAnimations'; +import { useReduceAnimations } from '../internals/hooks/useReduceAnimations'; import { DateCalendarClasses, getDateCalendarUtilityClass } from './dateCalendarClasses'; import { BaseDateValidationProps } from '../internals/models/validation'; import { useControlledValueWithTimezone } from '../internals/hooks/useValueWithTimezone'; @@ -48,11 +48,11 @@ function useDateCalendarDefaultizedProps( ): DateCalendarDefaultizedProps { const utils = useUtils(); const defaultDates = useDefaultDates(); - const defaultReduceAnimations = useDefaultReduceAnimations(); const themeProps = useThemeProps({ props, name, }); + const reduceAnimations = useReduceAnimations(themeProps.reduceAnimations); return { ...themeProps, @@ -61,7 +61,7 @@ function useDateCalendarDefaultizedProps( disableFuture: themeProps.disableFuture ?? false, openTo: themeProps.openTo ?? 'day', views: themeProps.views ?? ['year', 'day'], - reduceAnimations: themeProps.reduceAnimations ?? defaultReduceAnimations, + reduceAnimations, renderLoading: themeProps.renderLoading ?? (() => ...), minDate: applyDefaultDate(utils, themeProps.minDate, defaultDates.minDate), diff --git a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx b/packages/x-date-pickers/src/internals/components/PickerPopper/PickerPopper.tsx similarity index 85% rename from packages/x-date-pickers/src/internals/components/PickersPopper.tsx rename to packages/x-date-pickers/src/internals/components/PickerPopper/PickerPopper.tsx index ae5560440a096..91b2fe57ef5cd 100644 --- a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx +++ b/packages/x-date-pickers/src/internals/components/PickerPopper/PickerPopper.tsx @@ -20,21 +20,20 @@ import { import { styled, useThemeProps } from '@mui/material/styles'; import { TransitionProps as MuiTransitionProps } from '@mui/material/transitions'; import { SlotComponentPropsFromProps } from '@mui/x-internals/types'; -import { getPickersPopperUtilityClass, PickersPopperClasses } from './pickersPopperClasses'; -import { getActiveElement } from '../utils/utils'; -import { useDefaultReduceAnimations } from '../hooks/useDefaultReduceAnimations'; -import { usePickerPrivateContext } from '../hooks/usePickerPrivateContext'; -import { PickerOwnerState } from '../../models'; -import { usePickerContext } from '../../hooks'; +import { getPickerPopperUtilityClass, PickerPopperClasses } from './pickerPopperClasses'; +import { getActiveElement } from '../../utils/utils'; +import { usePickerPrivateContext } from '../../hooks/usePickerPrivateContext'; +import { PickerOwnerState } from '../../../models'; +import { usePickerContext } from '../../../hooks'; interface PickerPopperOwnerState extends PickerOwnerState { popperPlacement: PopperPlacementType; } -export interface PickersPopperSlots { +export interface PickerPopperSlots { /** * Custom component for the paper rendered inside the desktop picker's Popper. - * @default PickersPopperPaper + * @default PickerPopperPaper */ desktopPaper?: React.JSXElementConstructor; /** @@ -54,7 +53,7 @@ export interface PickersPopperSlots { popper?: React.ElementType; } -export interface PickersPopperSlotProps { +export interface PickerPopperSlotProps { /** * Props passed down to the desktop [Paper](https://mui.com/material-ui/api/paper/) component. */ @@ -73,45 +72,46 @@ export interface PickersPopperSlotProps { popper?: SlotComponentPropsFromProps; } -export interface PickerPopperProps { - role: 'tooltip' | 'dialog'; - anchorEl: MuiPopperProps['anchorEl']; +export interface ExportedPickerPopperProps { + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; /** - * @default "bottom" + * @default "bottom-start" */ + // Never used in the codebase, only here for theme augmentation. placement?: MuiPopperProps['placement']; +} + +export interface PickerPopperProps extends ExportedPickerPopperProps { + role: 'tooltip' | 'dialog'; containerRef?: React.Ref; children?: React.ReactNode; onBlur?: () => void; - slots?: PickersPopperSlots; - slotProps?: PickersPopperSlotProps; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - shouldRestoreFocus?: () => boolean; - reduceAnimations?: boolean; + slots?: PickerPopperSlots; + slotProps?: PickerPopperSlotProps; } -const useUtilityClasses = (classes: Partial | undefined) => { +const useUtilityClasses = (classes: Partial | undefined) => { const slots = { root: ['root'], paper: ['paper'], }; - return composeClasses(slots, getPickersPopperUtilityClass, classes); + return composeClasses(slots, getPickerPopperUtilityClass, classes); }; -const PickersPopperRoot = styled(MuiPopper, { - name: 'MuiPickersPopper', +const PickerPopperRoot = styled(MuiPopper, { + name: 'MuiPickerPopper', slot: 'Root', overridesResolver: (_, styles) => styles.root, })(({ theme }) => ({ zIndex: theme.zIndex.modal, })); -const PickersPopperPaper = styled(MuiPaper, { - name: 'MuiPickersPopper', +const PickerPopperPaper = styled(MuiPaper, { + name: 'MuiPickerPopper', slot: 'Paper', overridesResolver: (_, styles) => styles.paper, })<{ @@ -274,18 +274,18 @@ function useClickAwayListener( return [nodeRef, handleSynthetic, handleSynthetic]; } -interface PickersPopperPaperProps { +interface PickerPopperPaperWrapperProps { PaperComponent: React.ElementType; children: React.ReactNode; ownerState: PickerPopperOwnerState; paperClasses: string; onPaperClick: React.MouseEventHandler; onPaperTouchStart: React.TouchEventHandler; - paperSlotProps?: PickersPopperSlotProps['desktopPaper']; + paperSlotProps?: PickerPopperSlotProps['desktopPaper']; } -const PickersPopperPaperWrapper = React.forwardRef( - (props: PickersPopperPaperProps, ref: React.Ref) => { +const PickerPopperPaperWrapper = React.forwardRef( + (props: PickerPopperPaperWrapperProps, ref: React.Ref) => { const { PaperComponent, ownerState, @@ -330,24 +330,21 @@ const PickersPopperPaperWrapper = React.forwardRef( }, ); -export function PickersPopper(inProps: PickerPopperProps) { - const props = useThemeProps({ props: inProps, name: 'MuiPickersPopper' }); +export function PickerPopper(inProps: PickerPopperProps) { + const props = useThemeProps({ props: inProps, name: 'MuiPickerPopper' }); const { - anchorEl, children, containerRef = null, - shouldRestoreFocus, onBlur, role, - placement = 'bottom', + placement = 'bottom-start', slots, slotProps, - reduceAnimations: inReduceAnimations, classes: classesProp, } = props; - const { open } = usePickerContext(); - const { dismissViews } = usePickerPrivateContext(); + const { open, triggerRef, reduceAnimations } = usePickerContext(); + const { dismissViews, doesTheCurrentViewHasAnUI } = usePickerPrivateContext(); React.useEffect(() => { function handleKeyDown(nativeEvent: KeyboardEvent) { @@ -365,7 +362,7 @@ export function PickersPopper(inProps: PickerPopperProps) { const lastFocusedElementRef = React.useRef(null); React.useEffect(() => { - if (role === 'tooltip' || (shouldRestoreFocus && !shouldRestoreFocus())) { + if (role === 'tooltip' || !doesTheCurrentViewHasAnUI()) { return; } @@ -383,7 +380,7 @@ export function PickersPopper(inProps: PickerPopperProps) { } }); } - }, [open, role, shouldRestoreFocus]); + }, [open, role, doesTheCurrentViewHasAnUI]); const [clickAwayRef, onPaperClick, onPaperTouchStart] = useClickAwayListener( open, @@ -394,10 +391,11 @@ export function PickersPopper(inProps: PickerPopperProps) { const handlePaperRef = useForkRef(handleRef, clickAwayRef as React.Ref); const classes = useUtilityClasses(classesProp); - const defaultReduceAnimations = useDefaultReduceAnimations(); - const reduceAnimations = inReduceAnimations ?? defaultReduceAnimations; const { ownerState: pickerOwnerState } = usePickerPrivateContext(); - const ownerState: PickerPopperOwnerState = { ...pickerOwnerState, popperPlacement: placement }; + const ownerState: PickerPopperOwnerState = { + ...pickerOwnerState, + popperPlacement: placement, + }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Escape') { @@ -410,8 +408,8 @@ export function PickersPopper(inProps: PickerPopperProps) { const Transition = (slots?.desktopTransition ?? reduceAnimations) ? Fade : Grow; const FocusTrap = slots?.desktopTrapFocus ?? BaseFocusTrap; - const Paper = slots?.desktopPaper ?? PickersPopperPaper; - const Popper = slots?.popper ?? PickersPopperRoot; + const Paper = slots?.desktopPaper ?? PickerPopperPaper; + const Popper = slots?.popper ?? PickerPopperRoot; const popperProps = useSlotProps({ elementType: Popper, externalSlotProps: slotProps?.popper, @@ -419,8 +417,8 @@ export function PickersPopper(inProps: PickerPopperProps) { transition: true, role, open, - anchorEl, placement, + anchorEl: triggerRef.current, onKeyDown: handleKeyDown, }, className: classes.root, @@ -442,7 +440,7 @@ export function PickersPopper(inProps: PickerPopperProps) { {...slotProps?.desktopTrapFocus} > - {children} - + )} diff --git a/packages/x-date-pickers/src/internals/components/PickerPopper/index.ts b/packages/x-date-pickers/src/internals/components/PickerPopper/index.ts new file mode 100644 index 0000000000000..3119faa5835dc --- /dev/null +++ b/packages/x-date-pickers/src/internals/components/PickerPopper/index.ts @@ -0,0 +1,5 @@ +export { PickerPopper } from './PickerPopper'; +export type { ExportedPickerPopperProps } from './PickerPopper'; + +export { pickerPopperClasses, getPickerPopperUtilityClass } from './pickerPopperClasses'; +export type { PickerPopperClassKey, PickerPopperClasses } from './pickerPopperClasses'; diff --git a/packages/x-date-pickers/src/internals/components/PickerPopper/pickerPopperClasses.ts b/packages/x-date-pickers/src/internals/components/PickerPopper/pickerPopperClasses.ts new file mode 100644 index 0000000000000..7718ed7d304fb --- /dev/null +++ b/packages/x-date-pickers/src/internals/components/PickerPopper/pickerPopperClasses.ts @@ -0,0 +1,17 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface PickerPopperClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the paper element. */ + paper: string; +} + +export type PickerPopperClassKey = keyof PickerPopperClasses; + +export function getPickerPopperUtilityClass(slot: string) { + return generateUtilityClass('MuiPickerPopper', slot); +} + +export const pickerPopperClasses = generateUtilityClasses('MuiPickerPopper', ['root', 'paper']); diff --git a/packages/x-date-pickers/src/internals/components/PickerProvider.tsx b/packages/x-date-pickers/src/internals/components/PickerProvider.tsx index 30d4aa9be6735..426d972cf1882 100644 --- a/packages/x-date-pickers/src/internals/components/PickerProvider.tsx +++ b/packages/x-date-pickers/src/internals/components/PickerProvider.tsx @@ -16,6 +16,7 @@ import type { import { UsePickerViewsActionsContextValue, UsePickerViewsContextValue, + UsePickerViewsPrivateContextValue, } from '../hooks/usePicker/usePickerViews'; import { IsValidValueContext } from '../../hooks/useIsValidValue'; import { @@ -40,6 +41,8 @@ export const PickerPrivateContext = React.createContext {}, + hasUIView: true, + doesTheCurrentViewHasAnUI: () => true, }); /** @@ -118,6 +121,11 @@ export interface PickerContextValue< * Is always equal to "portrait" if the component you are accessing the context from is not wrapped by a picker. */ orientation: PickerOrientation; + /** + * Whether the heavy animations should be disabled. + * @default `@media(prefers-reduced-motion: reduce)` || `navigator.userAgent` matches Android <10 or iOS <13 + */ + reduceAnimations?: boolean; /** * The ref that should be attached to the element that triggers the Picker opening. * When using a built-in field component, this property is automatically handled. @@ -147,7 +155,9 @@ export interface PickerActionsContextValue< > extends UsePickerValueActionsContextValue, UsePickerViewsActionsContextValue {} -export interface PickerPrivateContextValue extends UsePickerValuePrivateContextValue { +export interface PickerPrivateContextValue + extends UsePickerValuePrivateContextValue, + UsePickerViewsPrivateContextValue { /** * The ownerState of the picker. */ diff --git a/packages/x-date-pickers/src/internals/components/pickersPopperClasses.ts b/packages/x-date-pickers/src/internals/components/pickersPopperClasses.ts deleted file mode 100644 index a1efe667f885a..0000000000000 --- a/packages/x-date-pickers/src/internals/components/pickersPopperClasses.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface PickersPopperClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the paper element. */ - paper: string; -} - -export type PickersPopperClassKey = keyof PickersPopperClasses; - -export function getPickersPopperUtilityClass(slot: string) { - return generateUtilityClass('MuiPickersPopper', slot); -} - -export const pickersPopperClasses = generateUtilityClasses('MuiPickersPopper', ['root', 'paper']); diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx index 8749e675362ee..15623d589ce1d 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import useSlotProps from '@mui/utils/useSlotProps'; import useForkRef from '@mui/utils/useForkRef'; import useId from '@mui/utils/useId'; -import { PickersPopper } from '../../components/PickersPopper'; +import { PickerPopper } from '../../components/PickerPopper/PickerPopper'; import { UseDesktopPickerParams, UseDesktopPickerProps } from './useDesktopPicker.types'; import { usePicker } from '../usePicker'; import { PickersLayout } from '../../../PickersLayout'; @@ -41,7 +41,6 @@ export const useDesktopPicker = < readOnly, autoFocus, localeText, - reduceAnimations, } = props; const fieldRef = React.useRef>(null); @@ -49,7 +48,7 @@ export const useDesktopPicker = < const labelId = useId(); const isToolbarHidden = innerSlotProps?.toolbar?.hidden ?? false; - const { providerProps, renderCurrentView, shouldRestoreFocus, ownerState } = usePicker< + const { providerProps, renderCurrentView, ownerState } = usePicker< PickerValue, TView, TExternalProps @@ -115,19 +114,11 @@ export const useDesktopPicker = < - + {renderCurrentView()} - + ); diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts index 58d97811de6cc..8c0c89c419b65 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts @@ -4,7 +4,10 @@ import { BasePickerProps, BaseNonRangeNonStaticPickerProps, } from '../../models/props/basePickerProps'; -import { PickersPopperSlots, PickersPopperSlotProps } from '../../components/PickersPopper'; +import { + PickerPopperSlots, + PickerPopperSlotProps, +} from '../../components/PickerPopper/PickerPopper'; import { UsePickerParams } from '../usePicker'; import { PickerFieldSlotProps, PickerOwnerState } from '../../../models'; import { @@ -23,7 +26,7 @@ import { UsePickerProviderNonStaticProps } from '../usePicker/usePickerProvider' export interface UseDesktopPickerSlots extends Pick< - PickersPopperSlots, + PickerPopperSlots, 'desktopPaper' | 'desktopTransition' | 'desktopTrapFocus' | 'popper' >, ExportedPickersLayoutSlots, @@ -41,7 +44,7 @@ export interface UseDesktopPickerSlots export interface ExportedUseDesktopPickerSlotProps< TEnableAccessibleFieldDOMStructure extends boolean, -> extends PickersPopperSlotProps, +> extends PickerPopperSlotProps, ExportedPickersLayoutSlotProps, PickerFieldUISlotPropsFromContext { field?: SlotComponentPropsFromProps< diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/index.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/index.ts index 3d7591dec661c..fc366eca34028 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/index.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/index.ts @@ -1,10 +1,5 @@ export { usePicker } from './usePicker'; -export type { - UsePickerProps, - UsePickerBaseProps, - UsePickerParams, - UsePickerResponse, -} from './usePicker.types'; +export type { UsePickerProps, UsePickerBaseProps, UsePickerParams } from './usePicker.types'; export type { PickerValueManager, diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts index a27c1306f22a2..4cbafd7f80590 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.ts @@ -1,5 +1,5 @@ import { warnOnce } from '@mui/x-internals/warning'; -import { UsePickerParams, UsePickerProps, UsePickerResponse } from './usePicker.types'; +import { UsePickerParams, UsePickerProps, UsePickerReturnValue } from './usePicker.types'; import { usePickerValue } from './usePickerValue'; import { usePickerViews } from './usePickerViews'; import { DateOrTimeViewWithMeridiem, PickerValidValue } from '../../models'; @@ -19,7 +19,7 @@ export const usePicker = < rendererInterceptor, fieldRef, localeText, -}: UsePickerParams): UsePickerResponse => { +}: UsePickerParams): UsePickerReturnValue => { if (process.env.NODE_ENV !== 'production') { if ((props as any).renderInput != null) { warnOnce([ @@ -56,7 +56,6 @@ export const usePicker = < return { // Picker views renderCurrentView: pickerViewsResponse.renderCurrentView, - shouldRestoreFocus: pickerViewsResponse.shouldRestoreFocus, // Picker provider providerProps, diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts index 4bf6424a9de93..96f06c6deea11 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePicker.types.ts @@ -6,7 +6,6 @@ import { import { UsePickerViewsProps, UsePickerViewParams, - UsePickerViewsResponse, UsePickerViewsBaseProps, } from './usePickerViews'; import { InferError, PickerOwnerState } from '../../../models'; @@ -57,10 +56,8 @@ export interface UsePickerParams< props: TExternalProps; } -export interface UsePickerResponse< - TValue extends PickerValidValue, - TView extends DateOrTimeViewWithMeridiem, -> extends Pick, 'shouldRestoreFocus' | 'renderCurrentView'> { +export interface UsePickerReturnValue { ownerState: PickerOwnerState; + renderCurrentView: () => React.ReactNode; providerProps: UsePickerProviderReturnValue; } diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts index efbbae1fb221b..06d1747a12222 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerProvider.ts @@ -19,6 +19,7 @@ import { useUtils } from '../useUtils'; import { arrayIncludes } from '../../utils/utils'; import { UsePickerViewsProviderParams } from './usePickerViews'; import { PickerFieldPrivateContextValue } from '../useField/useFieldInternalPropsWithDefaults'; +import { useReduceAnimations } from '../useReduceAnimations'; function getOrientation(): PickerOrientation { if (typeof window === 'undefined') { @@ -79,6 +80,8 @@ export function usePickerProvider< const utils = useUtils(); const orientation = usePickerOrientation(paramsFromUsePickerViews.views, props.orientation); + const reduceAnimations = useReduceAnimations(props.reduceAnimations); + const triggerRef = React.useRef(null); const ownerState = React.useMemo( @@ -126,6 +129,7 @@ export function usePickerProvider< readOnly: props.readOnly ?? false, variant, orientation, + reduceAnimations, triggerRef, triggerStatus, fieldFormat: props.format ?? '', @@ -135,6 +139,7 @@ export function usePickerProvider< paramsFromUsePickerViews.contextValue, variant, orientation, + reduceAnimations, props.disabled, props.readOnly, triggerRef, @@ -144,8 +149,16 @@ export function usePickerProvider< ); const privateContextValue = React.useMemo( - () => ({ ...paramsFromUsePickerValue.privateContextValue, ownerState }), - [paramsFromUsePickerValue, ownerState], + () => ({ + ...paramsFromUsePickerValue.privateContextValue, + ...paramsFromUsePickerViews.privateContextValue, + ownerState, + }), + [ + paramsFromUsePickerValue.privateContextValue, + paramsFromUsePickerViews.privateContextValue, + ownerState, + ], ); const actionsContextValue = React.useMemo( @@ -205,6 +218,11 @@ export interface UsePickerProviderProps extends FormProps { * Force rendering in particular orientation. */ orientation?: PickerOrientation; + /** + * If `true`, disable heavy animations. + * @default `@media(prefers-reduced-motion: reduce)` || `navigator.userAgent` matches Android <10 or iOS <13 + */ + reduceAnimations?: boolean; } /** diff --git a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.tsx b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.tsx index f8769c0109b27..4ab6e368e484d 100644 --- a/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.tsx +++ b/packages/x-date-pickers/src/internals/hooks/usePicker/usePickerViews.tsx @@ -56,11 +56,6 @@ export interface UsePickerViewsBaseProps< * If `undefined`, internally defined view will be used. */ viewRenderers: PickerViewRendererLookup; - /** - * If `true`, disable heavy animations. - * @default `@media(prefers-reduced-motion: reduce)` || `navigator.userAgent` matches Android <10 or iOS <13 - */ - reduceAnimations?: boolean; /** * The date used to generate the new value when both `value` and `defaultValue` are empty. * @default The closest valid date-time using the validation props, except callbacks like `shouldDisable<...>`. @@ -114,7 +109,6 @@ export interface PickerRendererInterceptorProps< export interface UsePickerViewsResponse { renderCurrentView: () => React.ReactNode; - shouldRestoreFocus: () => boolean; provider: UsePickerViewsProviderParams; } @@ -139,11 +133,24 @@ export interface UsePickerViewsContextValue boolean; +} + export interface UsePickerViewsProviderParams { hasUIView: boolean; views: readonly TView[]; contextValue: UsePickerViewsContextValue; actionsContextValue: UsePickerViewsActionsContextValue; + privateContextValue: UsePickerViewsPrivateContextValue; } /** @@ -212,7 +219,7 @@ export const usePickerViews = < ); const currentViewMode = viewModeLookup[view]; - const shouldRestoreFocus = useEventCallback(() => currentViewMode === 'UI'); + const doesTheCurrentViewHasAnUI = useEventCallback(() => currentViewMode === 'UI'); const [popperView, setPopperView] = React.useState( currentViewMode === 'UI' ? view : null, @@ -275,15 +282,20 @@ export const usePickerViews = < [actionsContextValue, views, popperView], ); + const privateContextValue = React.useMemo( + () => ({ hasUIView, doesTheCurrentViewHasAnUI }), + [hasUIView, doesTheCurrentViewHasAnUI], + ); + const providerParams: UsePickerViewsProviderParams = { hasUIView, views, contextValue, actionsContextValue, + privateContextValue, }; return { - shouldRestoreFocus, provider: providerParams, renderCurrentView: () => { if (popperView == null) { diff --git a/packages/x-date-pickers/src/internals/hooks/useDefaultReduceAnimations.ts b/packages/x-date-pickers/src/internals/hooks/useReduceAnimations.ts similarity index 83% rename from packages/x-date-pickers/src/internals/hooks/useDefaultReduceAnimations.ts rename to packages/x-date-pickers/src/internals/hooks/useReduceAnimations.ts index 72a400bf4ee58..1c8a174c85908 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDefaultReduceAnimations.ts +++ b/packages/x-date-pickers/src/internals/hooks/useReduceAnimations.ts @@ -12,7 +12,12 @@ const iOSVersion = export const slowAnimationDevices = (androidVersion && androidVersion < 10) || (iOSVersion && iOSVersion < 13) || false; -export const useDefaultReduceAnimations = () => { +export function useReduceAnimations(customReduceAnimations: boolean | undefined) { const prefersReduced = useMediaQuery(PREFERS_REDUCED_MOTION, { defaultMatches: false }); + + if (customReduceAnimations != null) { + return customReduceAnimations; + } + return prefersReduced || slowAnimationDevices; -}; +} diff --git a/packages/x-date-pickers/src/internals/index.ts b/packages/x-date-pickers/src/internals/index.ts index 6d138c66b4d30..429ec8e3f9bd8 100644 --- a/packages/x-date-pickers/src/internals/index.ts +++ b/packages/x-date-pickers/src/internals/index.ts @@ -22,8 +22,11 @@ export type { PickersModalDialogSlots, PickersModalDialogSlotProps, } from './components/PickersModalDialog'; -export { PickersPopper } from './components/PickersPopper'; -export type { PickersPopperSlots, PickersPopperSlotProps } from './components/PickersPopper'; +export { PickerPopper } from './components/PickerPopper/PickerPopper'; +export type { + PickerPopperSlots, + PickerPopperSlotProps, +} from './components/PickerPopper/PickerPopper'; export { PickersToolbar } from './components/PickersToolbar'; export type { PickersToolbarProps } from './components/PickersToolbar'; export { pickersToolbarClasses } from './components/pickersToolbarClasses'; @@ -51,12 +54,6 @@ export type { PickersArrowSwitcherClassKey, PickersArrowSwitcherClasses, } from './components/PickersArrowSwitcher/pickersArrowSwitcherClasses'; -export type { PickerPopperProps } from './components/PickersPopper'; -export { pickersPopperClasses } from './components/pickersPopperClasses'; -export type { - PickersPopperClassKey, - PickersPopperClasses, -} from './components/pickersPopperClasses'; export { PickersToolbarButton } from './components/PickersToolbarButton'; export { DAY_MARGIN, DIALOG_WIDTH, VIEW_HEIGHT } from './constants/dimensions'; @@ -166,7 +163,7 @@ export { onSpaceOrEnter, DEFAULT_DESKTOP_MODE_MEDIA_QUERY, } from './utils/utils'; -export { useDefaultReduceAnimations } from './hooks/useDefaultReduceAnimations'; +export { useReduceAnimations } from './hooks/useReduceAnimations'; export { applyDefaultViewProps } from './utils/views'; export { DayCalendar } from '../DateCalendar/DayCalendar'; diff --git a/packages/x-date-pickers/src/themeAugmentation/components.d.ts b/packages/x-date-pickers/src/themeAugmentation/components.d.ts index fe83141400e00..aa2954ed70460 100644 --- a/packages/x-date-pickers/src/themeAugmentation/components.d.ts +++ b/packages/x-date-pickers/src/themeAugmentation/components.d.ts @@ -78,9 +78,9 @@ export interface PickerComponents { defaultProps?: ComponentsProps['MuiPickersFadeTransitionGroup']; styleOverrides?: ComponentsOverrides['MuiPickersFadeTransitionGroup']; }; - MuiPickersPopper?: { - defaultProps?: ComponentsProps['MuiPickersPopper']; - styleOverrides?: ComponentsOverrides['MuiPickersPopper']; + MuiPickerPopper?: { + defaultProps?: ComponentsProps['MuiPickerPopper']; + styleOverrides?: ComponentsOverrides['MuiPickerPopper']; }; MuiPickersSlideTransition?: { defaultProps?: ComponentsProps['MuiPickersSlideTransition']; diff --git a/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts b/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts index 2722d0ad6267d..8c69153529f4f 100644 --- a/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts +++ b/packages/x-date-pickers/src/themeAugmentation/overrides.d.ts @@ -21,7 +21,7 @@ import { TimePickerToolbarClassKey } from '../TimePicker'; import { DateTimePickerToolbarClassKey, DateTimePickerTabsClassKey } from '../DateTimePicker'; import { PickersArrowSwitcherClassKey } from '../internals/components/PickersArrowSwitcher'; import { PickersToolbarClassKey } from '../internals/components/pickersToolbarClasses'; -import { PickersPopperClassKey } from '../internals/components/pickersPopperClasses'; +import { PickerPopperClassKey } from '../internals/components/PickerPopper'; import { PickersToolbarButtonClassKey } from '../internals/components/pickersToolbarButtonClasses'; import { PickersToolbarTextClassKey } from '../internals/components/pickersToolbarTextClasses'; import { DigitalClockClassKey } from '../DigitalClock'; @@ -58,7 +58,7 @@ export interface PickersComponentNameToClassKey { MuiPickersDay: PickersDayClassKey; MuiPickersFadeTransitionGroup: PickersFadeTransitionGroupClassKey; MuiPickersLayout: PickersLayoutClassKey; - MuiPickersPopper: PickersPopperClassKey; + MuiPickerPopper: PickerPopperClassKey; MuiPickersSlideTransition: PickersSlideTransitionClassKey; MuiPickersToolbar: PickersToolbarClassKey; MuiPickersToolbarButton: PickersToolbarButtonClassKey; diff --git a/packages/x-date-pickers/src/themeAugmentation/props.d.ts b/packages/x-date-pickers/src/themeAugmentation/props.d.ts index 11562506ec71c..b7b9ad68d35a8 100644 --- a/packages/x-date-pickers/src/themeAugmentation/props.d.ts +++ b/packages/x-date-pickers/src/themeAugmentation/props.d.ts @@ -13,7 +13,7 @@ import { LocalizationProviderProps } from '../LocalizationProvider'; import { PickersLayoutProps } from '../PickersLayout'; import { DayCalendarProps } from '../DateCalendar/DayCalendar'; import { ExportedPickersArrowSwitcherProps } from '../internals/components/PickersArrowSwitcher/PickersArrowSwitcher.types'; -import { PickerPopperProps } from '../internals/components/PickersPopper'; +import { ExportedPickerPopperProps } from '../internals/components/PickerPopper'; import { PickersToolbarProps } from '../internals/components/PickersToolbar'; import { PickersToolbarButtonProps } from '../internals/components/PickersToolbarButton'; import { ExportedPickersToolbarTextProps } from '../internals/components/PickersToolbarText'; @@ -74,7 +74,7 @@ export interface PickersComponentsPropsList { MuiPickersCalendarHeader: ExportedPickersCalendarHeaderProps; MuiPickersDay: PickersDayProps; MuiPickersFadeTransitionGroup: PickersFadeTransitionGroupProps; - MuiPickersPopper: PickerPopperProps; + MuiPickerPopper: ExportedPickerPopperProps; MuiPickersSlideTransition: ExportedSlideTransitionProps; MuiPickersToolbar: PickersToolbarProps; MuiPickersToolbarButton: PickersToolbarButtonProps; diff --git a/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts b/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts index b32594f9dcfc6..e584f4f7e3135 100644 --- a/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts +++ b/packages/x-date-pickers/src/themeAugmentation/themeAugmentation.spec.ts @@ -15,7 +15,7 @@ import { import { datePickerToolbarClasses } from '../DatePicker'; import { dateTimePickerToolbarClasses } from '../DateTimePicker'; import { pickersArrowSwitcherClasses } from '../internals/components/PickersArrowSwitcher'; -import { pickersPopperClasses } from '../internals/components/pickersPopperClasses'; +import { pickerPopperClasses } from '../internals/components/PickerPopper'; import { pickersDayClasses } from '../PickersDay'; import { timePickerToolbarClasses } from '../TimePicker'; import { digitalClockClasses } from '../DigitalClock'; @@ -354,20 +354,20 @@ createTheme({ }, }, }, - MuiPickersPopper: { + MuiPickerPopper: { defaultProps: { placement: 'bottom', - // @ts-expect-error invalid MuiPickersPopper prop + // @ts-expect-error invalid MuiPickerPopper prop someRandomProp: true, }, styleOverrides: { root: { backgroundColor: 'red', - [`.${pickersPopperClasses.paper}`]: { + [`.${pickerPopperClasses.paper}`]: { backgroundColor: 'green', }, }, - // @ts-expect-error invalid MuiPickersPopper class key + // @ts-expect-error invalid MuiPickerPopper class key content: { backgroundColor: 'blue', },