(createUtilsService('date-fns'));
6 |
7 | export const withUtilsService = (Component: React.ComponentType
) => {
8 | const withUtilsService: React.SFC> = (props) => (
9 |
10 | {(service) => }
11 |
12 | );
13 |
14 | withUtilsService.displayName = `withUtilsService(${Component.displayName || Component.name})`;
15 |
16 | return withUtilsService;
17 | };
18 |
--------------------------------------------------------------------------------
/docs/_shared/svgIcons/GithubIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { SvgIcon } from '@material-ui/core';
3 |
4 | const GitHub = (props) => (
5 |
6 |
7 |
8 | );
9 |
10 | export default GitHub;
11 |
--------------------------------------------------------------------------------
/docs/_shared/svgIcons/KawaiiIcon.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import useMediaQuery from '@material-ui/core/useMediaQuery';
3 | import { NoSsr } from '@material-ui/core';
4 | import { getRandomItem } from 'utils/helpers';
5 | import { useTheme } from '@material-ui/core/styles';
6 | import { Backpack, Ghost, Cat, IceCream, Browser, SpeechBubble, KawaiiProps } from 'react-kawaii';
7 |
8 | const icons = {
9 | ghost: Ghost,
10 | cat: Cat,
11 | backpack: Backpack,
12 | iceCream: IceCream,
13 | browser: Browser,
14 | bubble: SpeechBubble,
15 | };
16 |
17 | function getRandomIcon() {
18 | // @ts-ignore
19 | if (process.browser && window.Cypress) {
20 | return icons.ghost;
21 | }
22 |
23 | const icon = getRandomItem(Object.keys(icons));
24 | return icons[icon as keyof typeof icons];
25 | }
26 |
27 | interface KawaiiIconProps extends KawaiiProps {
28 | icon?: keyof typeof icons;
29 | className?: string;
30 | }
31 |
32 | const KawaiiIcon: React.FunctionComponent = ({ icon, size, ...other }) => {
33 | const theme = useTheme();
34 | const isXs = useMediaQuery(theme.breakpoints.down('xs'));
35 | const calculatedSize = size || isXs ? 230 : 320;
36 |
37 | const Component = React.useMemo(() => (icon ? icons[icon] : getRandomIcon()), [icon]);
38 |
39 | return (
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | KawaiiIcon.defaultProps = {
47 | mood: 'excited',
48 | };
49 |
50 | export default KawaiiIcon;
51 |
--------------------------------------------------------------------------------
/docs/_shared/svgIcons/LightbulbIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { SvgIcon } from '@material-ui/core';
3 |
4 | function LightbulbOutline(props: any) {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
12 | LightbulbOutline.muiName = 'SvgIcon';
13 |
14 | export default LightbulbOutline;
15 |
--------------------------------------------------------------------------------
/docs/_shared/svgIcons/Logo.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useTheme } from '@material-ui/core/styles';
3 |
4 | // ! This is very handcrafted svg for SSR. Don't make your logos this way 🤦🤦🤦
5 | export default function Logo() {
6 | const theme = useTheme();
7 | return (
8 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['next/babel', '@zeit/next-typescript/babel'],
3 | plugins: [
4 | '@babel/plugin-proposal-optional-chaining',
5 | '@babel/plugin-proposal-nullish-coalescing-operator',
6 | [
7 | 'babel-plugin-module-resolver',
8 | {
9 | root: ['./'],
10 | alias: {
11 | '@material-ui/pickers/CalendarSkeleton': '@material-ui/pickers/src/CalendarSkeleton',
12 | },
13 | },
14 | ],
15 | ],
16 | };
17 |
--------------------------------------------------------------------------------
/docs/fakeApi/randomDate.ts:
--------------------------------------------------------------------------------
1 | import { getDaysInMonth, isValid } from 'date-fns';
2 | import { NowRequest, NowResponse } from '@now/node';
3 |
4 | function getRandomNumber(min: number, max: number) {
5 | return Math.round(Math.random() * (max - min) + min);
6 | }
7 |
8 | // eslint-disable-next-line consistent-return
9 | export default function randomDate(req: NowRequest, res: NowResponse) {
10 | const { month } = req.query;
11 |
12 | if (!month || typeof month !== 'string') {
13 | res.status(400);
14 | return res.json({
15 | reason: 'month query param is required',
16 | });
17 | }
18 |
19 | const date = new Date(month);
20 | if (!isValid(date)) {
21 | res.status(422);
22 | return res.json({
23 | reason: 'cannot parse month value',
24 | });
25 | }
26 |
27 | setTimeout(() => {
28 | const daysInMonth = getDaysInMonth(date);
29 | const daysToHighlight = [1, 2, 3].map(() => getRandomNumber(1, daysInMonth));
30 |
31 | res.json({ daysToHighlight });
32 | }, 500); // fake some long work
33 | }
34 |
--------------------------------------------------------------------------------
/docs/layout/components/DrawerMenu.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from 'next/link';
3 | import NavigationMenu from './NavigationMenu';
4 | import { version } from '@material-ui/pickers/package.json';
5 | import { Divider, Toolbar, Typography, Theme } from '@material-ui/core';
6 | import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles';
7 |
8 | const styles = (theme: Theme) =>
9 | createStyles({
10 | drawerRoot: {
11 | width: 250,
12 | },
13 | drawerToolbar: {
14 | display: 'flex',
15 | flexDirection: 'column',
16 | justifyContent: 'center',
17 | alignItems: 'flex-start',
18 | },
19 | headerLink: {
20 | cursor: 'pointer',
21 | textDecoration: 'none',
22 | transition: 'color .2s ease-in-out',
23 | '&:hover': {
24 | color: theme.palette.primary.dark,
25 | textDecoration: 'underline',
26 | },
27 | },
28 | });
29 |
30 | const DrawerMenu: React.SFC> = ({ classes }) => (
31 |
32 |
33 |
34 |
35 | Material UI Pickers
36 |
37 |
38 |
39 |
40 | v{version}
41 |
42 |
43 |
44 |
45 |
46 |
47 | );
48 |
49 | export default withStyles(styles)(DrawerMenu);
50 |
--------------------------------------------------------------------------------
/docs/layout/components/NavigationMenu.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import NavItem from './NavItem';
3 | import { withRouter } from 'next/router';
4 | import { List } from '@material-ui/core';
5 | import { navItems } from './navigationMap';
6 | import { stringToTestId } from 'utils/helpers';
7 |
8 | class NavigationMenu extends React.Component {
9 | mapNavigation(depth: number) {
10 | return ({ title, children, href, as }: any) => {
11 | const { asPath } = this.props.router;
12 | const hasChildren = children && children.length > 0;
13 | const open = hasChildren
14 | ? children.some((item: any) => item.href === asPath || item.as === asPath)
15 | : false;
16 |
17 | return (
18 |
27 | {children && children.length > 0 && children.map(this.mapNavigation(depth + 1))}
28 |
29 | );
30 | };
31 | }
32 |
33 | render() {
34 | return {navItems.map(this.mapNavigation(0))}
;
35 | }
36 | }
37 |
38 | export default withRouter(NavigationMenu);
39 |
--------------------------------------------------------------------------------
/docs/layout/components/navigationMap.ts:
--------------------------------------------------------------------------------
1 | import PropTypesDoc from '../../prop-types.json';
2 |
3 | export const navItems = [
4 | {
5 | title: 'Getting Started',
6 | children: [
7 | { title: 'Installation', href: '/getting-started/installation' },
8 | { title: 'Usage', href: '/getting-started/usage' },
9 | { title: 'Parsing dates', href: '/getting-started/parsing' },
10 | ],
11 | },
12 | {
13 | title: 'Localization',
14 | children: [
15 | { title: 'Using date-fns', href: '/localization/date-fns' },
16 | { title: 'Using moment', href: '/localization/moment' },
17 | { title: 'Additional Calendar Systems', href: '/localization/calendar-systems' },
18 | ],
19 | },
20 | {
21 | title: 'Components Demo',
22 | children: [
23 | { title: 'Date Picker', href: '/demo/datepicker' },
24 | { title: 'Date Range Picker', href: '/demo/daterangepicker' },
25 | { title: 'Time Picker', href: '/demo/timepicker' },
26 | { title: 'Date & Time Picker', href: '/demo/datetime-picker' },
27 | ],
28 | },
29 | {
30 | title: 'Components API',
31 | children: Object.keys(PropTypesDoc)
32 | .filter((component) => !component.match(/^(Mobile|Desktop|Static)/))
33 | .map((component) => ({
34 | title: component,
35 | as: `/api/${component}`,
36 | href: `/api/props?component=${component}`,
37 | })),
38 | },
39 | {
40 | title: 'Guides',
41 | children: [
42 | { title: 'TypeScript', href: '/guides/typescript' },
43 | { title: 'Accessibility', href: '/guides/accessibility' },
44 | { title: 'Form integration', href: '/guides/forms' },
45 | { title: 'CSS overrides', href: '/guides/css-overrides' },
46 | { title: 'Passing date adapter', href: '/guides/date-adapter-passing' },
47 | { title: 'Date management customization', href: '/guides/date-io-customization' },
48 | {
49 | title: 'Open pickers programmatically',
50 | href: '/guides/controlling-programmatically',
51 | },
52 | { title: 'Static inner components', href: '/guides/static-components' },
53 | { title: 'Updating to v3', href: '/guides/upgrading-to-v3' },
54 | ],
55 | },
56 | ] as const;
57 |
--------------------------------------------------------------------------------
/docs/loaders/example-loader.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | const path = require('path');
3 | const safeJsonStringify = require('safe-json-stringify');
4 |
5 | const root = path.resolve(__dirname, '..');
6 |
7 | module.exports = function exampleLoader(source) {
8 | const relativePath = path.relative(root, this.resource);
9 |
10 | const escapedRawSource = safeJsonStringify(source.replace(/'/g, '"'));
11 | const sourceWithExportedContext =
12 | source +
13 | `\nexport const raw = ${escapedRawSource}` +
14 | `\nexport const relativePath = "${relativePath}"`;
15 |
16 | return sourceWithExportedContext;
17 | };
18 |
--------------------------------------------------------------------------------
/docs/notifications.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/docs/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import sinon from 'sinon';
2 | import React from 'react';
3 | import App from 'next/app';
4 | import cookies from 'next-cookies';
5 | import getPageContext from '../utils/getPageContext';
6 | import { PageWithContexts, ThemeType } from '../layout/PageWithContext';
7 |
8 | if (process.env.VISUAL_TESTING) {
9 | const now = new Date('2019-01-01T09:41:00.000Z');
10 | sinon.useFakeTimers(now.getTime());
11 | }
12 |
13 | class MyApp extends App<{ theme: ThemeType }> {
14 | pageContext = getPageContext();
15 |
16 | static async getInitialProps({ Component, ctx }: any) {
17 | let pageProps = {};
18 | const { theme } = cookies(ctx);
19 |
20 | if (Component.getInitialProps) {
21 | pageProps = await Component.getInitialProps(ctx);
22 | }
23 |
24 | return { theme, pageProps };
25 | }
26 |
27 | componentDidMount() {
28 | // Remove the server-side injected CSS.
29 | const jssStyles = document.querySelector('#jss-server-side');
30 | if (jssStyles && jssStyles.parentNode) {
31 | jssStyles.parentNode.removeChild(jssStyles);
32 | }
33 | }
34 |
35 | render() {
36 | const { Component, pageProps, theme } = this.props;
37 |
38 | return (
39 |
40 |
41 |
42 | );
43 | }
44 | }
45 |
46 | export default MyApp;
47 |
--------------------------------------------------------------------------------
/docs/pages/_error.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import React from 'react';
3 | import KawaiiIcon from '_shared/svgIcons/KawaiiIcon';
4 | import { styled } from '@material-ui/core/styles';
5 | import { Grid, Typography, NoSsr } from '@material-ui/core';
6 |
7 | const CenteredGrid = styled(Grid)({
8 | position: 'relative',
9 | top: '15vh',
10 | });
11 |
12 | const KawaiiIconWithMargin = styled(KawaiiIcon)({
13 | marginBottom: 65,
14 | });
15 |
16 | class Error extends React.Component<{ statusCode: number }> {
17 | static getInitialProps({ res, err }: any) {
18 | const statusCode = res ? res.statusCode : err ? err.statusCode : null;
19 | return { statusCode };
20 | }
21 |
22 | render() {
23 | const { statusCode } = this.props;
24 |
25 | return (
26 |
27 |
28 |
29 | 0.33 ? 'sad' : 'shocked'} />
30 |
31 |
32 |
33 | Error {statusCode}
34 |
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
42 | export default Error;
43 |
--------------------------------------------------------------------------------
/docs/pages/demo/datepicker/BasicDatePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DatePicker } from '@material-ui/pickers';
4 |
5 | export default function BasicDatePicker() {
6 | const [value, setValue] = React.useState(new Date());
7 |
8 | return (
9 | setValue(newValue)}
13 | renderInput={(props) => }
14 | />
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/docs/pages/demo/datepicker/CustomInput.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { styled } from '@material-ui/core/styles';
3 | import { DesktopDatePicker } from '@material-ui/pickers';
4 |
5 | const InputContainer = styled('div')({
6 | display: 'flex',
7 | alignItems: 'center',
8 | });
9 |
10 | export default function CustomInput() {
11 | const [value, setValue] = React.useState(new Date());
12 |
13 | return (
14 | setValue(newValue)}
18 | renderInput={({ inputRef, inputProps, InputProps }) => (
19 |
20 |
21 | {InputProps?.endAdornment}
22 |
23 | )}
24 | />
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/docs/pages/demo/datepicker/DatePickers.example.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle */
2 | import React from 'react';
3 | import TextField from '@material-ui/core/TextField';
4 | import { MobileDatePicker, DesktopDatePicker, DatePicker } from '@material-ui/pickers';
5 |
6 | export default function DatePickersVariants(demoProps: any) {
7 | const [value, setValue] = React.useState(new Date());
8 |
9 | return (
10 |
11 | setValue(newValue)}
21 | renderInput={(props) => }
22 | />
23 | setValue(newValue)}
28 | renderInput={(props) => }
29 | />
30 | setValue(newValue)}
37 | renderInput={(props) => }
38 | />
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/docs/pages/demo/datepicker/StaticDatePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import isWeekend from 'date-fns/isWeekend';
3 | import TextField from '@material-ui/core/TextField';
4 | import { StaticDatePicker } from '@material-ui/pickers';
5 | import { makeJSDateObject } from '../../../utils/helpers';
6 |
7 | function disableWeekends(date: Date) {
8 | // TODO: replace with implementation for your date library
9 | return isWeekend(makeJSDateObject(date));
10 | }
11 |
12 | export default function StaticDatePickerExample() {
13 | const [value, setValue] = React.useState(new Date());
14 |
15 | return (
16 |
17 | setValue(newValue)}
22 | renderInput={(props) => }
23 | />
24 | setValue(newValue)}
31 | renderInput={(props) => }
32 | />
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/docs/pages/demo/datepicker/ViewsDatePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DatePicker, MobileDatePicker } from '@material-ui/pickers';
4 |
5 | export default function YearDatePicker() {
6 | const [value, setValue] = React.useState(new Date());
7 |
8 | return (
9 |
10 | setValue(newValue)}
15 | renderInput={(props) => }
16 | />
17 | setValue(newValue)}
24 | renderInput={(props) => }
25 | />
26 | setValue(newValue)}
32 | renderInput={(props) => }
33 | />
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/docs/pages/demo/daterangepicker/BasicDateRangePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DateRangePicker, DateRange, DateRangeDelimiter } from '@material-ui/pickers';
4 |
5 | export default function BasicDateRangePicker() {
6 | const [value, setValue] = React.useState>([null, null]);
7 |
8 | return (
9 | setValue(newValue)}
14 | renderInput={(startProps, endProps) => (
15 |
16 |
17 | to
18 |
19 |
20 | )}
21 | />
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/docs/pages/demo/daterangepicker/CalendarsDateRangePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Grid from '@material-ui/core/Grid';
3 | import Typography from '@material-ui/core/Typography';
4 | import TextField from '@material-ui/core/TextField';
5 | import { DateRangePicker, DateRangeDelimiter, DateRange } from '@material-ui/pickers';
6 |
7 | export default function CalendarsDateRangePicker() {
8 | const [value, setValue] = React.useState>([null, null]);
9 |
10 | return (
11 |
12 | 1 calendar
13 | setValue(newValue)}
17 | renderInput={(startProps, endProps) => (
18 |
19 |
20 | to
21 |
22 |
23 | )}
24 | />
25 | 2 calendars
26 | setValue(newValue)}
30 | renderInput={(startProps, endProps) => (
31 |
32 |
33 | to
34 |
35 |
36 | )}
37 | />
38 | 3 calendars
39 | setValue(newValue)}
43 | renderInput={(startProps, endProps) => (
44 |
45 |
46 | to
47 |
48 |
49 | )}
50 | />
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/docs/pages/demo/daterangepicker/CustomRangeInputs.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { DateRangePicker, DateRange } from '@material-ui/pickers';
3 |
4 | export default function CustomRangeInputs() {
5 | const [selectedDate, handleDateChange] = React.useState>([null, null]);
6 |
7 | return (
8 | handleDateChange(date)}
12 | renderInput={(startProps, endProps) => (
13 |
14 | }
16 | {...startProps.inputProps}
17 | />
18 | } {...endProps.inputProps} />
19 |
20 | )}
21 | />
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/docs/pages/demo/daterangepicker/MinMaxDateRangePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import addWeeks from 'date-fns/addWeeks';
3 | import TextField from '@material-ui/core/TextField';
4 | import { Dayjs } from 'dayjs';
5 | import { Moment } from 'moment';
6 | import { DateTime } from 'luxon';
7 | import { DateRangePicker, DateRangeDelimiter, DateRange } from '@material-ui/pickers';
8 | // TODO remove relative import
9 | import { makeJSDateObject } from '../../../utils/helpers';
10 |
11 | function getWeeksAfter(date: Moment | DateTime | Dayjs | Date, amount: number) {
12 | // TODO: replace with implementation for your date library
13 | return date ? addWeeks(makeJSDateObject(date), amount) : undefined;
14 | }
15 |
16 | export default function MinMaxDateRangePicker() {
17 | const [value, setValue] = React.useState>([null, null]);
18 |
19 | return (
20 | setValue(newValue)}
26 | renderInput={(startProps, endProps) => (
27 |
28 |
29 | to
30 |
31 |
32 | )}
33 | />
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/docs/pages/demo/daterangepicker/ResponsiveDateRangePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import {
4 | MobileDateRangePicker,
5 | DateRangeDelimiter,
6 | DesktopDateRangePicker,
7 | DateRange,
8 | } from '@material-ui/pickers';
9 |
10 | export default function ResponsiveDateRangePicker() {
11 | const [value, setValue] = React.useState>([null, null]);
12 |
13 | return (
14 |
15 | setValue(newValue)}
19 | renderInput={(startProps, endProps) => (
20 |
21 |
22 | to
23 |
24 |
25 | )}
26 | />
27 | setValue(newValue)}
31 | renderInput={(startProps, endProps) => (
32 |
33 |
34 | to
35 |
36 |
37 | )}
38 | />
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/docs/pages/demo/daterangepicker/StaticDateRangePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { StaticDateRangePicker, DateRangeDelimiter, DateRange } from '@material-ui/pickers';
4 |
5 | export default function StaticDateRangePickerExample() {
6 | const [value, setValue] = React.useState>([null, null]);
7 |
8 | return (
9 |
10 | setValue(newValue)}
14 | renderInput={(startProps, endProps) => (
15 |
16 |
17 | to
18 |
19 |
20 | )}
21 | />
22 | setValue(newValue)}
26 | renderInput={(startProps, endProps) => (
27 |
28 |
29 | to
30 |
31 |
32 | )}
33 | />
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/docs/pages/demo/datetime-picker/BasicDateTimePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DateTimePicker } from '@material-ui/pickers';
4 |
5 | export default function BasicDateTimePicker() {
6 | const [selectedDate, handleDateChange] = React.useState(new Date());
7 |
8 | return (
9 |
10 | }
12 | label="DateTimePicker"
13 | value={selectedDate}
14 | onChange={handleDateChange}
15 | />
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/docs/pages/demo/datetime-picker/CustomDateTimePicker.example.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle, no-console */
2 | import * as React from 'react';
3 | import AlarmIcon from '@material-ui/icons/Alarm';
4 | import SnoozeIcon from '@material-ui/icons/Snooze';
5 | import TextField from '@material-ui/core/TextField';
6 | import ClockIcon from '@material-ui/icons/AccessTime';
7 | import { DateTimePicker, MobileDateTimePicker } from '@material-ui/pickers';
8 |
9 | export default function CustomDateTimePicker(demoProps: any) {
10 | const [clearedDate, setClearedDate] = React.useState(null);
11 | const [value, setValue] = React.useState(new Date('2019-01-01T18:54'));
12 |
13 | return (
14 |
15 | setValue(newValue)}
23 | minDate={new Date('2018-01-01')}
24 | leftArrowIcon={}
25 | rightArrowIcon={}
26 | leftArrowButtonText="Open previous month"
27 | rightArrowButtonText="Open next month"
28 | openPickerIcon={}
29 | minTime={new Date(0, 0, 0, 9)}
30 | maxTime={new Date(0, 0, 0, 20)}
31 | renderInput={(props) => (
32 |
33 | )}
34 | />
35 | setValue(newValue)}
38 | label="With error handler"
39 | onError={console.log}
40 | minDate={new Date('2018-01-01T00:00')}
41 | inputFormat={demoProps.__willBeReplacedGetFormatString({
42 | moment: 'YYYY/MM/DD hh:mm A',
43 | dateFns: 'yyyy/MM/dd hh:mm a',
44 | })}
45 | mask="___/__/__ __:__ _M"
46 | renderInput={(props) => }
47 | />
48 | setClearedDate(newValue)}
52 | renderInput={(props) => }
53 | />
54 |
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/docs/pages/demo/datetime-picker/DateTimePickers.example.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle, no-console */
2 | import * as React from 'react';
3 | import TextField from '@material-ui/core/TextField';
4 | import { MobileDateTimePicker, DesktopDateTimePicker, DateTimePicker } from '@material-ui/pickers';
5 |
6 | export default function DateTimePickerDemo(demoProps: any) {
7 | const [value, setValue] = React.useState(new Date('2018-01-01T00:00:00.000Z'));
8 |
9 | return (
10 |
11 | setValue(newValue)}
15 | label="24h clock"
16 | renderInput={(props) => }
17 | />
18 | setValue(newValue)}
24 | renderInput={(props) => }
25 | />
26 | }
28 | ampm={false}
29 | disablePast
30 | value={value}
31 | onChange={(newValue) => setValue(newValue)}
32 | onError={console.log}
33 | disableMaskedInput
34 | inputFormat={demoProps.__willBeReplacedGetFormatString({
35 | moment: 'YYYY/MM/DD HH:mm',
36 | dateFns: 'yyyy/MM/dd HH:mm',
37 | })}
38 | />
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/docs/pages/demo/datetime-picker/DateTimeValidation.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DateTimePicker } from '@material-ui/pickers';
4 |
5 | export default function DateTimePickerValidation() {
6 | const [value, setValue] = React.useState(new Date());
7 |
8 | return (
9 |
10 | }
12 | label="Ignore date and time"
13 | value={value}
14 | onChange={(newValue) => setValue(newValue)}
15 | minDateTime={new Date()}
16 | />
17 | }
19 | label="Ignore time in each day"
20 | value={value}
21 | onChange={(newValue) => setValue(newValue)}
22 | minDate={new Date('2020-02-14')}
23 | minTime={new Date(0, 0, 0, 8)}
24 | maxTime={new Date(0, 0, 0, 18, 45)}
25 | />
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/docs/pages/demo/datetime-picker/index.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import LinkedComponents from '_shared/LinkedComponents';
5 |
6 | import * as BasicDateTimePicker from './BasicDateTimePicker.example';
7 | import * as DateTimePickers from './DateTimePickers.example';
8 | import * as CustomDateTimePicker from './CustomDateTimePicker.example';
9 | import * as DateTimeValidation from './DateTimeValidation.example';
10 |
11 |
12 |
13 | ## Date & Time picker
14 |
15 | This component combines date & time pickers. It allows to select both date and time with the same control.
16 |
17 | **Note:** This component is not listed in [material design guidelines](https://material.io/components/)
18 |
19 |
20 |
21 | #### Basic usage
22 |
23 | Allows choosing date then time. There are too many views, so tabs are required to visually distinguish date/time steps.
24 |
25 |
26 |
27 | #### Responsiveness
28 |
29 | The date time picker component is designed and optimized for the device it runs on.
30 |
31 | - The "Mobile" version works best for touch devices and small screens.
32 | - The "Desktop" version works best for mouse devices and large screens.
33 |
34 | By default, the `DateTimePicker` component uses a `@media (pointer: fine)` media query to determine which version to use.
35 | This can be customized by `desktopModeMediaQuery` prop.
36 |
37 |
38 |
39 | #### Date & Time validation
40 |
41 | It is possible to restrict date and time selection in two ways:
42 |
43 | - using `minDateTime`/`maxDateTime`, you can restrict time selection before or after a particular moment in time
44 | - using `minTime`/`maxTime`, you can disable selecting times before or after a certain time each day respectively
45 |
46 |
47 |
48 | #### Customization
49 |
50 | Here are some examples of heavily customized date & time pickers
51 |
52 |
53 |
54 | #### API
55 |
56 |
57 |
--------------------------------------------------------------------------------
/docs/pages/demo/timepicker/BasicTimePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { TimePicker } from '@material-ui/pickers';
4 |
5 | export default function BasicTimePicker() {
6 | const [value, setValue] = React.useState(new Date());
7 |
8 | return (
9 |
10 | }
12 | label="12 hours"
13 | value={value}
14 | onChange={(newValue) => setValue(newValue)}
15 | />
16 | }
18 | ampm={false} // This is not needed if you are using localization
19 | label="24 hours"
20 | value={value}
21 | onChange={(newValue) => setValue(newValue)}
22 | />
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/docs/pages/demo/timepicker/SecondsTimePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { TimePicker } from '@material-ui/pickers';
4 |
5 | export default function SecondsTimePicker() {
6 | const [value, setValue] = React.useState(new Date());
7 |
8 | return (
9 |
10 | }
12 | ampm={false}
13 | openTo="hours"
14 | views={['hours', 'minutes', 'seconds']}
15 | inputFormat="HH:mm:ss"
16 | mask="__:__:__"
17 | label="With seconds"
18 | value={value}
19 | onChange={(newValue) => setValue(newValue)}
20 | />
21 | }
23 | ampmInClock
24 | openTo="minutes"
25 | views={['minutes', 'seconds']}
26 | inputFormat="mm:ss"
27 | mask="__:__"
28 | label="Minutes and seconds"
29 | value={value}
30 | onChange={(newValue) => setValue(newValue)}
31 | />
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/docs/pages/demo/timepicker/StaticTimePicker.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { StaticTimePicker } from '@material-ui/pickers';
4 |
5 | export default function StaticTimePickerExample() {
6 | const [value, setValue] = React.useState(new Date());
7 |
8 | return (
9 |
10 | setValue(newValue)}
14 | renderInput={(props) => }
15 | />
16 | setValue(newValue)}
22 | renderInput={(props) => }
23 | />
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/docs/pages/demo/timepicker/TimePickers.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { TimePicker, MobileTimePicker, DesktopTimePicker } from '@material-ui/pickers';
4 |
5 | export default function TimePickers() {
6 | const [value, setValue] = React.useState(new Date('2018-01-01T00:00:00.000Z'));
7 |
8 | return (
9 |
10 | setValue(newValue)}
15 | renderInput={(props) => }
16 | />
17 | setValue(newValue)}
23 | renderInput={(props) => }
24 | />
25 | {/* Alternative way to show am/pm */}
26 | }
36 | />
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/docs/pages/demo/timepicker/TimeValidation.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { TimePicker } from '@material-ui/pickers';
4 |
5 | export default function TimeValidation() {
6 | const [value, setValue] = React.useState(new Date('2020-01-01 12:00'));
7 |
8 | return (
9 |
10 | }
12 | value={value}
13 | onChange={(newValue) => setValue(newValue)}
14 | minTime={new Date(0, 0, 0, 8)}
15 | maxTime={new Date(0, 0, 0, 18, 45)}
16 | />
17 | }
19 | ampm={false}
20 | label="24 hours"
21 | minTime={new Date(0, 0, 0, 8)}
22 | maxTime={new Date(0, 0, 0, 18, 45)}
23 | value={value}
24 | onChange={(newValue) => setValue(newValue)}
25 | />
26 | }
28 | ampm={false}
29 | label="Disable odd hours"
30 | value={value}
31 | onChange={(newValue) => setValue(newValue)}
32 | shouldDisableTime={(timeValue, clockType) => {
33 | if (clockType === 'hours' && timeValue % 2) {
34 | return true;
35 | }
36 |
37 | return false;
38 | }}
39 | />
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/docs/pages/demo/timepicker/index.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import LinkedComponents from '_shared/LinkedComponents';
5 | import { Hidden } from '@material-ui/core';
6 |
7 | import * as TimeValidation from './TimeValidation.example';
8 | import * as BasicTimePicker from './BasicTimePicker.example';
9 | import * as TimePickers from './TimePickers.example';
10 | import * as StaticTimePicker from './StaticTimePicker.example';
11 | import * as SecondsTimePicker from './SecondsTimePicker.example';
12 |
13 |
14 |
15 | ## Time Picker
16 |
17 | Time pickers let user select a single time (in the hours:minutes format).
18 | The selected time is indicated by the filled circle at the end of the clock hand.
19 |
20 |
21 |
22 | #### Basic usage
23 |
24 | A time picker should adjust to a user’s preferred time setting, i.e. the 12-hour or 24-hour format.
25 |
26 |
27 |
28 | #### Responsiveness
29 |
30 | The time picker component is designed and optimized for the device it runs on.
31 |
32 | - The "Mobile" version works best for touch devices and small screens.
33 | - The "Desktop" version works best for mouse devices and large screens.
34 |
35 | By default, the `TimePicker` component uses a `@media (pointer: fine)` media query to determine which version to use.
36 | This can be customized by `desktopModeMediaQuery` prop.
37 |
38 |
39 |
40 | #### Time Validation
41 |
42 |
43 |
44 | #### Static mode
45 |
46 | It is possible to render any picker inline. This will be really helpful to build custom popover/modal containers.
47 | For that use `variant="static"`.
48 |
49 |
50 |
51 |
52 |
53 | #### Seconds
54 |
55 | Seconds input can be used for selection of a precise time point.
56 |
57 |
58 |
59 | #### API
60 |
61 |
62 |
--------------------------------------------------------------------------------
/docs/pages/getting-started/installation.mdx:
--------------------------------------------------------------------------------
1 | import PageMeta from '../../_shared/PageMeta';
2 |
3 |
7 |
8 | ## Installation
9 |
10 | Material UI Pickers is available as an [npm package](https://www.npmjs.com/package/@material-ui/pickers):
11 |
12 | ```
13 | npm i @material-ui/pickers
14 |
15 | yarn add @material-ui/pickers
16 | ```
17 |
18 | #### Peer Library
19 |
20 | `@material-ui/pickers` was designed to use the date management library of your choice.
21 | We are providing interfaces for [date-fns 2](https://date-fns.org/), [luxon](https://moment.github.io/luxon/), [dayjs](https://github.com/iamkun/dayjs) and [moment](https://momentjs.com/).
22 |
23 | ```
24 | npm i date-fns
25 | // or
26 | npm i moment
27 | // or
28 | npm i luxon
29 | // or
30 | npm i dayjs
31 | ```
32 |
33 | Tell pickers which date management library it should use with `LocalizationProvider`. This component takes a `dateAdapter`
34 | prop, and makes it available down the React tree thanks to [React Context](https://reactjs.org/docs/context.html). It should
35 | be used at the root of your component tree, or at the highest level you wish the pickers to be available.
36 |
37 | ```jsx
38 | import { LocalizationProvider } from '@material-ui/pickers';
39 |
40 | // pick an adapter for your date library
41 | import MomentUtils from '@material-ui/pickers/adapter/moment';
42 | import DateFnsUtils from '@material-ui/pickers/adapter/date-fns';
43 | import LuxonUtils from '@material-ui/pickers/adapter/luxon';
44 |
45 | function App() {
46 | return (
47 |
48 |
49 |
50 | );
51 | }
52 |
53 | ReactDOM.render(, document.querySelector('#app'));
54 | ```
55 |
56 | It is also possible to pass the [date adapter directly to the picker](/guides/date-adapter-passing) and avoid using context,
57 | but please use this possibility wisely.
58 |
--------------------------------------------------------------------------------
/docs/pages/getting-started/parsing.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import PageMeta from '_shared/PageMeta';
3 |
4 |
5 |
6 | ## Parsing dates
7 |
8 | Material UI Pickers relies on the date management library when the date should be parsed. Any other prop-types that accepts dates (e.g. `minDate`, `maxDate`) it can take string, number, Date object and so on.
9 |
10 |
11 |
12 | Find more information about parsing dates in the documentation for your library:
13 |
14 | - [date-fns](https://date-fns.org/v2.19.0/docs/parse)
15 | - [dayjs](https://day.js.org/docs/en/parse/parse)
16 | - [luxon](https://moment.github.io/luxon/docs/manual/parsing.html)
17 | - [moment](https://momentjs.com/docs/#/parsing/)
18 |
19 | P.S.: Pass any value to the picker, and if it won't be parsed as expected feel free to open an issue
20 | on [GitHub](https://github.com/mui-org/material-ui-pickers) 😎
21 |
--------------------------------------------------------------------------------
/docs/pages/getting-started/usage.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import PageMeta from '_shared/PageMeta';
3 | import { NoSsr } from '@material-ui/core';
4 | import { CODESANDBOX_EXAMPLE_ID } from '_constants';
5 |
6 |
7 |
8 | ## Usage
9 |
10 | @material-ui/pickers relies only on material-ui controls and the date management library of your choice. Please note that all components are controlled, meaning that it's required to pass the `value` and `onChange` props.
11 |
12 |
13 |
14 | #### Quick start
15 |
16 | **Note** `onChange` has a second parameter. So if you will not do `onChange={date => handleDateChange(date)}` you will get the console warning that [useState hook does not accept a second callback argument.](https://github.com/facebook/react/issues/14174)
17 |
18 | ```jsx
19 | import * as React from 'react';
20 | import DateFnsAdapter from '@material-ui/pickers/adapter/date-fns'; // choose your lib
21 | import { DatePicker, TimePicker, DateTimePicker, LocalizationProvider } from '@material-ui/pickers';
22 |
23 | function App() {
24 | const [selectedDate, handleDateChange] = React.useState(new Date());
25 |
26 | return (
27 |
28 | }
30 | value={selectedDate}
31 | onChange={(date) => handleDateChange(date)}
32 | />
33 | }
35 | value={selectedDate}
36 | onChange={(date) => handleDateChange(date)}
37 | />
38 | }
40 | value={selectedDate}
41 | onChange={(date) => handleDateChange(date)}
42 | />
43 |
44 | );
45 | }
46 | ```
47 |
48 |
49 |
61 |
62 |
--------------------------------------------------------------------------------
/docs/pages/guides/Accessibility.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DatePicker } from '@material-ui/pickers';
4 |
5 | export default function BasicDatePicker() {
6 | const [selectedDate, handleDateChange] = React.useState(new Date());
7 |
8 | return (
9 | handleDateChange(date)}
13 | renderInput={(props) => }
14 | />
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/docs/pages/guides/ControllingProgrammatically.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Button from '@material-ui/core/Button';
3 | import TextField from '@material-ui/core/TextField';
4 | import { makeStyles } from '@material-ui/core/styles';
5 | import { DatePicker } from '@material-ui/pickers';
6 |
7 | const useStyles = makeStyles({
8 | root: {
9 | display: 'flex',
10 | flexDirection: 'column',
11 | },
12 | });
13 |
14 | export default function ControllingProgrammaticallyExample() {
15 | const classes = useStyles();
16 | const [open, setOpen] = React.useState(false);
17 | const [value, setValue] = React.useState(new Date('2018-01-01T00:00:00.000Z'));
18 |
19 | return (
20 |
21 |
22 | }
24 | open={open}
25 | onOpen={() => setOpen(true)}
26 | onClose={() => setOpen(false)}
27 | label="Open me from button"
28 | inputFormat="d MMM yyyy"
29 | value={value}
30 | onChange={(newValue) => setValue(newValue)}
31 | />
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/docs/pages/guides/CssOverrides.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import isWeekend from 'date-fns/isWeekend';
3 | import TextField from '@material-ui/core/TextField';
4 | import { lightBlue } from '@material-ui/core/colors';
5 | import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
6 | import { DatePicker } from '@material-ui/pickers';
7 |
8 | const materialTheme = createMuiTheme({
9 | overrides: {
10 | MuiPickersToolbar: {
11 | root: {
12 | backgroundColor: lightBlue.A200,
13 | },
14 | },
15 | MuiPickersCalendarHeader: {
16 | root: {
17 | // backgroundColor: lightBlue.A200,
18 | // color: 'white',
19 | },
20 | },
21 | MuiPickersDay: {
22 | root: {
23 | color: lightBlue.A700,
24 | '&$disabled': {
25 | color: lightBlue['100'],
26 | },
27 | '&$selected': {
28 | backgroundColor: lightBlue['400'],
29 | },
30 | },
31 | today: {
32 | color: lightBlue['900'],
33 | },
34 | },
35 | MuiPickersModalDialog: {
36 | dialogAction: {
37 | color: lightBlue['400'],
38 | },
39 | },
40 | },
41 | });
42 |
43 | export default function CssOverrides() {
44 | const [selectedDate, handleDateChange] = React.useState(new Date());
45 |
46 | return (
47 |
48 | handleDateChange(date)}
52 | renderInput={(props) => }
53 | // @ts-ignore
54 | shouldDisableDate={isWeekend}
55 | />
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/docs/pages/guides/CssTheme.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { lime } from '@material-ui/core/colors';
4 | import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
5 | import { DateTimePicker } from '@material-ui/pickers';
6 |
7 | const defaultMaterialTheme = createMuiTheme({
8 | palette: {
9 | primary: lime,
10 | },
11 | });
12 |
13 | export default function CssThemeExample() {
14 | const [value, setValue] = React.useState(new Date());
15 |
16 | return (
17 |
18 | }
20 | label="Lime DateTimePicker"
21 | value={value}
22 | onChange={(newValue) => setValue(newValue)}
23 | />
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/docs/pages/guides/CssThemeSpacing.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { DateTimePicker } from '@material-ui/pickers';
4 | import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
5 |
6 | const muiTheme = createMuiTheme({
7 | spacing: 2,
8 | });
9 |
10 | export default function CssThemeExample() {
11 | const [selectedDate, handleDateChange] = React.useState();
12 |
13 | return (
14 |
15 | }
17 | label="2px spacing"
18 | value={selectedDate}
19 | onChange={(date) => handleDateChange(date)}
20 | />
21 |
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/docs/pages/guides/DateAdapterProp.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ruLocale from 'date-fns/locale/ru';
3 | import deLocale from 'date-fns/locale/de';
4 | import TextField from '@material-ui/core/TextField';
5 | import { DatePicker } from '@material-ui/pickers';
6 | import DateFnsAdapter from '@material-ui/pickers/adapter/date-fns';
7 |
8 | const staticDateAdapter = new DateFnsAdapter({ locale: ruLocale });
9 |
10 | export default function UsingDateAdapterProp() {
11 | const [locale] = React.useState(deLocale);
12 | const [value, setValue] = React.useState(new Date());
13 |
14 | const memoizedDateAdapter = React.useMemo(() => {
15 | return new DateFnsAdapter({ locale });
16 | }, [locale]);
17 |
18 | return (
19 |
20 | }
22 | value={value}
23 | onChange={(newValue) => setValue(newValue)}
24 | dateAdapter={staticDateAdapter}
25 | />
26 | setValue(newValue)}
29 | dateAdapter={memoizedDateAdapter}
30 | renderInput={(props) => (
31 |
32 | )}
33 | />
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/docs/pages/guides/Formats.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import frLocale from 'date-fns/locale/fr';
3 | import TextField from '@material-ui/core/TextField';
4 | import { DatePicker, LocalizationProvider } from '@material-ui/pickers';
5 | import DateFnsAdapter from '@material-ui/pickers/adapter/date-fns';
6 |
7 | const formats = {
8 | normalDate: 'd MMM yyy',
9 | keyboardDate: 'd MMM yyy',
10 | };
11 |
12 | export default function DateFnsLocalizationExample() {
13 | const [value, setValue] = React.useState(new Date());
14 |
15 | return (
16 |
17 | setValue(newValue)}
21 | clearText="vider"
22 | cancelText="annuler"
23 | renderInput={(props) => }
24 | />
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/docs/pages/guides/FormikValidationSchema.example.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | // @ts-nocheck
3 | import React from 'react';
4 | import Grid from '@material-ui/core/Grid';
5 | import TextField from '@material-ui/core/TextField';
6 | import { date, object } from 'yup';
7 | import { Formik, Form, Field, FieldProps } from 'formik';
8 | import { DatePicker, BaseDatePickerProps } from '@material-ui/pickers';
9 |
10 | interface DatePickerFieldProps extends FieldProps, BaseDatePickerProps {
11 | getShouldDisableDateError: (date: Date) => string;
12 | }
13 |
14 | function DatePickerField(props: DatePickerFieldProps) {
15 | const {
16 | field,
17 | form,
18 | getShouldDisableDateError,
19 | maxDate = new Date('2099-12-31'),
20 | minDate = new Date('1900-01-01'),
21 | ...other
22 | } = props;
23 | const currentError = form.errors[field.name];
24 |
25 | return (
26 | form.setFieldValue(field.name, newValue, true)}
33 | renderInput={(inputProps) => (
34 | form.setFieldTouched(field.name, true, true)}
41 | />
42 | )}
43 | {...other}
44 | />
45 | );
46 | }
47 |
48 | const schema = object({
49 | date: date().required().min(new Date()).max(new Date('2100-10-10')),
50 | });
51 |
52 | export default function FormikValidationSchemaExample() {
53 | return (
54 |
55 | {({ values, errors }) => (
56 |
68 | )}
69 |
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/docs/pages/guides/OverrideLogic.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { LocalizationProvider, DatePicker } from '@material-ui/pickers';
4 | import DateFnsAdapter from '@material-ui/pickers/adapter/date-fns';
5 |
6 | class OverriddenAdapter extends DateFnsAdapter {
7 | getYearRange(start: Date, end: Date) {
8 | return super.getYearRange(start, end).reverse();
9 | }
10 | }
11 |
12 | export default function OverrideLogicExample() {
13 | const [value, setValue] = React.useState(new Date());
14 |
15 | return (
16 |
17 | setValue(newValue)}
21 | renderInput={(props) => }
22 | />
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/docs/pages/guides/StaticComponents.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Paper, Button } from '@material-ui/core';
3 | import { PickersClockView, PickersCalendarView } from '@material-ui/pickers';
4 |
5 | export default function StaticPickers() {
6 | const [date, handleDateChange] = React.useState(new Date());
7 | const [calendarView, setCalendarView] = React.useState<'date' | 'year'>('date');
8 |
9 | return (
10 |
11 |
12 |
13 |
21 |
22 |
25 |
26 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/docs/pages/guides/controlling-programmatically.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import * as ControllingExample from './ControllingProgrammatically.example';
5 |
6 |
7 |
8 | ## Controlling programatically
9 |
10 |
11 |
12 | Any picker can be controlled by the `open` property.
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/pages/guides/css-overrides.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import * as CssTheme from './CssTheme.example';
5 | import * as CssOverrides from './CssOverrides.example';
6 | import * as CssThemeSpacing from './CssThemeSpacing.example';
7 |
8 |
9 |
10 | ## Override styles
11 |
12 | The default pickers appearance is built based on material-ui's theme provided. So pickers will take all
13 | colors/fonts/theme settings as any other material-ui component.
14 |
15 |
16 |
17 | #### Default theme
18 |
19 | You can customize the material-ui theme, that is passed to `ThemeProvider` and the pickers will leverage your settings.
20 |
21 |
22 |
23 | #### Override example
24 |
25 | _Date/Time pickers are quite simple controls from UX perspective, so most people just use the default appearance_
26 |
27 | That's why we are not providing any for-component classes api to override stylesheets for any particular
28 | component. The only way to override existing stylesheets are with the use of global material-ui theme
29 | overrides.
30 |
31 |
32 |
33 | #### Custom spacing
34 |
35 | Pickers will also leverage `spacing` from the material-ui's theme
36 |
37 |
38 |
39 | #### TypeScript
40 |
41 | For TypeScript users it's also required to extend default material-ui theme typings with pickers controls.
42 | This will also autocomplete classnames.
43 |
44 | Add the following to a TypeScript declaration file in your project, such as `overrides-mui.d.ts`:
45 |
46 |
47 |
48 | ```typescript
49 | import { MuiPickersComponentsToClassName } from '@material-ui/pickers/src/typings/overrides';
50 |
51 | declare module '@material-ui/core/styles/overrides' {
52 | export interface ComponentNameToClassKey extends MuiPickersComponentsToClassName {}
53 | }
54 | ```
55 |
--------------------------------------------------------------------------------
/docs/pages/guides/date-adapter-passing.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Code from '_shared/Code.tsx';
3 | import Example from '_shared/Example';
4 | import PageMeta from '_shared/PageMeta';
5 | import * as DateAdapterProp from './DateAdapterProp.example';
6 |
7 |
8 |
9 | ## Passing date adapter down to pickers
10 |
11 | There are a couple of ways to pass date-io adapter to pickers components.
12 |
13 |
14 |
15 | #### Context vs dateAdapter prop
16 |
17 | Recomended way to pass date adapter is using our `LocalizationProvider` and pass it through the context.
18 | Also there is a `dateAdapter` prop available, it allows to get rid of additional context.
19 |
20 | But you need to understand a few things:
21 |
22 | - `dateAdapter` will create a new context instance inside the date-picker, it may impact rendering performance if you have a lot of pickers on the screen
23 | - You must make sure your adapter is properly memoized before passing it down
24 | (if not – all components tree inside any picker will always rerender on your component change)
25 |
26 |
27 |
--------------------------------------------------------------------------------
/docs/pages/guides/date-io-customization.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Code from '_shared/Code.tsx';
3 | import Example from '_shared/Example';
4 | import PageMeta from '_shared/PageMeta';
5 | import utilsInterfaceCode from '!raw-loader!@date-io/core/IUtils.d.ts';
6 | import * as FormatsExample from './Formats.example';
7 | import * as OverrideLogicExample from './OverrideLogic.example';
8 |
9 |
10 |
11 | ## Customization date management logic
12 |
13 | For some reason, like timezone management and localization you may need to control how datepickers are working
14 | with your date management library.
15 |
16 |
17 |
18 | #### Global formats customization
19 |
20 | All the formats used by the datepicker can be changed by `libFormats` prop in `LocalizationProvider`.
21 | Find all availble formats in [Adapter interface](#utils-interface)
22 |
23 |
24 |
25 | #### Override date logic
26 |
27 | It is also possible to extend any adapter we providing and change the logic of date manipulations.
28 | Simply extend exported version of `date-io` adapter, and make it work as you need accordingly to [used interface](#utils-interface)
29 |
30 | > You can use ES6 class syntax or override values with the help of `.prototype` Object property.
31 |
32 |
33 |
34 | #### Utils interface
35 |
36 | _Note_ TDate - date object passed from the state (moment, native Date or Luxon`s DateTime)
37 |
38 |
39 |
--------------------------------------------------------------------------------
/docs/pages/guides/forms.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import * as FormikOurValidationExample from './FormikOurValidation.example';
5 | import * as FormikValidationSchema from './FormikValidationSchema.example';
6 |
7 |
8 |
9 | ## Integration into forms
10 |
11 | Pickers are quite complex controls, where date can be submitted from different places, so we can't provide events as arguments in
12 | an `onChange` callback. So you need to set up form field manually, based on the `onChange` param.
13 |
14 | Also we are providing prop `onError` that is ready-to-use for validation. This callback fires once
15 | the selected value by user is becomes invalid based on the passed validation props.
16 | It returns you a reason why error was occured so it is possible to render custom error message.
17 |
18 |
19 |
20 | #### Validation props
21 |
22 | There are different validation props for each component. The `reason` prop returned by `onError`
23 | will basically return you a name of prop that match an error (e.g. `minDate` or `shouldDisableDate`)
24 |
25 | For reference, here is the list of validation props for [DatePicker](/api/DatePicker) in the order of applying:
26 |
27 | - `shouldDisableDate`
28 | - `disableFuture`
29 | - `disablePast`
30 | - `minDate`
31 | - `maxDate`
32 |
33 | #### Formik
34 |
35 | Here is an example how to use `onError` callback in integration with [formik](https://jaredpalmer.com/formik)
36 |
37 |
38 |
39 | #### Formik with validation schema
40 |
41 | This example shows how to use formik and custom validation schema. When using this approach please make sure that
42 | your validation schema logic covers all the [validation props](#validation-props) that are passed to the `DatePicker`.
43 |
44 |
45 |
--------------------------------------------------------------------------------
/docs/pages/guides/static-components.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import LinkedComponents from '_shared/LinkedComponents';
5 | import * as StaticComponents from './StaticComponents.example';
6 |
7 |
8 |
9 | ## Static components
10 |
11 | In some places it's required to use some internal control for the calendar or some time input. Here you
12 | are! You can use any sub-control of the pickers directly.
13 |
14 |
15 |
16 | Also you can use our own HOC that is used for any picker which provides managing temporary
17 | chosen date and submitting state logic.
18 |
19 | **Warning:** Make sure, that _all_ your imports are consistent (or default or named) and **are not mixed**
20 |
21 | # FIX ME
22 |
23 | #### Static components example
24 |
25 | You can reuse our logic if you needed some advanced accepting or clearing fuctionality. We are providing special
26 | `useStaticState` for that. It returns you:
27 |
28 | - `inputProps` - props to manage date input state, format and validation.
29 | - `pickerProps` - props that should be spreaded directly to the component
30 | - `wrapperProps` - props to manage accept, dismiss, clear and any modal/inline functionality.
31 |
32 |
33 |
34 | #### API
35 |
36 |
37 |
--------------------------------------------------------------------------------
/docs/pages/index/LandingProperty.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { SvgIconProps } from '@material-ui/core/SvgIcon';
3 | import { Typography, makeStyles, Paper } from '@material-ui/core';
4 |
5 | interface LandingPropertyProps {
6 | title: string;
7 | description: string;
8 | icon: React.ComponentType;
9 | }
10 |
11 | const useStyles = makeStyles({
12 | propertyContainer: {
13 | display: 'flex',
14 | flexDirection: 'column',
15 | alignItems: 'center',
16 | padding: 24,
17 | height: '100%',
18 | margin: '0 16px',
19 | },
20 | icon: {
21 | marginBottom: 24,
22 | fontSize: 64,
23 | },
24 | title: {
25 | fontSize: '1.3em',
26 | lineHeight: 1.2,
27 | marginBottom: 24,
28 | },
29 | });
30 |
31 | export const LandingProperty: React.FC = ({
32 | title,
33 | description,
34 | icon: Icon,
35 | }) => {
36 | const classes = useStyles();
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/docs/pages/index/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Landing';
2 |
--------------------------------------------------------------------------------
/docs/pages/localization/Date-fns.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import frLocale from 'date-fns/locale/fr';
3 | import ruLocale from 'date-fns/locale/ru';
4 | import enLocale from 'date-fns/locale/en-US';
5 | import Button from '@material-ui/core/Button';
6 | import TextField from '@material-ui/core/TextField';
7 | import ButtonGroup from '@material-ui/core/ButtonGroup';
8 | import DateFnsAdapter from '@material-ui/pickers/adapter/date-fns';
9 | import { DatePicker, LocalizationProvider } from '@material-ui/pickers';
10 |
11 | const localeMap = {
12 | en: enLocale,
13 | fr: frLocale,
14 | ru: ruLocale,
15 | };
16 |
17 | const maskMap = {
18 | fr: '__/__/____',
19 | en: '__/__/____',
20 | ru: '__.__.____',
21 | };
22 |
23 | export default function DateFnsLocalizationExample() {
24 | const [locale, setLocale] = React.useState('ru');
25 | const [selectedDate, handleDateChange] = React.useState(new Date());
26 |
27 | const selectLocale = (newLocale: any) => {
28 | setLocale(newLocale);
29 | };
30 |
31 | return (
32 |
33 | handleDateChange(date)}
37 | renderInput={(props) => }
38 | />
39 |
40 | {Object.keys(localeMap).map((localeItem) => (
41 |
44 | ))}
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/docs/pages/localization/Hijri.example.tsx:
--------------------------------------------------------------------------------
1 | import 'moment/locale/ar-sa';
2 | import * as React from 'react';
3 | import moment, { Moment } from 'moment';
4 | import HijriAdapter from '@date-io/hijri';
5 | import TextField from '@material-ui/core/TextField';
6 | import { TimePicker, DateTimePicker, DatePicker, LocalizationProvider } from '@material-ui/pickers';
7 |
8 | export default function HijriExample() {
9 | const [selectedDate, handleDateChange] = React.useState(moment());
10 |
11 | return (
12 |
13 | }
16 | okText="موافق"
17 | cancelText="الغاء"
18 | clearText="مسح"
19 | inputFormat="iYYYY/iMM/iDD"
20 | value={selectedDate}
21 | onChange={(date) => handleDateChange(date)}
22 | minDate={moment('1937-03-14')}
23 | maxDate={moment('2076-11-26')}
24 | />
25 | }
28 | okText="موافق"
29 | cancelText="الغاء"
30 | clearText="مسح"
31 | inputFormat="hh:mm A"
32 | value={selectedDate}
33 | onChange={(date) => handleDateChange(date)}
34 | />
35 | }
37 | okText="موافق"
38 | cancelText="الغاء"
39 | inputFormat="iYYYY/iMM/iDD"
40 | value={selectedDate}
41 | onChange={(date) => handleDateChange(date)}
42 | minDate={moment('1937-03-14')}
43 | maxDate={moment('2076-11-26')}
44 | />
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/docs/pages/localization/Moment.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import 'moment/locale/fr';
3 | import 'moment/locale/ru';
4 | import moment, { Moment } from 'moment';
5 | import Button from '@material-ui/core/Button';
6 | import TextField from '@material-ui/core/TextField';
7 | import ButtonGroup from '@material-ui/core/ButtonGroup';
8 | import MomentAdapter from '@material-ui/pickers/adapter/moment';
9 | import { DatePicker, LocalizationProvider } from '@material-ui/pickers';
10 |
11 | moment.locale('fr'); // it is required to select default locale manually
12 |
13 | const localeMap = {
14 | en: 'en',
15 | fr: 'fr',
16 | ru: 'ru',
17 | };
18 |
19 | const maskMap = {
20 | fr: '__/__/____',
21 | en: '__/__/____',
22 | ru: '__.__.____',
23 | };
24 |
25 | export default function MomentLocalizationExample() {
26 | const [locale, setLocale] = React.useState('fr');
27 | const [selectedDate, handleDateChange] = React.useState(moment());
28 |
29 | const selectLocale = (newLocale: any) => {
30 | moment.locale(newLocale);
31 |
32 | setLocale(newLocale);
33 | };
34 |
35 | return (
36 |
37 | }
39 | mask={maskMap[locale]}
40 | value={selectedDate}
41 | onChange={(date) => handleDateChange(date)}
42 | />
43 |
44 | {Object.keys(localeMap).map((localeItem) => (
45 |
53 | ))}
54 |
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/docs/pages/localization/Persian.example.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import moment, { Moment } from 'moment';
3 | import JalaliAdapter from '@date-io/jalaali';
4 | import TextField from '@material-ui/core/TextField';
5 | import { loadPersian } from 'moment-jalaali';
6 | import { TimePicker, DateTimePicker, DatePicker, LocalizationProvider } from '@material-ui/pickers';
7 |
8 | loadPersian({ dialect: 'persian-modern', usePersianDigits: true });
9 |
10 | function PersianExample() {
11 | const [selectedDate, handleDateChange] = React.useState(moment());
12 |
13 | return (
14 |
15 | }
17 | clearable
18 | okText="تأیید"
19 | cancelText="لغو"
20 | clearText="پاک کردن"
21 | inputFormat="jYYYY/iMM/iDD"
22 | value={selectedDate}
23 | onChange={(date) => handleDateChange(date)}
24 | />
25 | }
27 | clearable
28 | okText="تأیید"
29 | cancelText="لغو"
30 | clearText="پاک کردن"
31 | inputFormat="hh:mm A"
32 | value={selectedDate}
33 | onChange={(date) => handleDateChange(date)}
34 | />
35 | }
37 | okText="تأیید"
38 | cancelText="لغو"
39 | inputFormat="jYYYY/iMM/iDD"
40 | value={selectedDate}
41 | onChange={(date) => handleDateChange(date)}
42 | />
43 |
44 | );
45 | }
46 |
47 | export default PersianExample;
48 |
--------------------------------------------------------------------------------
/docs/pages/localization/calendar-systems.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import * as PersianExample from './Persian.example';
5 | import * as HijriExample from './Hijri.example';
6 |
7 |
8 |
9 | ## Additional calendar systems
10 |
11 |
12 |
13 | Make sure that you have read the [right to left section](https://material-ui-next.com/guides/right-to-left/) of the
14 | material-ui documentation page before proceeding.
15 |
16 | Install the specified npm packages below for Jalaali or Hijri depending on which calendar system you will be using.
17 |
18 | #### Jalaali calendar system
19 |
20 | Install `@date-io/jalaali` and `moment-jalaali` packages from npm.
21 |
22 | ```
23 | npm install @date-io/jalaali moment-jalaali
24 | ```
25 |
26 | #### Example
27 |
28 | You can use the examples below. It is recommended that you change the font.
29 |
30 |
31 |
32 |
33 | #### Hijri calendar system
34 |
35 | Install `@date-io/hijri` and `moment-hijri` packages from npm.
36 |
37 | ```
38 | npm install @date-io/hijri moment-hijri
39 | ```
40 |
41 | To use it with Arabic locale, make sure to import `moment/locale/ar-sa`
42 |
43 | ```javascript
44 | import 'moment/locale/ar-sa';
45 | ```
46 |
47 | By default, the `DatePicker` uses `1900-01-01` for minDate and `2099-31-12` for maxDate.
48 | `moment-hijri` only supports dates from `1356-01-01` H (1937-03-14) to `1499-12-29` H (2076-11-26)
49 | so you will need to set `minDate` and `maxDate` on your `DatePicker` component otherwise your component will not work properly.
50 |
51 | ```jsx
52 |
57 | ```
58 |
59 | #### Example
60 |
61 | You can use the examples below. It is recommended that you change the font.
62 |
63 |
64 |
--------------------------------------------------------------------------------
/docs/pages/localization/date-fns.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import * as DateFnsExample from './Date-fns.example';
5 |
6 |
7 |
8 | ## Localization with date-fns
9 |
10 | When using date-fns, localization is performed by passing a locale object to the `LocalizationProvider` component.
11 |
12 |
13 |
14 | #### Basic example
15 |
16 | _Note_: Any picker in the component tree will be re-rendered automatically on locale change.
17 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/pages/localization/moment.mdx:
--------------------------------------------------------------------------------
1 | import Ad from '_shared/Ad';
2 | import Example from '_shared/Example';
3 | import PageMeta from '_shared/PageMeta';
4 | import * as MomentExample from './Moment.example';
5 |
6 |
7 |
8 | ## Localization with moment
9 |
10 | Moment localization is relying on the global moment object used.
11 |
12 |
13 |
14 | It is also possible to pass a configured global moment with selected locale, default timezone, etc.
15 | Pass the selected locale as a string to the provider to make pickers rerender automatically on
16 | locale change.
17 |
18 | #### Example
19 |
20 | _Note_: Any picker in the component tree will be rerendered automatically on locale change
21 |
22 |
23 |
--------------------------------------------------------------------------------
/docs/pages/regression/RegressionDay.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { IUtils } from '@date-io/core/IUtils';
3 | import { PickersDay, PickersDayProps } from '@material-ui/pickers';
4 |
5 | export const createRegressionDay = (utils: IUtils) => (
6 | day: any,
7 | selectedDate: any,
8 | dayProps: PickersDayProps
9 | ) => {
10 | return ;
11 | };
12 |
--------------------------------------------------------------------------------
/docs/pages/regression/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Regression';
2 |
--------------------------------------------------------------------------------
/docs/scripts/assign-production-domains.js:
--------------------------------------------------------------------------------
1 | const { execSync } = require('child_process');
2 | const { version } = require('../../lib/package.json');
3 |
4 | // dev.material-ui-pickers.dev is the automatically deployed for the `next` branch
5 |
6 | execSync('now alias dev.material-ui-pickers.dev next.material-ui-pickers.dev');
7 | execSync(
8 | `now alias dev.material-ui-pickers.dev v${version.replace(/\./g, '-')}.material-ui-pickers.dev`
9 | );
10 |
--------------------------------------------------------------------------------
/docs/scripts/generate-backers.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | require('dotenv').config();
3 |
4 | const fse = require('fs-extra');
5 | const path = require('path');
6 | const { patreon: patreonApi } = require('patreon');
7 |
8 | function processPatreonPledges(rawJson) {
9 | return (
10 | rawJson.data
11 | // sort by pledge amount
12 | .sort((a, b) => a.attributes.amount_cents - b.attributes.amount_cents)
13 | .reverse()
14 | .map(({ relationships }) => {
15 | const patronId = relationships.patron.data.id;
16 | const user = rawJson.included.find((entity) => entity.id === patronId);
17 |
18 | return user.attributes;
19 | })
20 | );
21 | }
22 |
23 | async function main() {
24 | if (!process.argv[2]) {
25 | throw new Error(
26 | `Please provide creator access token as argument.\n
27 | Find creator access token at https://www.patreon.com/portal/registration/register-clients\n
28 | Then pass token like: yarn generate-backers {your token here}`
29 | );
30 | }
31 |
32 | const patreonAPIClient = patreonApi(process.argv[2]);
33 | const { rawJson } = await patreonAPIClient('/campaigns/1559688/pledges');
34 | const processedPatreonResponse = processPatreonPledges(rawJson);
35 |
36 | await fse.writeFile(
37 | path.resolve(__dirname, '..', 'patrons.json'),
38 | JSON.stringify(processedPatreonResponse)
39 | );
40 | }
41 |
42 | main().catch(console.error);
43 |
--------------------------------------------------------------------------------
/docs/static/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/android-icon-144x144.png
--------------------------------------------------------------------------------
/docs/static/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/android-icon-192x192.png
--------------------------------------------------------------------------------
/docs/static/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/android-icon-36x36.png
--------------------------------------------------------------------------------
/docs/static/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/android-icon-48x48.png
--------------------------------------------------------------------------------
/docs/static/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/android-icon-72x72.png
--------------------------------------------------------------------------------
/docs/static/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/android-icon-96x96.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-114x114.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-120x120.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-144x144.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-152x152.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-180x180.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-57x57.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-60x60.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-72x72.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-76x76.png
--------------------------------------------------------------------------------
/docs/static/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/docs/static/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/apple-icon.png
--------------------------------------------------------------------------------
/docs/static/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/static/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/static/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/favicon-96x96.png
--------------------------------------------------------------------------------
/docs/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/favicon.ico
--------------------------------------------------------------------------------
/docs/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Material ui pickers",
3 | "name": "Material ui pickers docs",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "theme_color": "#3da044",
12 | "background_color": "#ffffff"
13 | }
14 |
--------------------------------------------------------------------------------
/docs/static/meta-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/meta-image.png
--------------------------------------------------------------------------------
/docs/static/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/ms-icon-144x144.png
--------------------------------------------------------------------------------
/docs/static/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/ms-icon-150x150.png
--------------------------------------------------------------------------------
/docs/static/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/ms-icon-310x310.png
--------------------------------------------------------------------------------
/docs/static/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mui/material-ui-pickers/e30c5c3ec5cce5a3d42cfec66388bae52427dd3b/docs/static/ms-icon-70x70.png
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true,
5 | "allowUmdGlobalAccess": true,
6 | "baseUrl": ".",
7 | "esModuleInterop": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "jsx": "react",
10 | "lib": ["dom", "es2017"],
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "noEmit": true,
14 | "noImplicitAny": true,
15 | "noUnusedLocals": false,
16 | "noUnusedParameters": false,
17 | "preserveConstEnums": true,
18 | "removeComments": false,
19 | "resolveJsonModule": true,
20 | "skipLibCheck": true,
21 | "sourceMap": true,
22 | "strict": true,
23 | "suppressImplicitAnyIndexErrors": true,
24 | "target": "esnext"
25 | },
26 | "include": ["./**/*.ts*", "./typings.d.ts"]
27 | }
28 |
--------------------------------------------------------------------------------
/docs/typings.d.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MuiPickersComponentsToClassName,
3 | MuiPickersComponentsPropsList,
4 | } from '@material-ui/pickers/src/typings';
5 |
6 | declare module 'moment-jalaali' {
7 | const value: any;
8 | export default value;
9 | }
10 |
11 | declare module '@material-ui/core/styles/overrides' {
12 | export interface ComponentNameToClassKey extends MuiPickersComponentsToClassName {}
13 | }
14 |
15 | declare module '@material-ui/core/styles/props' {
16 | export interface ComponentsPropsList extends MuiPickersComponentsPropsList {}
17 | }
18 |
19 | interface NavigatorClipboard {
20 | clipboard: {
21 | writeText: (value: string) => Promise;
22 | };
23 | }
24 |
25 | interface Navigator extends NavigatorClipboard {}
26 |
27 | declare module '*.mdx' {
28 | const value: React.ComponentType;
29 | export default value;
30 | }
31 |
--------------------------------------------------------------------------------
/docs/utils/NotificationManager.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ReactMarkdown from 'react-markdown';
3 | import { useSnackbar } from 'notistack';
4 | import { makeStyles } from '@material-ui/core/styles';
5 | import notifications from '../notifications.json';
6 |
7 | interface Notification {
8 | id: string;
9 | title: string;
10 | }
11 |
12 | const useStyles = makeStyles({
13 | notificationContainer: {
14 | '& > p': {
15 | margin: 4,
16 | },
17 | },
18 | });
19 |
20 | export function useNotification() {
21 | const styles = useStyles();
22 | const { enqueueSnackbar } = useSnackbar();
23 |
24 | React.useEffect(() => {
25 | // @ts-ignore
26 | if (window.Cypress) {
27 | return; // hide for visual regression and tests
28 | }
29 |
30 | const viewedNotifications: string[] = JSON.parse(
31 | localStorage.getItem('viewedNotifications') || '[]'
32 | );
33 |
34 | const notificationToShow = (notifications as Notification[]).find(
35 | (notification) => !viewedNotifications.some((viewedId) => viewedId === notification.id)
36 | );
37 |
38 | if (notificationToShow) {
39 | enqueueSnackbar(
40 | ,
44 | {
45 | anchorOrigin: {
46 | vertical: 'top',
47 | horizontal: 'center',
48 | },
49 | }
50 | );
51 |
52 | localStorage.setItem(
53 | 'viewedNotifications',
54 | JSON.stringify([...viewedNotifications, notificationToShow.id])
55 | );
56 | }
57 | }, [enqueueSnackbar, styles.notificationContainer]);
58 | }
59 |
60 | export const NotificationManager: React.FC = () => {
61 | useNotification();
62 | return null;
63 | };
64 |
--------------------------------------------------------------------------------
/docs/utils/anchor-autolink.js:
--------------------------------------------------------------------------------
1 | const visit = require('unist-util-visit');
2 |
3 | function inject(node, id) {
4 | delete node.data.hProperties.id;
5 |
6 | const text = node.children[0] && node.children[0].value;
7 | node.data.hChildren = [
8 | {
9 | type: 'element',
10 | tagName: 'a',
11 | properties: { id, className: ['anchor-link'] },
12 | },
13 | {
14 | type: 'text',
15 | value: text,
16 | },
17 | {
18 | type: 'element',
19 | tagName: 'a',
20 | children: [{ type: 'text', value: '#' }],
21 | properties: { href: `#${id}`, className: ['anchor-link-style'] },
22 | },
23 | ];
24 | }
25 |
26 | module.exports = () => (tree) => {
27 | visit(tree, 'heading', (node) => {
28 | const { data } = node;
29 | const id = data && data.hProperties && data.hProperties.id;
30 |
31 | if (id) {
32 | inject(node, id);
33 | }
34 | });
35 | };
36 |
--------------------------------------------------------------------------------
/docs/utils/getPageContext.ts:
--------------------------------------------------------------------------------
1 | import { SheetsRegistry } from 'jss';
2 | import { createGenerateClassName } from '@material-ui/core/styles';
3 |
4 | export interface PageContext {
5 | generateClassName: any;
6 | sheetsManager: Map;
7 | sheetsRegistry: SheetsRegistry;
8 | }
9 |
10 | export default function (): PageContext {
11 | return {
12 | // This is needed in order to deduplicate the injection of CSS in the page.
13 | sheetsManager: new Map(),
14 | // This is needed in order to inject the critical CSS.
15 | sheetsRegistry: new SheetsRegistry(),
16 | // The standard class name generator.
17 | generateClassName: createGenerateClassName(),
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/docs/utils/github-api.ts:
--------------------------------------------------------------------------------
1 | import fetch from 'isomorphic-fetch';
2 |
3 | let cacheRemaining = 0;
4 | const cache = {
5 | tags: null as { name: string }[] | null,
6 | };
7 |
8 | type CacheType = typeof cache;
9 |
10 | export function fetchGithubData(
11 | path: T
12 | ): Promise> {
13 | if (cache[path] && new Date().getTime() < cacheRemaining) {
14 | return Promise.resolve(cache[path] || ([] as any));
15 | }
16 |
17 | return fetch(`https://api.github.com/repos/mui-org/material-ui-pickers/${path}`, {
18 | headers: {
19 | // just a super basic readonly token, that makes api rate limit = 5000/hour
20 | Authorization: 'token 14ef125b9fbcf138ff9042b45f89f8b3c28f510a',
21 | },
22 | })
23 | .then((res) => {
24 | cacheRemaining = Number(res.headers.get('X-RateLimit-Reset')) * 1000;
25 | if (res.status > 400) {
26 | // We cannot update the cache on this step
27 | throw new Error('Could not fetch the data');
28 | }
29 |
30 | return res.json();
31 | })
32 | .then((data: NonNullable) => {
33 | cache[path] = data;
34 | return data;
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/docs/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | import dayjs, { Dayjs } from 'dayjs';
2 | import moment, { Moment } from 'moment';
3 | import { DateTime } from 'luxon';
4 |
5 | export function stringToTestId(string: string) {
6 | return string.replace(/[&/\\#,+()$~%.'":*?<>{}\s]/g, '');
7 | }
8 |
9 | export function makeJSDateObject(date: Date | Moment | DateTime | Dayjs) {
10 | if (date instanceof dayjs) {
11 | return (date as Dayjs).clone().toDate();
12 | }
13 |
14 | if (moment.isMoment(date)) {
15 | return (date as Moment).clone().toDate();
16 | }
17 |
18 | if (date instanceof DateTime) {
19 | return date.toJSDate();
20 | }
21 |
22 | if (date instanceof Date) {
23 | return new Date(date.getTime());
24 | }
25 |
26 | return date as any; // handle case with invalid input
27 | }
28 |
29 | export function copy(text: string) {
30 | return navigator.clipboard.writeText(text);
31 | }
32 |
33 | export function loadScript(src: string, position: Element) {
34 | const script = document.createElement('script');
35 | script.setAttribute('async', '');
36 | // eslint-disable-next-line no-console
37 | script.onerror = console.log;
38 | script.src = src;
39 | position.appendChild(script);
40 |
41 | return script;
42 | }
43 |
44 | export function getRandomItem(arr: T[]): T {
45 | return arr[Math.floor(Math.random() * arr.length)];
46 | }
47 |
--------------------------------------------------------------------------------
/docs/utils/prism.ts:
--------------------------------------------------------------------------------
1 | import prism from 'prismjs';
2 | import 'prismjs/components/prism-jsx';
3 | import 'prismjs/components/prism-diff';
4 | import 'prismjs/components/prism-typescript';
5 | import { ThemeType } from '../layout/PageWithContext';
6 |
7 | export type AvailableLanguages = 'jsx' | 'typescript' | 'markup' | 'json' | 'diff';
8 | export const prismThemes = {
9 | light: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism.min.css',
10 | dark: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism-okaidia.min.css',
11 | };
12 |
13 | export function setPrismTheme(type: ThemeType) {
14 | const newThemeCss = prismThemes[type];
15 | const currentLinkElement = document.querySelector('link[data-prism="true"]');
16 |
17 | if (!currentLinkElement) {
18 | throw new Error('Cannot find prism link');
19 | }
20 |
21 | const newLinkElement = document.createElement('link');
22 | newLinkElement.rel = 'stylesheet';
23 | newLinkElement.dataset.prism = 'true';
24 | newLinkElement.href = newThemeCss;
25 |
26 | document.head.appendChild(newLinkElement);
27 | currentLinkElement.remove();
28 | }
29 |
30 | export function highlight(code: string, language: AvailableLanguages) {
31 | return prism.highlight(code, prism.languages[language], language);
32 | }
33 |
--------------------------------------------------------------------------------
/docs/utils/table-styler.js:
--------------------------------------------------------------------------------
1 | const visit = require('unist-util-visit');
2 |
3 | module.exports = () => (tree) => {
4 | visit(tree, 'table', (node) => {
5 | node.data = { hProperties: { className: ['mui-pickers-markdown-table'] } };
6 | });
7 | };
8 |
--------------------------------------------------------------------------------
/e2e/component/DatePicker.spec.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { mountStaticPicker } from '../test-utils';
3 | import { StaticDatePicker } from '@material-ui/pickers';
4 |
5 | describe('', () => {
6 | it('Renders and show todays date', () => {
7 | mountStaticPicker((defaultProps) => );
8 |
9 | cy.findByTestId('datepicker-toolbar-date').contains('Sat, Oct 7');
10 | });
11 |
12 | it('Switches between months', () => {
13 | mountStaticPicker((defaultProps) => );
14 |
15 | cy.findByTestId('calendar-month-text').contains('October');
16 |
17 | cy.findByTestId('previous-arrow-button').click().click();
18 | cy.findByTestId('next-arrow-button').click().click().click();
19 |
20 | cy.wait(350);
21 |
22 | cy.findByTestId('calendar-month-text').contains('November');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/e2e/component/DateTimePicker.spec.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { DIALOG_WIDTH } from '../../lib/src/constants/dimensions';
3 | import { mountPickerWithState, mountStaticPicker } from '../test-utils';
4 | import { StaticDateTimePicker, MobileDateTimePicker } from '@material-ui/pickers';
5 |
6 | describe('', () => {
7 | it('Renders and show todays date', () => {
8 | mountStaticPicker((defaultProps) => );
9 |
10 | cy.contains('2017');
11 | cy.contains('Oct 7');
12 | cy.contains('07:36');
13 |
14 | cy.percySnapshot('', {
15 | widths: [DIALOG_WIDTH],
16 | });
17 | });
18 |
19 | it('Proper flow for date & time picking', () => {
20 | mountPickerWithState((defaultProps) => (
21 |
22 | ));
23 |
24 | cy.get('input').click();
25 |
26 | // Year
27 | cy.findByText('2015').click();
28 | cy.get('input').should('have.value', '10/07/2015 07:36 PM');
29 |
30 | // Date
31 | cy.findByLabelText('next month').click().click();
32 | cy.findByLabelText('Dec 22, 2015').click();
33 | cy.get('input').should('have.value', '12/22/2015 07:36 PM');
34 |
35 | // Hour
36 | cy.findByRole('menu').trigger('mouseup', { buttons: 1, offsetX: 66, offsetY: 157 });
37 | cy.get('input').should('have.value', '12/22/2015 08:36 PM');
38 |
39 | // Minute
40 | cy.findByRole('menu').trigger('mouseup', {
41 | buttons: 1,
42 | offsetX: 222,
43 | offsetY: 180,
44 | });
45 |
46 | cy.get('input').should('have.value', '12/22/2015 08:20 PM');
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/e2e/integration/App.spec.ts:
--------------------------------------------------------------------------------
1 | import { stringToTestId } from '../../docs/utils/helpers';
2 | import { navItems } from '../../docs/layout/components/navigationMap';
3 |
4 | describe('App navigation', () => {
5 | before(() => {
6 | cy.visit('/getting-started/installation');
7 | });
8 |
9 | navItems.forEach((navItem) => {
10 | context(navItem.title, () => {
11 | beforeEach(() => {
12 | cy.get(`[data-nav=${stringToTestId(navItem.title)}]`).as('nav-group');
13 | cy.get('@nav-group').click();
14 | });
15 |
16 | navItem.children &&
17 | navItem.children.forEach((leafItem: any) => {
18 | it(`Opens ${leafItem.title} page`, () => {
19 | cy.get('@nav-group')
20 | .find(`[data-nav=${stringToTestId(leafItem.title)}]`)
21 | .click();
22 |
23 | cy.url().should('contain', leafItem.as || leafItem.href);
24 | });
25 | });
26 | });
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/e2e/plugins/index.js:
--------------------------------------------------------------------------------
1 | const wp = require('@cypress/webpack-preprocessor');
2 | const percyHealthCheck = require('@percy/cypress/task');
3 | const babelLibConfig = require('../../lib/babel.config.js');
4 |
5 | module.exports = (on, config) => {
6 | const options = {
7 | webpackOptions: {
8 | mode: 'development',
9 | resolve: {
10 | extensions: ['.ts', '.tsx', '.js', '.json'],
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.(ts|tsx)?$/,
16 | loader: 'babel-loader',
17 | options: babelLibConfig,
18 | },
19 | ],
20 | },
21 | },
22 | };
23 |
24 | on('file:preprocessor', wp(options));
25 | on('task', percyHealthCheck);
26 |
27 | return config;
28 | };
29 |
--------------------------------------------------------------------------------
/e2e/support/commands.ts:
--------------------------------------------------------------------------------
1 | import '@percy/cypress';
2 | import 'cypress-plugin-tab';
3 | import '@testing-library/cypress/add-commands';
4 |
5 | declare global {
6 | namespace Cypress {
7 | interface Chainable {
8 | toggleTheme: typeof toggleTheme;
9 | }
10 | }
11 | }
12 |
13 | function toggleTheme(options?: Partial) {
14 | cy.get('[data-mui-test=toggle-theme-btn]').click(options);
15 | }
16 |
17 | Cypress.Commands.add('toggleTheme', toggleTheme);
18 |
--------------------------------------------------------------------------------
/e2e/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 | import { configure } from '@testing-library/cypress';
16 |
17 | configure({ testIdAttribute: 'data-mui-test' });
18 |
19 | require('./commands');
20 | require('cypress-react-unit-test/support');
21 |
--------------------------------------------------------------------------------
/e2e/test-utils.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import parse from 'date-fns/parseISO';
3 | import DateFnsAdapter from '@material-ui/pickers/adapter/date-fns';
4 | import TextField, { TextFieldProps } from '@material-ui/core/TextField';
5 | import { mount } from 'cypress-react-unit-test';
6 | import { DIALOG_WIDTH } from '../lib/src/constants/dimensions';
7 | import { LocalizationProvider, StaticDatePickerProps } from '@material-ui/pickers';
8 |
9 | // Time of the first commit to the pickers ❤️
10 | const momentInTime = parse('2017-10-07T19:36:00.000');
11 | const mockRequiredProps = {
12 | reduceAnimations: true, // speedup tests
13 | value: momentInTime,
14 | onChange: () => {},
15 | renderInput: (props: TextFieldProps) => ,
16 | };
17 |
18 | /* Globally mock time for all component tests */
19 | function mockTime() {
20 | cy.clock(momentInTime.getTime());
21 | }
22 |
23 | export function mountStaticPicker(
24 | createStaticPickerNode: (
25 | props: Pick
26 | ) => React.ReactNode
27 | ) {
28 | document.body.style.margin = '0px';
29 | cy.viewport(DIALOG_WIDTH, 500);
30 |
31 | mockTime();
32 |
33 | mount(
34 |
35 | {createStaticPickerNode(mockRequiredProps)}
36 |
37 | );
38 | }
39 |
40 | export function mountPicker(
41 | createPicker: (
42 | props: Pick
43 | ) => React.ReactNode
44 | ) {
45 | mockTime();
46 |
47 | mount(
48 |
49 | {createPicker(mockRequiredProps)}
50 |
51 | );
52 | }
53 |
54 | export function mountPickerWithState(
55 | createPicker: (
56 | props: Pick
57 | ) => JSX.Element
58 | ) {
59 | function PickerWithState() {
60 | const [value, setDate] = React.useState(momentInTime);
61 |
62 | return createPicker({ ...mockRequiredProps, value, onChange: (date) => setDate(date) });
63 | }
64 |
65 | mountPicker(() => );
66 | }
67 |
--------------------------------------------------------------------------------
/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "baseUrl": "../node_modules",
5 | "target": "es2015",
6 | "moduleResolution": "node",
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "resolveJsonModule": true,
10 | "jsx": "react",
11 | "lib": ["es5", "dom"],
12 | "types": [
13 | "cypress",
14 | "@percy/cypress",
15 | "@types/testing-library__cypress",
16 | "@material-ui/core",
17 | "@material-ui/pickers"
18 | ]
19 | },
20 | "include": ["./**/*.ts", "../docs/**/*", "component/**/*.tsx"]
21 | }
22 |
--------------------------------------------------------------------------------
/lib/.size-snapshot.json:
--------------------------------------------------------------------------------
1 | {
2 | "material-ui-pickers.esm.js": {
3 | "bundled": 209224,
4 | "minified": 110237,
5 | "gzipped": 28323,
6 | "treeshaked": {
7 | "rollup": {
8 | "code": 89207,
9 | "import_statements": 2087
10 | },
11 | "webpack": {
12 | "code": 98915
13 | }
14 | }
15 | },
16 | "material-ui-pickers.umd.js": {
17 | "bundled": 322787,
18 | "minified": 124100,
19 | "gzipped": 35010
20 | },
21 | "material-ui-pickers.umd.min.js": {
22 | "bundled": 277744,
23 | "minified": 114262,
24 | "gzipped": 32199
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/adapter/date-fns.ts:
--------------------------------------------------------------------------------
1 | export { default } from '@date-io/date-fns';
2 |
--------------------------------------------------------------------------------
/lib/adapter/dayjs.ts:
--------------------------------------------------------------------------------
1 | export { default } from '@date-io/dayjs';
2 |
--------------------------------------------------------------------------------
/lib/adapter/luxon.ts:
--------------------------------------------------------------------------------
1 | export { default } from '@date-io/luxon';
2 |
--------------------------------------------------------------------------------
/lib/adapter/moment.ts:
--------------------------------------------------------------------------------
1 | export { default } from '@date-io/moment';
2 |
--------------------------------------------------------------------------------
/lib/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | presets: [
5 | '@babel/preset-react',
6 | '@babel/preset-typescript',
7 | ['@babel/preset-env', { modules: false }],
8 | ],
9 | plugins: [
10 | 'optimize-clsx',
11 | ['@babel/plugin-proposal-class-properties', { loose: true }],
12 | // ['@babel/plugin-transform-runtime'],
13 | // for IE 11 support
14 | '@babel/plugin-transform-object-assign',
15 | '@babel/plugin-proposal-optional-chaining',
16 | '@babel/plugin-proposal-nullish-coalescing-operator',
17 | path.resolve(__dirname, './remove-prop-types.js'),
18 | ],
19 | env: {
20 | production: {
21 | plugins: [
22 | '@babel/plugin-transform-react-constant-elements',
23 | ['babel-plugin-react-remove-properties', { properties: ['data-mui-test'] }],
24 | ],
25 | },
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/lib/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "mountMode": true
3 | }
4 |
--------------------------------------------------------------------------------
/lib/jest.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | setupFilesAfterEnv: ['/src/__tests__/setup.ts'],
5 | testRegex: './src/__tests__/.*\\.test\\.(js|tsx|ts)$',
6 | testURL: 'http://localhost/',
7 | collectCoverage: true,
8 | transform: {
9 | '^.+\\.(ts|tsx)?$': 'ts-jest',
10 | },
11 | modulePathIgnorePatterns: ['/src/__tests__/dist/'],
12 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
13 | coveragePathIgnorePatterns: ['/src/__tests__/'],
14 | globals: {
15 | 'ts-jest': {
16 | tsConfig: path.resolve(__dirname, 'src', '__tests__', 'tsconfig.json'),
17 | },
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/lib/prepare-build-files.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const path = require('path');
3 | const fse = require('fs-extra');
4 |
5 | function copyReadme() {
6 | return fse
7 | .copyFile(
8 | path.resolve(__dirname, '..', 'README.md'),
9 | path.resolve(__dirname, 'build', 'README.md')
10 | )
11 | .then(() => console.log('> Copied README.md'));
12 | }
13 |
14 | function createAdapterPackageFile(name) {
15 | const packageJson = {
16 | name: `@material-ui/pickers-adapter-${name}`,
17 | module: 'index.js',
18 | main: 'index.cjs.js',
19 | typings: `../${name}.d.ts`,
20 | };
21 |
22 | return fse.writeFile(
23 | path.resolve(__dirname, 'build', 'adapter', name, 'package.json'),
24 | JSON.stringify(packageJson, null, 2)
25 | );
26 | }
27 |
28 | function createAdaptersPackages() {
29 | return Promise.all(
30 | ['luxon', 'date-fns', 'dayjs', 'moment'].map(createAdapterPackageFile)
31 | ).then(() => console.log('> Created package.json files for adapters'));
32 | }
33 |
34 | function createRootPackageFile() {
35 | return fse
36 | .readFile(path.resolve(__dirname, 'package.json'), 'utf8')
37 | .then((data) => JSON.parse(data))
38 | .then((packageData) => {
39 | // cleanup produced package
40 | const {
41 | devDependencies,
42 | jest,
43 | husky,
44 | main,
45 | module,
46 | 'lint-staged': ls,
47 | ...other
48 | } = packageData;
49 |
50 | const newPackage = {
51 | ...other,
52 | private: false,
53 | main: './dist/material-ui-pickers.js',
54 | module: './index.js',
55 | typings: './index.d.ts',
56 | };
57 |
58 | const buildPath = path.resolve(__dirname, 'build', 'package.json');
59 | const data = JSON.stringify(newPackage, null, 2);
60 |
61 | return fse
62 | .writeFile(buildPath, data)
63 | .then(() => console.log(`> Created package.json in ${buildPath}`));
64 | });
65 | }
66 |
67 | createRootPackageFile()
68 | .then(copyReadme)
69 | .then(createAdaptersPackages)
70 | .then(() => console.log('\nFinished build files preparation'))
71 | .catch(console.error);
72 |
--------------------------------------------------------------------------------
/lib/remove-prop-types.js:
--------------------------------------------------------------------------------
1 | const removePropTypes = (api) => {
2 | const { types, template } = api;
3 | const visitedKey = `remove-prop-types-${Date.now()}`;
4 | return {
5 | visitor: {
6 | AssignmentExpression(path) {
7 | if (
8 | types.isMemberExpression(path.node.left) &&
9 | types.isIdentifier(path.node.left.property) &&
10 | path.node.left.property.name === 'propTypes'
11 | ) {
12 | // Prevent infinity loop.
13 | if (path.node[visitedKey]) {
14 | return;
15 | }
16 | path.node[visitedKey] = true;
17 |
18 | const unsafeWrapTemplate = template(
19 | `
20 | if (process.env.NODE_ENV !== "production") {
21 | NODE;
22 | }
23 | `,
24 | { placeholderPattern: /^NODE$/ }
25 | );
26 | path.replaceWith(
27 | unsafeWrapTemplate({
28 | NODE: path.node,
29 | })
30 | );
31 | }
32 | },
33 | },
34 | };
35 | };
36 |
37 | module.exports = removePropTypes;
38 |
--------------------------------------------------------------------------------
/lib/src/CalendarSkeleton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import clsx from 'clsx';
3 | import Skeleton from '@material-ui/lab/Skeleton';
4 | import { makeStyles } from '@material-ui/core/styles';
5 | import { DAY_SIZE, DAY_MARGIN } from './constants/dimensions';
6 | import { withDefaultProps } from './_shared/withDefaultProps';
7 | import { useStyles as useCalendarStyles } from './views/Calendar/Calendar';
8 |
9 | export interface CalendarSkeletonProps extends React.HTMLProps {}
10 |
11 | const muiComponentConfig = {
12 | name: 'MuiPickersCalendarSkeleton',
13 | };
14 |
15 | export const useStyles = makeStyles(
16 | {
17 | root: {
18 | alignSelf: 'start',
19 | },
20 | daySkeleton: {
21 | margin: `0 ${DAY_MARGIN}px`,
22 | },
23 | hidden: {
24 | visibility: 'hidden',
25 | },
26 | },
27 | muiComponentConfig
28 | );
29 |
30 | const monthMap = [
31 | [0, 1, 1, 1, 1, 1, 1],
32 | [1, 1, 1, 1, 1, 1, 1],
33 | [1, 1, 1, 1, 1, 1, 1],
34 | [1, 1, 1, 1, 1, 1, 1],
35 | [1, 1, 1, 1, 0, 0, 0],
36 | ];
37 |
38 | export const CalendarSkeleton: React.FC = withDefaultProps(
39 | muiComponentConfig,
40 | (props) => {
41 | const { className, ...other } = props;
42 | const classes = useStyles();
43 | const calendarClasses = useCalendarStyles();
44 |
45 | return (
46 |
47 | {monthMap.map((week, index) => (
48 |
49 | {week.map((day, index2) => (
50 |
59 | ))}
60 |
61 | ))}
62 |
63 | );
64 | }
65 | );
66 |
67 | export default CalendarSkeleton;
68 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DatePicker';
2 |
--------------------------------------------------------------------------------
/lib/src/DateRangePicker/DateRangeDelimiter.tsx:
--------------------------------------------------------------------------------
1 | import Typography from '@material-ui/core/Typography';
2 | import { styled } from '@material-ui/core/styles';
3 | import { withDefaultProps } from '../_shared/withDefaultProps';
4 |
5 | export const DateRangeDelimiter = withDefaultProps(
6 | { name: 'MuiPickersDateRangeDelimiter' },
7 | styled(Typography)({
8 | margin: '0 16px',
9 | })
10 | );
11 |
12 | export type DateRangeDelimiterProps = React.ComponentProps;
13 |
--------------------------------------------------------------------------------
/lib/src/DateRangePicker/RangeTypes.ts:
--------------------------------------------------------------------------------
1 | import { ParsableDate } from '../constants/prop-types';
2 | import { AllSharedPickerProps } from '../Picker/SharedPickerProps';
3 | import { ExportedDateRangePickerInputProps } from './DateRangePickerInput';
4 |
5 | export type RangeInput = [ParsableDate, ParsableDate];
6 | export type DateRange = [TDate | null, TDate | null];
7 |
8 | export type AllSharedDateRangePickerProps = Omit<
9 | AllSharedPickerProps, DateRange>,
10 | 'renderInput' | 'orientation'
11 | > &
12 | ExportedDateRangePickerInputProps;
13 |
14 | export interface CurrentlySelectingRangeEndProps {
15 | currentlySelectingRangeEnd: 'start' | 'end';
16 | setCurrentlySelectingRangeEnd: (newSelectingEnd: 'start' | 'end') => void;
17 | }
18 |
--------------------------------------------------------------------------------
/lib/src/DateRangePicker/date-range-manager.ts:
--------------------------------------------------------------------------------
1 | import { DateRange } from './RangeTypes';
2 | import { MuiPickersAdapter } from '../_shared/hooks/useUtils';
3 |
4 | interface CalculateRangeChangeOptions {
5 | utils: MuiPickersAdapter;
6 | range: DateRange;
7 | newDate: unknown;
8 | currentlySelectingRangeEnd: 'start' | 'end';
9 | }
10 |
11 | export function calculateRangeChange({
12 | utils,
13 | range,
14 | newDate: selectedDate,
15 | currentlySelectingRangeEnd,
16 | }: CalculateRangeChangeOptions): { nextSelection: 'start' | 'end'; newRange: DateRange } {
17 | const [start, end] = range;
18 |
19 | if (currentlySelectingRangeEnd === 'start') {
20 | return Boolean(end) && utils.isAfter(selectedDate, end)
21 | ? { nextSelection: 'end', newRange: [selectedDate, null] }
22 | : { nextSelection: 'end', newRange: [selectedDate, end] };
23 | }
24 |
25 | return Boolean(start) && utils.isBefore(selectedDate, start)
26 | ? { nextSelection: 'end', newRange: [selectedDate, null] }
27 | : { nextSelection: 'start', newRange: [start, selectedDate] };
28 | }
29 |
30 | export function calculateRangePreview(options: CalculateRangeChangeOptions): DateRange {
31 | if (!options.newDate) {
32 | return [null, null];
33 | }
34 |
35 | const [start, end] = options.range;
36 | const { newRange } = calculateRangeChange(options);
37 |
38 | if (!start || !end) {
39 | return newRange;
40 | }
41 |
42 | const [previewStart, previewEnd] = newRange;
43 | return options.currentlySelectingRangeEnd === 'end' ? [end, previewEnd] : [previewStart, start];
44 | }
45 |
--------------------------------------------------------------------------------
/lib/src/DateTimePicker/date-time-utils.ts:
--------------------------------------------------------------------------------
1 | import { ParsableDate } from '../constants/prop-types';
2 | import { MuiPickersAdapter } from '../_shared/hooks/useUtils';
3 | import { DateValidationProps, validateDate } from '../_helpers/date-utils';
4 | import { TimeValidationProps, validateTime } from '../_helpers/time-utils';
5 |
6 | export function validateDateAndTime(
7 | utils: MuiPickersAdapter,
8 | value: unknown | ParsableDate,
9 | {
10 | minDate,
11 | maxDate,
12 | disableFuture,
13 | shouldDisableDate,
14 | disablePast,
15 | ...timeValidationProps
16 | }: DateValidationProps & TimeValidationProps
17 | ) {
18 | const dateValidationResult = validateDate(utils, value, {
19 | minDate,
20 | maxDate,
21 | disableFuture,
22 | shouldDisableDate,
23 | disablePast,
24 | });
25 |
26 | if (dateValidationResult !== null) {
27 | return dateValidationResult;
28 | }
29 |
30 | return validateTime(utils, value, timeValidationProps);
31 | }
32 |
33 | export type DateAndTimeValidationError = ReturnType;
34 |
--------------------------------------------------------------------------------
/lib/src/DateTimePicker/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DateTimePicker';
2 |
--------------------------------------------------------------------------------
/lib/src/LocalizationProvider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { DateIOFormats } from '@date-io/core/IUtils';
4 | import { MuiPickersAdapter } from './_shared/hooks/useUtils';
5 |
6 | export const MuiPickersAdapterContext = React.createContext(null);
7 |
8 | export interface LocalizationProviderProps {
9 | children?: React.ReactNode;
10 | dateAdapter: new (...args: any) => MuiPickersAdapter;
11 | dateFormats?: Partial;
12 | dateLibInstance?: any;
13 | locale?: any;
14 | }
15 |
16 | const LocalizationProvider: React.FC = (props) => {
17 | const { children, dateAdapter: Utils, dateFormats, dateLibInstance, locale } = props;
18 | const utils = React.useMemo(
19 | () => new Utils({ locale, formats: dateFormats, instance: dateLibInstance }),
20 | [Utils, locale, dateFormats, dateLibInstance]
21 | );
22 |
23 | return (
24 | {children}
25 | );
26 | };
27 |
28 | LocalizationProvider.propTypes = {
29 | dateAdapter: PropTypes.func.isRequired,
30 | locale: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
31 | /**
32 | * Your component tree.
33 | */
34 | children: PropTypes.node,
35 | } as any;
36 |
37 | export default LocalizationProvider;
38 |
--------------------------------------------------------------------------------
/lib/src/TimePicker/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './TimePicker';
2 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DateRangePickerLegacy.test.tsx:
--------------------------------------------------------------------------------
1 | // Note that most of use cases are covered in cypress tests e2e/integration/DateRange.spec.ts
2 | import * as React from 'react';
3 | import TextField, { TextFieldProps } from '@material-ui/core/TextField';
4 | import { mount, utilsToUse } from './test-utils';
5 | import { DesktopDateRangePicker } from '../DateRangePicker/DateRangePicker';
6 |
7 | const defaultRangeRenderInput = (startProps: TextFieldProps, endProps: TextFieldProps) => (
8 |
9 |
10 |
11 |
12 | );
13 |
14 | describe('DateRangePicker', () => {
15 | it('allows select range', () => {
16 | const component = mount(
17 |
26 | );
27 |
28 | expect(component.find('[data-mui-test="DateRangeHighlight"]').length).toBe(31);
29 | });
30 |
31 | it('prop: calendars', () => {
32 | const component = mount(
33 |
43 | );
44 |
45 | expect(component.find('Calendar').length).toBe(3);
46 | expect(component.find('button[data-mui-test="DateRangeDay"]').length).toBe(90);
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DateTimePickerRoot.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { ReactWrapper } from 'enzyme';
4 | import { clickOKButton } from './commands';
5 | import { mount, utilsToUse, toHaveBeenCalledExceptMoment } from './test-utils';
6 | import { MobileDateTimePicker, DateTimePickerProps } from '../DateTimePicker/DateTimePicker';
7 |
8 | describe('e2e - DateTimePicker', () => {
9 | let component: ReactWrapper;
10 | const onChangeMock = jest.fn();
11 |
12 | beforeEach(() => {
13 | jest.clearAllMocks();
14 | component = mount(
15 | }
26 | />
27 | );
28 | });
29 |
30 | it('Should renders', () => {
31 | expect(component).toBeTruthy();
32 | });
33 |
34 | it('Should display year view', () => {
35 | component.find('ToolbarButton').first().simulate('click');
36 |
37 | expect(component.find('[data-mui-test="year"]').length).toBe(200);
38 | component.find('[data-mui-test="year"]').at(1).simulate('click');
39 |
40 | clickOKButton(component);
41 | expect(onChangeMock).toHaveBeenCalled();
42 | });
43 |
44 | it('Should render hour view', () => {
45 | component.find('ToolbarButton').at(2).simulate('click');
46 |
47 | expect(component.find('ClockView').props().type).toBe('hours');
48 | });
49 |
50 | it('Should render minutes view', () => {
51 | component.find('ToolbarButton').at(3).simulate('click');
52 | expect(component.find('ClockView').props().type).toBe('minutes');
53 | });
54 |
55 | it('Should change meridiem', () => {
56 | component.find('button[data-mui-test="in-clock-pm-btn"]').simulate('click');
57 |
58 | clickOKButton(component);
59 | toHaveBeenCalledExceptMoment(onChangeMock, [utilsToUse.date('2018-01-01T12:00:00.000Z')]);
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/lib/src/__tests__/KeyboardDatePicker.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { ReactWrapper } from 'enzyme';
4 | import { mount } from './test-utils';
5 | import { DesktopDatePicker, DatePickerProps } from '../DatePicker/DatePicker';
6 |
7 | describe('e2e -- DatePicker keyboard input', () => {
8 | // Doesn't work
9 | if (process.env.UTILS === 'dayjs') {
10 | it('noop', () => {});
11 | return;
12 | }
13 |
14 | const onChangeMock = jest.fn();
15 | let component: ReactWrapper;
16 |
17 | beforeEach(() => {
18 | component = mount(
19 | (
21 |
28 | )}
29 | label="Masked input"
30 | inputFormat={process.env.UTILS === 'moment' ? 'DD/MM/YYYY' : 'dd/MM/yyyy'}
31 | value={new Date('2018-01-01T00:00:00.000Z')}
32 | onChange={onChangeMock}
33 | InputAdornmentProps={{
34 | disableTypography: true,
35 | }}
36 | />
37 | );
38 | });
39 |
40 | it('Should properly set value on change keyboard', () => {
41 | const e = { target: { value: '10/11/2018' } };
42 |
43 | component.find('input').simulate('change', e);
44 | expect(component.find('input').prop('value')).toBe('10/11/2018');
45 |
46 | component.find('input').simulate('blur');
47 | expect(onChangeMock).toBeCalled();
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/lib/src/__tests__/KeyboardDateTimePicker.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { ReactWrapper } from 'enzyme';
4 | import { mount, utilsToUse } from './test-utils';
5 | import { DesktopDateTimePicker, DateTimePickerProps } from '../DateTimePicker/DateTimePicker';
6 |
7 | const format = process.env.UTILS === 'moment' ? 'MM/DD/YYYY HH:mm' : 'MM/dd/yyyy hh:mm';
8 |
9 | describe('e2e - DesktopDateTimePicker', () => {
10 | let component: ReactWrapper;
11 | const onChangeMock = jest.fn();
12 | const onCloseMock = jest.fn();
13 | const onOpenMock = jest.fn();
14 |
15 | beforeEach(() => {
16 | jest.clearAllMocks();
17 | component = mount(
18 | }
26 | value={utilsToUse.date('2018-01-01T00:00:00.000Z')}
27 | />
28 | );
29 | });
30 |
31 | it('Should renders', () => {
32 | expect(component).toBeTruthy();
33 | });
34 |
35 | it('Should open modal with picker on click', () => {
36 | component.find('button#keyboard-button').simulate('click');
37 | expect(component.find('div[role="dialog"]').length).toBe(1);
38 | expect(onOpenMock).toHaveBeenCalled();
39 | });
40 |
41 | it('Should apply keyboard input', () => {
42 | component.find('input').simulate('change', { target: { value: '02/01/2018 10:00' } });
43 | expect(onChangeMock).toHaveBeenCalled();
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/lib/src/__tests__/Theme.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import TextField from '@material-ui/core/TextField';
3 | import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
4 | import { mount } from './test-utils';
5 | import { DatePicker } from '../DatePicker';
6 | import { DateTimePicker } from '../DateTimePicker/DateTimePicker';
7 |
8 | const theme = createMuiTheme({
9 | palette: {
10 | type: 'dark',
11 | },
12 | });
13 |
14 | it('Should renders without crash in dark theme', () => {
15 | const component = mount(
16 |
17 | }
19 | open
20 | openTo="hours"
21 | value={null}
22 | onChange={jest.fn()}
23 | />
24 |
25 | );
26 |
27 | expect(component).toBeTruthy();
28 | });
29 |
30 | it('Should render component with different orientation', () => {
31 | const component = mount(
32 | }
34 | open
35 | orientation="landscape"
36 | value={null}
37 | onChange={jest.fn()}
38 | />
39 | );
40 |
41 | expect(component).toBeTruthy();
42 | });
43 |
--------------------------------------------------------------------------------
/lib/src/__tests__/commands.tsx:
--------------------------------------------------------------------------------
1 | import { ReactWrapper } from 'enzyme';
2 |
3 | export const clickOKButton = (component: ReactWrapper) => {
4 | component
5 | .find('ForwardRef(DialogActions) WithStyles(ForwardRef(Button))')
6 | .at(1)
7 | .simulate('click');
8 | };
9 |
--------------------------------------------------------------------------------
/lib/src/__tests__/setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 |
3 | const Enzyme = require('enzyme');
4 | const EnzymeAdapter = require('enzyme-adapter-react-16');
5 |
6 | // Setup enzyme's react adapter
7 | Enzyme.configure({ adapter: new EnzymeAdapter() });
8 |
9 | // Convert any console error into a thrown error
10 | const consoleError = console.error;
11 | const consoleWarn = console.warn;
12 |
13 | function logAndThrowArgs(...args: any[]) {
14 | if (args[0] instanceof Error) {
15 | throw args[0];
16 | } else {
17 | // combine multi args into a string
18 | const message = args
19 | .map((value) => {
20 | if (typeof value === 'object') {
21 | return JSON.stringify(value);
22 | }
23 |
24 | return value;
25 | })
26 | .join(' ');
27 | throw new Error(message);
28 | }
29 | }
30 |
31 | console.error = (...args: any[]) => {
32 | consoleError.apply(console, args as any);
33 | logAndThrowArgs(...args);
34 | };
35 |
36 | // Waiting to fix https://github.com/mui-org/material-ui-pickers/issues/1924
37 | if (process.env.UTILS !== 'moment') {
38 | console.warn = (...args: any[]) => {
39 | consoleWarn.apply(console, args as any);
40 | logAndThrowArgs(...args);
41 | };
42 | }
43 |
--------------------------------------------------------------------------------
/lib/src/__tests__/shallow/Month.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { ShallowWrapper } from 'enzyme';
3 | import { shallow, utilsToUse } from '../test-utils';
4 | import { Month, MonthProps } from '../../views/Calendar/Month';
5 |
6 | describe('Month', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
12 | Oct
13 |
14 | );
15 | });
16 |
17 | it('Should render', () => {
18 | expect(component).toBeTruthy();
19 | });
20 | });
21 |
22 | describe('Month - disabled state', () => {
23 | let component: ShallowWrapper;
24 |
25 | beforeEach(() => {
26 | component = shallow(
27 |
28 | Oct
29 |
30 | );
31 | });
32 |
33 | it('Should render in disabled state', () => {
34 | expect(component.prop('tabIndex')).toBe(-1);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/lib/src/__tests__/shallow/MonthSelection.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { ReactWrapper } from 'enzyme';
3 | import { Month } from '../../views/Calendar/Month';
4 | import { mount, utilsToUse } from '../test-utils';
5 | import { MonthSelection, MonthSelectionProps } from '../../views/Calendar/MonthSelection';
6 |
7 | describe('MonthSelection', () => {
8 | let component: ReactWrapper>;
9 |
10 | beforeEach(() => {
11 | component = mount(
12 |
18 | );
19 | });
20 |
21 | it('Should render disabled months before min date and after max date', () => {
22 | expect(component.find(Month).map((month) => month.prop('disabled'))).toEqual([
23 | true,
24 | true,
25 | false,
26 | false,
27 | false,
28 | true,
29 | true,
30 | true,
31 | true,
32 | true,
33 | true,
34 | true,
35 | ]);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/lib/src/__tests__/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": [],
4 | "include": ["../../typings.d.ts", "./typings.d.ts", "./**/*.tsx", "./**/*.ts", "./typescript/*"],
5 | "compilerOptions": {
6 | "emitDeclarationOnly": false,
7 | "noEmit": false,
8 | "outDir": "./dist",
9 | "sourceMap": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/src/__tests__/typescript/Overrides.tstest.tsx:
--------------------------------------------------------------------------------
1 | // Related: ../../__tests__/typings.d.ts
2 | import { createMuiTheme } from '@material-ui/core';
3 |
4 | createMuiTheme({
5 | overrides: {
6 | MuiPickersClock: {
7 | clock: {
8 | display: 'flex',
9 | },
10 | },
11 | MuiPickersMonth: {
12 | root: {
13 | color: 'red',
14 | },
15 | },
16 | },
17 | });
18 |
19 | // Allows to mix overrides for both pickers and core components
20 | createMuiTheme({
21 | overrides: {
22 | MuiPickersClock: {
23 | clock: {
24 | display: 'flex',
25 | },
26 | },
27 | MuiButton: {
28 | root: {
29 | color: 'red',
30 | },
31 | },
32 | },
33 | });
34 |
35 | // Throws error if class key is invalid
36 | createMuiTheme({
37 | overrides: {
38 | MuiPickersClock: {
39 | // @ts-expect-error: Throws error if class key is invalid
40 | click: {
41 | display: 'flex',
42 | },
43 | },
44 | MuiPickersCalendarView: {
45 | viewTransitionContainer: {
46 | marginRight: 15,
47 | },
48 | // @ts-expect-error: Throws error if class key is invalid
49 | somethingInvalid: {
50 | color: 'blacl',
51 | },
52 | },
53 | },
54 | });
55 |
--------------------------------------------------------------------------------
/lib/src/__tests__/typescript/ThemeDefaultProps.tstest.tsx:
--------------------------------------------------------------------------------
1 | // Related: ../../__tests__/typings.d.ts
2 | import { createMuiTheme } from '@material-ui/core';
3 |
4 | createMuiTheme({
5 | props: {
6 | MuiPickersDatePicker: {
7 | disableMaskedInput: true,
8 | },
9 | MuiPickersTimePicker: {
10 | ampmInClock: true,
11 | },
12 | MuiPickersDay: {
13 | showDaysOutsideCurrentMonth: true,
14 | },
15 | MuiPickersCalendarView: {
16 | reduceAnimations: true,
17 | },
18 | },
19 | });
20 |
21 | // Allows to mix overrides for both pickers and core components
22 | createMuiTheme({
23 | props: {
24 | MuiPickersCalendarView: {
25 | reduceAnimations: true,
26 | },
27 | MuiPopover: {
28 | open: false,
29 | },
30 | },
31 | });
32 |
33 | // Throws error if class key is invalid
34 | createMuiTheme({
35 | props: {
36 | MuiPickersCalendarView: {
37 | // @ts-expect-error: Throws error if class key is invalid
38 | somethingInvalid: 123,
39 | },
40 | MuiPickersDay: {
41 | onSuspend: () => {},
42 | // @ts-expect-error: Throws error if class key is invalid
43 | showDaysOutsideCurrentMonthTypo: false,
44 | },
45 | },
46 | });
47 |
--------------------------------------------------------------------------------
/lib/src/__tests__/typings.d.ts:
--------------------------------------------------------------------------------
1 | import { MuiPickersComponentsToClassName, MuiPickersComponentsPropsList } from '../typings';
2 |
3 | declare module '@material-ui/core/styles/overrides' {
4 | export interface ComponentNameToClassKey extends MuiPickersComponentsToClassName {}
5 | }
6 |
7 | declare module '@material-ui/core/styles/props' {
8 | export interface ComponentsPropsList extends MuiPickersComponentsPropsList {}
9 | }
10 |
--------------------------------------------------------------------------------
/lib/src/_helpers/utils.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | /* Use it instead of .includes method for IE support */
4 | export function arrayIncludes(array: T[] | readonly T[], itemOrItems: T | T[]) {
5 | if (Array.isArray(itemOrItems)) {
6 | return itemOrItems.every((item) => array.indexOf(item) !== -1);
7 | }
8 |
9 | return array.indexOf(itemOrItems) !== -1;
10 | }
11 |
12 | export const onSpaceOrEnter = (
13 | innerFn: () => void,
14 | onFocus?: (event: React.KeyboardEvent) => void
15 | ) => (event: React.KeyboardEvent) => {
16 | if (event.key === 'Enter' || event.key === ' ') {
17 | innerFn();
18 |
19 | // prevent any side effects
20 | event.preventDefault();
21 | event.stopPropagation();
22 | }
23 |
24 | if (onFocus) {
25 | onFocus(event);
26 | }
27 | };
28 |
29 | /* Quick untyped helper to improve function composition readability */
30 | export const pipe = (...fns: ((...args: any[]) => any)[]) =>
31 | fns.reduceRight(
32 | (prevFn, nextFn) => (...args) => nextFn(prevFn(...args)),
33 | (value) => value
34 | );
35 |
36 | export const executeInTheNextEventLoopTick = (fn: () => void) => {
37 | setTimeout(fn, 0);
38 | };
39 |
40 | export function createDelegatedEventHandler(
41 | fn: (event: TEvent) => void,
42 | onEvent?: (event: TEvent) => void
43 | ) {
44 | return (event: TEvent) => {
45 | fn(event);
46 |
47 | if (onEvent) {
48 | onEvent(event);
49 | }
50 | };
51 | }
52 |
53 | export function mergeRefs(refs: (React.Ref | undefined)[]) {
54 | return (value: T) => {
55 | refs.forEach((ref) => {
56 | if (typeof ref === 'function') {
57 | ref(value);
58 | } else if (typeof ref === 'object' && ref != null) {
59 | // @ts-ignore .current is not a readonly, hold on ts
60 | ref.current = value;
61 | }
62 | });
63 | };
64 | }
65 |
--------------------------------------------------------------------------------
/lib/src/_shared/KeyboardDateInput.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import IconButton from '@material-ui/core/IconButton';
4 | import InputAdornment from '@material-ui/core/InputAdornment';
5 | import { useForkRef } from '@material-ui/core/utils';
6 | import { useUtils } from './hooks/useUtils';
7 | import { CalendarIcon } from './icons/CalendarIcon';
8 | import { useMaskedInput } from './hooks/useMaskedInput';
9 | import { DateInputProps, DateInputRefs } from './PureDateInput';
10 | import { getTextFieldAriaText } from '../_helpers/text-field-helper';
11 |
12 | export const KeyboardDateInput: React.FC = ({
13 | containerRef,
14 | inputRef = null,
15 | forwardedRef = null,
16 | disableOpenPicker: hideOpenPickerButton,
17 | getOpenDialogAriaText = getTextFieldAriaText,
18 | InputAdornmentProps,
19 | InputProps,
20 | openPicker: onOpen,
21 | OpenPickerButtonProps,
22 | openPickerIcon = ,
23 | renderInput,
24 | ...other
25 | }) => {
26 | const utils = useUtils();
27 | const inputRefHandle = useForkRef(inputRef, forwardedRef);
28 | const textFieldProps = useMaskedInput(other);
29 | const adornmentPosition = InputAdornmentProps?.position || 'end';
30 |
31 | return renderInput({
32 | ref: containerRef,
33 | inputRef: inputRefHandle,
34 | ...textFieldProps,
35 | InputProps: {
36 | ...InputProps,
37 | [`${adornmentPosition}Adornment`]: hideOpenPickerButton ? undefined : (
38 |
39 |
47 | {openPickerIcon}
48 |
49 |
50 | ),
51 | },
52 | });
53 | };
54 |
55 | KeyboardDateInput.propTypes = {
56 | acceptRegex: PropTypes.instanceOf(RegExp),
57 | getOpenDialogAriaText: PropTypes.func,
58 | mask: PropTypes.string,
59 | OpenPickerButtonProps: PropTypes.object,
60 | openPickerIcon: PropTypes.node,
61 | renderInput: PropTypes.func.isRequired,
62 | rifmFormatter: PropTypes.func,
63 | };
64 |
--------------------------------------------------------------------------------
/lib/src/_shared/ToolbarButton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import clsx from 'clsx';
3 | import Button, { ButtonProps } from '@material-ui/core/Button';
4 | import { makeStyles } from '@material-ui/core/styles';
5 | import { TypographyProps } from '@material-ui/core/Typography';
6 | import ToolbarText from './ToolbarText';
7 | import { ExtendMui } from '../typings/helpers';
8 |
9 | export interface ToolbarButtonProps extends ExtendMui {
10 | align?: TypographyProps['align'];
11 | selected: boolean;
12 | typographyClassName?: string;
13 | value: React.ReactNode;
14 | variant: TypographyProps['variant'];
15 | }
16 |
17 | export const useStyles = makeStyles(
18 | {
19 | root: {
20 | padding: 0,
21 | minWidth: '16px',
22 | textTransform: 'none',
23 | },
24 | },
25 | { name: 'MuiPickersToolbarButton' }
26 | );
27 |
28 | export const ToolbarButton: React.FunctionComponent = (props) => {
29 | const { align, className, selected, typographyClassName, value, variant, ...other } = props;
30 | const classes = useStyles();
31 |
32 | return (
33 |
47 | );
48 | };
49 |
50 | ToolbarButton.displayName = 'ToolbarButton';
51 |
52 | export default ToolbarButton;
53 |
--------------------------------------------------------------------------------
/lib/src/_shared/ToolbarText.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import clsx from 'clsx';
3 | import Typography, { TypographyProps } from '@material-ui/core/Typography';
4 | import { makeStyles, fade } from '@material-ui/core/styles';
5 | import { ExtendMui } from '../typings/helpers';
6 |
7 | export interface ToolbarTextProps extends ExtendMui {
8 | selected?: boolean;
9 | value: React.ReactNode;
10 | }
11 |
12 | export const useStyles = makeStyles(
13 | (theme) => {
14 | const textColor =
15 | theme.palette.type === 'light'
16 | ? theme.palette.primary.contrastText
17 | : theme.palette.getContrastText(theme.palette.background.default);
18 |
19 | return {
20 | root: {
21 | transition: theme.transitions.create('color'),
22 | color: fade(textColor, 0.54),
23 | '&$selected': {
24 | color: textColor,
25 | },
26 | },
27 | selected: {},
28 | };
29 | },
30 | { name: 'MuiPickersToolbarText' }
31 | );
32 |
33 | const ToolbarText: React.FC = (props) => {
34 | const { className, selected, value, ...other } = props;
35 | const classes = useStyles();
36 | return (
37 |
43 | {value}
44 |
45 | );
46 | };
47 |
48 | export default ToolbarText;
49 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/date-helpers-hooks.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useUtils } from './useUtils';
3 | import { ParsableDate } from '../../constants/prop-types';
4 |
5 | export type OverrideParsableDateProps = Omit<
6 | TProps,
7 | TKey
8 | > &
9 | Partial>>;
10 |
11 | export function useParsedDate(
12 | possiblyUnparsedValue: ParsableDate
13 | ): TDate | undefined {
14 | const utils = useUtils();
15 | return React.useMemo(
16 | () =>
17 | typeof possiblyUnparsedValue === 'undefined' ? undefined : utils.date(possiblyUnparsedValue)!,
18 | [possiblyUnparsedValue, utils]
19 | );
20 | }
21 |
22 | interface MonthValidationOptions {
23 | disablePast?: boolean;
24 | disableFuture?: boolean;
25 | minDate: unknown;
26 | maxDate: unknown;
27 | }
28 |
29 | export function useNextMonthDisabled(
30 | month: unknown,
31 | { disableFuture, maxDate }: Pick
32 | ) {
33 | const utils = useUtils();
34 | return React.useMemo(() => {
35 | const now = utils.date();
36 | const lastEnabledMonth = utils.startOfMonth(
37 | disableFuture && utils.isBefore(now, maxDate) ? now : maxDate
38 | );
39 | return !utils.isAfter(lastEnabledMonth, month);
40 | }, [disableFuture, maxDate, month, utils]);
41 | }
42 |
43 | export function usePreviousMonthDisabled(
44 | month: unknown,
45 | { disablePast, minDate }: Pick
46 | ) {
47 | const utils = useUtils();
48 |
49 | return React.useMemo(() => {
50 | const now = utils.date();
51 | const firstEnabledMonth = utils.startOfMonth(
52 | disablePast && utils.isAfter(now, minDate) ? now : minDate
53 | );
54 | return !utils.isBefore(firstEnabledMonth, month);
55 | }, [disablePast, minDate, month, utils]);
56 | }
57 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useCanAutoFocus.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const CanAutoFocusContext = React.createContext(true);
4 |
5 | export const useCanAutoFocus = () => React.useContext(CanAutoFocusContext);
6 |
7 | export function useAutoFocusControl(open: boolean) {
8 | const [canAutoFocus, setCanAutoFocus] = React.useState(false);
9 |
10 | React.useEffect(() => {
11 | if (!open) {
12 | setCanAutoFocus(false);
13 | }
14 | }, [open]);
15 |
16 | return {
17 | canAutoFocus,
18 | onOpen: React.useCallback(() => setCanAutoFocus(true), []),
19 | };
20 | }
21 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useIsLandscape.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useIsomorphicEffect } from './useKeyDown';
3 | import { arrayIncludes } from '../../_helpers/utils';
4 | import { DateTimePickerView } from '../../DateTimePicker';
5 | import { BasePickerProps } from '../../typings/BasePicker';
6 |
7 | const getOrientation = () => {
8 | if (typeof window === 'undefined') {
9 | return 'portrait';
10 | }
11 |
12 | if (window.screen && window.screen.orientation && window.screen.orientation.angle) {
13 | return Math.abs(window.screen.orientation.angle) === 90 ? 'landscape' : 'portrait';
14 | }
15 |
16 | // Support IOS safari
17 | if (window.orientation) {
18 | return Math.abs(Number(window.orientation)) === 90 ? 'landscape' : 'portrait';
19 | }
20 |
21 | return 'portrait';
22 | };
23 |
24 | export function useIsLandscape(
25 | views: DateTimePickerView[],
26 | customOrientation?: BasePickerProps['orientation']
27 | ): boolean {
28 | const [orientation, setOrientation] = React.useState(
29 | getOrientation()
30 | );
31 |
32 | useIsomorphicEffect(() => {
33 | const eventHandler = () => {
34 | setOrientation(getOrientation());
35 | };
36 | window.addEventListener('orientationchange', eventHandler);
37 | return () => {
38 | window.removeEventListener('orientationchange', eventHandler);
39 | };
40 | }, []);
41 |
42 | if (arrayIncludes(views, ['hours', 'minutes', 'seconds'])) {
43 | // could not display 13:34:44 in landscape mode
44 | return false;
45 | }
46 |
47 | const orientationToUse = customOrientation || orientation;
48 | return orientationToUse === 'landscape';
49 | }
50 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useKeyDown.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const useIsomorphicEffect =
4 | typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
5 |
6 | type KeyHandlers = Record void>;
7 |
8 | export function runKeyHandler(
9 | event: KeyboardEvent | React.KeyboardEvent,
10 | keyHandlers: KeyHandlers
11 | ) {
12 | const handler = keyHandlers[event.keyCode];
13 | if (handler) {
14 | handler();
15 | // if event was handled prevent other side effects (e.g. page scroll)
16 | event.preventDefault();
17 | }
18 | }
19 |
20 | export function useKeyDownHandler(active: boolean, keyHandlers: KeyHandlers) {
21 | const keyHandlersRef = React.useRef(keyHandlers);
22 | keyHandlersRef.current = keyHandlers;
23 |
24 | return React.useCallback(
25 | (event: React.KeyboardEvent) => {
26 | if (active) {
27 | runKeyHandler(event, keyHandlersRef.current);
28 | }
29 | },
30 | [active]
31 | );
32 | }
33 |
34 | export function useGlobalKeyDown(active: boolean, keyHandlers: KeyHandlers) {
35 | const keyHandlersRef = React.useRef(keyHandlers);
36 | keyHandlersRef.current = keyHandlers;
37 |
38 | useIsomorphicEffect(() => {
39 | if (active) {
40 | const handleKeyDown = (event: KeyboardEvent) => {
41 | runKeyHandler(event, keyHandlersRef.current);
42 | };
43 | window.addEventListener('keydown', handleKeyDown);
44 | return () => {
45 | window.removeEventListener('keydown', handleKeyDown);
46 | };
47 | }
48 |
49 | return undefined;
50 | }, [active]);
51 | }
52 |
53 | export const keycode = {
54 | ArrowUp: 38,
55 | ArrowDown: 40,
56 | ArrowLeft: 37,
57 | ArrowRight: 39,
58 | Enter: 13,
59 | Home: 36,
60 | End: 35,
61 | PageUp: 33,
62 | PageDown: 34,
63 | Esc: 27,
64 | };
65 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useOpenState.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { BasePickerProps } from '../../typings/BasePicker';
3 |
4 | export function useOpenState({ open, onOpen, onClose }: BasePickerProps) {
5 | const isControllingOpenProp = React.useRef(typeof open === 'boolean').current;
6 | const [openState, setIsOpenState] = React.useState(false);
7 |
8 | // It is required to update inner state in useEffect in order to avoid situation when
9 | // Our component is not mounted yet, but `open` state is set to `true` (e.g. initially opened)
10 | React.useEffect(() => {
11 | if (isControllingOpenProp) {
12 | if (typeof open !== 'boolean') {
13 | throw new Error('You must not mix controlling and uncontrolled mode for `open` prop');
14 | }
15 |
16 | setIsOpenState(open);
17 | }
18 | }, [isControllingOpenProp, open]);
19 |
20 | const setIsOpen = React.useCallback(
21 | (newIsOpen: boolean) => {
22 | if (!isControllingOpenProp) {
23 | setIsOpenState(newIsOpen);
24 | }
25 |
26 | return newIsOpen ? onOpen && onOpen() : onClose && onClose();
27 | },
28 | [isControllingOpenProp, onOpen, onClose]
29 | );
30 |
31 | return { isOpen: openState, setIsOpen };
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useTraceUpdate.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | // ! Important not to use this hook in production build
3 |
4 | export function useDebuggingTraceUpdate(props: any) {
5 | const prev = React.useRef(props);
6 | React.useEffect(() => {
7 | // @ts-ignore
8 | const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
9 | if (prev.current[k] !== v) {
10 | ps[k] = [prev.current[k], v];
11 | }
12 | return ps;
13 | }, {});
14 | if (Object.keys(changedProps).length > 0) {
15 | // eslint-disable-next-line no-console
16 | console.log('Changed props:', changedProps);
17 | }
18 | prev.current = props;
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useUtils.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { IUtils } from '@date-io/core/IUtils';
3 | import { MuiPickersAdapterContext } from '../../LocalizationProvider';
4 |
5 | export type MuiPickersAdapter = IUtils;
6 |
7 | // TODO uncomment when syntax will be allowed by next babel
8 | function checkUtils(utils: MuiPickersAdapter | null) /* :asserts utils is MuiPickersAdapter */ {
9 | if (!utils) {
10 | throw new Error(
11 | 'Can not find utils in context. It looks like you forgot to wrap your component in LocalizationProvider, or pass dateAdapter prop directly.'
12 | );
13 | }
14 | }
15 |
16 | export function useUtils() {
17 | const utils = React.useContext(MuiPickersAdapterContext);
18 | checkUtils(utils);
19 |
20 | return utils as MuiPickersAdapter;
21 | }
22 |
23 | export function useNow() {
24 | const utils = useUtils();
25 | const now = React.useRef(utils.date());
26 |
27 | return now.current!;
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useValidation.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { useUtils, MuiPickersAdapter } from './useUtils';
3 |
4 | export interface ValidationProps {
5 | /**
6 | * Callback that fired when input value or new `value` prop validation returns **new** validation error (or value is valid after error).
7 | * In case of validation error detected `reason` prop return non-null value and `TextField` must be displayed in `error` state.
8 | * This can be used to render appropriate form error.
9 | *
10 | * [Read the guide](https://next.material-ui-pickers.dev/guides/forms) about form integration and error displaying.
11 | * @DateIOType
12 | */
13 | onError?: (reason: TError, value: TDateValue) => void;
14 | }
15 |
16 | export interface ValidationHookOptions {
17 | defaultValidationError?: TError;
18 | isSameError?: (a: TError, b: TError) => boolean;
19 | }
20 |
21 | const defaultIsSameError = (a: unknown, b: unknown) => a === b;
22 |
23 | export function makeValidationHook<
24 | TError,
25 | TDateValue,
26 | TProps extends ValidationProps
27 | >(
28 | validateFn: (utils: MuiPickersAdapter, value: TDateValue, props: TProps) => TError,
29 | { defaultValidationError, isSameError = defaultIsSameError }: ValidationHookOptions = {}
30 | ) {
31 | return (value: TDateValue, props: TProps) => {
32 | const utils = useUtils();
33 | const previousValidationErrorRef = React.useRef(
34 | defaultValidationError || null
35 | ) as React.MutableRefObject;
36 |
37 | const validationError = validateFn(utils, value, props);
38 |
39 | React.useEffect(() => {
40 | if (props.onError && !isSameError(validationError, previousValidationErrorRef.current)) {
41 | props.onError(validationError, value);
42 | }
43 |
44 | previousValidationErrorRef.current = validationError;
45 | }, [previousValidationErrorRef, props, validationError, value]);
46 |
47 | return validationError;
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/lib/src/_shared/hooks/useViews.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { arrayIncludes } from '../../_helpers/utils';
3 | import { PickerSelectionState } from './usePickerState';
4 | import { AnyPickerView } from '../../Picker/SharedPickerProps';
5 |
6 | export type PickerOnChangeFn = (
7 | date: TDate | null,
8 | selectionState?: PickerSelectionState
9 | ) => void;
10 |
11 | export function useViews({
12 | views,
13 | openTo,
14 | onChange,
15 | isMobileKeyboardViewOpen,
16 | toggleMobileKeyboardView,
17 | }: {
18 | views: AnyPickerView[];
19 | openTo: AnyPickerView;
20 | onChange: PickerOnChangeFn;
21 | isMobileKeyboardViewOpen: boolean;
22 | toggleMobileKeyboardView: () => void;
23 | }) {
24 | const [openView, setOpenView] = React.useState(
25 | openTo && arrayIncludes(views, openTo) ? openTo : views[0]
26 | );
27 |
28 | const setOpenViewEnhanced = React.useCallback(
29 | (...args: Parameters) => {
30 | if (isMobileKeyboardViewOpen) {
31 | toggleMobileKeyboardView();
32 | }
33 |
34 | setOpenView(...args);
35 | },
36 | [isMobileKeyboardViewOpen, toggleMobileKeyboardView]
37 | );
38 |
39 | const previousView = views[views.indexOf(openView!) - 1];
40 | const nextView = views[views.indexOf(openView!) + 1];
41 |
42 | const openNext = React.useCallback(() => {
43 | if (nextView) {
44 | setOpenViewEnhanced(nextView);
45 | }
46 | }, [nextView, setOpenViewEnhanced]);
47 |
48 | const handleChangeAndOpenNext = React.useCallback(
49 | (date: unknown, currentViewSelectionState?: PickerSelectionState) => {
50 | const isSelectionFinishedOnCurrentView = currentViewSelectionState === 'finish';
51 | const globalSelectionState =
52 | isSelectionFinishedOnCurrentView && Boolean(nextView)
53 | ? 'partial'
54 | : currentViewSelectionState;
55 |
56 | onChange(date, globalSelectionState);
57 | if (isSelectionFinishedOnCurrentView) {
58 | openNext();
59 | }
60 | },
61 | [nextView, onChange, openNext]
62 | );
63 |
64 | return {
65 | nextView,
66 | previousView,
67 | openNext,
68 | handleChangeAndOpenNext,
69 | openView,
70 | setOpenView: setOpenViewEnhanced,
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/ArrowDropDown.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const ArrowDropDownIcon = createSvgIcon(, 'ArrowDropDown');
8 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/ArrowLeft.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const ArrowLeftIcon = createSvgIcon(
8 | ,
9 | 'ArrowLeft'
10 | );
11 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/ArrowRight.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const ArrowRightIcon = createSvgIcon(
8 | ,
9 | 'ArrowRight'
10 | );
11 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/CalendarIcon.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const CalendarIcon = createSvgIcon(
8 | ,
9 | 'Calendar'
10 | );
11 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/Clock.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const ClockIcon = createSvgIcon(
8 |
9 |
10 |
11 | ,
12 | 'Clock'
13 | );
14 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/DateRange.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const DateRangeIcon = createSvgIcon(
8 | ,
9 | 'DateRange'
10 | );
11 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/Pen.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const PenIcon = createSvgIcon(
8 | ,
9 | 'Pen'
10 | );
11 |
--------------------------------------------------------------------------------
/lib/src/_shared/icons/Time.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { createSvgIcon } from '@material-ui/core/utils';
3 |
4 | /**
5 | * @ignore - internal component.
6 | */
7 | export const TimeIcon = createSvgIcon(
8 |
9 |
10 |
11 | ,
12 | 'Time'
13 | );
14 |
--------------------------------------------------------------------------------
/lib/src/_shared/withDateAdapterProp.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { MuiPickersAdapter } from './hooks/useUtils';
3 | import { MuiPickersAdapterContext } from '../LocalizationProvider';
4 |
5 | export interface WithDateAdapterProps {
6 | /**
7 | * Allows to pass configured date-io adapter directly. More info [here](https://next.material-ui-pickers.dev/guides/date-adapter-passing)
8 | * ```jsx
9 | * dateAdapter={new DateFnsAdapter({ locale: ruLocale })}
10 | * ```
11 | */
12 | dateAdapter?: MuiPickersAdapter;
13 | }
14 |
15 | export function withDateAdapterProp(
16 | Component: React.ComponentType
17 | ): React.FC> {
18 | return ({ dateAdapter, ...other }: TProps & WithDateAdapterProps) => {
19 | if (dateAdapter) {
20 | return (
21 |
22 |
23 |
24 | );
25 | }
26 | return ;
27 | };
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/_shared/withDefaultProps.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import getThemeProps from '@material-ui/styles/getThemeProps';
3 | import { useTheme } from '@material-ui/core/styles';
4 |
5 | export function useDefaultProps(props: T, { name }: { name: string }) {
6 | const theme = useTheme();
7 |
8 | return getThemeProps({
9 | props,
10 | theme,
11 | name,
12 | });
13 | }
14 |
15 | export function withDefaultProps(
16 | componentConfig: { name: string },
17 | Component: React.ComponentType
18 | ): React.FC {
19 | const componentName = componentConfig.name.replace('Mui', '');
20 |
21 | const WithDefaultProps = (props: T) => {
22 | Component.displayName = componentName;
23 | const propsWithDefault = useDefaultProps(props, componentConfig);
24 |
25 | return ;
26 | };
27 |
28 | WithDefaultProps.displayName = `WithDefaultProps(${componentName})`;
29 | return WithDefaultProps;
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/constants/ClockType.ts:
--------------------------------------------------------------------------------
1 | export type ClockViewType = 'hours' | 'minutes' | 'seconds';
2 |
--------------------------------------------------------------------------------
/lib/src/constants/dimensions.ts:
--------------------------------------------------------------------------------
1 | export const DIALOG_WIDTH = 320;
2 |
3 | export const DIALOG_WIDTH_WIDER = 325;
4 |
5 | export const VIEW_HEIGHT = 358;
6 |
7 | export const DAY_SIZE = 36;
8 |
9 | export const DAY_MARGIN = 2;
10 |
11 | export const IS_TOUCH_DEVICE_MEDIA = '@media (pointer: fine)';
12 |
--------------------------------------------------------------------------------
/lib/src/constants/prop-types.ts:
--------------------------------------------------------------------------------
1 | import * as PropTypes from 'prop-types';
2 |
3 | export const date = PropTypes.oneOfType([
4 | PropTypes.object,
5 | PropTypes.string,
6 | PropTypes.number,
7 | PropTypes.instanceOf(Date),
8 | ]);
9 |
10 | const datePickerView = PropTypes.oneOf(['year', 'month', 'day']);
11 |
12 | export type ParsableDate = string | number | Date | null | undefined | TDate;
13 |
14 | export const DomainPropTypes = { date, datePickerView };
15 |
16 | export const defaultMinDate = new Date('1900-01-01') as unknown;
17 |
18 | export const defaultMaxDate = new Date('2099-12-31') as unknown;
19 |
--------------------------------------------------------------------------------
/lib/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './DatePicker';
2 |
3 | export { DatePickerToolbar } from './DatePicker/DatePickerToolbar';
4 |
5 | export * from './TimePicker';
6 |
7 | export { TimePickerToolbar } from './TimePicker/TimePickerToolbar';
8 |
9 | export * from './DateTimePicker';
10 |
11 | export { DateTimePickerToolbar } from './DateTimePicker/DateTimePickerToolbar';
12 |
13 | export * from './DateRangePicker/DateRangePicker';
14 |
15 | export { DateRangePickerToolbar } from './DateRangePicker/DateRangePickerToolbar';
16 |
17 | export { Calendar as PickersCalendar } from './views/Calendar/Calendar';
18 |
19 | export { CalendarView as PickersCalendarView } from './views/Calendar/CalendarView';
20 |
21 | export { Day as PickersDay } from './views/Calendar/Day';
22 |
23 | export { ClockView as PickersClockView } from './views/Clock/ClockView';
24 |
25 | export { Clock as PickersClock } from './views/Clock/Clock';
26 |
27 | export { default as PickersBasePickers } from './Picker/Picker';
28 |
29 | export { useUtils } from './_shared/hooks/useUtils';
30 |
31 | export { usePickerState } from './_shared/hooks/usePickerState';
32 |
33 | export * from './typings/BasePicker';
34 |
35 | export {
36 | default as LocalizationProvider,
37 | MuiPickersAdapterContext as MuiPickersContext,
38 | } from './LocalizationProvider';
39 |
40 | // TODO replace the following syntax with new ts export type { } syntax when will be supported by rollup
41 |
42 | export type PickersCalendarProps = import('./views/Calendar/Calendar').CalendarProps;
43 | export type PickersCalendarViewProps<
44 | TDate
45 | > = import('./views/Calendar/CalendarView').CalendarViewProps;
46 | export type PickersDayProps = import('./views/Calendar/Day').DayProps;
47 | export type PickersClockViewProps = import('./views/Clock/ClockView').ClockViewProps;
48 | export type PickersClockProps = import('./views/Clock/Clock').ClockProps;
49 | export type ToolbarComponentProps = import('./Picker/SharedPickerProps').ToolbarComponentProps;
50 | export type DateRangeDelimiterProps = import('./DateRangePicker/DateRangeDelimiter').DateRangeDelimiterProps;
51 | export type LocalizationProviderProps = import('./LocalizationProvider').LocalizationProviderProps;
52 | export type DateRange = import('./DateRangePicker/RangeTypes').DateRange;
53 | export type RangeInput = import('./DateRangePicker/RangeTypes').RangeInput;
54 |
--------------------------------------------------------------------------------
/lib/src/typings/BasePicker.tsx:
--------------------------------------------------------------------------------
1 | import { ParsableDate } from '../constants/prop-types';
2 | import { ToolbarComponentProps } from '../Picker/SharedPickerProps';
3 |
4 | export interface BasePickerProps {
5 | /**
6 | * Picker value.
7 | */
8 | value: TInputValue;
9 | /**
10 | * onChange callback @DateIOType.
11 | */
12 | onChange: (date: TDateValue, keyboardInputValue?: string) => void;
13 | /**
14 | * If `true` picker will immediately close after submitting full date.
15 | *
16 | * @default `true` for Desktop, `false` for Mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop).
17 | */
18 | disableCloseOnSelect?: boolean;
19 | /**
20 | * Format string.
21 | */
22 | inputFormat?: string;
23 | /**
24 | * Disable picker and text field.
25 | */
26 | disabled?: boolean;
27 | /**
28 | * Make picker read only.
29 | */
30 | readOnly?: boolean;
31 | /**
32 | * Callback fired when date is accepted @DateIOType.
33 | */
34 | onAccept?: (date: TDateValue | null) => void;
35 | /**
36 | * On open callback.
37 | */
38 | onOpen?: () => void;
39 | /**
40 | * On close callback.
41 | */
42 | onClose?: () => void;
43 | /**
44 | * Controlled picker open state.
45 | */
46 | open?: boolean;
47 | /**
48 | * Show toolbar even in desktop mode.
49 | */
50 | showToolbar?: boolean;
51 | /**
52 | * Force rendering in particular orientation.
53 | */
54 | orientation?: 'portrait' | 'landscape';
55 | /**
56 | * Component that will replace default toolbar renderer.
57 | */
58 | ToolbarComponent?: React.ComponentType;
59 | /**
60 | * Mobile picker title, displaying in the toolbar.
61 | *
62 | * @default "SELECT DATE"
63 | */
64 | toolbarTitle?: React.ReactNode;
65 | /**
66 | * Mobile picker date value placeholder, displaying if `value` === `null`.
67 | *
68 | * @default "–"
69 | */
70 | toolbarPlaceholder?: React.ReactNode;
71 | /**
72 | * Date format, that is displaying in toolbar.
73 | */
74 | toolbarFormat?: string;
75 | /**
76 | * className applied to the root component.
77 | */
78 | className?: string;
79 | }
80 |
--------------------------------------------------------------------------------
/lib/src/typings/helpers.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * All standard components exposed by `material-ui` are `StyledComponents` with
3 | * certain `classes`, on which one can also set a top-level `className` and inline
4 | * `style`.
5 | */
6 | export type ExtendMui = Omit<
7 | C,
8 | 'classes' | 'theme' | Removals
9 | >;
10 |
11 | export type MakeOptional = {
12 | [P in K]?: T[P] | undefined;
13 | } &
14 | Omit;
15 |
16 | export type MakeRequired = {
17 | [X in Exclude]?: T[X];
18 | } &
19 | {
20 | [P in K]-?: T[P];
21 | };
22 |
--------------------------------------------------------------------------------
/lib/src/typings/index.ts:
--------------------------------------------------------------------------------
1 | export * from './overrides';
2 | export * from './props';
3 |
--------------------------------------------------------------------------------
/lib/src/views/Calendar/FadeTransitionGroup.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import clsx from 'clsx';
3 | import { makeStyles } from '@material-ui/core/styles';
4 | import { CSSTransition, TransitionGroup } from 'react-transition-group';
5 |
6 | interface FadeTransitionProps {
7 | transKey: React.Key;
8 | className?: string;
9 | reduceAnimations: boolean;
10 | children: React.ReactElement;
11 | }
12 |
13 | const animationDuration = 500;
14 | export const useStyles = makeStyles(
15 | (theme) => {
16 | return {
17 | root: {
18 | display: 'block',
19 | position: 'relative',
20 | },
21 | fadeEnter: {
22 | willChange: 'transform',
23 | opacity: 0,
24 | },
25 | fadeEnterActive: {
26 | opacity: 1,
27 | transition: theme.transitions.create('opacity', {
28 | duration: animationDuration,
29 | }),
30 | },
31 | fadeExit: {
32 | opacity: 1,
33 | },
34 | fadeExitActive: {
35 | opacity: 0,
36 | transition: theme.transitions.create('opacity', {
37 | duration: animationDuration / 2,
38 | }),
39 | },
40 | };
41 | },
42 | { name: 'MuiPickersFadeTransition' }
43 | );
44 |
45 | export const FadeTransitionGroup: React.FC = ({
46 | children,
47 | className,
48 | reduceAnimations,
49 | transKey,
50 | }) => {
51 | const classes = useStyles();
52 | if (reduceAnimations) {
53 | return children;
54 | }
55 |
56 | const transitionClasses = {
57 | exit: classes.fadeExit,
58 | enterActive: classes.fadeEnterActive,
59 | enter: classes.fadeEnter,
60 | exitActive: classes.fadeExitActive,
61 | };
62 |
63 | return (
64 |
67 | React.cloneElement(element, {
68 | classNames: transitionClasses,
69 | })
70 | }
71 | >
72 |
79 | {children}
80 |
81 |
82 | );
83 | };
84 |
--------------------------------------------------------------------------------
/lib/src/views/Calendar/Month.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import clsx from 'clsx';
3 | import Typography from '@material-ui/core/Typography';
4 | import { makeStyles } from '@material-ui/core/styles';
5 | import { onSpaceOrEnter } from '../../_helpers/utils';
6 |
7 | export interface MonthProps {
8 | children: React.ReactNode;
9 | disabled?: boolean;
10 | onSelect: (value: any) => void;
11 | selected?: boolean;
12 | value: any;
13 | }
14 |
15 | export const useStyles = makeStyles(
16 | (theme) => ({
17 | root: {
18 | flex: '1 0 33.33%',
19 | display: 'flex',
20 | alignItems: 'center',
21 | justifyContent: 'center',
22 | cursor: 'pointer',
23 | outline: 'none',
24 | height: 64,
25 | transition: theme.transitions.create('font-size', { duration: '100ms' }),
26 | '&:focus': {
27 | color: theme.palette.primary.main,
28 | fontWeight: theme.typography.fontWeightMedium,
29 | },
30 | '&:disabled': {
31 | pointerEvents: 'none',
32 | color: theme.palette.text.secondary,
33 | },
34 | '&$selected': {
35 | color: theme.palette.primary.main,
36 | fontWeight: theme.typography.fontWeightMedium,
37 | },
38 | },
39 | selected: {},
40 | }),
41 | { name: 'MuiPickersMonth' }
42 | );
43 |
44 | export const Month: React.FC = (props) => {
45 | const { disabled, onSelect, selected, value, ...other } = props;
46 | const classes = useStyles();
47 | const handleSelection = () => {
48 | onSelect(value);
49 | };
50 |
51 | return (
52 |
66 | );
67 | };
68 |
69 | Month.displayName = 'Month';
70 |
71 | export default Month;
72 |
--------------------------------------------------------------------------------
/lib/src/views/MobileKeyboardInputView.tsx:
--------------------------------------------------------------------------------
1 | import { styled } from '@material-ui/core/styles';
2 |
3 | export const MobileKeyboardInputView = styled('div')(
4 | {
5 | padding: '16px 24px',
6 | },
7 | { name: 'MuiPickersMobileKeyboardInputView' }
8 | );
9 |
--------------------------------------------------------------------------------
/lib/src/wrappers/DesktopWrapper.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as PropTypes from 'prop-types';
3 | import { WrapperProps } from './Wrapper';
4 | import { StaticWrapperProps } from './StaticWrapper';
5 | import { InnerMobileWrapperProps } from './MobileWrapper';
6 | import { WrapperVariantContext } from './WrapperVariantContext';
7 | import { KeyboardDateInput } from '../_shared/KeyboardDateInput';
8 | import { InnerDesktopTooltipWrapperProps } from './DesktopTooltipWrapper';
9 | import { PickersPopper, ExportedPickerPopperProps } from '../_shared/PickersPopper';
10 | import { CanAutoFocusContext, useAutoFocusControl } from '../_shared/hooks/useCanAutoFocus';
11 |
12 | export interface InnerDesktopWrapperProps extends ExportedPickerPopperProps {}
13 |
14 | export interface DesktopWrapperProps
15 | extends InnerDesktopWrapperProps,
16 | WrapperProps,
17 | Partial {}
18 |
19 | export const DesktopWrapper: React.FC = (props) => {
20 | const {
21 | children,
22 | DateInputProps,
23 | KeyboardDateInputComponent = KeyboardDateInput,
24 | onDismiss,
25 | open,
26 | PopperProps,
27 | TransitionComponent,
28 | } = props;
29 | const inputRef = React.useRef(null);
30 | const { canAutoFocus, onOpen } = useAutoFocusControl(open);
31 |
32 | return (
33 |
34 |
35 |
36 |
45 | {children}
46 |
47 |
48 |
49 | );
50 | };
51 |
52 | DesktopWrapper.propTypes = {
53 | onOpen: PropTypes.func,
54 | onClose: PropTypes.func,
55 | } as any;
56 |
--------------------------------------------------------------------------------
/lib/src/wrappers/ResponsiveWrapper.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import useMediaQuery from '@material-ui/core/useMediaQuery';
3 | import { IS_TOUCH_DEVICE_MEDIA } from '../constants/dimensions';
4 | import { MobileWrapperProps, MobileWrapper } from './MobileWrapper';
5 | import { DesktopWrapperProps, DesktopWrapper } from './DesktopWrapper';
6 | import { DesktopTooltipWrapperProps, DesktopTooltipWrapper } from './DesktopTooltipWrapper';
7 |
8 | export interface ResponsiveWrapperProps
9 | extends DesktopWrapperProps,
10 | DesktopTooltipWrapperProps,
11 | MobileWrapperProps {
12 | /**
13 | * CSS media query when `Mobile` mode will be changed to `Desktop`.
14 | *
15 | * @default "@media (pointer: fine)"
16 | * @example "@media (min-width: 720px)" or theme.breakpoints.up("sm")
17 | */
18 | desktopModeMediaQuery?: string;
19 | }
20 |
21 | export const makeResponsiveWrapper = (
22 | DesktopWrapperComponent: React.FC,
23 | MobileWrapperComponent: React.FC
24 | ) => {
25 | const ResponsiveWrapper: React.FC = ({
26 | cancelText,
27 | clearable,
28 | clearText,
29 | desktopModeMediaQuery = IS_TOUCH_DEVICE_MEDIA,
30 | DialogProps,
31 | displayStaticWrapperAs,
32 | okText,
33 | PopperProps,
34 | showTodayButton,
35 | todayText,
36 | TransitionComponent,
37 | ...other
38 | }) => {
39 | const isDesktop = useMediaQuery(desktopModeMediaQuery);
40 |
41 | return isDesktop ? (
42 |
47 | ) : (
48 |
58 | );
59 | };
60 |
61 | return ResponsiveWrapper;
62 | };
63 |
64 | export const ResponsiveWrapper = makeResponsiveWrapper(DesktopWrapper, MobileWrapper);
65 |
66 | export const ResponsiveTooltipWrapper = makeResponsiveWrapper(DesktopTooltipWrapper, MobileWrapper);
67 |
--------------------------------------------------------------------------------
/lib/src/wrappers/StaticWrapper.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { makeStyles } from '@material-ui/core/styles';
3 | import { DIALOG_WIDTH } from '../constants/dimensions';
4 | import { WrapperVariantContext, IsStaticVariantContext } from './WrapperVariantContext';
5 |
6 | const useStyles = makeStyles(
7 | (theme) => ({
8 | root: {
9 | overflow: 'hidden',
10 | minWidth: DIALOG_WIDTH,
11 | display: 'flex',
12 | flexDirection: 'column',
13 | backgroundColor: theme.palette.background.paper,
14 | },
15 | }),
16 | { name: 'MuiPickersStaticWrapper' }
17 | );
18 |
19 | export interface StaticWrapperProps {
20 | /**
21 | * Force static wrapper inner components to be rendered in mobile or desktop mode
22 | *
23 | * @default "static"
24 | */
25 | displayStaticWrapperAs?: 'desktop' | 'mobile' | 'static';
26 | }
27 |
28 | export const StaticWrapper: React.FC = (props) => {
29 | const { displayStaticWrapperAs = 'static', children } = props;
30 | const classes = useStyles();
31 | const isStatic = true;
32 |
33 | return (
34 |
35 |
36 | {children}
37 |
38 |
39 | );
40 | };
41 |
--------------------------------------------------------------------------------
/lib/src/wrappers/WrapperVariantContext.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { WrapperVariant } from './Wrapper';
3 |
4 | // consider getting rid from wrapper variant
5 | export const WrapperVariantContext = React.createContext(null);
6 |
7 | export const IsStaticVariantContext = React.createContext(false);
8 |
--------------------------------------------------------------------------------
/lib/tsconfig.adapters.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "rootDir": "./adapter",
5 | "outDir": "build/adapter"
6 | },
7 | "include": ["./adapter/**/*.ts*", "./typings.d.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/lib/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "rootDir": "src",
4 | "module": "commonjs",
5 | "outDir": "build",
6 | "declaration": true,
7 | "emitDeclarationOnly": true,
8 | "target": "es5",
9 | "lib": ["dom", "es2016"],
10 | "types": ["jest"],
11 | "skipLibCheck": true,
12 | "jsx": "react",
13 | "moduleResolution": "node",
14 | "noImplicitAny": true,
15 | "strict": true,
16 | "allowSyntheticDefaultImports": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "esModuleInterop": true,
19 | "suppressImplicitAnyIndexErrors": true,
20 | "resolveJsonModule": true
21 | },
22 | "include": ["src/**/*.ts*", "./typings.d.ts"],
23 | "exclude": ["./src/__tests__/**/*", "./rollup.config.js", "build", "cypress/**/*.ts*"]
24 | }
25 |
--------------------------------------------------------------------------------
/lib/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@date-io/type' {
2 | export type DateType = unknown;
3 | }
4 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "build": {
4 | "env": {
5 | "IS_NOW": "true"
6 | }
7 | },
8 | "builds": [
9 | { "src": "docs/next.config.js", "use": "@now/next" },
10 | {
11 | "src": "docs/fakeApi/*",
12 | "use": "@now/node"
13 | }
14 | ],
15 | "routes": [
16 | {
17 | "src": "/fakeApi/(.*)$",
18 | "dest": "/docs/fakeApi/$1.ts"
19 | },
20 | { "src": "/api/(?[^/]+)$", "dest": "docs/api/props?component=$name" },
21 | { "src": "/(.*)", "dest": "docs/$1" }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/scripts/deduplicate.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const { spawn } = require('child_process');
3 | const path = require('path');
4 | const fs = require('fs');
5 | const deduplicate = require('yarn-deduplicate');
6 |
7 | const lockFile = path.resolve(__dirname, '../yarn.lock');
8 | const yarnlock = fs.readFileSync(lockFile, 'utf8');
9 |
10 | const duplicates = deduplicate.listDuplicates(yarnlock);
11 | if (duplicates.length === 0) {
12 | console.log('No duplicated packages found');
13 | process.exit(0);
14 | }
15 |
16 | console.log(
17 | `${duplicates.length} duplicated package(s) found\n${duplicates.map((x) => ` ${x}`).join('\n')}`
18 | );
19 |
20 | if (process.env.CI) {
21 | console.error(
22 | [
23 | `Error: There are currently ${duplicates.length} duplicated package(s).`,
24 | `To deduplicate run "yarn deduplicate"`,
25 | ].join('\n')
26 | );
27 | process.exit(1);
28 | }
29 |
30 | console.log('Deduplicating package(s)');
31 | fs.writeFileSync(lockFile, deduplicate.fixDuplicates(yarnlock));
32 |
33 | const yarn = spawn('yarn', {
34 | shell: true,
35 | stdio: 'inherit',
36 | cwd: path.resolve(__dirname, '..'),
37 | });
38 |
39 | yarn.on('close', (code) => {
40 | process.exit(code);
41 | });
42 |
--------------------------------------------------------------------------------