Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[POC][pickers] Explore the Base UI Calendar component #16069

Draft
wants to merge 143 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
143 commits
Select commit Hold shift + click to select a range
94945e6
[POC][pickers] Explore the Base UI Calendar component
flaviendelangle Jan 3, 2025
7b43c65
Work
flaviendelangle Jan 3, 2025
5d8f364
Work
flaviendelangle Jan 4, 2025
6f97267
Work
flaviendelangle Jan 4, 2025
f199c3a
Clean
flaviendelangle Jan 4, 2025
736ee25
Work
flaviendelangle Jan 4, 2025
2b720cd
Work
flaviendelangle Jan 5, 2025
29c4a6f
Work
flaviendelangle Jan 5, 2025
fda4d43
Work
flaviendelangle Jan 5, 2025
dfeffff
Improve keyboard navigation
flaviendelangle Jan 5, 2025
e055cbe
Add demo
flaviendelangle Jan 5, 2025
2cd7ef0
Disable invalid values
flaviendelangle Jan 5, 2025
c5b9ffc
Fix
flaviendelangle Jan 5, 2025
25a76b9
Work
flaviendelangle Jan 5, 2025
1f56a5a
Work
flaviendelangle Jan 5, 2025
bfaf318
Improve demo
flaviendelangle Jan 5, 2025
3a09a2d
Work
flaviendelangle Jan 5, 2025
6be6fb8
Work
flaviendelangle Jan 5, 2025
cc6d56d
Add keyboard navigation across months
flaviendelangle Jan 5, 2025
d137663
Fix
flaviendelangle Jan 5, 2025
3bc4ff6
Work
flaviendelangle Jan 5, 2025
35aec85
Work
flaviendelangle Jan 5, 2025
f9bb1ef
Work
flaviendelangle Jan 5, 2025
e1f4663
Add navigation buttons
flaviendelangle Jan 5, 2025
38e7853
Work on demos
flaviendelangle Jan 5, 2025
cb59fcd
Work on demos
flaviendelangle Jan 5, 2025
fc01fb8
Fix
flaviendelangle Jan 5, 2025
d9de26f
Add support for tab index
flaviendelangle Jan 6, 2025
be70f63
Work
flaviendelangle Jan 6, 2025
75154f5
1st version of multi grid keyboard navigation works
flaviendelangle Jan 6, 2025
9020f78
Fix CSS
flaviendelangle Jan 6, 2025
9c7e6da
Remove unused code
flaviendelangle Jan 6, 2025
3fdfb53
Fix
flaviendelangle Jan 6, 2025
6a3efe5
Work
flaviendelangle Jan 6, 2025
b786c3a
Fix value section with multiple days
flaviendelangle Jan 6, 2025
f200c63
Work
flaviendelangle Jan 6, 2025
3fd505f
Work
flaviendelangle Jan 6, 2025
c9e6e9e
Fix demos
flaviendelangle Jan 6, 2025
2c9b3ec
Add support for month grid'
flaviendelangle Jan 6, 2025
d1458c2
Work
flaviendelangle Jan 6, 2025
e26e97b
Work
flaviendelangle Jan 6, 2025
ea655c8
Work
flaviendelangle Jan 6, 2025
63f5361
Work
flaviendelangle Jan 6, 2025
90b8030
Work
flaviendelangle Jan 6, 2025
4b3c24f
Work
flaviendelangle Jan 6, 2025
d87c117
Work
flaviendelangle Jan 6, 2025
b3191fd
Work
flaviendelangle Jan 6, 2025
99b125c
Work
flaviendelangle Jan 6, 2025
e8c25cd
Work
flaviendelangle Jan 6, 2025
c8cca4e
Work
flaviendelangle Jan 6, 2025
88a7adc
Work
flaviendelangle Jan 6, 2025
b85ee12
Work
flaviendelangle Jan 6, 2025
d43110d
Work
flaviendelangle Jan 6, 2025
be9e91a
Work
flaviendelangle Jan 6, 2025
879292f
Add cross year keyboard navigation in the month grid
flaviendelangle Jan 6, 2025
1111663
Work
flaviendelangle Jan 6, 2025
d8923b8
Work
flaviendelangle Jan 6, 2025
96c9ed5
Work
flaviendelangle Jan 6, 2025
e1561e2
Add decade demo
flaviendelangle Jan 6, 2025
3e38cba
Improve demos
flaviendelangle Jan 6, 2025
cfe40cf
Fix
flaviendelangle Jan 6, 2025
6564b18
Add data attrs and css vars files
flaviendelangle Jan 6, 2025
cd4f96b
Fix data attr name
flaviendelangle Jan 6, 2025
c9e77b2
Fix data attr name
flaviendelangle Jan 6, 2025
a9a2997
Remove useless contexts
flaviendelangle Jan 7, 2025
f485808
Work on demos
flaviendelangle Jan 7, 2025
0576bca
Work
flaviendelangle Jan 7, 2025
3ca7af1
Fix
flaviendelangle Jan 7, 2025
9bf4870
Work
flaviendelangle Jan 7, 2025
c3f8e0f
Work
flaviendelangle Jan 7, 2025
3487e5b
Work
flaviendelangle Jan 7, 2025
5f455fe
Add isInvalid state and data attr
flaviendelangle Jan 7, 2025
d236ff8
Start working on the range calendar
flaviendelangle Jan 7, 2025
2075c83
Work on range calendar
flaviendelangle Jan 7, 2025
70b871e
Add week row on range calendar
flaviendelangle Jan 7, 2025
3713e80
Work
flaviendelangle Jan 7, 2025
2ee4b70
Work
flaviendelangle Jan 9, 2025
9ae7954
Working on range calendar
flaviendelangle Jan 9, 2025
e16ee97
Basic range calendar working
flaviendelangle Jan 9, 2025
3fcf170
Work on drag & drop
flaviendelangle Jan 9, 2025
f1b72a9
Work
flaviendelangle Jan 9, 2025
bea8b97
Drag & drop basic version is working
flaviendelangle Jan 9, 2025
8995e23
WOrk
flaviendelangle Jan 9, 2025
9468ee1
Improve range demo
flaviendelangle Jan 9, 2025
6626330
Fix
flaviendelangle Jan 9, 2025
5c728a8
Fix
flaviendelangle Jan 9, 2025
721b418
Work
flaviendelangle Jan 10, 2025
bb8f365
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 10, 2025
1ec28a6
Add month and year navigation in range calendar
flaviendelangle Jan 10, 2025
ae3f22f
Add preview on range calendar
flaviendelangle Jan 10, 2025
33d7ecf
Work on preview
flaviendelangle Jan 10, 2025
9bb9a4d
Work
flaviendelangle Jan 10, 2025
314ef81
Work
flaviendelangle Jan 10, 2025
c256168
Fix
flaviendelangle Jan 10, 2025
906d945
Add automatic scroll on year list and grid
flaviendelangle Jan 10, 2025
ad3e4b6
Fix CI
flaviendelangle Jan 10, 2025
14b7876
Add auto scroll to month list and grid
flaviendelangle Jan 10, 2025
7703b30
Clean
flaviendelangle Jan 10, 2025
304ac5d
Clean
flaviendelangle Jan 10, 2025
7a37c89
Work
flaviendelangle Jan 10, 2025
9804618
Fix
flaviendelangle Jan 10, 2025
3d49a9e
Fix
flaviendelangle Jan 10, 2025
f116155
Add notes on the doc
flaviendelangle Jan 10, 2025
19b4db9
Work on mobile d&d
flaviendelangle Jan 13, 2025
01c32b5
Add focus on mount prop
flaviendelangle Jan 13, 2025
7594047
Fix
flaviendelangle Jan 13, 2025
02de511
Fix
flaviendelangle Jan 13, 2025
fef7e41
Add reversed order recipe
flaviendelangle Jan 13, 2025
a70b7a4
Fix
flaviendelangle Jan 13, 2025
d555a22
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 13, 2025
561b85e
Add booking demo
flaviendelangle Jan 13, 2025
d9aa45f
Improve CSS
flaviendelangle Jan 13, 2025
5d59149
Fix demo
flaviendelangle Jan 13, 2025
e447dc6
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 13, 2025
ab16486
Work
flaviendelangle Jan 13, 2025
3e7e6e7
Add data-empty to the roots
flaviendelangle Jan 13, 2025
c423ce0
Fix
flaviendelangle Jan 13, 2025
8e3591a
Work
flaviendelangle Jan 13, 2025
42fa613
Fix
flaviendelangle Jan 13, 2025
63af144
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 14, 2025
ef921e9
Start working on month range
flaviendelangle Jan 14, 2025
c511f4f
Work on dnd
flaviendelangle Jan 14, 2025
9506c88
Work
flaviendelangle Jan 14, 2025
19b867d
Basic month range picker working
flaviendelangle Jan 14, 2025
0ab7792
Clean code
flaviendelangle Jan 14, 2025
d862993
New demo disableHoverPreview
flaviendelangle Jan 14, 2025
158846e
Add years range picker
flaviendelangle Jan 14, 2025
d3af922
Work
flaviendelangle Jan 14, 2025
d930582
Work on range
flaviendelangle Jan 14, 2025
4f7c47f
Work
flaviendelangle Jan 14, 2025
c870fd0
clean
flaviendelangle Jan 14, 2025
990a4df
Add render function to the root components
flaviendelangle Jan 14, 2025
437913f
Add anatomy to the doc
flaviendelangle Jan 14, 2025
24e596b
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 15, 2025
7ff238c
Work on a full MD2 recipe
flaviendelangle Jan 15, 2025
9190799
Work on MD recipe
flaviendelangle Jan 15, 2025
e2b0bb7
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 15, 2025
bb8dafa
Slide work but super slow
flaviendelangle Jan 15, 2025
8352fa0
Work
flaviendelangle Jan 16, 2025
9fafcd4
Add explainations
flaviendelangle Jan 16, 2025
51b76ab
Fix view management
flaviendelangle Jan 16, 2025
d3e4c3b
Merge branch 'master' into base-ui-calendar
flaviendelangle Jan 16, 2025
8657331
Improve MUI demo
flaviendelangle Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ module.exports = {
rules: {
...baseline.rules,
...(ENABLE_REACT_COMPILER_PLUGIN ? { 'react-compiler/react-compiler': 'error' } : {}),
'@typescript-eslint/no-redeclare': 'off',
'import/export': 'off', // Mostly handled by Typescript itself. ESLint produces false positives with declaration merging.
// TODO move to @mui/monorepo, codebase is moving away from default exports https://github.com/mui/material-ui/issues/21862
'import/prefer-default-export': 'off',
'import/no-relative-packages': 'error',
Expand Down
179 changes: 179 additions & 0 deletions docs/data/date-pickers/base-calendar/DateCalendarDemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import * as React from 'react';

import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
// eslint-disable-next-line no-restricted-imports
import {
Calendar,
useCalendarContext,
} from '@mui/x-date-pickers/internals/base/Calendar';
import styles from './calendar.module.css';

function Header(props) {
const { activeSection, onActiveSectionChange } = props;
const { visibleDate } = useCalendarContext();

return (
<header className={styles.Header}>
<div className={styles.HeaderBlock}>
<Calendar.SetVisibleMonth
target="previous"
className={styles.SetVisibleMonth}
disabled={activeSection !== 'day' ? true : undefined}
>
</Calendar.SetVisibleMonth>
<button
type="button"
onClick={() =>
onActiveSectionChange(activeSection === 'month' ? 'day' : 'month')
}
disabled={activeSection === 'year' ? true : undefined}
className={styles.SetActiveSectionMonth}
>
{visibleDate.format('MMMM')}
</button>
<Calendar.SetVisibleMonth
target="next"
disabled={activeSection !== 'day' ? true : undefined}
className={styles.SetVisibleMonth}
>
</Calendar.SetVisibleMonth>
</div>
<div className={styles.HeaderBlock}>
<Calendar.SetVisibleYear
target="previous"
disabled={activeSection === 'year' ? true : undefined}
className={styles.SetVisibleYear}
>
</Calendar.SetVisibleYear>
<button
type="button"
onClick={() =>
onActiveSectionChange(activeSection === 'year' ? 'day' : 'year')
}
className={styles.SetActiveSectionYear}
>
{visibleDate.format('YYYY')}
</button>
<Calendar.SetVisibleYear
target="next"
disabled={activeSection === 'year' ? true : undefined}
className={styles.SetVisibleYear}
>
</Calendar.SetVisibleYear>
</div>
</header>
);
}

export default function DateCalendarDemo() {
const [value, setValue] = React.useState(null);
const [activeSection, setActiveSection] = React.useState('day');
const [hasNavigated, setHasNavigated] = React.useState(false);

const handleActiveSectionChange = React.useCallback(
(newActiveSection) => {
setActiveSection(newActiveSection);
setHasNavigated(true);
},
[setActiveSection, setHasNavigated],
);

const handleValueChange = React.useCallback(
(newValue, context) => {
if (context.section === 'month' || context.section === 'year') {
handleActiveSectionChange('day');
}

setValue(newValue);
},
[handleActiveSectionChange],
);

return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Calendar.Root
value={value}
onValueChange={handleValueChange}
className={styles.Root}
>
<Header
activeSection={activeSection}
onActiveSectionChange={handleActiveSectionChange}
/>
{activeSection === 'year' && (
<Calendar.YearsList
focusOnMount={hasNavigated}
className={styles.YearsList}
>
{({ years }) =>
years.map((year) => (
<Calendar.YearsCell
value={year}
className={styles.YearsCell}
key={year.toString()}
/>
))
}
</Calendar.YearsList>
)}
{activeSection === 'month' && (
<Calendar.MonthsList
focusOnMount={hasNavigated}
className={styles.MonthsList}
>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell
value={month}
className={styles.MonthsCell}
key={month.toString()}
/>
))
}
</Calendar.MonthsList>
)}
{activeSection === 'day' && (
<Calendar.DaysGrid focusOnMount={hasNavigated} className={styles.DaysGrid}>
<Calendar.DaysGridHeader className={styles.DaysGridHeader}>
{({ days }) =>
days.map((day) => (
<Calendar.DaysGridHeaderCell
value={day}
key={day.toString()}
className={styles.DaysGridHeaderCell}
/>
))
}
</Calendar.DaysGridHeader>
<Calendar.DaysGridBody className={styles.DaysGridBody}>
{({ weeks }) =>
weeks.map((week) => (
<Calendar.DaysWeekRow
value={week}
key={week.toString()}
className={styles.DaysWeekRow}
>
{({ days }) =>
days.map((day) => (
<Calendar.DaysCell
value={day}
key={day.toString()}
className={styles.DaysCell}
/>
))
}
</Calendar.DaysWeekRow>
))
}
</Calendar.DaysGridBody>
</Calendar.DaysGrid>
)}
</Calendar.Root>
</LocalizationProvider>
);
}
184 changes: 184 additions & 0 deletions docs/data/date-pickers/base-calendar/DateCalendarDemo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import * as React from 'react';
import { Dayjs } from 'dayjs';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
// eslint-disable-next-line no-restricted-imports
import {
Calendar,
useCalendarContext,
} from '@mui/x-date-pickers/internals/base/Calendar';
import styles from './calendar.module.css';

