diff --git a/docs/pages/x/api/date-pickers/date-range-picker-day.json b/docs/pages/x/api/date-pickers/date-range-picker-day.json index fc19b9e50ec6..fa57a2557f2b 100644 --- a/docs/pages/x/api/date-pickers/date-range-picker-day.json +++ b/docs/pages/x/api/date-pickers/date-range-picker-day.json @@ -23,6 +23,7 @@ "disableMargin": { "type": { "name": "bool" }, "default": "false" }, "disableRipple": { "type": { "name": "bool" }, "default": "false" }, "disableTouchRipple": { "type": { "name": "bool" }, "default": "false" }, + "draggable": { "type": { "name": "bool" }, "default": "false" }, "focusRipple": { "type": { "name": "bool" }, "default": "false" }, "focusVisibleClassName": { "type": { "name": "string" } }, "isVisuallySelected": { "type": { "name": "bool" } }, diff --git a/docs/translations/api-docs/date-pickers/date-range-picker-day/date-range-picker-day.json b/docs/translations/api-docs/date-pickers/date-range-picker-day/date-range-picker-day.json index d24a2914f43b..56e31a43dd71 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker-day/date-range-picker-day.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker-day/date-range-picker-day.json @@ -22,6 +22,9 @@ "disableTouchRipple": { "description": "If true, the touch ripple effect is disabled." }, + "draggable": { + "description": "If true, the day can be dragged to change the current date range." + }, "focusRipple": { "description": "If true, the base button will have a keyboard focus ripple." }, diff --git a/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx b/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx index 29c0be8863b8..95ca252d165c 100644 --- a/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx @@ -50,6 +50,11 @@ export interface DateRangePickerDayProps * Indicates if the day should be visually selected. */ isVisuallySelected?: boolean; + /** + * If `true`, the day can be dragged to change the current date range. + * @default false + */ + draggable?: boolean; } type OwnerState = DateRangePickerDayProps & { @@ -154,10 +159,11 @@ const DateRangePickerDayRoot = styled('div', { }, styles.root, ], -})<{ ownerState: OwnerState }>(({ theme, ownerState }) => - ownerState.isHiddenDayFiller - ? {} - : { +})<{ ownerState: OwnerState }>(({ theme }) => ({ + variants: [ + { + props: { isHiddenDayFiller: false }, + style: { [`&:first-of-type .${dateRangePickerDayClasses.rangeIntervalDayPreview}`]: { ...startBorderStyle, borderLeftColor: (theme.vars || theme).palette.divider, @@ -166,25 +172,44 @@ const DateRangePickerDayRoot = styled('div', { ...endBorderStyle, borderRightColor: (theme.vars || theme).palette.divider, }, - ...(ownerState.isHighlighting && { - borderRadius: 0, - color: (theme.vars || theme).palette.primary.contrastText, - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.focusOpacity})` - : alpha(theme.palette.primary.main, theme.palette.action.focusOpacity), - '&:first-of-type': startBorderStyle, - '&:last-of-type': endBorderStyle, - }), - ...((ownerState.isStartOfHighlighting || ownerState.isFirstVisibleCell) && { - ...startBorderStyle, - paddingLeft: 0, - }), - ...((ownerState.isEndOfHighlighting || ownerState.isLastVisibleCell) && { - ...endBorderStyle, - paddingRight: 0, - }), }, -); + }, + { + props: { isHiddenDayFiller: false, isHighlighting: true }, + style: { + borderRadius: 0, + color: (theme.vars || theme).palette.primary.contrastText, + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.focusOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.focusOpacity), + '&:first-of-type': startBorderStyle, + '&:last-of-type': endBorderStyle, + }, + }, + { + props: ({ + ownerState: { isHiddenDayFiller, isStartOfHighlighting, isFirstVisibleCell }, + }: { + ownerState: OwnerState; + }) => !isHiddenDayFiller && (isStartOfHighlighting || isFirstVisibleCell), + style: { + ...startBorderStyle, + paddingLeft: 0, + }, + }, + { + props: ({ + ownerState: { isHiddenDayFiller, isEndOfHighlighting, isLastVisibleCell }, + }: { + ownerState: OwnerState; + }) => !isHiddenDayFiller && (isEndOfHighlighting || isLastVisibleCell), + style: { + ...endBorderStyle, + paddingRight: 0, + }, + }, + ], +})); DateRangePickerDayRoot.propTypes = { // ----------------------------- Warning -------------------------------- @@ -209,24 +234,42 @@ const DateRangePickerDayRangeIntervalPreview = styled('div', { }, styles.rangeIntervalPreview, ], -})<{ ownerState: OwnerState }>(({ theme, ownerState }) => ({ +})<{ ownerState: OwnerState }>(({ theme }) => ({ // replace default day component margin with transparent border to avoid jumping on preview border: '2px solid transparent', - ...(ownerState.isPreviewing && - !ownerState.isHiddenDayFiller && { - borderRadius: 0, - border: `2px dashed ${(theme.vars || theme).palette.divider}`, - borderLeftColor: 'transparent', - borderRightColor: 'transparent', - ...((ownerState.isStartOfPreviewing || ownerState.isFirstVisibleCell) && { + variants: [ + { + props: { isPreviewing: true, isHiddenDayFiller: false }, + style: { + borderRadius: 0, + border: `2px dashed ${(theme.vars || theme).palette.divider}`, + borderLeftColor: 'transparent', + borderRightColor: 'transparent', + }, + }, + { + props: ({ + ownerState: { isPreviewing, isHiddenDayFiller, isStartOfPreviewing, isFirstVisibleCell }, + }: { + ownerState: OwnerState; + }) => isPreviewing && !isHiddenDayFiller && (isStartOfPreviewing || isFirstVisibleCell), + style: { borderLeftColor: (theme.vars || theme).palette.divider, ...startBorderStyle, - }), - ...((ownerState.isEndOfPreviewing || ownerState.isLastVisibleCell) && { + }, + }, + { + props: ({ + ownerState: { isPreviewing, isHiddenDayFiller, isEndOfPreviewing, isLastVisibleCell }, + }: { + ownerState: OwnerState; + }) => isPreviewing && !isHiddenDayFiller && (isEndOfPreviewing || isLastVisibleCell), + style: { borderRightColor: (theme.vars || theme).palette.divider, ...endBorderStyle, - }), - }), + }, + }, + ], })); DateRangePickerDayRangeIntervalPreview.propTypes = { @@ -248,19 +291,22 @@ const DateRangePickerDayDay = styled(PickersDay, { ], })<{ ownerState: OwnerState; -}>(({ ownerState }) => ({ +}>({ // Required to overlap preview border transform: 'scale(1.1)', '& > *': { transform: 'scale(0.9)', }, - ...(ownerState.draggable && { - cursor: 'grab', - }), - ...(ownerState.draggable && { - touchAction: 'none', - }), -})) as unknown as ( + variants: [ + { + props: { draggable: true }, + style: { + cursor: 'grab', + touchAction: 'none', + }, + }, + ], +}) as unknown as ( props: PickersDayProps & { ownerState: OwnerState }, ) => React.JSX.Element; @@ -405,6 +451,11 @@ DateRangePickerDayRaw.propTypes = { * @default false */ disableTouchRipple: PropTypes.bool, + /** + * If `true`, the day can be dragged to change the current date range. + * @default false + */ + draggable: PropTypes.bool, /** * If `true`, the base button will have a keyboard focus ripple. * @default false diff --git a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx index 32fbf3ec18ba..4d4c6155c38e 100644 --- a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx +++ b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePickerToolbar.tsx @@ -76,24 +76,43 @@ const DateTimeRangePickerToolbarStart = styled(DateTimePickerToolbar, { name: 'MuiDateTimeRangePickerToolbar', slot: 'StartToolbar', overridesResolver: (_, styles) => styles.startToolbar, -})>(({ ownerState }) => ({ +})>({ borderBottom: 'none', - ...(ownerState?.toolbarVariant !== 'desktop' - ? { + variants: [ + { + props: ({ toolbarVariant }: DateTimeRangePickerStartOrEndToolbarProps) => + toolbarVariant !== 'desktop', + style: { padding: '12px 8px 0 12px', - } - : { + }, + }, + { + props: { toolbarVariant: 'desktop' }, + style: { paddingBottom: 0, - }), -})) as DateTimeRangePickerStartOrEndToolbarComponent; + }, + }, + ], +}) as DateTimeRangePickerStartOrEndToolbarComponent; const DateTimeRangePickerToolbarEnd = styled(DateTimePickerToolbar, { name: 'MuiDateTimeRangePickerToolbar', slot: 'EndToolbar', overridesResolver: (_, styles) => styles.endToolbar, -})>(({ ownerState }) => ({ - padding: ownerState?.toolbarVariant !== 'desktop' ? '12px 8px 12px 12px' : undefined, -})) as DateTimeRangePickerStartOrEndToolbarComponent; +})>({ + variants: [ + { + props: ({ + ownerState: { toolbarVariant }, + }: { + ownerState: DateTimeRangePickerStartOrEndToolbarProps; + }) => toolbarVariant !== 'desktop', + style: { + padding: '12px 8px 12px 12px', + }, + }, + ], +}) as DateTimeRangePickerStartOrEndToolbarComponent; const DateTimeRangePickerToolbar = React.forwardRef(function DateTimeRangePickerToolbar< TDate extends PickerValidDate, diff --git a/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx b/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx index 820e0f5ed83a..9e0dbdba817d 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx @@ -45,11 +45,16 @@ const DatePickerToolbarTitle = styled(Typography, { name: 'MuiDatePickerToolbar', slot: 'Title', overridesResolver: (_, styles) => styles.title, -})<{ ownerState: DatePickerToolbarProps }>(({ ownerState }) => ({ - ...(ownerState.isLandscape && { - margin: 'auto 16px auto auto', - }), -})); +})<{ ownerState: DatePickerToolbarProps }>({ + variants: [ + { + props: { isLandscape: true }, + style: { + margin: 'auto 16px auto auto', + }, + }, + ], +}); type DatePickerToolbarComponent = (( props: DatePickerToolbarProps & React.RefAttributes, diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index f100e3fbe33b..21593a3f6081 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -60,25 +60,36 @@ const DateTimePickerToolbarRoot = styled(PickersToolbar, { name: 'MuiDateTimePickerToolbar', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ - paddingLeft: ownerState.toolbarVariant === 'desktop' && !ownerState.isLandscape ? 24 : 16, - paddingRight: ownerState.toolbarVariant === 'desktop' && !ownerState.isLandscape ? 0 : 16, - borderBottom: - ownerState.toolbarVariant === 'desktop' - ? `1px solid ${(theme.vars || theme).palette.divider}` - : undefined, - borderRight: - ownerState.toolbarVariant === 'desktop' && ownerState.isLandscape - ? `1px solid ${(theme.vars || theme).palette.divider}` - : undefined, +})<{ ownerState: DateTimePickerToolbarProps }>(({ theme }) => ({ + paddingLeft: 16, + paddingRight: 16, justifyContent: 'space-around', position: 'relative', - ...(ownerState.toolbarVariant === 'desktop' && { - [`& .${pickersToolbarClasses.content} .${pickersToolbarTextClasses.selected}`]: { - color: (theme.vars || theme).palette.primary.main, - fontWeight: theme.typography.fontWeightBold, + variants: [ + { + props: { toolbarVariant: 'desktop' }, + style: { + borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, + [`& .${pickersToolbarClasses.content} .${pickersToolbarTextClasses.selected}`]: { + color: (theme.vars || theme).palette.primary.main, + fontWeight: theme.typography.fontWeightBold, + }, + }, }, - }), + { + props: { toolbarVariant: 'desktop', isLandscape: true }, + style: { + borderRight: `1px solid ${(theme.vars || theme).palette.divider}`, + }, + }, + { + props: { toolbarVariant: 'desktop', isLandscape: false }, + style: { + paddingLeft: 24, + paddingRight: 0, + }, + }, + ], })); DateTimePickerToolbarRoot.propTypes = { @@ -117,22 +128,33 @@ const DateTimePickerToolbarTimeContainer = styled('div', { name: 'MuiDateTimePickerToolbar', slot: 'TimeContainer', overridesResolver: (props, styles) => styles.timeContainer, -})<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => { - const direction = - ownerState.isLandscape && ownerState.toolbarVariant !== 'desktop' ? 'column' : 'row'; +})<{ ownerState: DateTimePickerToolbarProps }>(({ theme }) => { return { display: 'flex', - flexDirection: direction, - ...(ownerState.toolbarVariant === 'desktop' && { - ...(!ownerState.isLandscape && { - gap: 9, - marginRight: 4, - alignSelf: 'flex-end', - }), - }), + flexDirection: 'row', ...(theme.direction === 'rtl' && { - flexDirection: `${direction}-reverse`, + flexDirection: 'row-reverse', }), + variants: [ + { + props: ({ isLandscape, toolbarVariant }: DateTimePickerToolbarProps) => + isLandscape && toolbarVariant !== 'desktop', + style: { + flexDirection: 'column', + ...(theme.direction === 'rtl' && { + flexDirection: 'column-reverse', + }), + }, + }, + { + props: { toolbarVariant: 'desktop', isLandscape: false }, + style: { + gap: 9, + marginRight: 4, + alignSelf: 'flex-end', + }, + }, + ], }; }); @@ -140,12 +162,17 @@ const DateTimePickerToolbarTimeDigitsContainer = styled('div', { name: 'MuiDateTimePickerToolbar', slot: 'TimeDigitsContainer', overridesResolver: (props, styles) => styles.timeDigitsContainer, -})<{ ownerState: DateTimePickerToolbarProps }>(({ theme, ownerState }) => ({ +})<{ ownerState: DateTimePickerToolbarProps }>(({ theme }) => ({ display: 'flex', - ...(ownerState.toolbarVariant === 'desktop' && { gap: 1.5 }), ...(theme.direction === 'rtl' && { flexDirection: 'row-reverse', }), + variants: [ + { + props: { toolbarVariant: 'desktop' }, + style: { gap: 1.5 }, + }, + ], })); DateTimePickerToolbarTimeContainer.propTypes = { @@ -168,10 +195,18 @@ const DateTimePickerToolbarSeparator = styled(PickersToolbarText, { overridesResolver: (props, styles) => styles.separator, })<{ ownerState: DateTimePickerToolbarProps; -}>(({ ownerState }) => ({ - margin: ownerState.toolbarVariant === 'desktop' ? 0 : '0 4px 0 2px', +}>({ + margin: '0 4px 0 2px', cursor: 'default', -})); + variants: [ + { + props: { toolbarVariant: 'desktop' }, + style: { + margin: 0, + }, + }, + ], +}); // Taken from TimePickerToolbar const DateTimePickerToolbarAmPmSelection = styled('div', { @@ -184,21 +219,26 @@ const DateTimePickerToolbarAmPmSelection = styled('div', { ], })<{ ownerState: DateTimePickerToolbarProps; -}>(({ ownerState }) => ({ +}>({ display: 'flex', flexDirection: 'column', marginRight: 'auto', marginLeft: 12, - ...(ownerState.isLandscape && { - margin: '4px 0 auto', - flexDirection: 'row', - justifyContent: 'space-around', - width: '100%', - }), [`& .${dateTimePickerToolbarClasses.ampmLabel}`]: { fontSize: 17, }, -})); + variants: [ + { + props: { isLandscape: true }, + style: { + margin: '4px 0 auto', + flexDirection: 'row', + justifyContent: 'space-around', + width: '100%', + }, + }, + ], +}); /** * Demos: diff --git a/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx b/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx index cca99858f8ae..1af619f65904 100644 --- a/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx +++ b/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx @@ -58,12 +58,15 @@ const DayCalendarSkeletonDay = styled(Skeleton, { name: 'MuiDayCalendarSkeleton', slot: 'DaySkeleton', overridesResolver: (props, styles) => styles.daySkeleton, -})<{ ownerState: { day: number } }>(({ ownerState }) => ({ +})<{ ownerState: { day: number } }>({ margin: `0 ${DAY_MARGIN}px`, - ...(ownerState.day === 0 && { - visibility: 'hidden', - }), -})); + variants: [ + { + props: { day: 0 }, + style: { visibility: 'hidden' }, + }, + ], +}); DayCalendarSkeletonDay.propTypes = { // ----------------------------- Warning -------------------------------- diff --git a/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx b/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx index e3824be36907..19b45e4c99a2 100644 --- a/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx +++ b/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx @@ -35,14 +35,24 @@ const DigitalClockRoot = styled(PickerViewRoot, { name: 'MuiDigitalClock', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: DigitalClockProps & { alreadyRendered: boolean } }>(({ ownerState }) => ({ +})<{ ownerState: DigitalClockProps & { alreadyRendered: boolean } }>({ overflowY: 'auto', width: '100%', '@media (prefers-reduced-motion: no-preference)': { - scrollBehavior: ownerState.alreadyRendered ? 'smooth' : 'auto', + scrollBehavior: 'auto', }, maxHeight: DIGITAL_CLOCK_VIEW_HEIGHT, -})); + variants: [ + { + props: { alreadyRendered: true }, + style: { + '@media (prefers-reduced-motion: no-preference)': { + scrollBehavior: 'smooth', + }, + }, + }, + ], +}); const DigitalClockList = styled(MenuList, { name: 'MuiDigitalClock', diff --git a/packages/x-date-pickers/src/MonthCalendar/PickersMonth.tsx b/packages/x-date-pickers/src/MonthCalendar/PickersMonth.tsx index bbb4bcb1db97..daae053f66c4 100644 --- a/packages/x-date-pickers/src/MonthCalendar/PickersMonth.tsx +++ b/packages/x-date-pickers/src/MonthCalendar/PickersMonth.tsx @@ -47,12 +47,13 @@ const PickersMonthRoot = styled('div', { overridesResolver: (_, styles) => [styles.root], })<{ ownerState: PickersMonthProps; -}>(({ ownerState }) => ({ - flexBasis: ownerState.monthsPerRow === 3 ? '33.3%' : '25%', +}>({ display: 'flex', alignItems: 'center', justifyContent: 'center', -})); + flexBasis: '33.3%', + variants: [{ props: { monthsPerRow: 4 }, style: { flexBasis: '25%' } }], +}); const PickersMonthButton = styled('button', { name: 'MuiPickersMonth', diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx index ced944954913..cbc6a60df7a0 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx @@ -53,13 +53,13 @@ const MultiSectionDigitalClockSectionRoot = styled(MenuList, { slot: 'Root', overridesResolver: (_, styles) => styles.root, })<{ ownerState: MultiSectionDigitalClockSectionProps & { alreadyRendered: boolean } }>( - ({ theme, ownerState }) => ({ + ({ theme }) => ({ maxHeight: DIGITAL_CLOCK_VIEW_HEIGHT, width: 56, padding: 0, overflow: 'hidden', '@media (prefers-reduced-motion: no-preference)': { - scrollBehavior: ownerState.alreadyRendered ? 'smooth' : 'auto', + scrollBehavior: 'auto', }, '@media (pointer: fine)': { '&:hover': { @@ -78,6 +78,16 @@ const MultiSectionDigitalClockSectionRoot = styled(MenuList, { // subtracting the height of one item, extra margin and borders to make sure the max height is correct height: 'calc(100% - 40px - 6px)', }, + variants: [ + { + props: { alreadyRendered: true }, + style: { + '@media (prefers-reduced-motion: no-preference)': { + scrollBehavior: 'smooth', + }, + }, + }, + ], }), ); diff --git a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx index a18d69b4540f..e7bbfe369dc5 100644 --- a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx +++ b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx @@ -87,14 +87,19 @@ const PickersCalendarHeaderSwitchViewButton = styled(IconButton, { overridesResolver: (_, styles) => styles.switchViewButton, })<{ ownerState: PickersCalendarHeaderOwnerState; -}>(({ ownerState }) => ({ +}>({ marginRight: 'auto', - ...(ownerState.view === 'year' && { - [`.${pickersCalendarHeaderClasses.switchViewIcon}`]: { - transform: 'rotate(180deg)', + variants: [ + { + props: { view: 'year' }, + style: { + [`.${pickersCalendarHeaderClasses.switchViewIcon}`]: { + transform: 'rotate(180deg)', + }, + }, }, - }), -})); + ], +}); const PickersCalendarHeaderSwitchViewIcon = styled(ArrowDropDownIcon, { name: 'MuiPickersCalendarHeader', diff --git a/packages/x-date-pickers/src/PickersDay/PickersDay.tsx b/packages/x-date-pickers/src/PickersDay/PickersDay.tsx index b0ed7b3ac0eb..a614588756fd 100644 --- a/packages/x-date-pickers/src/PickersDay/PickersDay.tsx +++ b/packages/x-date-pickers/src/PickersDay/PickersDay.tsx @@ -127,7 +127,7 @@ const useUtilityClasses = (ownerState: PickersDayProps) => { return composeClasses(slots, getPickersDayUtilityClass, classes); }; -const styleArg = ({ theme, ownerState }: { theme: Theme; ownerState: OwnerState }) => ({ +const styleArg = ({ theme }: { theme: Theme }) => ({ ...theme.typography.caption, width: DAY_SIZE, height: DAY_SIZE, @@ -170,19 +170,28 @@ const styleArg = ({ theme, ownerState }: { theme: Theme; ownerState: OwnerState [`&.${pickersDayClasses.disabled}&.${pickersDayClasses.selected}`]: { opacity: 0.6, }, - ...(!ownerState.disableMargin && { - margin: `0 ${DAY_MARGIN}px`, - }), - ...(ownerState.outsideCurrentMonth && - ownerState.showDaysOutsideCurrentMonth && { - color: (theme.vars || theme).palette.text.secondary, - }), - ...(!ownerState.disableHighlightToday && - ownerState.today && { - [`&:not(.${pickersDayClasses.selected})`]: { - border: `1px solid ${(theme.vars || theme).palette.text.secondary}`, + variants: [ + { + props: { disableMargin: false }, + style: { + margin: `0 ${DAY_MARGIN}px`, }, - }), + }, + { + props: { outsideCurrentMonth: true, showDaysOutsideCurrentMonth: true }, + style: { + color: (theme.vars || theme).palette.text.secondary, + }, + }, + { + props: { disableHighlightToday: false, today: true }, + style: { + [`&:not(.${pickersDayClasses.selected})`]: { + border: `1px solid ${(theme.vars || theme).palette.text.secondary}`, + }, + }, + }, + ], }); const overridesResolver = ( @@ -213,8 +222,8 @@ const PickersDayFiller = styled('div', { name: 'MuiPickersDay', slot: 'Root', overridesResolver, -})<{ ownerState: OwnerState }>(({ theme, ownerState }) => ({ - ...styleArg({ theme, ownerState }), +})<{ ownerState: OwnerState }>(({ theme }) => ({ + ...styleArg({ theme }), // visibility: 'hidden' does not work here as it hides the element from screen readers as well opacity: 0, pointerEvents: 'none', diff --git a/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx b/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx index ac97bf691f5d..0937bce1cb7f 100644 --- a/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx +++ b/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx @@ -23,23 +23,33 @@ const PickersLayoutRoot = styled('div', { name: 'MuiPickersLayout', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: { isLandscape: boolean } }>(({ theme, ownerState }) => ({ +})<{ ownerState: { isLandscape: boolean } }>(({ theme }) => ({ display: 'grid', gridAutoColumns: 'max-content auto max-content', gridAutoRows: 'max-content auto max-content', - [`& .${pickersLayoutClasses.toolbar}`]: ownerState.isLandscape - ? { - gridColumn: theme.direction === 'rtl' ? 3 : 1, - gridRow: '2 / 3', - } - : { gridColumn: '2 / 4', gridRow: 1 }, - [`.${pickersLayoutClasses.shortcuts}`]: ownerState.isLandscape - ? { gridColumn: '2 / 4', gridRow: 1 } - : { - gridColumn: theme.direction === 'rtl' ? 3 : 1, - gridRow: '2 / 3', - }, [`& .${pickersLayoutClasses.actionBar}`]: { gridColumn: '1 / 4', gridRow: 3 }, + variants: [ + { + props: { isLandscape: true }, + style: { + [`& .${pickersLayoutClasses.toolbar}`]: { + gridColumn: theme.direction === 'rtl' ? 3 : 1, + gridRow: '2 / 3', + }, + [`.${pickersLayoutClasses.shortcuts}`]: { gridColumn: '2 / 4', gridRow: 1 }, + }, + }, + { + props: { isLandscape: false }, + style: { + [`& .${pickersLayoutClasses.toolbar}`]: { gridColumn: '2 / 4', gridRow: 1 }, + [`& .${pickersLayoutClasses.shortcuts}`]: { + gridColumn: theme.direction === 'rtl' ? 3 : 1, + gridRow: '2 / 3', + }, + }, + }, + ], })); PickersLayoutRoot.propTypes = { diff --git a/packages/x-date-pickers/src/PickersTextField/PickersFilledInput/PickersFilledInput.tsx b/packages/x-date-pickers/src/PickersTextField/PickersFilledInput/PickersFilledInput.tsx index 95d9343c5893..e86c0556e22d 100644 --- a/packages/x-date-pickers/src/PickersTextField/PickersFilledInput/PickersFilledInput.tsx +++ b/packages/x-date-pickers/src/PickersTextField/PickersFilledInput/PickersFilledInput.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { FormControlState, useFormControl } from '@mui/material/FormControl'; import { styled, useThemeProps } from '@mui/material/styles'; +import { shouldForwardProp } from '@mui/system'; import { refType } from '@mui/utils'; import composeClasses from '@mui/utils/composeClasses'; import { @@ -23,7 +24,8 @@ const PickersFilledInputRoot = styled(PickersInputBaseRoot, { name: 'MuiPickersFilledInput', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: OwnerStateType }>(({ theme, ownerState }) => { + shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'disableUnderline', +})<{ ownerState: OwnerStateType }>(({ theme }) => { const light = theme.palette.mode === 'light'; const bottomLineColor = light ? 'rgba(0, 0, 0, 0.42)' : 'rgba(255, 255, 255, 0.7)'; const backgroundColor = light ? 'rgba(0, 0, 0, 0.06)' : 'rgba(255, 255, 255, 0.09)'; @@ -51,65 +53,85 @@ const PickersFilledInputRoot = styled(PickersInputBaseRoot, { [`&.${pickersFilledInputClasses.disabled}`]: { backgroundColor: theme.vars ? theme.vars.palette.FilledInput.disabledBg : disabledBackground, }, - ...(!ownerState.disableUnderline && { - '&::after': { - borderBottom: `2px solid ${ - (theme.vars || theme).palette[ownerState.color || 'primary']?.main - }`, - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '""', - position: 'absolute', - right: 0, - transform: 'scaleX(0)', - transition: theme.transitions.create('transform', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&.${pickersFilledInputClasses.focused}:after`]: { - // translateX(0) is a workaround for Safari transform scale bug - // See https://github.com/mui/material-ui/issues/31766 - transform: 'scaleX(1) translateX(0)', - }, - [`&.${pickersFilledInputClasses.error}`]: { - '&:before, &:after': { - borderBottomColor: (theme.vars || theme).palette.error.main, + variants: [ + ...Object.keys((theme.vars ?? theme).palette) + // @ts-ignore + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color, disableUnderline: false }, + style: { + '&::after': { + // @ts-ignore + borderBottom: `2px solid ${(theme.vars || theme).palette[color]?.main}`, + }, + }, + })), + { + props: { disableUnderline: false }, + style: { + '&::after': { + left: 0, + bottom: 0, + // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 + content: '""', + position: 'absolute', + right: 0, + transform: 'scaleX(0)', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shorter, + easing: theme.transitions.easing.easeOut, + }), + pointerEvents: 'none', // Transparent to the hover style. + }, + [`&.${pickersFilledInputClasses.focused}:after`]: { + // translateX(0) is a workaround for Safari transform scale bug + // See https://github.com/mui/material-ui/issues/31766 + transform: 'scaleX(1) translateX(0)', + }, + [`&.${pickersFilledInputClasses.error}`]: { + '&:before, &:after': { + borderBottomColor: (theme.vars || theme).palette.error.main, + }, + }, + '&::before': { + borderBottom: `1px solid ${ + theme.vars + ? `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${theme.vars.opacity.inputUnderline})` + : bottomLineColor + }`, + left: 0, + bottom: 0, + // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 + content: '"\\00a0"', + position: 'absolute', + right: 0, + transition: theme.transitions.create('border-bottom-color', { + duration: theme.transitions.duration.shorter, + }), + pointerEvents: 'none', // Transparent to the hover style. + }, + [`&:hover:not(.${pickersFilledInputClasses.disabled}, .${pickersFilledInputClasses.error}):before`]: + { + borderBottom: `1px solid ${(theme.vars || theme).palette.text.primary}`, + }, + [`&.${pickersFilledInputClasses.disabled}:before`]: { + borderBottomStyle: 'dotted', + }, }, }, - '&::before': { - borderBottom: `1px solid ${ - theme.vars - ? `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${theme.vars.opacity.inputUnderline})` - : bottomLineColor - }`, - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '"\\00a0"', - position: 'absolute', - right: 0, - transition: theme.transitions.create('border-bottom-color', { - duration: theme.transitions.duration.shorter, - }), - pointerEvents: 'none', // Transparent to the hover style. + { + props: ({ startAdornment }: OwnerStateType) => !!startAdornment, + style: { + paddingLeft: 12, + }, }, - [`&:hover:not(.${pickersFilledInputClasses.disabled}, .${pickersFilledInputClasses.error}):before`]: - { - borderBottom: `1px solid ${(theme.vars || theme).palette.text.primary}`, + { + props: ({ endAdornment }: OwnerStateType) => !!endAdornment, + style: { + paddingRight: 12, }, - [`&.${pickersFilledInputClasses.disabled}:before`]: { - borderBottomStyle: 'dotted', }, - }), - ...(ownerState.startAdornment && { - paddingLeft: 12, - }), - ...(ownerState.endAdornment && { - paddingRight: 12, - }), + ], }; }); @@ -117,31 +139,47 @@ const PickersFilledSectionsContainer = styled(PickersInputBaseSectionsContainer, name: 'MuiPickersFilledInput', slot: 'sectionsContainer', overridesResolver: (props, styles) => styles.sectionsContainer, -})<{ ownerState: OwnerStateType }>(({ ownerState }) => ({ +})<{ ownerState: OwnerStateType }>({ paddingTop: 25, paddingRight: 12, paddingBottom: 8, paddingLeft: 12, - ...(ownerState.size === 'small' && { - paddingTop: 21, - paddingBottom: 4, - }), - ...(ownerState.startAdornment && { - paddingLeft: 0, - }), - ...(ownerState.endAdornment && { - paddingRight: 0, - }), - ...(ownerState.hiddenLabel && { - paddingTop: 16, - paddingBottom: 17, - }), - ...(ownerState.hiddenLabel && - ownerState.size === 'small' && { - paddingTop: 8, - paddingBottom: 9, - }), -})); + variants: [ + { + props: { size: 'small' }, + style: { + paddingTop: 21, + paddingBottom: 4, + }, + }, + { + props: ({ startAdornment }: OwnerStateType) => !!startAdornment, + style: { + paddingLeft: 0, + }, + }, + { + props: ({ endAdornment }: OwnerStateType) => !!endAdornment, + style: { + paddingRight: 0, + }, + }, + { + props: { hiddenLabel: true }, + style: { + paddingTop: 16, + paddingBottom: 17, + }, + }, + { + props: { hiddenLabel: true, size: 'small' }, + style: { + paddingTop: 8, + paddingBottom: 9, + }, + }, + ], +}); const useUtilityClasses = (ownerState: OwnerStateType) => { const { classes, disableUnderline } = ownerState; @@ -175,7 +213,13 @@ const PickersFilledInput = React.forwardRef(function PickersFilledInput( name: 'MuiPickersFilledInput', }); - const { label, autoFocus, ownerState: ownerStateProp, ...other } = props; + const { + label, + autoFocus, + disableUnderline = false, + ownerState: ownerStateProp, + ...other + } = props; const muiFormControl = useFormControl(); @@ -190,6 +234,7 @@ const PickersFilledInput = React.forwardRef(function PickersFilledInput( return ( styles.root, -})<{ ownerState: OwnerStateType }>(({ theme, ownerState }) => { +})<{ ownerState: OwnerStateType }>(({ theme }) => { const light = theme.palette.mode === 'light'; let bottomLineColor = light ? 'rgba(0, 0, 0, 0.42)' : 'rgba(255, 255, 255, 0.7)'; if (theme.vars) { @@ -26,58 +26,73 @@ const PickersInputRoot = styled(PickersInputBaseRoot, { 'label + &': { marginTop: 16, }, - ...(!ownerState.disableUnderline && { - '&::after': { - background: 'red', + variants: [ + ...Object.keys((theme.vars ?? theme).palette) // @ts-ignore - borderBottom: `2px solid ${(theme.vars || theme).palette[ownerState.color].main}`, - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '""', - position: 'absolute', - right: 0, - transform: 'scaleX(0)', - transition: theme.transitions.create('transform', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&.${pickersInputClasses.focused}:after`]: { - // translateX(0) is a workaround for Safari transform scale bug - // See https://github.com/mui/material-ui/issues/31766 - transform: 'scaleX(1) translateX(0)', - }, - [`&.${pickersInputClasses.error}`]: { - '&:before, &:after': { - borderBottomColor: (theme.vars || theme).palette.error.main, - }, - }, - '&::before': { - borderBottom: `1px solid ${bottomLineColor}`, - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '"\\00a0"', - position: 'absolute', - right: 0, - transition: theme.transitions.create('border-bottom-color', { - duration: theme.transitions.duration.shorter, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&:hover:not(.${pickersInputClasses.disabled}, .${pickersInputClasses.error}):before`]: { - borderBottom: `2px solid ${(theme.vars || theme).palette.text.primary}`, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - borderBottom: `1px solid ${bottomLineColor}`, + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color }, + style: { + '&::after': { + // @ts-ignore + borderBottom: `2px solid ${(theme.vars || theme).palette[color].main}`, + }, + }, + })), + { + props: { disableUnderline: false }, + style: { + '&::after': { + background: 'red', + left: 0, + bottom: 0, + // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 + content: '""', + position: 'absolute', + right: 0, + transform: 'scaleX(0)', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shorter, + easing: theme.transitions.easing.easeOut, + }), + pointerEvents: 'none', // Transparent to the hover style. + }, + [`&.${pickersInputClasses.focused}:after`]: { + // translateX(0) is a workaround for Safari transform scale bug + // See https://github.com/mui/material-ui/issues/31766 + transform: 'scaleX(1) translateX(0)', + }, + [`&.${pickersInputClasses.error}`]: { + '&:before, &:after': { + borderBottomColor: (theme.vars || theme).palette.error.main, + }, + }, + '&::before': { + borderBottom: `1px solid ${bottomLineColor}`, + left: 0, + bottom: 0, + // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 + content: '"\\00a0"', + position: 'absolute', + right: 0, + transition: theme.transitions.create('border-bottom-color', { + duration: theme.transitions.duration.shorter, + }), + pointerEvents: 'none', // Transparent to the hover style. + }, + [`&:hover:not(.${pickersInputClasses.disabled}, .${pickersInputClasses.error}):before`]: { + borderBottom: `2px solid ${(theme.vars || theme).palette.text.primary}`, + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + borderBottom: `1px solid ${bottomLineColor}`, + }, + }, + [`&.${pickersInputClasses.disabled}:before`]: { + borderBottomStyle: 'dotted', + }, }, }, - [`&.${pickersInputClasses.disabled}:before`]: { - borderBottomStyle: 'dotted', - }, - }), + ], }; }); @@ -113,7 +128,13 @@ const PickersInput = React.forwardRef(function PickersInput( name: 'MuiPickersInput', }); - const { label, autoFocus, ownerState: ownerStateProp, ...other } = props; + const { + label, + autoFocus, + disableUnderline = false, + ownerState: ownerStateProp, + ...other + } = props; const muiFormControl = useFormControl(); @@ -121,6 +142,7 @@ const PickersInput = React.forwardRef(function PickersInput( ...props, ...ownerStateProp, ...muiFormControl, + disableUnderline, color: muiFormControl?.color || 'primary', }; const classes = useUtilityClasses(ownerState); @@ -198,6 +220,11 @@ PickersInput.propTypes = { }), }), ]), + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, /** * The components used for each slot inside. * diff --git a/packages/x-date-pickers/src/PickersTextField/PickersInputBase/PickersInputBase.tsx b/packages/x-date-pickers/src/PickersTextField/PickersInputBase/PickersInputBase.tsx index 6d37ff718641..1ec0341917be 100644 --- a/packages/x-date-pickers/src/PickersTextField/PickersInputBase/PickersInputBase.tsx +++ b/packages/x-date-pickers/src/PickersTextField/PickersInputBase/PickersInputBase.tsx @@ -6,6 +6,7 @@ import useForkRef from '@mui/utils/useForkRef'; import { refType } from '@mui/utils'; import composeClasses from '@mui/utils/composeClasses'; import capitalize from '@mui/utils/capitalize'; +import { useSlotProps } from '@mui/base/utils'; import visuallyHidden from '@mui/utils/visuallyHidden'; import { pickersInputBaseClasses, @@ -26,7 +27,7 @@ export const PickersInputBaseRoot = styled('div', { name: 'MuiPickersInputBase', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: OwnerStateType }>(({ theme, ownerState }) => ({ +})<{ ownerState: OwnerStateType }>(({ theme }) => ({ ...theme.typography.body1, color: (theme.vars || theme).palette.text.primary, cursor: 'text', @@ -37,16 +38,19 @@ export const PickersInputBaseRoot = styled('div', { position: 'relative', boxSizing: 'border-box', // Prevent padding issue with fullWidth. letterSpacing: `${round(0.15 / 16)}em`, - ...(ownerState.fullWidth && { - width: '100%', - }), + variants: [ + { + props: { fullWidth: true }, + style: { width: '100%' }, + }, + ], })); export const PickersInputBaseSectionsContainer = styled(PickersSectionListRoot, { name: 'MuiPickersInputBase', slot: 'SectionsContainer', overridesResolver: (props, styles) => styles.sectionsContainer, -})<{ ownerState: OwnerStateType }>(({ theme, ownerState }) => ({ +})<{ ownerState: OwnerStateType }>(({ theme }) => ({ padding: '4px 0 5px', fontFamily: theme.typography.fontFamily, fontSize: 'inherit', @@ -59,22 +63,34 @@ export const PickersInputBaseSectionsContainer = styled(PickersSectionListRoot, letterSpacing: 'inherit', // Baseline behavior width: '182px', - ...(ownerState.size === 'small' && { - paddingTop: 1, - }), ...(theme.direction === 'rtl' && { textAlign: 'right /*! @noflip */' as any }), - ...(!(ownerState.adornedStart || ownerState.focused || ownerState.filled) && { - color: 'currentColor', - ...(ownerState.label == null && - (theme.vars + variants: [ + { + props: { size: 'small' }, + style: { + paddingTop: 1, + }, + }, + { + props: { adornedStart: false, focused: false, filled: false }, + style: { + color: 'currentColor', + opacity: 0, + }, + }, + { + // Can't use the object notation because label can be null or undefined + props: ({ adornedStart, focused, filled, label }: OwnerStateType) => + !adornedStart && !focused && !filled && label == null, + style: theme.vars ? { opacity: theme.vars.opacity.inputPlaceholder, } : { opacity: theme.palette.mode === 'light' ? 0.42 : 0.5, - })), - ...(ownerState.label != null && { opacity: 0 }), - }), + }, + }, + ], })); const PickersInputBaseSection = styled(PickersSectionListSection, { @@ -185,6 +201,7 @@ const PickersInputBase = React.forwardRef(function PickersInputBase( startAdornment, renderSuffix, slots, + slotProps, contentEditable, tabIndex, onInput, @@ -247,16 +264,22 @@ const PickersInputBase = React.forwardRef(function PickersInputBase( const classes = useUtilityClasses(ownerState); const InputRoot = slots?.root || PickersInputBaseRoot; + const inputRootProps = useSlotProps({ + elementType: InputRoot, + externalSlotProps: slotProps?.root, + externalForwardedProps: other, + additionalProps: { + 'aria-invalid': muiFormControl.error, + ref: handleRootRef, + }, + className: classes.root, + ownerState, + }); + const InputSectionsContainer = slots?.input || PickersInputBaseSectionsContainer; return ( - + {startAdornment} ({ fontSize: 'inherit', })); -const OutlineLegend = styled('legend')<{ ownerState: any }>(({ ownerState, theme }) => ({ +const OutlineLegend = styled('legend')<{ ownerState: any }>(({ theme }) => ({ float: 'unset', // Fix conflict with bootstrap width: 'auto', // Fix conflict with bootstrap overflow: 'hidden', // Fix Horizontal scroll when label too long - ...(!ownerState.withLabel && { - padding: 0, - lineHeight: '11px', // sync with `height` in `legend` styles - transition: theme.transitions.create('width', { - duration: 150, - easing: theme.transitions.easing.easeOut, - }), - }), - ...(ownerState.withLabel && { - display: 'block', // Fix conflict with normalize.css and sanitize.css - padding: 0, - height: 11, // sync with `lineHeight` in `legend` styles - fontSize: '0.75em', - visibility: 'hidden', - maxWidth: 0.01, - transition: theme.transitions.create('max-width', { - duration: 50, - easing: theme.transitions.easing.easeOut, - }), - whiteSpace: 'nowrap', - '& > span': { - paddingLeft: 5, - paddingRight: 5, - display: 'inline-block', - opacity: 0, - visibility: 'visible', + variants: [ + { + props: { withLabel: false }, + style: { + padding: 0, + lineHeight: '11px', // sync with `height` in `legend` styles + transition: theme.transitions.create('width', { + duration: 150, + easing: theme.transitions.easing.easeOut, + }), + }, }, - ...(ownerState.notched && { - maxWidth: '100%', - transition: theme.transitions.create('max-width', { - duration: 100, - easing: theme.transitions.easing.easeOut, - delay: 50, - }), - }), - }), + { + props: { withLabel: true }, + style: { + display: 'block', // Fix conflict with normalize.css and sanitize.css + padding: 0, + height: 11, // sync with `lineHeight` in `legend` styles + fontSize: '0.75em', + visibility: 'hidden', + maxWidth: 0.01, + transition: theme.transitions.create('max-width', { + duration: 50, + easing: theme.transitions.easing.easeOut, + }), + whiteSpace: 'nowrap', + '& > span': { + paddingLeft: 5, + paddingRight: 5, + display: 'inline-block', + opacity: 0, + visibility: 'visible', + }, + }, + }, + { + props: { withLabel: true, notched: true }, + style: { + maxWidth: '100%', + transition: theme.transitions.create('max-width', { + duration: 100, + easing: theme.transitions.easing.easeOut, + delay: 50, + }), + }, + }, + ], })); /** diff --git a/packages/x-date-pickers/src/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.tsx b/packages/x-date-pickers/src/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.tsx index 2cca79b9691a..2dbcec6510a9 100644 --- a/packages/x-date-pickers/src/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.tsx +++ b/packages/x-date-pickers/src/PickersTextField/PickersOutlinedInput/PickersOutlinedInput.tsx @@ -23,7 +23,7 @@ const PickersOutlinedInputRoot = styled(PickersInputBaseRoot, { name: 'MuiPickersOutlinedInput', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: OwnerStateType }>(({ theme, ownerState }) => { +})<{ ownerState: OwnerStateType }>(({ theme }) => { const borderColor = theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)'; return { @@ -42,8 +42,6 @@ const PickersOutlinedInputRoot = styled(PickersInputBaseRoot, { }, [`&.${pickersOutlinedInputClasses.focused} .${pickersOutlinedInputClasses.notchedOutline}`]: { borderStyle: 'solid', - // @ts-ignore - borderColor: (theme.vars || theme).palette[ownerState.color].main, borderWidth: 2, }, [`&.${pickersOutlinedInputClasses.disabled}`]: { @@ -57,6 +55,19 @@ const PickersOutlinedInputRoot = styled(PickersInputBaseRoot, { [`&.${pickersOutlinedInputClasses.error} .${pickersOutlinedInputClasses.notchedOutline}`]: { borderColor: (theme.vars || theme).palette.error.main, }, + variants: Object.keys((theme.vars ?? theme).palette) + // @ts-ignore + .filter((key) => (theme.vars ?? theme).palette[key].main) + .map((color) => ({ + props: { color }, + style: { + [`&.${pickersOutlinedInputClasses.focused}:not(.${pickersOutlinedInputClasses.error}) .${pickersOutlinedInputClasses.notchedOutline}`]: + { + // @ts-ignore + borderColor: (theme.vars || theme).palette[color].main, + }, + }, + })), }; }); @@ -64,12 +75,17 @@ const PickersOutlinedInputSectionsContainer = styled(PickersInputBaseSectionsCon name: 'MuiPickersOutlinedInput', slot: 'SectionsContainer', overridesResolver: (props, styles) => styles.sectionsContainer, -})<{ ownerState: OwnerStateType }>(({ ownerState }) => ({ +})<{ ownerState: OwnerStateType }>({ padding: '16.5px 0', - ...(ownerState.size === 'small' && { - padding: '8.5px 0', - }), -})); + variants: [ + { + props: { size: 'small' }, + style: { + padding: '8.5px 0', + }, + }, + ], +}); const useUtilityClasses = (ownerState: OwnerStateType) => { const { classes } = ownerState; @@ -207,6 +223,11 @@ PickersOutlinedInput.propTypes = { }), }), ]), + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, /** * The components used for each slot inside. * diff --git a/packages/x-date-pickers/src/TimeClock/Clock.tsx b/packages/x-date-pickers/src/TimeClock/Clock.tsx index 8b1562440561..23a583f9dd71 100644 --- a/packages/x-date-pickers/src/TimeClock/Clock.tsx +++ b/packages/x-date-pickers/src/TimeClock/Clock.tsx @@ -104,7 +104,7 @@ const ClockSquareMask = styled('div', { name: 'MuiClock', slot: 'SquareMask', overridesResolver: (_, styles) => styles.squareMask, -})<{ ownerState: ClockSquareMaskOwnerState }>(({ ownerState }) => ({ +})<{ ownerState: ClockSquareMaskOwnerState }>({ width: '100%', height: '100%', position: 'absolute', @@ -113,9 +113,10 @@ const ClockSquareMask = styled('div', { // Disable scroll capabilities. touchAction: 'none', userSelect: 'none', - ...(ownerState.disabled - ? {} - : { + variants: [ + { + props: { disabled: false }, + style: { '@media (pointer: fine)': { cursor: 'pointer', borderRadius: '50%', @@ -123,8 +124,10 @@ const ClockSquareMask = styled('div', { '&:active': { cursor: 'move', }, - }), -})); + }, + }, + ], +}); const ClockPin = styled('div', { name: 'MuiClock', @@ -145,7 +148,7 @@ const ClockAmButton = styled(IconButton, { name: 'MuiClock', slot: 'AmButton', overridesResolver: (_, styles) => styles.amButton, -})<{ ownerState: ClockProps }>(({ theme, ownerState }) => ({ +})<{ ownerState: ClockProps }>(({ theme }) => ({ zIndex: 1, position: 'absolute', bottom: 8, @@ -153,20 +156,25 @@ const ClockAmButton = styled(IconButton, { paddingLeft: 4, paddingRight: 4, width: CLOCK_HOUR_WIDTH, - ...(ownerState.meridiemMode === 'am' && { - backgroundColor: (theme.vars || theme).palette.primary.main, - color: (theme.vars || theme).palette.primary.contrastText, - '&:hover': { - backgroundColor: (theme.vars || theme).palette.primary.light, + variants: [ + { + props: { meridiemMode: 'am' }, + style: { + backgroundColor: (theme.vars || theme).palette.primary.main, + color: (theme.vars || theme).palette.primary.contrastText, + '&:hover': { + backgroundColor: (theme.vars || theme).palette.primary.light, + }, + }, }, - }), + ], })); const ClockPmButton = styled(IconButton, { name: 'MuiClock', slot: 'PmButton', overridesResolver: (_, styles) => styles.pmButton, -})<{ ownerState: ClockProps }>(({ theme, ownerState }) => ({ +})<{ ownerState: ClockProps }>(({ theme }) => ({ zIndex: 1, position: 'absolute', bottom: 8, @@ -174,13 +182,18 @@ const ClockPmButton = styled(IconButton, { paddingLeft: 4, paddingRight: 4, width: CLOCK_HOUR_WIDTH, - ...(ownerState.meridiemMode === 'pm' && { - backgroundColor: (theme.vars || theme).palette.primary.main, - color: (theme.vars || theme).palette.primary.contrastText, - '&:hover': { - backgroundColor: (theme.vars || theme).palette.primary.light, + variants: [ + { + props: { meridiemMode: 'pm' }, + style: { + backgroundColor: (theme.vars || theme).palette.primary.main, + color: (theme.vars || theme).palette.primary.contrastText, + '&:hover': { + backgroundColor: (theme.vars || theme).palette.primary.light, + }, + }, }, - }), + ], })); const ClockMeridiemText = styled(Typography, { @@ -212,7 +225,7 @@ export function Clock(inProps: ClockProps) selectedId, type, viewValue, - disabled, + disabled = false, readOnly, className, } = props; diff --git a/packages/x-date-pickers/src/TimeClock/ClockNumber.tsx b/packages/x-date-pickers/src/TimeClock/ClockNumber.tsx index 90b48c09b5ba..bab778903644 100644 --- a/packages/x-date-pickers/src/TimeClock/ClockNumber.tsx +++ b/packages/x-date-pickers/src/TimeClock/ClockNumber.tsx @@ -40,7 +40,7 @@ const ClockNumberRoot = styled('span', { { [`&.${clockNumberClasses.disabled}`]: styles.disabled }, { [`&.${clockNumberClasses.selected}`]: styles.selected }, ], -})<{ ownerState: ClockNumberProps }>(({ theme, ownerState }) => ({ +})<{ ownerState: ClockNumberProps }>(({ theme }) => ({ height: CLOCK_HOUR_WIDTH, width: CLOCK_HOUR_WIDTH, position: 'absolute', @@ -61,10 +61,15 @@ const ClockNumberRoot = styled('span', { pointerEvents: 'none', color: (theme.vars || theme).palette.text.disabled, }, - ...(ownerState.inner && { - ...theme.typography.body2, - color: (theme.vars || theme).palette.text.secondary, - }), + variants: [ + { + props: { inner: true }, + style: { + ...theme.typography.body2, + color: (theme.vars || theme).palette.text.secondary, + }, + }, + ], })); /** diff --git a/packages/x-date-pickers/src/TimeClock/ClockPointer.tsx b/packages/x-date-pickers/src/TimeClock/ClockPointer.tsx index a77c40b32736..735b228846ec 100644 --- a/packages/x-date-pickers/src/TimeClock/ClockPointer.tsx +++ b/packages/x-date-pickers/src/TimeClock/ClockPointer.tsx @@ -34,16 +34,21 @@ const ClockPointerRoot = styled('div', { overridesResolver: (_, styles) => styles.root, })<{ ownerState: ClockPointerProps & ClockPointerState; -}>(({ theme, ownerState }) => ({ +}>(({ theme }) => ({ width: 2, backgroundColor: (theme.vars || theme).palette.primary.main, position: 'absolute', left: 'calc(50% - 1px)', bottom: '50%', transformOrigin: 'center bottom 0px', - ...(ownerState.shouldAnimate && { - transition: theme.transitions.create(['transform', 'height']), - }), + variants: [ + { + props: { shouldAnimate: true }, + style: { + transition: theme.transitions.create(['transform', 'height']), + }, + }, + ], })); const ClockPointerThumb = styled('div', { @@ -52,7 +57,7 @@ const ClockPointerThumb = styled('div', { overridesResolver: (_, styles) => styles.thumb, })<{ ownerState: ClockPointerProps & ClockPointerState; -}>(({ theme, ownerState }) => ({ +}>(({ theme }) => ({ width: 4, height: 4, backgroundColor: (theme.vars || theme).palette.primary.contrastText, @@ -62,9 +67,14 @@ const ClockPointerThumb = styled('div', { left: `calc(50% - ${CLOCK_HOUR_WIDTH / 2}px)`, border: `${(CLOCK_HOUR_WIDTH - 4) / 2}px solid ${(theme.vars || theme).palette.primary.main}`, boxSizing: 'content-box', - ...(ownerState.hasSelected && { - backgroundColor: (theme.vars || theme).palette.primary.main, - }), + variants: [ + { + props: { hasSelected: true }, + style: { + backgroundColor: (theme.vars || theme).palette.primary.main, + }, + }, + ], })); /** diff --git a/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx b/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx index ba4fd2c6e61f..ff2bf30ac5fd 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx @@ -81,16 +81,21 @@ const TimePickerToolbarHourMinuteLabel = styled('div', { ], })<{ ownerState: TimePickerToolbarProps; -}>(({ theme, ownerState }) => ({ +}>(({ theme }) => ({ display: 'flex', justifyContent: 'flex-end', alignItems: 'flex-end', - ...(ownerState.isLandscape && { - marginTop: 'auto', - }), ...(theme.direction === 'rtl' && { flexDirection: 'row-reverse', }), + variants: [ + { + props: { isLandscape: true }, + style: { + marginTop: 'auto', + }, + }, + ], })); TimePickerToolbarHourMinuteLabel.propTypes = { @@ -117,21 +122,26 @@ const TimePickerToolbarAmPmSelection = styled('div', { ], })<{ ownerState: TimePickerToolbarProps; -}>(({ ownerState }) => ({ +}>({ display: 'flex', flexDirection: 'column', marginRight: 'auto', marginLeft: 12, - ...(ownerState.isLandscape && { - margin: '4px 0 auto', - flexDirection: 'row', - justifyContent: 'space-around', - flexBasis: '100%', - }), [`& .${timePickerToolbarClasses.ampmLabel}`]: { fontSize: 17, }, -})); + variants: [ + { + props: { isLandscape: true }, + style: { + margin: '4px 0 auto', + flexDirection: 'row', + justifyContent: 'space-around', + flexBasis: '100%', + }, + }, + ], +}); TimePickerToolbarAmPmSelection.propTypes = { // ----------------------------- Warning -------------------------------- diff --git a/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx b/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx index 8dce10be09ff..cc8248d37797 100644 --- a/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx +++ b/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx @@ -43,12 +43,13 @@ const PickersYearRoot = styled('div', { name: 'MuiPickersYear', slot: 'Root', overridesResolver: (_, styles) => [styles.root], -})<{ ownerState: PickersYearProps }>(({ ownerState }) => ({ - flexBasis: ownerState.yearsPerRow === 3 ? '33.3%' : '25%', +})<{ ownerState: PickersYearProps }>({ display: 'flex', alignItems: 'center', justifyContent: 'center', -})); + flexBasis: '33.3%', + variants: [{ props: { yearsPerRow: 4 }, style: { flexBasis: '25%' } }], +}); const PickersYearButton = styled('button', { name: 'MuiPickersYear', diff --git a/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.tsx b/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.tsx index 926e68a1c033..6ade141f2f38 100644 --- a/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.tsx @@ -38,11 +38,14 @@ const PickersArrowSwitcherButton = styled(IconButton, { overridesResolver: (props, styles) => styles.button, })<{ ownerState: PickersArrowSwitcherProps; -}>(({ ownerState }) => ({ - ...(ownerState.hidden && { - visibility: 'hidden', - }), -})); +}>({ + variants: [ + { + props: { hidden: true }, + style: { visibility: 'hidden' }, + }, + ], +}); const useUtilityClasses = (ownerState: PickersArrowSwitcherOwnerState) => { const { classes } = ownerState; diff --git a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx b/packages/x-date-pickers/src/internals/components/PickersPopper.tsx index 5e472471459b..6ad3bdedcdfb 100644 --- a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersPopper.tsx @@ -112,13 +112,19 @@ const PickersPopperPaper = styled(MuiPaper, { overridesResolver: (_, styles) => styles.paper, })<{ ownerState: PickersPopperOwnerState; -}>(({ ownerState }) => ({ +}>({ outline: 0, transformOrigin: 'top center', - ...(ownerState.placement.includes('top') && { - transformOrigin: 'bottom center', - }), -})); + variants: [ + { + props: ({ placement }: PickersPopperOwnerState) => + ['top', 'top-start', 'top-end'].includes(placement), + style: { + transformOrigin: 'bottom center', + }, + }, + ], +}); function clickedRootScrollbar(event: MouseEvent, doc: Document) { return ( diff --git a/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx b/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx index 97b41ca9b0f4..780756015029 100644 --- a/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersToolbar.tsx @@ -32,19 +32,24 @@ const PickersToolbarRoot = styled('div', { overridesResolver: (props, styles) => styles.root, })<{ ownerState: PickersToolbarProps; -}>(({ theme, ownerState }) => ({ +}>(({ theme }) => ({ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'space-between', padding: theme.spacing(2, 3), - ...(ownerState.isLandscape && { - height: 'auto', - maxWidth: 160, - padding: 16, - justifyContent: 'flex-start', - flexWrap: 'wrap', - }), + variants: [ + { + props: { isLandscape: true }, + style: { + height: 'auto', + maxWidth: 160, + padding: 16, + justifyContent: 'flex-start', + flexWrap: 'wrap', + }, + }, + ], })); const PickersToolbarContent = styled('div', { @@ -53,15 +58,31 @@ const PickersToolbarContent = styled('div', { overridesResolver: (props, styles) => styles.content, })<{ ownerState: PickersToolbarProps; -}>(({ ownerState }) => ({ +}>({ display: 'flex', flexWrap: 'wrap', width: '100%', - justifyContent: ownerState.isLandscape ? 'flex-start' : 'space-between', - flexDirection: ownerState.isLandscape ? ownerState.landscapeDirection ?? 'column' : 'row', flex: 1, - alignItems: ownerState.isLandscape ? 'flex-start' : 'center', -})); + justifyContent: 'space-between', + alignItems: 'center', + flexDirection: 'row', + variants: [ + { + props: { isLandscape: true }, + style: { + justifyContent: 'flex-start', + alignItems: 'flex-start', + flexDirection: 'column', + }, + }, + { + props: { isLandscape: true, landscapeDirection: 'row' }, + style: { + flexDirection: 'row', + }, + }, + ], +}); type PickersToolbarComponent = (( props: React.PropsWithChildren> &