From 706048932296d62e0721829a1647383acc3ab77e Mon Sep 17 00:00:00 2001 From: flavien Date: Tue, 26 Nov 2024 15:18:58 +0100 Subject: [PATCH] Work --- docs/data/date-pickers/rfc/calendar.md | 169 +++++++++++- docs/data/date-pickers/rfc/field.md | 346 +++++++++++++++++++------ docs/data/date-pickers/rfc/picker.md | 6 +- 3 files changed, 434 insertions(+), 87 deletions(-) diff --git a/docs/data/date-pickers/rfc/calendar.md b/docs/data/date-pickers/rfc/calendar.md index ac4cfa1aa5ab7..a5ec1a977ca3c 100644 --- a/docs/data/date-pickers/rfc/calendar.md +++ b/docs/data/date-pickers/rfc/calendar.md @@ -5,23 +5,182 @@ title: DX - Calendar # Fields -

This page describes how people can use date views with Material UI and how they can build custom date views.

+

This page describes how people can use date views with Material UI and how they can build custom date views.

:::success This page extends the initial proposal made in [#15598](https://github.com/mui/mui-x/issues/15598) ::: -## With Material UI +## With Material UI ### Basic usage -TODO +The `@mui/x-date-pickers` and `@mui/x-date-pickers-pro` packages expose four calendar components: +Those components are self-contained components (meaning they don't use composition). + +- `` + + Allows users to edit a date (its day in the month, its month and its year). + + ```tsx + import { DateCalendar } from '@mui/x-date-pickers/DateCalendar />'; + + ; + ``` + +- `` + + Allows users to edit the month of a date. + + ```tsx + import { MonthCalendar } from '@mui/x-date-pickers/MonthCalendar />'; + + ; + ``` + +- `` + + Allows users to edit the year of a date. + + ```tsx + import { YearCalendar } from '@mui/x-date-pickers/YearCalendar />'; + + ; + ``` + +- `` + + Allows users to edit a range of dates (for now it only handles the day in the month, but could handle the month and the year in the future). + + ```tsx + import { DateRangeCalendar } from '@mui/x-date-pickers/DateRangeCalendar />'; + + ; + ``` ### Override part of the UI -TODO +You can use slots to override part of the UI in self-contained components: + +```tsx + +``` + +:::success +The concept of slots does not fit this use case very well, but the exploration of a better DX to override part of the UI in self-contained component is outside the scope of this documentation, so this RFC uses the tools currently available. +::: + +The `` component can be built in a few different ways: + +1. From scratch: + + This is mostly viable for components that don't interact a lot with the picker state. For example, if someone wants to build a custom header for their calendar that just displays the current month, they could do it from scratch: + + ```tsx + import { useCalendarContext } from '@base-ui/x-date-pickers/Calendar'; + + function CustomCalendarHeader() { + const { currentMonth } = useCalendarContext(); + + return
{currentMonth.format('MMMM YYYY')}
; + } + ``` + + This is not the recommended way, but nothing prevents it. + + :::success + The `calendarHeader` slot does not receive `currentMonth` as a prop but instead access it using `useCalendarContext()`. + That way, this components can be composed more freely, it now only has to be mounted inside ``. + ::: + +2. Using the `Calendar.*` primitives exposed by `@base-ui/x-date-pickers/Calendar`: + + If the user wants to totally own the styling of this part of the UI (because he wants to build something really different from Material UI), he can use `Calendar.*` only for this part of the UI while still using `@mui/x-date-pickers` for everything he doesn't want to deeply customize: + + ```tsx + import { Calendar } from '@base-ui/x-date-pickers/Calendar'; + + function CustomCalendarHeader() { + return ( + + + + ); + } + ``` + +3. Using the `PickerCalendarHeader*` components exposed by `@mui/x-date-pickers/PickerCalendarHeader`: + + ```tsx + import { + PickersCalendarHeaderRoot, + PickersCalendarHeaderLabel, + } from '@mui/x-date-pickers/PickersCalendarHeader'; + + function CustomCalendarHeader() { + return ( + + + + + + ); + } + ``` + + :::success + The components like `` would be built on top of their `@base-ui/x-date-pickers/Calendar` counterparts and would be used to build ``. The packages expose several version but they don't have logic duplication. + Internally, the code would look something like that: + + ```tsx + import { Calendar } from '@base-ui/x-date-pickers/Calendar'; + + export const PickerCalendarHeaderRoot = styled(Calendar.Header.Root)({ + display: 'flex', + alignItems: 'center', + marginTop: 12, + marginBottom: 4, + paddingLeft: 24, + paddingRight: 12, + }); + + export const PickerCalendarHeaderLabel = styled(Calendar.Header.Label)({ + /** ... */ + }); + + // This component is purely presentational and not present in `@base-ui/x-date-pickers/Calendar'. + export const PickerCalendarHeaderLabelContainer = styled('div')({ + /** ... */ + }); + + export const PickerCalendarHeader = (props) => { + const { format, ...other } = props; + return ( + + + + + + ); + }; + ``` + + ::: + + :::success + This one is unclear. + Maybe those composable but styled components should only be exposed for parts of the UI where the Material UI implementation has some complexity and people want to be able to use composition to customize it without going fully unstyled for this part of the UI. + + And if those composable but styled components are something worth doing, then they need to have a coherent export strategy. + Should it be: + + 1. `` like it would be today + 2. `` to match the exports from `@base-ui/x-date-pickers` + 3. Something else? + + ::: -## Without Material UI +## Without Material UI ### Basic usage diff --git a/docs/data/date-pickers/rfc/field.md b/docs/data/date-pickers/rfc/field.md index 7dbe01fd1cc0a..13fffc8b720aa 100644 --- a/docs/data/date-pickers/rfc/field.md +++ b/docs/data/date-pickers/rfc/field.md @@ -5,18 +5,18 @@ title: DX - Fields # Fields -