function Header(props: {
activeSection: 'day' | 'month' | 'year';
onActiveSectionChange: (newActiveSection: 'day' | 'month' | 'year') => void;
}) {
const { activeSection, onActiveSectionChange } = props;
const { visibleDate } = useCalendarContext();

return (
<header className={styles.Header}>
<div className={styles.HeaderBlock}>
<Calendar.SetVisibleMonth
target="previous"
className={styles.SetVisibleMonth}
disabled={activeSection !== 'day' ? true : undefined}
>
</Calendar.SetVisibleMonth>
<button
type="button"
onClick={() =>
onActiveSectionChange(activeSection === 'month' ? 'day' : 'month')
}
disabled={activeSection === 'year' ? true : undefined}
className={styles.SetActiveSectionMonth}
>
{visibleDate.format('MMMM')}
</button>
<Calendar.SetVisibleMonth
target="next"
disabled={activeSection !== 'day' ? true : undefined}
className={styles.SetVisibleMonth}
>
</Calendar.SetVisibleMonth>
</div>
<div className={styles.HeaderBlock}>
<Calendar.SetVisibleYear
target="previous"
disabled={activeSection === 'year' ? true : undefined}
className={styles.SetVisibleYear}
>
</Calendar.SetVisibleYear>
<button
type="button"
onClick={() =>
onActiveSectionChange(activeSection === 'year' ? 'day' : 'year')
}
className={styles.SetActiveSectionYear}
>
{visibleDate.format('YYYY')}
</button>
<Calendar.SetVisibleYear
target="next"
disabled={activeSection === 'year' ? true : undefined}
className={styles.SetVisibleYear}
>
</Calendar.SetVisibleYear>
</div>
</header>
);
}

