Skip to content

Commit

Permalink
[pickers] Let the field components handle their opening UI, and allow…
Browse files Browse the repository at this point in the history
… field editing on mobile pickers (mui#15671)

Signed-off-by: Flavien DELANGLE <flaviendelangle@gmail.com>
Co-authored-by: Lukas Tyla <llukas.tyla@gmail.com>
  • Loading branch information
flaviendelangle and LukasTy authored Jan 14, 2025
1 parent c7f8cd6 commit 7c505ea
Show file tree
Hide file tree
Showing 152 changed files with 1,859 additions and 1,197 deletions.
4 changes: 2 additions & 2 deletions docs/data/date-pickers/base-concepts/base-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ Each _Picker_ is available in a responsive, desktop and mobile variant:
- The responsive component (for example `DatePicker`) which renders the desktop component or the mobile one depending on the device it runs on.

- The desktop component (for example `DesktopDatePicker`) which works best for mouse devices and large screens.
It renders the views inside a popover and allows editing values directly inside the field.
It renders the views inside a popover and a field for keyboard editing.

- The mobile component (for example `MobileDatePicker`) which works best for touch devices and small screens.
It renders the view inside a modal and does not allow editing values directly inside the field.
It renders the view inside a modal and a field for keyboard editing.

{{"demo": "ResponsivePickers.js"}}

Expand Down
13 changes: 2 additions & 11 deletions docs/data/date-pickers/calendar-systems/AdapterHijri.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,7 @@ const cacheRtl = createCache({

function ButtonDateTimeField(props) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
const {
InputProps,
slotProps,
slots,
ownerState,
label,
focused,
name,
...other
} = forwardedProps;
const { ownerState, label, focused, name, ...other } = forwardedProps;

const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
Expand All @@ -54,7 +45,7 @@ function ButtonDateTimeField(props) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
ref={InputProps?.ref}
ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
Expand Down
13 changes: 2 additions & 11 deletions docs/data/date-pickers/calendar-systems/AdapterHijri.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@ const cacheRtl = createCache({

function ButtonDateTimeField(props: DateTimePickerFieldProps) {
const { internalProps, forwardedProps } = useSplitFieldProps(props, 'date');
const {
InputProps,
slotProps,
slots,
ownerState,
label,
focused,
name,
...other
} = forwardedProps;
const { ownerState, label, focused, name, ...other } = forwardedProps;

const pickerContext = usePickerContext();
const parsedFormat = useParsedFormat();
Expand All @@ -58,7 +49,7 @@ function ButtonDateTimeField(props: DateTimePickerFieldProps) {
{...other}
variant="outlined"
color={hasValidationError ? 'error' : 'primary'}
ref={InputProps?.ref}
ref={pickerContext.triggerRef}
onClick={() => pickerContext.setOpen((prev) => !prev)}
>
{label ? `${label}: ${valueStr}` : valueStr}
Expand Down
18 changes: 14 additions & 4 deletions docs/data/date-pickers/custom-field/BrowserV7Field.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as React from 'react';
import useForkRef from '@mui/utils/useForkRef';
import { styled } from '@mui/material/styles';
import IconButton from '@mui/material/IconButton';
import { CalendarIcon } from '@mui/x-date-pickers/icons';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField';
import { Unstable_PickersSectionList as PickersSectionList } from '@mui/x-date-pickers/PickersSectionList';
import { usePickerContext } from '@mui/x-date-pickers/hooks';

const BrowserFieldRoot = styled('div', { name: 'BrowserField', slot: 'Root' })({
display: 'flex',
Expand Down Expand Up @@ -41,6 +44,8 @@ const BrowserDateField = React.forwardRef((props, ref) => {
onInput,
onPaste,
onKeyDown,
// Should be passed to the button that opens the picker
openPickerAriaLabel,
// Can be passed to a hidden <input /> element
onChange,
value,
Expand All @@ -55,16 +60,15 @@ const BrowserDateField = React.forwardRef((props, ref) => {
readOnly,
focused,
error,
InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},
// The rest can be passed to the root element
...other
} = fieldResponse;

const handleRef = useForkRef(InputPropsRef, ref);
const pickerContext = usePickerContext();
const handleRef = useForkRef(pickerContext.triggerRef, ref);

return (
<BrowserFieldRoot ref={handleRef} {...other}>
{startAdornment}
<BrowserFieldContent>
<PickersSectionList
elements={elements}
Expand All @@ -78,7 +82,13 @@ const BrowserDateField = React.forwardRef((props, ref) => {
onKeyDown={onKeyDown}
/>
</BrowserFieldContent>
{endAdornment}
<IconButton
onClick={() => pickerContext.setOpen((prev) => !prev)}
sx={{ marginLeft: 1.5 }}
aria-label={openPickerAriaLabel}
>
<CalendarIcon />
</IconButton>
</BrowserFieldRoot>
);
});
Expand Down
20 changes: 15 additions & 5 deletions docs/data/date-pickers/custom-field/BrowserV7Field.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react';
import useForkRef from '@mui/utils/useForkRef';
import { styled } from '@mui/material/styles';
import IconButton from '@mui/material/IconButton';
import { CalendarIcon } from '@mui/x-date-pickers/icons';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import {
Expand All @@ -10,6 +12,7 @@ import {
} from '@mui/x-date-pickers/DatePicker';
import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField';
import { Unstable_PickersSectionList as PickersSectionList } from '@mui/x-date-pickers/PickersSectionList';
import { usePickerContext } from '@mui/x-date-pickers/hooks';

const BrowserFieldRoot = styled('div', { name: 'BrowserField', slot: 'Root' })({
display: 'flex',
Expand Down Expand Up @@ -48,6 +51,9 @@ const BrowserDateField = React.forwardRef(
onPaste,
onKeyDown,

// Should be passed to the button that opens the picker
openPickerAriaLabel,

// Can be passed to a hidden <input /> element
onChange,
value,
Expand All @@ -66,17 +72,15 @@ const BrowserDateField = React.forwardRef(
focused,
error,

InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},

// The rest can be passed to the root element
...other
} = fieldResponse;

const handleRef = useForkRef(InputPropsRef, ref);
const pickerContext = usePickerContext();
const handleRef = useForkRef(pickerContext.triggerRef, ref);

return (
<BrowserFieldRoot ref={handleRef} {...other}>
{startAdornment}
<BrowserFieldContent>
<PickersSectionList
elements={elements}
Expand All @@ -90,7 +94,13 @@ const BrowserDateField = React.forwardRef(
onKeyDown={onKeyDown}
/>
</BrowserFieldContent>
{endAdornment}
<IconButton
onClick={() => pickerContext.setOpen((prev) => !prev)}
sx={{ marginLeft: 1.5 }}
aria-label={openPickerAriaLabel}
>
<CalendarIcon />
</IconButton>
</BrowserFieldRoot>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => {
const startTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
ownerState: { ...props, position: 'start' },
ownerState: { position: 'start' },
});

const endTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
ownerState: { ...props, position: 'end' },
ownerState: { position: 'end' },
});

const fieldResponse = useMultiInputDateRangeField({
Expand All @@ -119,6 +119,21 @@ const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => {
unstableEndFieldRef,
});

const {
// The multi input range field do not support clearable and onClear
onClear: onClearStartDate,
clearable: isStartDateClearable,
openPickerAriaLabel: openPickerStartDateAriaLabel,
...startDateProps
} = fieldResponse.startDate;
const {
// The multi input range field do not support clearable and onClear
onClear: onClearEndDate,
clearable: isEndDateClearable,
openPickerAriaLabel: openPickerEndDateAriaLabel,
...endDateProps
} = fieldResponse.endDate;

return (
<Stack
ref={ref}
Expand All @@ -127,9 +142,9 @@ const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => {
overflow="auto"
className={className}
>
<BrowserTextField {...fieldResponse.startDate} />
<BrowserTextField {...startDateProps} />
<span></span>
<BrowserTextField {...fieldResponse.endDate} />
<BrowserTextField {...endDateProps} />
</Stack>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ interface BrowserMultiInputDateRangeFieldProps
DateRangePickerFieldProps,
'unstableFieldRef' | 'clearable' | 'onClear'
>,
MultiInputFieldRefs {}
MultiInputFieldRefs {
slotProps: {
textField: any;
};
}

type BrowserMultiInputDateRangeFieldComponent = ((
props: BrowserMultiInputDateRangeFieldProps & React.RefAttributes<HTMLDivElement>,
Expand All @@ -130,13 +134,13 @@ const BrowserMultiInputDateRangeField = React.forwardRef(
const startTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
ownerState: { ...props, position: 'start' },
ownerState: { position: 'start' } as any,
}) as MultiInputFieldSlotTextFieldProps;

const endTextFieldProps = useSlotProps({
elementType: 'input',
externalSlotProps: slotProps?.textField,
ownerState: { ...props, position: 'end' },
ownerState: { position: 'end' } as any,
}) as MultiInputFieldSlotTextFieldProps;

const fieldResponse = useMultiInputDateRangeField<
Expand All @@ -158,6 +162,21 @@ const BrowserMultiInputDateRangeField = React.forwardRef(
unstableEndFieldRef,
});

const {
// The multi input range field do not support clearable and onClear
onClear: onClearStartDate,
clearable: isStartDateClearable,
openPickerAriaLabel: openPickerStartDateAriaLabel,
...startDateProps
} = fieldResponse.startDate;
const {
// The multi input range field do not support clearable and onClear
onClear: onClearEndDate,
clearable: isEndDateClearable,
openPickerAriaLabel: openPickerEndDateAriaLabel,
...endDateProps
} = fieldResponse.endDate;

return (
<Stack
ref={ref}
Expand All @@ -166,9 +185,9 @@ const BrowserMultiInputDateRangeField = React.forwardRef(
overflow="auto"
className={className}
>
<BrowserTextField {...fieldResponse.startDate} />
<BrowserTextField {...startDateProps} />
<span></span>
<BrowserTextField {...fieldResponse.endDate} />
<BrowserTextField {...endDateProps} />
</Stack>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,12 @@ const BrowserSingleInputDateRangeField = React.forwardRef((props, ref) => {
readOnly,
focused,
error,
InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},
// The rest can be passed to the root element
...other
} = fieldResponse;

const pickerContext = usePickerContext();
const handleRef = useForkRef(InputPropsRef, ref);
const handleRef = useForkRef(pickerContext.triggerRef, ref);

return (
<BrowserFieldRoot
Expand All @@ -75,7 +74,6 @@ const BrowserSingleInputDateRangeField = React.forwardRef((props, ref) => {
minWidth: 300,
}}
>
{startAdornment}
<BrowserFieldContent>
<PickersSectionList
elements={elements}
Expand All @@ -89,7 +87,6 @@ const BrowserSingleInputDateRangeField = React.forwardRef((props, ref) => {
onKeyDown={onKeyDown}
/>
</BrowserFieldContent>
{endAdornment}
<InputAdornment position="end">
<IconButton onClick={() => pickerContext.setOpen((prev) => !prev)}>
<DateRangeIcon />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,12 @@ const BrowserSingleInputDateRangeField = React.forwardRef(
focused,
error,

InputProps: { ref: InputPropsRef, startAdornment, endAdornment } = {},

// The rest can be passed to the root element
...other
} = fieldResponse;

const pickerContext = usePickerContext();
const handleRef = useForkRef(InputPropsRef, ref);
const handleRef = useForkRef(pickerContext.triggerRef, ref);

return (
<BrowserFieldRoot
Expand All @@ -94,7 +92,6 @@ const BrowserSingleInputDateRangeField = React.forwardRef(
minWidth: 300,
}}
>
{startAdornment}
<BrowserFieldContent>
<PickersSectionList
elements={elements}
Expand All @@ -108,7 +105,6 @@ const BrowserSingleInputDateRangeField = React.forwardRef(
onKeyDown={onKeyDown}
/>
</BrowserFieldContent>
{endAdornment}
<InputAdornment position="end">
<IconButton onClick={() => pickerContext.setOpen((prev) => !prev)}>
<DateRangeIcon />
Expand Down
Loading

0 comments on commit 7c505ea

Please sign in to comment.