This page describes how people can use field with Material UI and how they can build custom fields while keeping the built-in editing behavior.

+

This page describes how people can use field with Material UI and how they can build custom fields while keeping the built-in editing behavior.

:::success This page extends the initial proposal made in [this Github comment](https://github.com/mui/mui-x/issues/14496#issuecomment-2348917294) ::: -## With Material UI +## Basic standalone usage -### Basic usage +### With Material UI The `@mui/x-date-pickers` package exposes one field per value type. -Those fields are self-contained components (meaning they don't use composition). +Those components are self-contained components (meaning they don't use composition). Here is a basic example of a ``: @@ -41,10 +41,129 @@ The behavior is the same for all the other fields: ``` :::success -I'm proposing to rename `` into `` for concision when the single input range fields will become the default and when the multi input range fields will be gathered into a single component. +This RFC proposes to rename `` into `` for concision when the single input range fields become the default and when the multi input range fields are gathered into a single component. ::: -### Multi input range field +:::success +All these self-contained components are built on top of `PickerField`. +An intermediary internal component will probably be needed to only create the component once and then have each field pass its `manager` to it. +::: + +### Without Material UI + +```tsx +import { useDateManager } from '@base-ui/x-date-pickers/managers'; +import { PickerField } from '@base-ui/x-date-pickers/PickerField'; + +function CustomDateField(props) { + const manager = useDateManager(); + + return ( + + + {(section) => ( + + + + + + )} + + + ); +} +``` + +The field can then be rendered just like the Material UI fields: + +```tsx + +``` + +## Basic usage inside a picker + +### With Material UI + +:::success +No DX change here compared to today +::: + +```tsx +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; + +; +``` + +:::success +The concept of slots does not fit this use case very well, but the exploration of a better DX to override part of the UI in self-contained component is outside the scope of this documentation, so this RFC uses the tools currently available. +::: + +### Without Material UI + +#### Inside a picker from `@mui/x-date-pickers` + +Even if most applications with a custom field probably want to remove `@mui/material` entirely, using these custom fields inside a self contained picker component from `@mui/x-date-pickers/DatePicker` is totally doable. +This can be useful for application using Material UI but with some specific needs for the fields or to allow for a gradual migration away from Material UI. + +```tsx +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; + +; +``` + +:::success +The concept of slots does not fit this use case very well, but the exploration of a better DX to override part of the UI in self-contained component is outside the scope of this documentation, so this RFC uses the tools currently available. +::: + +#### Inside an unstyled picker + +The custom field can also be used inside a picker built with the composable `Picker.*` component from `@base-ui/x-date-pickers/Picker`: + +```tsx +import { useDateManager } from '@base-ui/x-date-pickers/managers'; +import { Picker } from '@base-ui/x-date-pickers/Picker'; + +function CustomDatePicker(props) { + const manager = useDateManager(); + + return ( + + + + {/** See picker documentation */} + + + ); +} +``` + +People could of course also inline their field if they want: + +```tsx +import { useDateManager } from '@base-ui/x-date-pickers/managers'; +import { Picker } from '@base-ui/x-date-pickers/Picker'; + +function CustomDatePicker(props) { + const manager = useDateManager(); + + return ( + + {/** See demo above */} + + {/** See picker documentation */} + + + ); +} +``` + +:::success +When doing that, the `Picker.Field.Root` doesn't have to receive props like `minDate` because they will be accessed using `usePickerContext` (or maybe a dedicated `usePickerValidationContext`). This makes composition viable between the picker and the field (same for the views). +::: + +## Basic usage (multi input picker) + +### With Material UI The `@mui/x-date-pickers` package also exposes a component to create multi input range fields as follow: @@ -59,113 +178,182 @@ function MultiInputDateTimeRangeField(props: MultiInputDateTimeRangeFieldProps) } ``` +When used inside a picker, `` can be passed directly and will read `manager` from `usePickerContext`: + +```tsx + +``` + :::success -I have a POC of this in [#15505](https://github.com/mui/mui-x/pull/15505) +I have a POC of this in [#15505](https://github.com/mui/mui-x/pull/15505). ::: -## Without Material UI +### Without Material UI -### Basic usage +:::warning +This is not planned for now. +`Picker.*` could contain some additional components that would be the building blocks for `` but it does not seem to be the priority for now. +::: + +## Clearable field + +### With Material UI + +:::success +No DX change here compared to today +::: + +```tsx + + + +``` + +### Without Material UI + +People can use the `` component to add a button to clear the value: ```tsx import { useDateManager } from '@base-ui/x-date-pickers/managers'; import { PickerField } from '@base-ui/x-date-pickers/PickerField'; -// Misses some parts like the clear button for now function CustomDateField(props) { const manager = useDateManager(); return ( - - {(section) => ( - - - - - - )} - + {/** See demo above */} + + + ); } ``` -The field can then either be: +## Anatomy of `Picker.*` + +### `` + +Top level component that wraps the other components. +It would expend `Field.Root` from `@base-ui-components/react/Field`. + +#### Props + +- `manager`: `PickerManager` - **required for standalone fields** + + See [#15395](https://github.com/mui/mui-x/issues/15395) for context. + +- **Value props**: `value`, `defaultValue`, `referenceDate`, `onChange`, `onError` and `timezone`. + + Same typing and behavior as today. + +- **Validation props**: list based on the `manager` prop + + For `useDateManager()` it would be `maxDate`, `minDate`, `disableFuture`, `disablePast`, `shouldDisableDate`, `shouldDisableMonth`, `shouldDisableYear`. + + Same typing and behavior as today. + +- **Form props**: `disabled`, `readOnly`. + + Same typing and behavior as today. + +- `format`: `string` (default value applied by the `manager`). + +- `formatDensity`: `'dense' | 'spacious'`, default: `'dense'`. + +- `shouldRespectLeadingZeros`: `boolean`, default: `false`. + +- `selectedSections`: `FieldSelectedSections` + +- `onSelectedSectionsChange`: `(newValue: FieldSelectedSections) => void` + +- `unstableFieldRef`: `React.Ref>` + + This one may not be needed. If its only used for internal purpose, it can probably be made internal (the picker would pass a ref using a private context instead of a prop). + + Keeping it unstable will help removing it. + +- `autoFocus`: `boolean` + +:::success +All the props that the picker can pass to the field (validation props, value props, etc...) are read both from the props and from `usePickerContext` so that it can be used inside a picker built with composition. + +That way, users only have to pass the props specific to the field to ``: + +```tsx + + + {/** See demo above */} + + + {/** See picker documentation */} + + +``` + +::: + +### `` + +It would expend `Field.Control` from `@base-ui-components/react/Field`. + +It expects a function as it's children, which will be called for every section to render: + +```tsx +{(section) =>
{section.value}
}
+``` + +It also renders a hidden input which contains the stringified value and can be used for form submission and testing. + +#### Props + +- `children`: `(section: InferFieldSection) => React.ReactNode` + +### `` -1. Used as standalone: +Renders a single section (for instance the year of the hour of the current value). - ```tsx - - ``` +#### Props -2. Used inside a self-contained `` component from `@mui/x-date-pickers/DatePicker`: +- `children`: `React.ReactNode` +- `section`: `InferFieldSection` (can be `FieldSection` or `FieldRangeSection`) - **required**. - ```tsx - import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +### `` - ; - ``` +Renders the content of a single section. - :::success - The concept of slots does not fit this use-case very well, but the exploration of a better DX to override part of the UI in self-contained component is outside the scope of this documentation, so I'm using the tools we currently have. - ::: +#### Props -3. Used inside a composable `Picker.*` component from `@base-ui/x-date-pickers/Picker` +- nothing? - ```tsx - import { useDateManager } from '@base-ui/x-date-pickers/managers'; - import { Picker } from '@base-ui/x-date-pickers/Picker'; +### `` + +Renders the separator to display before or after the current section. + +```tsx + +``` - function CustomDatePicker(props) { - const manager = useDateManager(); +#### Props - return ( - - - - {/** See the pickers documentation for more details */} - - - ); - } - ``` +- `position`: `'before' | 'after'` - **required**. - People could of course also inline their field if they want: +### `` - ```tsx - import { useDateManager } from '@base-ui/x-date-pickers/managers'; - import { Picker } from '@base-ui/x-date-pickers/Picker'; +Renders the button to clear the value of the field. - function CustomDatePicker(props) { - const manager = useDateManager(); +#### Props - return ( - - - {/** See demo above */} - - - {/** See the pickers documentation for more details */} - - - ); - } - ``` +- `children`: `React.ReactNode` - :::warning - I'm wondering if we could make `manager` optional on `` and by default use the one exposed by the `` component in a React context. - That way people could create a single field component for all their pickers, as long as they don't want to use them as standalone. +- `onClear`: `React.MouseEventHandler` - If we go that way, then the `` component would no longer receive the `manager` prop in the component above: + :::success + The new DX is a good opportunity to discuss the behavior of this prop. + The behavior should either be: - ```tsx - - {/** See demo above */} - - {/** See the pickers documentation for more details */} - - - ``` + 1. `onClear` is defined on ``. It is also fired when doing Ctrl + A and then Backspace. + 2. `onClear` is defined on `` (or not defined at all and people just use `onClick`). It is only fired when clicking on this button. - ::: + ::: diff --git a/docs/data/date-pickers/rfc/picker.md b/docs/data/date-pickers/rfc/picker.md index e78b13123f36b..69f86d473e9e7 100644 --- a/docs/data/date-pickers/rfc/picker.md +++ b/docs/data/date-pickers/rfc/picker.md @@ -5,13 +5,13 @@ title: DX - Pickers # Fields -

This page describes how people can use picker with Material UI and how they can build custom pickers.

+

This page describes how people can use picker with Material UI and how they can build custom pickers.

:::success This page extends the initial proposal made in [#14718](https://github.com/mui/mui-x/issues/14718) ::: -## With Material UI +## With Material UI ### Basic usage @@ -21,7 +21,7 @@ TODO TODO -## Without Material UI +## Without Material UI ### Basic usage