export default function DateCalendarDemo() {
const [value, setValue] = React.useState<Dayjs | null>(null);
const [activeSection, setActiveSection] = React.useState<'day' | 'month' | 'year'>(
'day',
);
const [hasNavigated, setHasNavigated] = React.useState(false);

const handleActiveSectionChange = React.useCallback(
(newActiveSection: 'day' | 'month' | 'year') => {
setActiveSection(newActiveSection);
setHasNavigated(true);
},
[setActiveSection, setHasNavigated],
);

const handleValueChange = React.useCallback(
(newValue: Dayjs | null, context: Calendar.Root.ValueChangeHandlerContext) => {
if (context.section === 'month' || context.section === 'year') {
handleActiveSectionChange('day');
}

setValue(newValue);
},
[handleActiveSectionChange],
);

return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Calendar.Root
value={value}
onValueChange={handleValueChange}
className={styles.Root}
>
<Header
activeSection={activeSection}
onActiveSectionChange={handleActiveSectionChange}
/>
{activeSection === 'year' && (
<Calendar.YearsList
focusOnMount={hasNavigated}
className={styles.YearsList}
>
{({ years }) =>
years.map((year) => (
<Calendar.YearsCell
value={year}
className={styles.YearsCell}
key={year.toString()}
/>
))
}
</Calendar.YearsList>
)}
{activeSection === 'month' && (
<Calendar.MonthsList
focusOnMount={hasNavigated}
className={styles.MonthsList}
>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell
value={month}
className={styles.MonthsCell}
key={month.toString()}
/>
))
}
</Calendar.MonthsList>
)}
{activeSection === 'day' && (
<Calendar.DaysGrid focusOnMount={hasNavigated} className={styles.DaysGrid}>
<Calendar.DaysGridHeader className={styles.DaysGridHeader}>
{({ days }) =>
days.map((day) => (
<Calendar.DaysGridHeaderCell
value={day}
key={day.toString()}
className={styles.DaysGridHeaderCell}
/>
))
}
</Calendar.DaysGridHeader>
<Calendar.DaysGridBody className={styles.DaysGridBody}>
{({ weeks }) =>
weeks.map((week) => (
<Calendar.DaysWeekRow
value={week}
key={week.toString()}
className={styles.DaysWeekRow}
>
{({ days }) =>
days.map((day) => (
<Calendar.DaysCell
value={day}
key={day.toString()}
className={styles.DaysCell}
/>
))
}
</Calendar.DaysWeekRow>
))
}
</Calendar.DaysGridBody>
</Calendar.DaysGrid>
)}
</Calendar.Root>
</LocalizationProvider>
);
}
Loading
Loading