;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
17 | );
18 | });
19 |
20 | it('Should renders', () => {
21 | // console.log(component.debug());
22 | expect(component).toBeTruthy();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/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 |
16 | // Import commands.js using ES2015 syntax:
17 | require('./commands');
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/docs/src/_shared/UtilsServiceContext.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { UtilsService, createUtilsService } from '../utils/utilsService';
3 | import { Omit } from '@material-ui/core';
4 |
5 | export const { Provider: UtilsServiceContextProvider, Consumer } = React.createContext<
6 | UtilsService
7 | >(createUtilsService('date-fns'));
8 |
9 | export const withUtilsService = (Component: React.ComponentType
) => {
10 | const withUtilsService: React.SFC> = props => (
11 | {service => }
12 | );
13 |
14 | withUtilsService.displayName = `withUtilsService(${Component.displayName || Component.name})`;
15 |
16 | return withUtilsService;
17 | };
18 |
--------------------------------------------------------------------------------
/lib/src/__tests__/MuiPickersUtilsProvider.test.tsx:
--------------------------------------------------------------------------------
1 | import DateFnsUtils from '@date-io/date-fns';
2 | import { shallow, ShallowWrapper } from 'enzyme'; // required to use just shallow here because utils prop override
3 | import * as React from 'react';
4 | import MuiPickersUtilsProvider, { MuiPickersUtilsProviderProps } from '../MuiPickersUtilsProvider';
5 |
6 | describe('MuiPickersUtilsProvider', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
12 |
13 |
14 | );
15 | });
16 |
17 | it('Should render context provider', () => {
18 | expect(component).toBeTruthy();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DatePicker/YearSelection.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { YearSelection, YearSelectionProps } from '../../DatePicker/components/YearSelection';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | describe('YearSelection', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
17 | );
18 | });
19 |
20 | it('Should renders', () => {
21 | // console.log(component.debug());
22 | expect(component).toBeTruthy();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DateTimePicker/DateTimePicker.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { DateTimePicker, DateTimePickerProps } from '../../DateTimePicker/DateTimePicker';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | describe('DateTimePicker', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
17 | );
18 | });
19 |
20 | it('Should renders', () => {
21 | // console.log(component.debug());
22 | expect(component).toBeTruthy();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/lib/src/__tests__/TimePicker/TimePicker.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { TimePicker, TimePickerProps } from '../../TimePicker/TimePicker';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | describe('TimePicker', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
18 | );
19 | });
20 |
21 | it('Should renders', () => {
22 | // console.log(component.debug());
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/lib/src/__tests__/setup.js:
--------------------------------------------------------------------------------
1 | const Enzyme = require('enzyme');
2 | const EnzymeAdapter = require('enzyme-adapter-react-16');
3 |
4 | // Setup enzyme's react adapter
5 | Enzyme.configure({ adapter: new EnzymeAdapter() });
6 |
7 | // Convert any console error into a thrown error
8 | const error = console.error;
9 | console.error = (...args) => {
10 | error.apply(console, args);
11 | if (args[0] instanceof Error) {
12 | throw args[0];
13 | } else {
14 | // combine multi args into a string
15 | const message = args
16 | .map(value => {
17 | if (typeof value === 'object') {
18 | return JSON.stringify(value);
19 | } else {
20 | return value;
21 | }
22 | })
23 | .join(' ');
24 | throw new Error(message);
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DatePicker/DatePickerModal.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { DatePickerModal, DatePickerModalProps } from '../../DatePicker/DatePickerModal';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | const spy = jest.fn();
7 |
8 | const props = {
9 | keyboard: true,
10 | format: 'YYYY',
11 | onChange: spy,
12 | value: utilsToUse.date('2018'),
13 | };
14 |
15 | describe('DatePickerModal', () => {
16 | let component: ShallowWrapper;
17 |
18 | beforeEach(() => {
19 | component = shallow();
20 | });
21 |
22 | it('Should renders', () => {
23 | // console.log(component.debug());
24 | expect(component).toBeTruthy();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/ControllingProgrammatically.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SourcablePanel from '_shared/SourcablePanel';
3 | import { Typography } from '@material-ui/core';
4 |
5 | const ControllingProgrammatically = () => (
6 |
7 |
8 | Control programmatically
9 |
10 |
11 |
12 | Any picker can be controlled by ref
13 | property which add an ability open any picker from the code. See an example below
14 |
15 |
16 |
20 |
21 | );
22 |
23 | export default ControllingProgrammatically;
24 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DateTimePicker/DateTimePickerInline.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { DateTimePickerInlineProps, InlineDateTimePicker } from '../../DateTimePicker';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | const spy = jest.fn();
7 |
8 | const props = {
9 | keyboard: true,
10 | format: 'YYYY',
11 | onChange: spy,
12 | value: utilsToUse.date('2018'),
13 | };
14 |
15 | describe('DatePickerModal', () => {
16 | let component: ShallowWrapper;
17 |
18 | beforeEach(() => {
19 | component = shallow();
20 | });
21 |
22 | it('Should renders', () => {
23 | // console.log(component.debug());
24 | expect(component).toBeTruthy();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: feature request
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 | 
12 |
13 | **Is your feature request related to a problem? Please describe.**
14 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
15 |
16 | **Describe the solution you'd like**
17 | A clear and concise description of what you want to happen.
18 |
19 | **Describe alternatives you've considered**
20 | A clear and concise description of any alternative solutions or features you've considered.
21 |
22 | **Additional context**
23 | Add any other context or screenshots about the feature request here.
24 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DatePicker/CalendarHeader.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { CalendarHeader, CalendarHeaderProps } from '../../DatePicker/components/CalendarHeader';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | describe('CalendarHeader', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallow(
11 |
19 | );
20 | });
21 |
22 | it('Should renders', () => {
23 | // console.log(component.debug());
24 | expect(component).toBeTruthy();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/TimePicker/KeyboardTimePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { TimePicker } from 'material-ui-pickers';
3 |
4 | export default class BasicUsage extends PureComponent {
5 | state = {
6 | selectedDate: new Date(),
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: cypress/base:8
6 | environment:
7 | ## this enables colors in the output
8 | TERM: xterm
9 | working_directory: ~/app
10 |
11 | steps:
12 | - checkout
13 | - restore_cache:
14 | keys:
15 | - v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
16 | - v1-deps-{{ .Branch }}
17 | - v1-deps
18 | - run:
19 | name: Install Dependencies
20 | command: npm ci && npx lerna bootstrap
21 | - save_cache:
22 | key: v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
23 | paths:
24 | - ~/.npm
25 | - ~/.cache
26 | - run: npx lerna run build
27 | - run: npx lerna run serve & npx wait-on http://localhost:3002 && npm run e2e:run -- --record
28 |
--------------------------------------------------------------------------------
/docs/scripts/generate-backers.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = 'production';
2 | require('react-scripts/config/env');
3 |
4 | const fse = require('fs-extra');
5 | const path = require('path');
6 | const patreonApi = require('patreon').patreon;
7 |
8 | const patreonAPIClient = patreonApi(process.env.CREATOR_TOKEN);
9 |
10 | patreonAPIClient('/campaigns/1559688/pledges')
11 | .then(({ rawJson }) =>
12 | rawJson.data
13 | // sort by pledge amount
14 | .sort((a, b) => a.attributes.amount_cents - b.attributes.amount_cents)
15 | .reverse()
16 | .map(({ relationships }) => {
17 | const patronId = relationships.patron.data.id;
18 | const user = rawJson.included.find(entity => entity.id === patronId);
19 |
20 | return user.attributes;
21 | })
22 | )
23 | .then(users =>
24 | fse.writeFile(
25 | path.resolve(__dirname, '..', 'src', 'patrons.json'),
26 | JSON.stringify(users)
27 | )
28 | );
29 |
--------------------------------------------------------------------------------
/docs/src/_shared/svgIcons/GithubIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SvgIcon from '@material-ui/core/SvgIcon';
3 |
4 | const GitHub = props => (
5 |
6 |
7 |
8 | );
9 |
10 | export default GitHub;
11 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DateTimePicker/DateTimePickerTabs.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import {
4 | DateTimePickerTabs,
5 | DateTimePickerTabsProps,
6 | } from '../../DateTimePicker/components/DateTimePickerTabs';
7 | import { DateTimePicker } from '../../DateTimePicker/DateTimePicker';
8 | import { shallow } from '../test-utils';
9 |
10 | describe('DateTimePickerTabs', () => {
11 | let component: ShallowWrapper;
12 |
13 | beforeEach(() => {
14 | component = shallow(
15 |
23 | );
24 | });
25 |
26 | it('Should renders', () => {
27 | // console.log(component.debug());
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/docs/src/Pages/Localization/Date-fns/DateFnsLocalization.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SourcablePanel from '../../../_shared/SourcablePanel';
3 | import { Typography } from '@material-ui/core';
4 |
5 | const DateFnsLocalization = () => (
6 |
7 |
8 | Localization date-fns
9 |
10 |
11 |
12 | Date-fns localization simply performs by passing date-fns locale object to the
13 | MuiPickerUtilsProvider
14 |
15 |
16 |
21 | Note that pickers would be rerender automatically on locale change
22 |
23 | }
24 | />
25 |
26 | );
27 |
28 | export default DateFnsLocalization;
29 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DateTimePicker/DateTimePickerHeader.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import {
4 | DateTimePickerHeader,
5 | DateTimePickerHeaderProps,
6 | } from '../../DateTimePicker/components/DateTimePickerHeader';
7 | import { shallow, utilsToUse } from '../test-utils';
8 |
9 | describe('DateTimePickerHeader', () => {
10 | let component: ShallowWrapper;
11 |
12 | beforeEach(() => {
13 | component = shallow(
14 | jest.fn()}
16 | date={utilsToUse.date('01-01-2017')}
17 | classes={{} as any}
18 | meridiemMode="am"
19 | openView="year"
20 | onOpenViewChange={jest.fn()}
21 | utils={utilsToUse}
22 | />
23 | );
24 | });
25 |
26 | it('Should renders', () => {
27 | // console.log(component.debug());
28 | expect(component).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/docs/scripts/docgen.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const parser = require('react-docgen-typescript').withDefaultConfig({
4 | skipPropsWithoutDoc: true,
5 | });
6 |
7 | const doc = {};
8 | const srcPath = path.resolve(__dirname, '..', '..', 'lib', 'src');
9 | const files = [
10 | 'DatePicker/DatePickerModal.tsx',
11 | 'TimePicker/TimePickerModal.tsx',
12 | 'DateTimePicker/DateTimePickerModal.tsx',
13 | ];
14 |
15 | files.forEach(filePart => {
16 | const file = path.join(srcPath, filePart);
17 | const parsedDoc = parser.parse(file)[0];
18 |
19 | doc[filePart] = Object.entries(parsedDoc.props)
20 | .filter(
21 | ([key, value]) =>
22 | value.description && !value.parent.fileName.includes('@types')
23 | )
24 | .reduce((obj, [key, value]) => {
25 | obj[key] = value;
26 | return obj;
27 | }, {});
28 | });
29 |
30 | fs.writeFileSync(
31 | path.resolve(__dirname, '..', 'src', 'prop-types.json'),
32 | JSON.stringify(doc)
33 | );
34 |
--------------------------------------------------------------------------------
/docs/src/Pages/GettingStarted/QuickStart.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import DateFnsUtils from '@date-io/date-fns';
3 | import { MuiPickersUtilsProvider } from 'material-ui-pickers';
4 | import { TimePicker } from 'material-ui-pickers';
5 | import { DatePicker } from 'material-ui-pickers';
6 | import { DateTimePicker } from 'material-ui-pickers';
7 |
8 | export default class App extends PureComponent {
9 | state = {
10 | selectedDate: new Date(),
11 | };
12 |
13 | handleDateChange = date => {
14 | this.setState({ selectedDate: date });
15 | };
16 |
17 | render() {
18 | const { selectedDate } = this.state;
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/docs/src/index.tsx:
--------------------------------------------------------------------------------
1 | import rtl from 'jss-rtl';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 |
5 | import App from './App';
6 | import { BrowserRouter } from 'react-router-dom';
7 | import { create } from 'jss';
8 | import { createGenerateClassName, jssPreset } from '@material-ui/core/styles';
9 | import JssProvider from 'react-jss/lib/JssProvider';
10 |
11 | import * as serviceWorker from './serviceWorker';
12 | import './index.css';
13 |
14 | // @ts-ignore
15 | window.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__ = true;
16 |
17 | // @ts-ignore Configure JSS
18 | const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
19 |
20 | // Custom Material-UI class name generator.
21 | const generateClassName = createGenerateClassName();
22 |
23 | ReactDOM.render(
24 |
25 |
26 |
27 |
28 | ,
29 | document.getElementById('root')
30 | );
31 |
32 | // do not cache the docs
33 | serviceWorker.unregister();
34 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/TimePicker/InlineTimePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { InlineTimePicker } from 'material-ui-pickers';
3 |
4 | export default class InlineTimePickerDemo extends PureComponent {
5 | state = {
6 | selectedDate: '2018-01-01T00:00:00.000Z',
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/Formik.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SourcablePanel from '_shared/SourcablePanel';
3 | import { Typography } from '@material-ui/core';
4 |
5 | const Formik = () => (
6 |
7 |
8 | Integration to form
9 |
10 |
11 |
12 | Pickers are quite complex controls, where date can be submitted
13 | from different places, so we can't provide event as argument in
14 | onChange callback. Also we are providing date validation out of
15 | the box, so it may be tricky to integrate pickers to the form.
16 | Here are some examples!
17 |
18 |
19 |
24 | Here is example of how to use material-ui-pickers with formik
25 |
26 | }
27 | />
28 |
29 | );
30 |
31 | export default Formik;
32 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Dmitriy Kovalenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DatePicker/Month.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { Month, MonthProps } from '../../DatePicker/components/Month';
4 | import { shallow, utilsToUse } from '../test-utils';
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 |
33 | Oct
34 |
35 | );
36 | });
37 |
38 | it('Should render in disabled state', () => {
39 | expect(component.prop('tabIndex')).toBe(-1);
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/TimePicker/SecondsTimePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { TimePicker } from 'material-ui-pickers';
3 |
4 | export default class BasicUsage extends PureComponent {
5 | state = {
6 | selectedDate: new Date(),
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
19 |
26 |
27 |
28 |
29 |
37 |
38 |
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/src/_shared/MaskedInput.tsx:
--------------------------------------------------------------------------------
1 | import * as PropTypes from 'prop-types';
2 | import * as React from 'react';
3 | import MaskedInput, { MaskedInputProps } from 'react-text-mask';
4 |
5 | export interface CustomMaskedInputProps extends MaskedInputProps {
6 | mask?: MaskedInputProps['mask'];
7 | inputRef: React.Ref;
8 | }
9 |
10 | export default class Input extends React.PureComponent {
11 | public static propTypes: any = {
12 | mask: PropTypes.any,
13 | inputRef: PropTypes.func.isRequired,
14 | };
15 |
16 | public createInputRef = (ref: MaskedInput | null) => {
17 | const { inputRef } = this.props;
18 |
19 | if (inputRef && typeof inputRef === 'function') {
20 | // @ts-ignore inputElement exists in Masked input. Issue in typings
21 | inputRef(ref ? ref.inputElement : null);
22 | }
23 | };
24 |
25 | public render() {
26 | const { inputRef, keepCharPositions, ...rest } = this.props;
27 |
28 | return this.props.mask ? (
29 |
30 | ) : (
31 |
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/docs/src/Pages/Localization/Moment/MomentLocalization.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import SourcablePanel from '_shared/SourcablePanel';
3 | import { Typography } from '@material-ui/core';
4 |
5 | const MomentLocalization = () => (
6 |
7 |
8 | Localization moment
9 |
10 |
11 | Moment localization relying on the global moment object used.
12 |
13 |
14 | It is possible to pass configured global moment with selected locale, default timezone, etc.
15 | Also pass selected locale as string to the provider to make pickers rerenders automatically on
16 | locale change.
17 |
18 |
23 | Note that pickers would be rerender automatically on locale change
24 |
25 | }
26 | />
27 |
28 | );
29 |
30 | export default MomentLocalization;
31 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DatePicker/MonthSelection.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import Month from '../../DatePicker/components/Month';
4 | import { MonthSelection, MonthSelectionProps } from '../../DatePicker/components/MonthSelection';
5 | import { shallow, utilsToUse } from '../test-utils';
6 |
7 | describe('MonthSelection', () => {
8 | let component: ShallowWrapper;
9 |
10 | beforeEach(() => {
11 | component = shallow(
12 |
20 | );
21 | });
22 |
23 | it('Should render disabled months before min date and after max date', () => {
24 | expect(component.find(Month).map(month => month.prop('disabled'))).toEqual([
25 | true,
26 | true,
27 | false,
28 | false,
29 | false,
30 | true,
31 | true,
32 | true,
33 | true,
34 | true,
35 | true,
36 | true,
37 | ]);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
12 |
13 | 
14 |
15 | ## Environment
16 |
17 | | Tech | Version |
18 | |---------------------|---------|
19 | | material-ui-pickers | |
20 | | material-ui | |
21 | | React | |
22 | | Browser | |
23 | | Peer library | |
24 |
25 | ## Steps to reproduce
26 | 1.
27 | 2.
28 | 3.
29 |
30 | ## Expected behavior
31 |
32 |
33 | ## Actual behavior
34 |
35 |
36 | ## Live example
37 |
43 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/StaticPickers.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Typography } from '@material-ui/core';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 |
5 | const StaticPickers = () => (
6 |
7 |
8 | Static pickers
9 |
10 |
11 |
12 | Somewhere its required to use some internal control for calendar or some timeinput. Here you
13 | are! You can use directly any sub-control of the pickers. Please note - if you want to use
14 | internal controls ALL your imports must be from the relative paths
15 |
16 |
17 |
18 | Also you can use our own HOC that is using for any picker which provide managing temporary
19 | chosen date and submitting state logic.
20 |
21 |
22 |
27 | Please make sure that your imports are consistent
28 |
29 | }
30 | />
31 |
32 | );
33 |
34 | export default StaticPickers;
35 |
--------------------------------------------------------------------------------
/docs/src/Landing/components/PatreonSponsors.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import { withStyles, Avatar, List, ListItem, ListItemText } from '@material-ui/core';
5 | import patrons from '../../patrons.json';
6 |
7 | class PatreonSponsors extends Component {
8 | static propTypes = {
9 | classes: PropTypes.object.isRequired,
10 | };
11 |
12 | render() {
13 | const { classes } = this.props;
14 |
15 | if (patrons.length === 0) {
16 | return 'There is no sponsors yet 😢';
17 | }
18 |
19 | return (
20 |
21 | {patrons.map(patron => (
22 |
23 |
24 |
25 |
26 |
27 |
28 | ))}
29 |
30 | );
31 | }
32 | }
33 |
34 | const styles = {
35 | spinner: {
36 | margin: '0 auto',
37 | },
38 | patronList: {
39 | maxWidth: 400,
40 | margin: '0 auto',
41 | },
42 | };
43 |
44 | export default withStyles(styles)(PatreonSponsors);
45 |
--------------------------------------------------------------------------------
/lib/src/_shared/WithUtils.tsx:
--------------------------------------------------------------------------------
1 | import { IUtils } from '@date-io/core/IUtils';
2 | import { Omit } from '@material-ui/core';
3 | import * as React from 'react';
4 | import { MuiPickersContext } from '../MuiPickersUtilsProvider';
5 | import { MaterialUiPickersDate } from '../typings/date';
6 |
7 | export interface WithUtilsProps {
8 | utils: IUtils;
9 | }
10 |
11 | const checkUtils = (utils: IUtils | null | undefined) => {
12 | if (!utils) {
13 | // tslint:disable-next-line
14 | throw new Error(
15 | 'Can not find utils in context. You either a) forgot to wrap your component tree in MuiPickersUtilsProvider; or b) mixed named and direct file imports. Recommendation: use named imports from the module index.'
16 | );
17 | }
18 | };
19 |
20 | export const withUtils = () => (Component: React.ComponentType
) => {
21 | const WithUtils: React.SFC> = props => (
22 |
23 | {utils => {
24 | checkUtils(utils);
25 | return ;
26 | }}
27 |
28 | );
29 |
30 | WithUtils.displayName = `WithUtils(${Component.displayName || Component.name})`;
31 |
32 | return WithUtils;
33 | };
34 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/components/DayWrapper.tsx:
--------------------------------------------------------------------------------
1 | import * as PropTypes from 'prop-types';
2 | import * as React from 'react';
3 |
4 | export interface DayWrapperProps {
5 | children: React.ReactNode;
6 | dayInCurrentMonth?: boolean;
7 | disabled?: boolean;
8 | onSelect: (value: any) => void;
9 | value: any;
10 | }
11 |
12 | class DayWrapper extends React.PureComponent {
13 | public static propTypes: any = {
14 | children: PropTypes.node.isRequired,
15 | dayInCurrentMonth: PropTypes.bool,
16 | disabled: PropTypes.bool,
17 | onSelect: PropTypes.func.isRequired,
18 | value: PropTypes.any.isRequired,
19 | };
20 |
21 | public static defaultProps = {
22 | dayInCurrentMonth: true,
23 | disabled: false,
24 | };
25 |
26 | public handleClick = () => {
27 | this.props.onSelect(this.props.value);
28 | };
29 |
30 | public render() {
31 | const { children, value, dayInCurrentMonth, disabled, onSelect, ...other } = this.props;
32 |
33 | return (
34 |
40 | {children}
41 |
42 | );
43 | }
44 | }
45 |
46 | export default DayWrapper;
47 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/FormatsCustomization.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import format from 'date-fns/format';
3 | import frLocale from 'date-fns/locale/fr';
4 | import { DatePicker } from 'material-ui-pickers';
5 | import DateFnsUtils from '@date-io/date-fns';
6 | import { MuiPickersUtilsProvider } from 'material-ui-pickers';
7 |
8 | class LocalizedUtils extends DateFnsUtils {
9 | getDatePickerHeaderText(date) {
10 | return format(date, 'd MMM yyyy', { locale: this.locale });
11 | }
12 | }
13 |
14 | export default class DateFnsLocalizationExample extends PureComponent {
15 | state = {
16 | selectedDate: new Date(),
17 | };
18 |
19 | handleDateChange = date => {
20 | this.setState({ selectedDate: date });
21 | };
22 |
23 | render() {
24 | const { selectedDate } = this.state;
25 |
26 | return (
27 |
28 |
29 |
38 |
39 |
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/TimePicker/TimePickerBasic.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { TimePicker } from 'material-ui-pickers';
3 |
4 | export default class BasicUsage extends PureComponent {
5 | state = {
6 | selectedDate: new Date(),
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
19 |
25 |
26 |
27 |
28 |
35 |
36 |
37 |
38 |
46 |
47 |
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DateTimePicker/BasicDateTimePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { DateTimePicker } from 'material-ui-pickers';
3 |
4 | export default class BasicDateTimePicker extends PureComponent {
5 | state = {
6 | selectedDate: new Date(),
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
19 |
24 |
25 |
26 |
27 |
35 |
36 |
37 |
38 |
45 |
46 |
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/lib/src/MuiPickersUtilsProvider.tsx:
--------------------------------------------------------------------------------
1 | import { IUtils } from '@date-io/core/IUtils';
2 | import * as PropTypes from 'prop-types';
3 | import * as React from 'react';
4 | import { MaterialUiPickersDate } from './typings/date';
5 |
6 | export const MuiPickersContext = React.createContext | null>(null);
7 | // TODO remove in v3.0
8 | export const MuiPickersContextConsumer = MuiPickersContext.Consumer;
9 |
10 | export interface MuiPickersUtilsProviderProps {
11 | utils: any;
12 | children: React.ReactNode;
13 | locale?: any;
14 | moment?: any;
15 | }
16 |
17 | export default class MuiPickersUtilsProvider extends React.Component {
18 | public static propTypes: any = {
19 | utils: PropTypes.func.isRequired,
20 | locale: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
21 | children: PropTypes.oneOfType([
22 | PropTypes.element.isRequired,
23 | PropTypes.arrayOf(PropTypes.element.isRequired),
24 | ]).isRequired,
25 | moment: PropTypes.func,
26 | };
27 |
28 | public static getDerivedStateFromProps({
29 | utils: Utils,
30 | locale,
31 | moment,
32 | }: MuiPickersUtilsProviderProps) {
33 | return {
34 | utils: new Utils({ locale, moment }),
35 | };
36 | }
37 |
38 | public state = {
39 | utils: null,
40 | };
41 |
42 | public render() {
43 | return ;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/src/__tests__/TimePicker/TimePickerView.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { TimePickerView, TimePickerViewProps } from '../../TimePicker/components/TimePickerView';
4 | import { shallow, utilsToUse } from '../test-utils';
5 |
6 | describe('TimePickerView', () => {
7 | let component: ShallowWrapper;
8 | let onChangeMock: any;
9 |
10 | beforeEach(() => {
11 | onChangeMock = jest.fn();
12 | component = shallow(
13 |
22 | );
23 | });
24 |
25 | it('Should renders', () => {
26 | expect(component).toBeTruthy();
27 | });
28 |
29 | if (process.env.UTILS !== 'moment') {
30 | it('Should dispatch onChange onSecondsChange', () => {
31 | (component.instance() as TimePickerView).handleSecondsChange(45, true);
32 | expect(onChangeMock).toHaveBeenCalledWith(utilsToUse.date('01-01-2017 12:00:45'), true);
33 | });
34 |
35 | it('Should dispatch onChange on', () => {
36 | (component.instance() as TimePickerView).handleMinutesChange(45, true);
37 | expect(onChangeMock).toHaveBeenCalledWith(utilsToUse.date('01-01-2017 12:45'), true);
38 | });
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DateTimePicker/DateTimePickerDemo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypesTable from '_shared/PropTypesTable';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 | import { Typography } from '@material-ui/core';
5 |
6 | const DateTimePickerDemo = () => (
7 |
8 |
9 | Date & time picker
10 |
11 |
12 | This component is not from material design guidelines.
13 |
14 |
15 | Its a combination of date & time picker and allows that uses the modal to select both date and
16 | time with one control.
17 |
18 |
19 |
23 |
24 |
28 |
29 |
34 | Applied mostly all customization, that available for date & time pickers
35 |
36 | }
37 | />
38 |
39 |
40 |
41 | );
42 |
43 | export default DateTimePickerDemo;
44 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/StaticPickers.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import Paper from '@material-ui/core/Paper';
3 |
4 | import DateFnsUtils from '@date-io/date-fns';
5 | import { BasePicker, MuiPickersUtilsProvider, TimePickerView, Calendar } from 'material-ui-pickers';
6 |
7 | class StaticPickers extends PureComponent {
8 | state = {
9 | selectedDate: new Date(),
10 | };
11 |
12 | handleDateChange = date => {
13 | this.setState({ selectedDate: date });
14 | };
15 |
16 | render() {
17 | const { selectedDate } = this.state;
18 |
19 | return (
20 |
21 |
22 | {({
23 | date,
24 | handleAccept,
25 | handleChange,
26 | handleClear,
27 | handleDismiss,
28 | handleSetTodayDate,
29 | handleTextFieldChange,
30 | pick12hOr24hFormat,
31 | }) => (
32 |
41 | )}
42 |
43 |
44 | );
45 | }
46 | }
47 |
48 | export default StaticPickers;
49 |
--------------------------------------------------------------------------------
/lib/src/index.ts:
--------------------------------------------------------------------------------
1 | import { DatePickerInlineProps, DatePickerProps } from './DatePicker';
2 | import { DateTimePickerInlineProps, DateTimePickerProps } from './DateTimePicker';
3 | import { TimePickerInlineProps, TimePickerProps } from './TimePicker';
4 | import { MaterialUiPickersDate } from './typings/date';
5 |
6 | export type TimePickerProps = TimePickerProps;
7 |
8 | export type TimePickerInlineProps = TimePickerInlineProps;
9 |
10 | export type DatePickerProps = DatePickerProps;
11 |
12 | export type DatePickerInlineProps = DatePickerInlineProps;
13 |
14 | export type DateTimePickerProps = DateTimePickerProps;
15 |
16 | export type DateTimePickerInlineProps = DateTimePickerInlineProps;
17 |
18 | export type MaterialUiPickersDate = MaterialUiPickersDate;
19 |
20 | export { default as DatePicker, InlineDatePicker } from './DatePicker';
21 |
22 | export { default as TimePicker, InlineTimePicker } from './TimePicker';
23 |
24 | export { default as DateTimePicker, InlineDateTimePicker } from './DateTimePicker';
25 |
26 | export { default as BasePicker } from './_shared/BasePicker';
27 |
28 | export { default as Calendar } from './DatePicker/components/Calendar';
29 |
30 | export { default as Day } from './DatePicker/components/Day';
31 |
32 | export { default as TimePickerView } from './TimePicker/components/TimePickerView';
33 |
34 | export { default as Clock } from './TimePicker/components/Clock';
35 |
36 | export {
37 | default as MuiPickersUtilsProvider,
38 | MuiPickersContext,
39 | MuiPickersContextConsumer,
40 | } from './MuiPickersUtilsProvider';
41 |
--------------------------------------------------------------------------------
/lib/src/_shared/ToolbarButton.tsx:
--------------------------------------------------------------------------------
1 | import { Theme } from '@material-ui/core';
2 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
3 | import Typography, { TypographyProps } from '@material-ui/core/Typography';
4 | import clsx from 'clsx';
5 | import * as PropTypes from 'prop-types';
6 | import * as React from 'react';
7 | import { ExtendMui } from '../typings/extendMui';
8 |
9 | export interface ToolbarButtonProps extends ExtendMui, WithStyles {
10 | selected: boolean;
11 | label: string;
12 | }
13 |
14 | const ToolbarButton: React.SFC = ({
15 | classes,
16 | selected,
17 | label,
18 | className = null,
19 | ...other
20 | }) => (
21 |
27 | {label}
28 |
29 | );
30 |
31 | (ToolbarButton as any).propTypes = {
32 | selected: PropTypes.bool.isRequired,
33 | label: PropTypes.string.isRequired,
34 | classes: PropTypes.any.isRequired,
35 | className: PropTypes.string,
36 | innerRef: PropTypes.any,
37 | };
38 |
39 | ToolbarButton.defaultProps = {
40 | className: '',
41 | };
42 |
43 | export const styles = (theme: Theme) => ({
44 | toolbarBtn: {
45 | cursor: 'pointer',
46 | color: 'rgba(255, 255, 255, 0.54)',
47 | },
48 | toolbarBtnSelected: {
49 | color: theme.palette.common.white,
50 | },
51 | });
52 |
53 | export default withStyles(styles, { name: 'MuiPickersToolbarButton' })(ToolbarButton);
54 |
--------------------------------------------------------------------------------
/docs/src/Pages/GettingStarted/ParsingDates.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Typography } from '@material-ui/core';
3 |
4 | const ParsingDates = () => (
5 |
6 |
7 | Parsing dates
8 |
9 |
10 |
11 | Material-UI pickers rely on the date management library when the date should be parsed. For
12 | any prop-types, that accept actually the date (e.g.
13 | minDate , maxDate )
14 | accept string, number, Date object and so on.
15 |
16 |
17 |
18 | Find more information about parsing dates in docs for your library:
19 |
20 |
21 |
34 |
35 |
36 | Pass any value to the picker, and if it won`t be parsed as expected feel free to open issue on
37 | our github 😎
38 |
39 |
40 | );
41 |
42 | export default ParsingDates;
43 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DatePicker/BasicDatePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { DatePicker } from 'material-ui-pickers';
3 |
4 | export default class BasicDatePicker extends PureComponent {
5 | state = {
6 | selectedDate: new Date(),
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
19 |
25 |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
51 |
52 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/docs/src/Pages/Localization/Persian/PersianCalendar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Code from '_shared/Code';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 | import { Typography } from '@material-ui/core';
5 |
6 | const PersianCalendar = () => (
7 |
8 |
9 | Persian Calendar System
10 |
11 |
12 |
13 | Make sure you have read the{' '}
14 | right to left section of the
15 | material-ui documentation page before proceeding.
16 |
17 |
18 |
19 | You will also need to install the
20 | @date-io/jalaali
21 | package from npm.
22 |
23 |
24 |
25 |
26 |
31 |
32 | You can use the examples below. It is recommended that you change the font.
33 |
34 |
35 |
36 | Also, to make sure the example is fully functional, don't forget to change the
37 | direction of the page to Right to Left from the top right corner.
38 |
39 |
40 | }
41 | />
42 |
43 | );
44 |
45 | export default PersianCalendar;
46 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DatePicker/YearMonthPicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { DatePicker } from 'material-ui-pickers';
3 |
4 | export default class YearMonthPicker extends PureComponent {
5 | state = {
6 | selectedDate: '2018-04-01T00:00:00.000Z',
7 | };
8 |
9 | handleDateChange = date => {
10 | this.setState({ selectedDate: date });
11 | };
12 |
13 | render() {
14 | const { selectedDate } = this.state;
15 |
16 | return (
17 |
18 |
19 |
26 |
27 |
28 |
29 |
38 |
39 |
40 |
41 |
49 |
50 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/Formik.example.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { DatePicker } from 'material-ui-pickers';
3 | import { Formik, Form, Field } from 'formik';
4 | import Code from '_shared/Code';
5 | import Grid from '@material-ui/core/Grid';
6 |
7 | const DatePickerField = ({ field, form, ...other }) => {
8 | const currentError = form.errors[field.name];
9 | return (
10 | form.setFieldError(field.name, error)}
20 | onChange={date => form.setFieldValue(field.name, date, true)}
21 | mask={value => (value ? [/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/] : [])}
22 | {...other}
23 | />
24 | );
25 | };
26 |
27 | const FormikExample = () => {
28 | return (
29 |
30 | {({ values, errors }) => (
31 |
44 | )}
45 |
46 | );
47 | };
48 |
49 | export default FormikExample;
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "material-ui-pickers",
3 | "version": "2.0.0",
4 | "description": "Material-ui pickers root package",
5 | "main": "index.js",
6 | "directories": {
7 | "doc": "docs",
8 | "lib": "lib"
9 | },
10 | "scripts": {
11 | "start": "lerna run start --parallel",
12 | "bump:patch": "lerna exec -- npm version patch",
13 | "bump:minor": "lerna exec -- npm version minor",
14 | "release": "lerna run release --scope --parallel material-ui-pickers && lerna run deploy --scope --parallel docs",
15 | "release:patch": "npm run bump:patch && npm run release",
16 | "release:minor": "npm run bump:minor && npm run release",
17 | "postinstall": "lerna bootstrap",
18 | "e2e:open": "cypress open",
19 | "e2e:run": "cypress run"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/dmtrKovalenko/material-ui-pickers.git"
24 | },
25 | "keywords": [],
26 | "author": "",
27 | "license": "ISC",
28 | "bugs": {
29 | "url": "https://github.com/dmtrKovalenko/material-ui-pickers/issues"
30 | },
31 | "homepage": "https://github.com/dmtrKovalenko/material-ui-pickers#readme",
32 | "devDependencies": {
33 | "husky": "^1.1.2",
34 | "lerna": "^3.4.3",
35 | "lint-staged": "^7.3.0",
36 | "prettier": "^1.14.3"
37 | },
38 | "husky": {
39 | "hooks": {
40 | "pre-commit": "lint-staged"
41 | }
42 | },
43 | "lint-staged": {
44 | "*.{js,jsx,ts,tsx,json,css,md}": [
45 | "prettier --write",
46 | "git add"
47 | ]
48 | },
49 | "dependencies": {
50 | "cypress": "^3.1.4",
51 | "wait-on": "^3.2.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/src/_helpers/text-field-helper.ts:
--------------------------------------------------------------------------------
1 | import { DateTextFieldProps } from '../_shared/DateTextField';
2 | import { MaterialUiPickersDate } from '../typings/date';
3 |
4 | export const getDisplayDate = ({
5 | utils,
6 | value,
7 | format,
8 | invalidLabel,
9 | emptyLabel,
10 | labelFunc,
11 | }: DateTextFieldProps) => {
12 | const isEmpty = value === null;
13 | const date = utils.date(value);
14 |
15 | if (labelFunc) {
16 | return labelFunc(isEmpty ? null : date, invalidLabel!);
17 | }
18 |
19 | if (isEmpty) {
20 | return emptyLabel;
21 | }
22 |
23 | return utils.isValid(date) ? utils.format(date, format) : invalidLabel;
24 | };
25 |
26 | export const getError = (
27 | value: MaterialUiPickersDate,
28 | props: DateTextFieldProps
29 | ): React.ReactNode => {
30 | const {
31 | utils,
32 | maxDate,
33 | minDate,
34 | disablePast,
35 | disableFuture,
36 | maxDateMessage,
37 | minDateMessage,
38 | invalidDateMessage,
39 | } = props;
40 |
41 | // if null - do not show error
42 | if (utils.isNull(value)) {
43 | return '';
44 | }
45 |
46 | if (!utils.isValid(value)) {
47 | return invalidDateMessage;
48 | }
49 |
50 | if (
51 | (maxDate && utils.isAfter(value, utils.endOfDay(utils.date(maxDate)))) ||
52 | (disableFuture && utils.isAfter(value, utils.endOfDay(utils.date())))
53 | ) {
54 | return maxDateMessage;
55 | }
56 |
57 | if (
58 | (minDate && utils.isBefore(value, utils.startOfDay(utils.date(minDate)))) ||
59 | (disablePast && utils.isBefore(value, utils.startOfDay(utils.date())))
60 | ) {
61 | return minDateMessage;
62 | }
63 |
64 | return '';
65 | };
66 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/ControllingProgrammatically.example.jsx:
--------------------------------------------------------------------------------
1 | import Button from '@material-ui/core/Button';
2 | import withStyles from '@material-ui/core/styles/withStyles';
3 | import { InlineDatePicker } from 'material-ui-pickers';
4 | import PropTypes from 'prop-types';
5 | import React, { PureComponent } from 'react';
6 |
7 | class ControllingProgrammaticallyExample extends PureComponent {
8 | static propTypes = {
9 | classes: PropTypes.object.isRequired,
10 | };
11 |
12 | state = {
13 | selectedDate: new Date(),
14 | };
15 |
16 | handleDateChange = date => {
17 | this.setState({ selectedDate: date });
18 | };
19 |
20 | openPicker = e => {
21 | // do not pass Event for default pickers
22 | this.picker.open(e);
23 | };
24 |
25 | render() {
26 | const { selectedDate } = this.state;
27 |
28 | return (
29 |
30 |
31 |
32 |
33 | {
40 | console.log(node); // check console to view the api of wrapper
41 | this.picker = node;
42 | }}
43 | />
44 |
45 |
46 | );
47 | }
48 | }
49 |
50 | const styles = {
51 | container: {
52 | display: 'flex',
53 | flexDirection: 'column',
54 | },
55 | };
56 |
57 | export default withStyles(styles)(ControllingProgrammaticallyExample);
58 |
--------------------------------------------------------------------------------
/lib/copy.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fse = require('fs-extra');
3 |
4 | function copyReadme() {
5 | return fse.copyFile(
6 | path.resolve(__dirname, '..', 'README.md'),
7 | path.resolve(__dirname, 'build', 'README.md')
8 | );
9 | }
10 |
11 | function createPackageFile() {
12 | return new Promise(resolve => {
13 | fse.readFile(path.resolve(__dirname, 'package.json'), 'utf8', (err, data) => {
14 | if (err) {
15 | throw err;
16 | }
17 |
18 | resolve(data);
19 | });
20 | })
21 | .then(data => JSON.parse(data))
22 | .then(packageData => {
23 | // cleanup produced package
24 | const {
25 | devDependencies,
26 | jest,
27 | husky,
28 | main,
29 | module,
30 | typings,
31 | 'lint-staged': ls,
32 | scripts: { postinstall },
33 | ...other
34 | } = packageData;
35 |
36 | const newPackage = {
37 | ...other,
38 | private: false,
39 | main: main.replace('build/', ''),
40 | module: module.replace('build/', ''),
41 | typings: typings.replace('build/', ''),
42 | scripts: {
43 | postinstall,
44 | },
45 | };
46 |
47 | return new Promise(resolve => {
48 | const buildPath = path.resolve(__dirname, 'build', 'package.json');
49 | const data = JSON.stringify(newPackage, null, 2);
50 | fse.writeFile(buildPath, data, err => {
51 | if (err) throw err;
52 | console.log(`Created package.json in ${buildPath}`);
53 | resolve();
54 | });
55 | });
56 | });
57 | }
58 |
59 | createPackageFile().then(() => copyReadme());
60 |
--------------------------------------------------------------------------------
/docs/src/layout/DrawerMenu.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import {
4 | Divider,
5 | Toolbar,
6 | Typography,
7 | withStyles,
8 | createStyles,
9 | Theme,
10 | WithStyles,
11 | } from '@material-ui/core';
12 |
13 | import NavigationMenu from './NavigationMenu';
14 | import { version } from '../../package.json';
15 |
16 | const DrawerMenu: React.SFC> = ({ classes }) => (
17 |
39 | );
40 |
41 | const styles = (theme: Theme) =>
42 | createStyles({
43 | drawerRoot: {
44 | width: 250,
45 | },
46 | drawerToolbar: {
47 | display: 'flex',
48 | flexDirection: 'column',
49 | justifyContent: 'center',
50 | alignItems: 'flex-start',
51 | },
52 | headerLink: {
53 | transition: 'color .2s ease-in-out',
54 | '&:hover': {
55 | color: theme.palette.primary.dark,
56 | textDecoration: 'underline',
57 | },
58 | },
59 | });
60 |
61 | export default withStyles(styles)(DrawerMenu);
62 |
--------------------------------------------------------------------------------
/lib/src/_shared/PickerToolbar.tsx:
--------------------------------------------------------------------------------
1 | import { Theme } from '@material-ui/core';
2 | import createStyles from '@material-ui/core/styles/createStyles';
3 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
4 | import Toolbar, { ToolbarProps } from '@material-ui/core/Toolbar';
5 | import clsx from 'clsx';
6 | import * as PropTypes from 'prop-types';
7 | import * as React from 'react';
8 | import { ExtendMui } from '../typings/extendMui';
9 |
10 | export interface PickerToolbarProps extends ExtendMui, WithStyles {
11 | children: React.ReactNodeArray;
12 | }
13 |
14 | const PickerToolbar: React.SFC = ({
15 | children,
16 | className = null,
17 | classes,
18 | ...other
19 | }) => {
20 | return (
21 |
22 | {children}
23 |
24 | );
25 | };
26 |
27 | (PickerToolbar as any).propTypes = {
28 | children: PropTypes.arrayOf(PropTypes.node).isRequired,
29 | className: PropTypes.string,
30 | classes: PropTypes.any.isRequired,
31 | innerRef: PropTypes.any,
32 | };
33 |
34 | PickerToolbar.defaultProps = {
35 | className: '',
36 | };
37 |
38 | export const styles = (theme: Theme) =>
39 | createStyles({
40 | toolbar: {
41 | display: 'flex',
42 | flexDirection: 'column',
43 | alignItems: 'flex-start',
44 | justifyContent: 'center',
45 | height: 100,
46 | backgroundColor:
47 | theme.palette.type === 'light'
48 | ? theme.palette.primary.main
49 | : theme.palette.background.default,
50 | },
51 | });
52 |
53 | export default withStyles(styles, { name: 'MuiPickersToolbar' })(PickerToolbar);
54 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/CssOverrides.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import { DatePicker } from 'material-ui-pickers';
3 | import { MuiThemeProvider, createMuiTheme } from '@material-ui/core';
4 |
5 | import lightBlue from '@material-ui/core/colors/lightBlue';
6 |
7 | const materialTheme = createMuiTheme({
8 | overrides: {
9 | MuiPickersToolbar: {
10 | toolbar: {
11 | backgroundColor: lightBlue.A200,
12 | },
13 | },
14 | MuiPickersCalendarHeader: {
15 | switchHeader: {
16 | // backgroundColor: lightBlue.A200,
17 | // color: 'white',
18 | },
19 | },
20 | MuiPickersDay: {
21 | day: {
22 | color: lightBlue.A700,
23 | },
24 | isSelected: {
25 | backgroundColor: lightBlue['400'],
26 | },
27 | current: {
28 | color: lightBlue['900'],
29 | },
30 | },
31 | MuiPickersModal: {
32 | dialogAction: {
33 | color: lightBlue['400'],
34 | },
35 | },
36 | },
37 | });
38 |
39 | export default class BasicDatePicker extends PureComponent {
40 | state = {
41 | selectedDate: new Date(),
42 | };
43 |
44 | handleDateChange = date => {
45 | this.setState({ selectedDate: date });
46 | };
47 |
48 | render() {
49 | const { selectedDate } = this.state;
50 |
51 | return (
52 |
53 |
54 |
60 |
61 |
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/FormatsCustomization.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Code from '_shared/Code';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 | import { Typography } from '@material-ui/core';
5 |
6 | // eslint-disable-next-line import/no-webpack-loader-syntax
7 | import utilsInterfaceCode from '!raw-loader!@date-io/core/IUtils.d.ts';
8 |
9 | const FormatCustomization = () => (
10 |
11 |
12 | Format customization
13 |
14 |
15 |
16 | For localization purpose may be needed to change displaying values in the pickers modal,
17 | because default formats can be not idiomatic for some localizations. There utils can help you.
18 |
19 |
20 |
21 | It`s possible to override any of displaying date values by inheritance of utils passed to
22 | MuiPickersProvider.
23 |
24 |
25 |
30 | You can use ES6 class syntax or override values with a help of .prototype property
31 |
32 | }
33 | />
34 |
35 |
36 | Utils interface
37 |
38 |
39 |
40 | Where TDate - date object passed from state (moment, native Date or Luxon`s DateTime)
41 |
42 |
43 |
44 |
45 | );
46 |
47 | export default FormatCustomization;
48 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DatePicker/InlineDatePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import { InlineDatePicker } from 'material-ui-pickers';
2 | import React, { Fragment, PureComponent } from 'react';
3 | import { withUtilsService } from '../../../_shared/UtilsServiceContext';
4 |
5 | class InlineDatePickerDemo extends PureComponent {
6 | state = {
7 | selectedDate: '2018-01-01T00:00:00.000Z',
8 | };
9 |
10 | handleDateChange = date => {
11 | this.setState({ selectedDate: date });
12 | };
13 |
14 | render() {
15 | const { selectedDate } = this.state;
16 |
17 | return (
18 |
19 |
20 |
25 |
26 |
27 |
28 |
35 |
36 |
37 |
38 |
51 |
52 |
53 | );
54 | }
55 | }
56 |
57 | export default withUtilsService(InlineDatePickerDemo);
58 |
--------------------------------------------------------------------------------
/docs/src/Pages/Regression/Regression.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from 'react';
2 | import { Grid, Typography } from '@material-ui/core';
3 | import { DatePicker } from 'material-ui-pickers';
4 | import { createRegressionDay as createRegressionDayRenderer } from './RegressionDay';
5 | import { MuiPickersContext } from 'material-ui-pickers';
6 | import LeftArrowIcon from '@material-ui/icons/KeyboardArrowLeft';
7 | import RightArrowIcon from '@material-ui/icons/KeyboardArrowRight';
8 |
9 | export function Regression() {
10 | const utils = useContext(MuiPickersContext);
11 | const [date, changeDate] = useState(new Date('2019-01-01T00:00:00.000Z'));
12 |
13 | const sharedProps = {
14 | value: date,
15 | onChange: changeDate,
16 | style: { margin: '0 10px' },
17 | leftArrowIcon: ,
18 | rightArrowIcon: ,
19 | renderDay: createRegressionDayRenderer(utils!),
20 | KeyboardButtonProps: {
21 | className: 'keyboard-btn',
22 | },
23 | };
24 |
25 | return (
26 | <>
27 |
28 | This page is using for the automate regression of material-ui-pickers.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
43 | >
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/docs/src/Pages/Guides/CssOverrides.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Code from '_shared/Code';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 | import { Typography } from '@material-ui/core';
5 | import typescriptOverrideCode from './CssOverridesTypescript.example'; // eslint-disable-line
6 |
7 | const CssOverrides = () => (
8 |
9 |
10 | Override stylesheet
11 |
12 |
13 |
14 | Default pickers appearance built based on material-ui theme provided. So pickers will take all
15 | colors/fonts/theme setting as any other material-ui components.
16 |
17 |
18 |
19 | But we are not providing any for-component classes api to override stylesheet for particular
20 | component. Only one way to override existed stylesheet - usage of global material-ui theme
21 | overrides.
22 |
23 |
24 |
29 | You can find the override component name and class in the generated classnames for pickers
30 | components.
31 |
32 | }
33 | />
34 |
35 |
36 | For typescript users
37 |
38 |
39 |
40 | Override default material-ui theme to attach picker's component override. (This will also
41 | autocomplete classnames for overrides)
42 |
43 |
44 |
45 |
46 | );
47 |
48 | export default CssOverrides;
49 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/TimePicker/TimePickerDemo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypesTable from '_shared/PropTypesTable';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 | import { Typography } from '@material-ui/core';
5 |
6 | const TimePickerDemo = () => (
7 |
8 |
9 | Time picker
10 |
11 |
12 | Time pickers use a dialog to select a single time (in the hours:minutes format).
13 |
14 |
15 | The selected time is indicated by the filled circle at the end of the clock hand.
16 |
17 |
18 |
23 | A time picker should adjusts to a user’s preferred time setting, i.e. the 12-hour or
24 | 24-hour format.
25 |
26 | }
27 | />
28 |
29 |
34 | Seconds input can be used for selection of precise time point
35 |
36 | }
37 | />
38 |
39 |
43 |
44 |
48 |
49 |
50 |
51 | );
52 |
53 | export default TimePickerDemo;
54 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DatePicker/DatePickerDemo.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypesTable from '_shared/PropTypesTable';
3 | import SourcablePanel from '_shared/SourcablePanel';
4 | import { Typography } from '@material-ui/core';
5 |
6 | const DatePickerDemo = () => (
7 |
8 |
9 | Date picker
10 |
11 |
12 | Date pickers use a dialog window to select a single date.
13 |
14 |
15 | The selected day is indicated by a filled circle. The current day is indicated by a different
16 | color and type weight.
17 |
18 |
19 |
23 |
24 |
28 |
29 |
33 |
34 |
38 |
39 |
44 | Customization performing by overriding render method for Day component.
45 | Here example with moment-js
46 |
47 | }
48 | />
49 |
50 |
51 |
52 | );
53 |
54 | export default DatePickerDemo;
55 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DatePicker/KeyboardDatePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, PureComponent } from 'react';
2 | import { DatePicker } from 'material-ui-pickers';
3 | import { withUtilsService } from '../../../_shared/UtilsServiceContext';
4 |
5 | class KeyboardDatePicker extends PureComponent {
6 | state = {
7 | selectedDate: new Date(),
8 | };
9 |
10 | handleDateChange = date => {
11 | this.setState({ selectedDate: date });
12 | };
13 |
14 | render() {
15 | const { selectedDate } = this.state;
16 |
17 | return (
18 |
19 |
20 | console.log('Keyboard Input:', e.target.value)}
29 | />
30 |
31 |
32 |
33 | pass plain array if you are not controlling value outside
42 | mask={value =>
43 | value ? [/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/] : []
44 | }
45 | value={selectedDate}
46 | onChange={this.handleDateChange}
47 | disableOpenOnEnter
48 | animateYearScrolling={false}
49 | />
50 |
51 |
52 | );
53 | }
54 | }
55 |
56 | export default withUtilsService(KeyboardDatePicker);
57 |
--------------------------------------------------------------------------------
/docs/src/Pages/Components/DateTimePicker/InlineDateTimePicker.example.jsx:
--------------------------------------------------------------------------------
1 | import { InlineDateTimePicker } from 'material-ui-pickers';
2 | import React, { Fragment, PureComponent } from 'react';
3 | import { withUtilsService } from '../../../_shared/UtilsServiceContext';
4 |
5 | class InlineDateTimePickerDemo extends PureComponent {
6 | state = {
7 | selectedDate: '2018-01-01T00:00:00.000Z',
8 | };
9 |
10 | handleDateChange = date => {
11 | this.setState({ selectedDate: date });
12 | };
13 |
14 | render() {
15 | const { selectedDate } = this.state;
16 |
17 | return (
18 |
19 |
20 |
25 |
26 |
27 |
28 |
59 |
60 |
61 | );
62 | }
63 | }
64 |
65 | export default withUtilsService(InlineDateTimePickerDemo);
66 |
--------------------------------------------------------------------------------
/lib/src/TimePicker/TimePickerInline.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import BasePicker, { BasePickerProps } from '../_shared/BasePicker';
4 | import { ExtendWrapper } from '../wrappers/ExtendWrapper';
5 | import InlineWrapper, { OuterInlineWrapperProps } from '../wrappers/InlineWrapper';
6 | import TimePicker, { BaseTimePickerProps } from './TimePicker';
7 |
8 | export interface TimePickerInlineProps
9 | extends BasePickerProps,
10 | BaseTimePickerProps,
11 | ExtendWrapper {}
12 |
13 | export const TimePickerInline: React.SFC = props => {
14 | const {
15 | ampm,
16 | format,
17 | forwardedRef,
18 | initialFocusedDate,
19 | minutesStep,
20 | onChange,
21 | seconds,
22 | value,
23 | ...other
24 | } = props;
25 |
26 | return (
27 |
28 | {({
29 | date,
30 | utils,
31 | handleChange,
32 | handleTextFieldChange,
33 | isAccepted,
34 | pick12hOr24hFormat,
35 | handleAccept,
36 | handleClear,
37 | }) => (
38 |
48 |
55 |
56 | )}
57 |
58 | );
59 | };
60 |
61 | export default React.forwardRef((props: TimePickerInlineProps, ref) => (
62 |
63 | ));
64 |
--------------------------------------------------------------------------------
/docs/src/Pages/GettingStarted/Usage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Code from '_shared/Code';
4 | import { Typography, withStyles } from '@material-ui/core';
5 | import quickStartCode from '!raw-loader!./QuickStart.example'; // eslint-disable-line
6 |
7 | const sandBoxId = 'q9l2j10wr4';
8 |
9 | const Usage = ({ classes }) => (
10 |
11 |
12 | Usage
13 |
14 |
15 |
16 | Material-UI-pickers rely only on material-ui controls and the date-management lib you have
17 | choose. Please note that all components are controlled, thats means that its required to pass
18 | value and
19 | onChange props.
20 |
21 |
22 |
23 | Quick Start
24 |
25 |
26 |
27 | Here is a quick example you to get started
28 |
29 |
30 |
31 |
32 |
33 | Interactive example
34 |
35 |
36 |
48 |
49 | );
50 |
51 | Usage.propTypes = {
52 | classes: PropTypes.object.isRequired,
53 | };
54 |
55 | const styles = {
56 | quickStartHeader: {
57 | marginTop: '1em',
58 | },
59 | };
60 |
61 | export default withStyles(styles)(Usage);
62 |
--------------------------------------------------------------------------------
/lib/src/__tests__/e2e/TimePicker.test.tsx:
--------------------------------------------------------------------------------
1 | import { ReactWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import TimePicker, { TimePickerProps } from '../../TimePicker/TimePicker';
4 | import { mount, utilsToUse } from '../test-utils';
5 |
6 | describe('e2e - TimePicker', () => {
7 | let component: ReactWrapper;
8 | const onChangeMock = jest.fn();
9 |
10 | beforeEach(() => {
11 | jest.clearAllMocks();
12 | component = mount(
13 |
14 | );
15 | });
16 |
17 | it('Should renders', () => {
18 | expect(component).toBeTruthy();
19 | });
20 |
21 | it('Should submit onChange on moving', () => {
22 | component.find('Clock div[role="menu"]').simulate('mouseMove', {
23 | buttons: 1,
24 | nativeEvent: {
25 | offsetX: 20,
26 | offsetY: 15,
27 | },
28 | });
29 |
30 | expect(onChangeMock).toHaveBeenCalled();
31 | });
32 |
33 | it('Should submit hourview (mouse move)', () => {
34 | component.find('Clock div[role="menu"]').simulate('mouseUp', {
35 | nativeEvent: {
36 | offsetX: 20,
37 | offsetY: 15,
38 | },
39 | });
40 |
41 | expect(onChangeMock).toHaveBeenCalled();
42 | });
43 |
44 | it('Should change minutes (touch)', () => {
45 | component.setState({ openView: 'minutes' });
46 | component.find('Clock div[role="menu"]').simulate('touchMove', {
47 | buttons: 1,
48 | changedTouches: [
49 | {
50 | clientX: 20,
51 | clientY: 15,
52 | },
53 | ],
54 | });
55 |
56 | expect(onChangeMock).toHaveBeenCalled();
57 |
58 | component.find('Clock div[role="menu"]').simulate('touchEnd', {
59 | buttons: 1,
60 | changedTouches: [
61 | {
62 | clientX: 20,
63 | clientY: 15,
64 | },
65 | ],
66 | });
67 |
68 | expect(onChangeMock).toHaveBeenCalled();
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/lib/src/TimePicker/TimePickerModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import BasePicker, { BasePickerProps } from '../_shared/BasePicker';
4 | import { ExtendWrapper } from '../wrappers/ExtendWrapper';
5 | import ModalWrapper, { ModalWrapperProps } from '../wrappers/ModalWrapper';
6 | import TimePicker, { BaseTimePickerProps } from './TimePicker';
7 |
8 | export interface TimePickerModalProps
9 | extends BasePickerProps,
10 | BaseTimePickerProps,
11 | ExtendWrapper {}
12 |
13 | export const TimePickerModal: React.SFC = props => {
14 | const {
15 | ampm,
16 | autoOk,
17 | format,
18 | forwardedRef,
19 | initialFocusedDate,
20 | minutesStep,
21 | onChange,
22 | seconds,
23 | value,
24 | ...other
25 | } = props;
26 |
27 | return (
28 |
29 | {({
30 | date,
31 | utils,
32 | handleAccept,
33 | handleChange,
34 | handleClear,
35 | handleDismiss,
36 | handleSetTodayDate,
37 | handleTextFieldChange,
38 | isAccepted,
39 | pick12hOr24hFormat,
40 | }) => (
41 |
53 |
60 |
61 | )}
62 |
63 | );
64 | };
65 |
66 | export default React.forwardRef((props: TimePickerModalProps, ref) => (
67 |
68 | ));
69 |
--------------------------------------------------------------------------------
/docs/src/_shared/Code.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import { withStyles, Theme, WithStyles } from '@material-ui/core/styles';
4 | import lightStyle from 'react-syntax-highlighter/dist/styles/prism/prism';
5 | import darkStyle from 'react-syntax-highlighter/dist/styles/prism/darcula';
6 |
7 | // @ts-ignore
8 | import jsx from 'react-syntax-highlighter/dist/languages/prism/jsx';
9 | // @ts-ignore
10 | import typescript from 'react-syntax-highlighter/dist/languages/prism/typescript';
11 | // @ts-ignore
12 | import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/dist/prism-light';
13 | import { ThemeContext } from '../App';
14 |
15 | registerLanguage('jsx', jsx);
16 | registerLanguage('typescript', typescript);
17 |
18 | const styles = (theme: Theme) => ({
19 | root: {
20 | margin: '0',
21 | fontFamily: theme.typography.fontFamily,
22 | fontSize: '1em',
23 | color: theme.palette.text.primary,
24 | padding: 10,
25 | backgroundColor: theme.palette.background.paper,
26 |
27 | '& pre': {
28 | borderRadius: 3,
29 | overflow: 'auto !important',
30 | margin: '0 !important',
31 | backgroundColor: theme.palette.background.paper + ' !important',
32 | },
33 | },
34 | margin: {
35 | margin: '10px 0 30px',
36 | },
37 | });
38 |
39 | type CodeProps = {
40 | text: string;
41 | withMargin?: boolean;
42 | language?: 'jsx' | 'typescript' | 'markup';
43 | } & WithStyles;
44 |
45 | const Code: React.SFC = ({ classes, language, text, withMargin }) => {
46 | return (
47 |
48 |
49 | {theme => (
50 |
51 | {text}
52 |
53 | )}
54 |
55 |
56 | );
57 | };
58 |
59 | Code.defaultProps = {
60 | withMargin: false,
61 | language: 'jsx',
62 | };
63 |
64 | export default withStyles(styles)(Code);
65 |
--------------------------------------------------------------------------------
/lib/src/__tests__/TimePicker/Clock.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { Clock, ClockProps } from '../../TimePicker/components/Clock';
4 | import { shallow } from '../test-utils';
5 |
6 | const mouseClockEvent = {
7 | preventDefault: jest.fn(),
8 | stopPropagation: jest.fn(),
9 | buttons: 1,
10 | nativeEvent: {
11 | offsetX: 10,
12 | offsetY: 25,
13 | },
14 | };
15 |
16 | describe('Clock', () => {
17 | let component: ShallowWrapper;
18 | const onChangeMock = jest.fn();
19 |
20 | beforeEach(() => {
21 | onChangeMock.mockReset();
22 | component = shallow(
23 | foo]}
29 | />
30 | );
31 | });
32 |
33 | it('Should renders', () => {
34 | // console.log(component.debug());
35 | expect(component).toBeTruthy();
36 | });
37 |
38 | it('Should set time on mouse move with click', () => {
39 | component.find('[role="menu"]').simulate('mouseMove', mouseClockEvent);
40 |
41 | expect(onChangeMock).toHaveBeenCalledWith(52, false);
42 | });
43 |
44 | it('Should set isMoving = false on mouse up', () => {
45 | (component.instance() as Clock).isMoving = true;
46 | component.find('[role="menu"]').simulate('mouseUp', mouseClockEvent);
47 |
48 | expect((component.instance() as Clock).isMoving).toBeFalsy();
49 | });
50 |
51 | it('Should set time on touch move', () => {
52 | component.find('[role="menu"]').simulate('touchMove', {
53 | preventDefault: jest.fn(),
54 | stopPropagation: jest.fn(),
55 | changedTouches: [{ clientX: 10, clientY: 15 }],
56 | target: {
57 | getBoundingClientRect: () => ({ left: 0, top: 0 }),
58 | },
59 | });
60 |
61 | expect(onChangeMock).toHaveBeenCalledWith(52, false);
62 | });
63 |
64 | it('Should set isMoving = false on touch end', () => {
65 | component.find('[role="menu"]').simulate('touchEnd');
66 | expect((component.instance() as Clock).isMoving).toBeFalsy();
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/lib/src/__tests__/e2e/DateTimePicker.test.tsx:
--------------------------------------------------------------------------------
1 | import { ReactWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import DateTimePicker, { DateTimePickerProps } from '../../DateTimePicker/DateTimePicker';
4 | import { mount, utilsToUse } from '../test-utils';
5 |
6 | describe('e2e - DateTimePicker', () => {
7 | let component: ReactWrapper;
8 | const onChangeMock = jest.fn();
9 |
10 | beforeEach(() => {
11 | jest.clearAllMocks();
12 | component = mount(
13 |
22 | );
23 | });
24 |
25 | it('Should renders', () => {
26 | expect(component).toBeTruthy();
27 | });
28 |
29 | it('Should render year selection', () => {
30 | component
31 | .find('ToolbarButton')
32 | .first()
33 | .simulate('click');
34 |
35 | expect(component.find('Year').length).toBe(201);
36 |
37 | component
38 | .find('Year')
39 | .at(1)
40 | .simulate('click');
41 | expect(onChangeMock).toHaveBeenCalled();
42 | });
43 |
44 | it('Should render hour view', () => {
45 | component
46 | .find('ToolbarButton')
47 | .at(2)
48 | .simulate('click');
49 | expect(component.find('TimePickerView').props().type).toBe('hours');
50 | });
51 |
52 | it('Should render minutes view', () => {
53 | component
54 | .find('ToolbarButton')
55 | .at(4)
56 | .simulate('click');
57 | expect(component.find('TimePickerView').props().type).toBe('minutes');
58 | });
59 |
60 | it('Should change meridiem', () => {
61 | component
62 | .find('ToolbarButton')
63 | .at(6)
64 | .simulate('click');
65 |
66 | if (process.env.UTILS === 'moment') {
67 | expect(onChangeMock).toHaveBeenCalled();
68 | return;
69 | }
70 |
71 | expect(onChangeMock).toHaveBeenCalledWith(utilsToUse.date('2018-01-01T12:00:00.000Z'), false);
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/lib/src/_helpers/time-utils.ts:
--------------------------------------------------------------------------------
1 | import { IUtils } from '@date-io/core/IUtils';
2 | import { MeridiemMode } from '../DateTimePicker/components/DateTimePickerHeader';
3 | import { MaterialUiPickersDate } from '../typings/date';
4 |
5 | const center = {
6 | x: 260 / 2,
7 | y: 260 / 2,
8 | };
9 |
10 | const basePoint = {
11 | x: center.x,
12 | y: 0,
13 | };
14 |
15 | const cx = basePoint.x - center.x;
16 | const cy = basePoint.y - center.y;
17 |
18 | const rad2deg = (rad: number) => rad * 57.29577951308232;
19 |
20 | const getAngleValue = (step: number, offsetX: number, offsetY: number) => {
21 | const x = offsetX - center.x;
22 | const y = offsetY - center.y;
23 |
24 | const atan = Math.atan2(cx, cy) - Math.atan2(x, y);
25 |
26 | let deg = rad2deg(atan);
27 | deg = Math.round(deg / step) * step;
28 | deg %= 360;
29 |
30 | const value = Math.floor(deg / step) || 0;
31 | const delta = Math.pow(x, 2) + Math.pow(y, 2);
32 | const distance = Math.sqrt(delta);
33 |
34 | return { value, distance };
35 | };
36 |
37 | export const getHours = (offsetX: number, offsetY: number, ampm: boolean) => {
38 | // tslint:disable-next-line
39 | let { value, distance } = getAngleValue(30, offsetX, offsetY);
40 | value = value || 12;
41 |
42 | if (!ampm) {
43 | if (distance < 90) {
44 | value += 12;
45 | value %= 24;
46 | }
47 | } else {
48 | value %= 12;
49 | }
50 |
51 | return value;
52 | };
53 |
54 | export const getMinutes = (offsetX: number, offsetY: number, step = 1) => {
55 | const angleStep = step * 6;
56 | let { value } = getAngleValue(angleStep, offsetX, offsetY);
57 | value = (value * step) % 60;
58 |
59 | return value;
60 | };
61 |
62 | export const convertToMeridiem = (
63 | time: MaterialUiPickersDate,
64 | meridiem: MeridiemMode,
65 | ampm: boolean,
66 | utils: IUtils
67 | ) => {
68 | if (ampm) {
69 | const currentMeridiem = utils.getHours(time) >= 12 ? 'pm' : 'am';
70 | if (currentMeridiem !== meridiem) {
71 | const hours = meridiem === 'am' ? utils.getHours(time) - 12 : utils.getHours(time) + 12;
72 |
73 | return utils.setHours(time, hours);
74 | }
75 | }
76 |
77 | return time;
78 | };
79 |
--------------------------------------------------------------------------------
/lib/src/__tests__/wrappers/ModalWrapper.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { ModalDialog } from '../../_shared/ModalDialog';
4 | import ModalWrapper, { ModalWrapperProps } from '../../wrappers/ModalWrapper';
5 | import { shallow } from '../test-utils';
6 |
7 | describe('ModalWrapper', () => {
8 | let component: ShallowWrapper;
9 |
10 | beforeEach(() => {
11 | component = shallow(
12 |
22 | foo
23 |
24 | );
25 | });
26 |
27 | it('Should renders', () => {
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | it('Should dispatch onOpen callback', () => {
32 | component.find('WithUtils(DateTextField)').simulate('click');
33 | expect((component.state() as ModalWrapper['state']).open).toBeTruthy();
34 | expect((component.instance() as ModalWrapper).props.onOpen).toHaveBeenCalled();
35 | });
36 |
37 | it('Should dispatch onClose callback', () => {
38 | component.setState({ open: true });
39 | component.find('WithStyles(ModalDialog)').simulate('dismiss');
40 | expect((component.state() as ModalWrapper['state']).open).toBeFalsy();
41 | expect((component.instance() as ModalWrapper).props.onClose).toHaveBeenCalled();
42 | });
43 |
44 | it('Should dispatch onAccept when accepted', () => {
45 | component.setState({ open: true });
46 | component.find('WithStyles(ModalDialog)').simulate('accept');
47 | expect((component.state() as ModalWrapper['state']).open).toBeFalsy();
48 | expect((component.instance() as ModalWrapper).props.onAccept).toHaveBeenCalled();
49 | });
50 |
51 | it('Should dispatch onClear', () => {
52 | component.setState({ open: true });
53 | component.find('WithStyles(ModalDialog)').simulate('clear');
54 | expect((component.state() as ModalWrapper['state']).open).toBeFalsy();
55 | expect((component.instance() as ModalWrapper).props.onClear).toHaveBeenCalled();
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "2.2.1",
4 | "private": true,
5 | "dependencies": {
6 | "@date-io/core": "^1.1.0",
7 | "@date-io/date-fns": "^1.1.0",
8 | "@date-io/dayjs": "^1.1.0",
9 | "@date-io/jalaali": "^1.0.4",
10 | "@date-io/luxon": "^1.1.0",
11 | "@date-io/moment": "^1.1.0",
12 | "@material-ui/core": "^3.9.2",
13 | "@material-ui/icons": "^3.0.2",
14 | "@types/luxon": "^1.11.0",
15 | "@types/moment-jalaali": "^0.7.4",
16 | "@types/node": "^10.12.24",
17 | "@types/react": "^16.8.2",
18 | "@types/react-dom": "^16.8.0",
19 | "@types/react-jss": "^8.6.1",
20 | "@types/react-router": "^4.4.3",
21 | "@types/react-router-dom": "^4.3.1",
22 | "@types/react-syntax-highlighter": "^10.1.0",
23 | "clsx": "^1.0.2",
24 | "date-fns": "^2.0.0-alpha.27",
25 | "dayjs": "^1.8.5",
26 | "formik": "^1.5.0",
27 | "jss-rtl": "^0.2.3",
28 | "luxon": "^1.11.1",
29 | "material-ui-pickers": "*",
30 | "moment": "^2.24.0",
31 | "moment-jalaali": "^0.8.3",
32 | "raw-loader": "^0.5.1",
33 | "react": "^16.8.1",
34 | "react-app-rewire-aliases": "^0.2.0",
35 | "react-app-rewired": "^1.6.2",
36 | "react-dom": "^16.8.1",
37 | "react-jss": "^8.6.1",
38 | "react-router": "^4.3.1",
39 | "react-router-dom": "^4.3.1",
40 | "react-scripts": "^2.1.3",
41 | "react-syntax-highlighter": "^10.1.2",
42 | "react-transition-group": "^2.5.3",
43 | "serve": "^10.1.2",
44 | "typescript": "^3.3.3"
45 | },
46 | "scripts": {
47 | "start": "react-scripts start",
48 | "build": "react-scripts build",
49 | "test": "react-scripts test",
50 | "eject": "react-scripts eject",
51 | "serve": "serve -s build -p 3002",
52 | "docs:generate": "node scripts/docgen",
53 | "backers:generate": "node scripts/generate-backers",
54 | "deploy": "npm run docs:generate && npm run backers:generate && npm run build && firebase deploy"
55 | },
56 | "eslintConfig": {
57 | "extends": "react-app"
58 | },
59 | "browserslist": [
60 | ">0.2%",
61 | "not dead",
62 | "not ie <= 11",
63 | "not op_mini all"
64 | ],
65 | "devDependencies": {
66 | "patreon": "^0.4.1",
67 | "react-docgen-typescript": "^1.12.3"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/src/__tests__/test-utils.tsx:
--------------------------------------------------------------------------------
1 | import DateFnsUtils from '@date-io/date-fns';
2 | import LuxonUtils from '@date-io/luxon';
3 | import MomentUtils from '@date-io/moment';
4 | import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
5 | import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider';
6 | import * as enzyme from 'enzyme';
7 | import * as React from 'react';
8 | import { WithUtilsProps } from '../_shared/WithUtils';
9 | import MuiPickersUtilsProvider from '../MuiPickersUtilsProvider';
10 |
11 | const theme = createMuiTheme({
12 | typography: {
13 | useNextVariants: true,
14 | },
15 | });
16 |
17 | const getUtilClass = () => {
18 | switch (process.env.UTILS) {
19 | case 'moment':
20 | return MomentUtils;
21 | case 'date-fns':
22 | return DateFnsUtils;
23 | case 'luxon':
24 | return LuxonUtils;
25 | default:
26 | return DateFnsUtils;
27 | }
28 | };
29 |
30 | export const UtilClassToUse: any = getUtilClass();
31 | export const utilsToUse = new UtilClassToUse();
32 |
33 | // jest.doMock('../_shared/WithUtils', () => {
34 | // const WithUtils = () => (Component: React.ComponentType) => {
35 | // const withUtils: React.SFC = props => (
36 | //
37 | // );
38 | // withUtils.displayName = `WithUtils(${Component.displayName ||
39 | // Component.name})`;
40 | //
41 | // return withUtils;
42 | // };
43 | //
44 | // return { default: WithUtils };
45 | // });
46 |
47 | const getComponentWithUtils = (element: React.ReactElement
) =>
48 | React.cloneElement(element, { utils: utilsToUse } as any);
49 |
50 | export const shallow =
(element: React.ReactElement
) =>
51 | enzyme.shallow(getComponentWithUtils(element));
52 |
53 | export const mount =
(element: React.ReactElement
) =>
54 | enzyme.mount(
55 |
56 | {element}
57 |
58 | );
59 |
60 | export const shallowRender = (render: (props: any) => React.ReactElement) => {
61 | return enzyme.shallow(render({ utils: utilsToUse, classes: {} as any, theme: {} as any }));
62 | };
63 |
--------------------------------------------------------------------------------
/docs/src/Pages/Router.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route } from 'react-router';
3 |
4 | import DateFnsLocalization from './Localization/Date-fns/DateFnsLocalization';
5 | import MomentLocalization from './Localization/Moment/MomentLocalization';
6 | import PersianCalendar from './Localization/Persian/PersianCalendar';
7 | import DatePickerDemo from './Components/DatePicker/DatePickerDemo';
8 | import TimePickerDemo from './Components/TimePicker/TimePickerDemo';
9 | import DateTimePickerDemo from './Components/DateTimePicker/DateTimePickerDemo';
10 | import CssOverrides from './Guides/CssOverrides';
11 | import FormatCustomization from './Guides/FormatsCustomization';
12 | import ControllingProgrammatically from './Guides/ControllingProgrammatically';
13 | import StaticPickers from './Guides/StaticPickers';
14 | import Installation from './GettingStarted/Installation';
15 | import Usage from './GettingStarted/Usage';
16 | import ParsingDates from './GettingStarted/ParsingDates';
17 | import Landing from '../Landing/Landing';
18 | import Formik from './Guides/Formik';
19 | import { Regression } from './Regression/Regression';
20 |
21 | export default () => (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | );
45 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/components/Month.tsx:
--------------------------------------------------------------------------------
1 | import { Theme } from '@material-ui/core';
2 | import createStyles from '@material-ui/core/styles/createStyles';
3 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
4 | import Typography from '@material-ui/core/Typography';
5 | import clsx from 'clsx';
6 | import * as PropTypes from 'prop-types';
7 | import * as React from 'react';
8 |
9 | export interface MonthProps extends WithStyles {
10 | children: React.ReactNode;
11 | disabled?: boolean;
12 | onSelect: (value: any) => void;
13 | selected?: boolean;
14 | value: any;
15 | }
16 |
17 | export class Month extends React.PureComponent {
18 | public static defaultProps = {
19 | selected: false,
20 | disabled: false,
21 | };
22 |
23 | public handleClick = () => {
24 | this.props.onSelect(this.props.value);
25 | };
26 |
27 | public render() {
28 | const { classes, selected, disabled, value, children, ...other } = this.props;
29 |
30 | return (
31 |
46 | );
47 | }
48 | }
49 |
50 | export const styles = (theme: Theme) =>
51 | createStyles({
52 | root: {
53 | flex: '1 0 33.33%',
54 | display: 'flex',
55 | alignItems: 'center',
56 | justifyContent: 'center',
57 | cursor: 'pointer',
58 | outline: 'none',
59 | height: 75,
60 | transition: theme.transitions.create('font-size', { duration: '100ms' }),
61 | '&:focus': {
62 | color: theme.palette.primary.main,
63 | fontWeight: theme.typography.fontWeightMedium,
64 | },
65 | },
66 | selected: {
67 | color: theme.palette.primary.main,
68 | fontWeight: theme.typography.fontWeightMedium,
69 | },
70 | disabled: {
71 | pointerEvents: 'none',
72 | color: theme.palette.text.hint,
73 | },
74 | });
75 |
76 | export default withStyles(styles, { name: 'MuiPickersMonth' })(Month);
77 |
--------------------------------------------------------------------------------
/docs/src/layout/NavigationMenu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { List } from '@material-ui/core';
4 | import { withRouter } from 'react-router-dom';
5 | import NavItem from './NavItem';
6 |
7 | const navItems = [
8 | {
9 | title: 'Getting Started',
10 | children: [
11 | { title: 'Installation', href: '/installation' },
12 | { title: 'Usage', href: '/usage' },
13 | { title: 'Parsing dates', href: '/parsing' },
14 | ],
15 | },
16 | {
17 | title: 'Localization',
18 | children: [
19 | { title: 'Using date-fns', href: '/localization/date-fns' },
20 | { title: 'Using moment', href: '/localization/moment' },
21 | { title: 'Persian Calendar System', href: '/localization/persian' },
22 | ],
23 | },
24 | {
25 | title: 'Components',
26 | children: [
27 | { title: 'Date Picker', href: '/api/datepicker' },
28 | { title: 'Time Picker', href: '/api/timepicker' },
29 | { title: 'Date & Time Picker', href: '/api/datetimepicker' },
30 | ],
31 | },
32 | {
33 | title: 'Guides',
34 | children: [
35 | { title: 'Form integration', href: '/guides/formik-integration' },
36 | { title: 'CSS overrides', href: '/guides/css-overrides' },
37 | { title: 'Global format customization', href: '/guides/formats' },
38 | {
39 | title: 'Open pickers programmatically',
40 | href: '/guides/controlling-programmatically',
41 | },
42 | { title: 'Static picker`s components', href: '/guides/static-pickers' },
43 | ],
44 | },
45 | ];
46 |
47 | class NavigationMenu extends React.Component {
48 | mapNavigation(depth) {
49 | return ({ title, children, href }) => {
50 | const { location } = this.props;
51 | const open =
52 | children && children.length > 0
53 | ? children.some(item => item.href === location.pathname)
54 | : false;
55 |
56 | return (
57 |
58 | {children && children.length > 0 && children.map(this.mapNavigation(depth + 1))}
59 |
60 | );
61 | };
62 | }
63 |
64 | render() {
65 | return {navItems.map(this.mapNavigation(0))}
;
66 | }
67 | }
68 |
69 | NavigationMenu.propTypes = {
70 | location: PropTypes.object.isRequired,
71 | };
72 |
73 | export default withRouter(NavigationMenu);
74 |
--------------------------------------------------------------------------------
/docs/src/Pages/Localization/Persian/PersianCalendar.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import moment from 'moment';
3 | import jMoment from 'moment-jalaali';
4 | import { Typography } from '@material-ui/core';
5 | import {
6 | TimePicker,
7 | DateTimePicker,
8 | DatePicker,
9 | MuiPickersUtilsProvider,
10 | } from 'material-ui-pickers';
11 | import JalaliUtils from '@date-io/jalaali';
12 |
13 | jMoment.loadPersian({ dialect: 'persian-modern', usePersianDigits: true });
14 |
15 | export default class BasicUsage extends Component {
16 | state = {
17 | selectedDate: moment(),
18 | };
19 |
20 | handleDateChange = date => {
21 | this.setState({ selectedDate: date });
22 | };
23 |
24 | render() {
25 | const { selectedDate } = this.state;
26 |
27 | return (
28 |
29 |
30 |
31 | Date picker
32 |
33 |
34 | (date ? date.format('jYYYY/jMM/jDD') : '')}
40 | value={selectedDate}
41 | onChange={this.handleDateChange}
42 | animateYearScrolling={false}
43 | />
44 |
45 |
46 |
47 |
48 | Time picker
49 |
50 |
51 | (date ? date.format('hh:mm A') : '')}
57 | value={selectedDate}
58 | onChange={this.handleDateChange}
59 | />
60 |
61 |
62 |
63 |
64 | DateTime picker
65 |
66 |
67 | (date ? date.format('jYYYY/jMM/jDD hh:mm A') : '')}
71 | value={selectedDate}
72 | onChange={this.handleDateChange}
73 | />
74 |
75 |
76 | );
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/src/__tests__/e2e/DateTimePickerInline.test.tsx:
--------------------------------------------------------------------------------
1 | import { PopoverProps } from '@material-ui/core/Popover';
2 | import { ReactWrapper } from 'enzyme';
3 | import * as React from 'react';
4 | import DateTimePickerInline, {
5 | DateTimePickerInlineProps,
6 | } from '../../DateTimePicker/DateTimePickerInline';
7 | import { mount, utilsToUse } from '../test-utils';
8 |
9 | describe('e2e - DateTimePickerInline', () => {
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 |
24 | );
25 | });
26 |
27 | it('Should renders', () => {
28 | expect(component).toBeTruthy();
29 | });
30 |
31 | it('Should open modal with picker on click', () => {
32 | component.find('input').simulate('click');
33 | expect(component.find('WithStyles(Popover)').props().open).toBeTruthy();
34 | expect(onOpenMock).toHaveBeenCalled();
35 | });
36 |
37 | it('Should close on popover close request', () => {
38 | const popoverOnClose = (component.find('WithStyles(Popover)').props() as PopoverProps).onClose;
39 | if (!popoverOnClose) {
40 | throw new Error('expected popoverOnClose');
41 | }
42 |
43 | popoverOnClose({} as any);
44 | expect(component.find('WithStyles(Popover)').props().open).toBeFalsy();
45 | expect(onCloseMock).toHaveBeenCalled();
46 | });
47 |
48 | it('Should get the full workflow for datetime picker', () => {
49 | // Date
50 | component.find('input').simulate('click');
51 | component
52 | .find('Day button')
53 | .at(10)
54 | .simulate('click');
55 |
56 | expect(component.find('Clock').prop('type')).toBe('hours');
57 |
58 | // Hour
59 | component
60 | .find('Clock div[role="menu"]')
61 | .simulate('mouseUp', { nativeEvent: { offsetX: 10, offsetY: 20 } });
62 |
63 | expect(component.find('Clock').prop('type')).toBe('minutes');
64 |
65 | // Minutes
66 | component
67 | .find('Clock div[role="menu"]')
68 | .simulate('mouseUp', { nativeEvent: { offsetX: 10, offsetY: 20 } });
69 |
70 | expect(onChangeMock).toHaveBeenCalled();
71 | expect(onCloseMock).toHaveBeenCalled();
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/lib/src/__tests__/e2e/DatePickerInline.test.tsx:
--------------------------------------------------------------------------------
1 | import { PopoverProps } from '@material-ui/core/Popover';
2 | import { ReactWrapper } from 'enzyme';
3 | import * as React from 'react';
4 | import DatePickerInline, { DatePickerInlineProps } from '../../DatePicker/DatePickerInline';
5 | import { mount, utilsToUse } from '../test-utils';
6 |
7 | describe('e2e - DatePickerInline', () => {
8 | let component: ReactWrapper;
9 | const onChangeMock = jest.fn();
10 | const onCloseMock = jest.fn();
11 | const onOpenMock = jest.fn();
12 |
13 | beforeEach(() => {
14 | jest.clearAllMocks();
15 | component = mount(
16 |
23 | );
24 | });
25 |
26 | it('Should renders', () => {
27 | expect(component).toBeTruthy();
28 | });
29 |
30 | it('Should open modal with picker on click', () => {
31 | component.find('input').simulate('click');
32 | expect(component.find('WithStyles(Popover)').props().open).toBeTruthy();
33 | expect(onOpenMock).toHaveBeenCalled();
34 | });
35 |
36 | it('Should close on popover close request', () => {
37 | const popoverOnClose = (component.find('WithStyles(Popover)').props() as PopoverProps).onClose;
38 | if (!popoverOnClose) {
39 | throw new Error('expected popoverOnClose');
40 | }
41 | popoverOnClose({} as any);
42 | expect(component.find('WithStyles(Popover)').props().open).toBeFalsy();
43 | expect(onCloseMock).toHaveBeenCalled();
44 | });
45 |
46 | it('Should dispatch onChange and close on day select', () => {
47 | component.find('input').simulate('click');
48 | component
49 | .find('Day button')
50 | .at(10)
51 | .simulate('click');
52 |
53 | expect(onChangeMock).toHaveBeenCalled();
54 | expect(component.find('WithStyles(Popover)').props().open).toBeFalsy();
55 | });
56 | });
57 |
58 | describe('e2e - InlineDatePicker onlyCalendar', () => {
59 | it('Should not render toolbar', () => {
60 | const component = mount(
61 |
68 | );
69 |
70 | component.find('input').simulate('click');
71 | expect(component.find('PickerToolbar').length).toBe(0);
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/lib/src/TimePicker/components/ClockNumbers.tsx:
--------------------------------------------------------------------------------
1 | import { IUtils } from '@date-io/core/IUtils';
2 | import * as React from 'react';
3 | import { MaterialUiPickersDate } from '../../typings/date';
4 | import ClockNumber from './ClockNumber';
5 |
6 | export const getHourNumbers = ({
7 | ampm,
8 | utils,
9 | date,
10 | }: {
11 | ampm: boolean;
12 | utils: IUtils;
13 | date: MaterialUiPickersDate;
14 | }) => {
15 | const currentHours = utils.getHours(date);
16 |
17 | const hourNumbers: JSX.Element[] = [];
18 | const startHour = ampm ? 1 : 0;
19 | const endHour = ampm ? 12 : 23;
20 |
21 | const isSelected = (hour: number) => {
22 | if (ampm) {
23 | if (hour === 12) {
24 | return currentHours === 12 || currentHours === 0;
25 | }
26 |
27 | return currentHours === hour || currentHours - 12 === hour;
28 | }
29 |
30 | return currentHours === hour;
31 | };
32 |
33 | for (let hour = startHour; hour <= endHour; hour += 1) {
34 | let label = hour.toString();
35 |
36 | if (hour === 0) {
37 | label = '00';
38 | }
39 |
40 | const props = {
41 | index: hour,
42 | label: utils.formatNumber(label),
43 | selected: isSelected(hour),
44 | isInner: !ampm && (hour === 0 || hour > 12),
45 | };
46 |
47 | hourNumbers.push();
48 | }
49 |
50 | return hourNumbers;
51 | };
52 |
53 | export const getMinutesNumbers = ({
54 | value,
55 | utils,
56 | }: {
57 | value: number;
58 | utils: IUtils;
59 | }) => {
60 | const f = utils.formatNumber;
61 |
62 | return [
63 | ,
64 | ,
65 | ,
66 | ,
67 | ,
68 | ,
69 | ,
70 | ,
71 | ,
72 | ,
73 | ,
74 | ,
75 | ];
76 | };
77 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/components/Year.tsx:
--------------------------------------------------------------------------------
1 | import { Theme } from '@material-ui/core';
2 | import createStyles from '@material-ui/core/styles/createStyles';
3 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
4 | import Typography from '@material-ui/core/Typography';
5 | import clsx from 'clsx';
6 | import * as PropTypes from 'prop-types';
7 | import * as React from 'react';
8 |
9 | export interface YearProps extends WithStyles {
10 | children: React.ReactNode;
11 | disabled?: boolean;
12 | onSelect: (value: any) => void;
13 | selected?: boolean;
14 | value: any;
15 | }
16 |
17 | export class Year extends React.PureComponent {
18 | public static propTypes: any = {
19 | children: PropTypes.node.isRequired,
20 | classes: PropTypes.object.isRequired,
21 | disabled: PropTypes.bool,
22 | onSelect: PropTypes.func.isRequired,
23 | selected: PropTypes.bool,
24 | value: PropTypes.any.isRequired,
25 | innerRef: PropTypes.any,
26 | };
27 |
28 | public static defaultProps = {
29 | selected: false,
30 | disabled: false,
31 | };
32 |
33 | public handleClick = () => {
34 | this.props.onSelect(this.props.value);
35 | };
36 |
37 | public render() {
38 | const { classes, selected, disabled, value, children, ...other } = this.props;
39 |
40 | return (
41 |
56 | );
57 | }
58 | }
59 |
60 | export const styles = (theme: Theme) =>
61 | createStyles({
62 | root: {
63 | height: theme.spacing.unit * 5,
64 | display: 'flex',
65 | alignItems: 'center',
66 | justifyContent: 'center',
67 | cursor: 'pointer',
68 | outline: 'none',
69 | '&:focus': {
70 | color: theme.palette.primary.main,
71 | fontWeight: theme.typography.fontWeightMedium,
72 | },
73 | },
74 | selected: {
75 | margin: '10px 0',
76 | fontWeight: theme.typography.fontWeightMedium,
77 | },
78 | disabled: {
79 | pointerEvents: 'none',
80 | color: theme.palette.text.hint,
81 | },
82 | });
83 |
84 | export default withStyles(styles, { name: 'MuiPickersYear' })(Year);
85 |
--------------------------------------------------------------------------------
/docs/src/App.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Layout from './layout/Layout';
3 | import Router from './Pages/Router';
4 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core';
5 | import DateFnsUtils from '@date-io/date-fns';
6 | import MomentUtils from '@date-io/moment';
7 | import LuxonUtils from '@date-io/luxon';
8 | import DayJsUtils from '@date-io/dayjs';
9 | import { MuiPickersUtilsProvider } from 'material-ui-pickers';
10 | import { UtilsServiceContextProvider } from './_shared/UtilsServiceContext';
11 | import { createUtilsService } from './utils/utilsService';
12 |
13 | export const utilsMap = {
14 | moment: MomentUtils,
15 | luxon: LuxonUtils,
16 | dayjs: DayJsUtils,
17 | 'date-fns': DateFnsUtils,
18 | };
19 |
20 | export type UtilsLib = keyof typeof utilsMap;
21 |
22 | interface AppState {
23 | direction: 'rtl' | 'ltr';
24 | theme: 'light' | 'dark';
25 | utils: UtilsLib;
26 | }
27 |
28 | export const ThemeContext = React.createContext<'light' | 'dark'>('light');
29 |
30 | class App extends React.Component<{}, AppState> {
31 | state: AppState = {
32 | direction: 'ltr',
33 | theme: 'light',
34 | utils: 'date-fns',
35 | };
36 |
37 | toggleDirection = () => {
38 | const newDirection = this.state.direction === 'ltr' ? 'rtl' : 'ltr';
39 |
40 | document.body.dir = newDirection;
41 | this.setState({ direction: newDirection });
42 | };
43 |
44 | toggleTheme = () => {
45 | this.setState({
46 | theme: this.state.theme === 'light' ? 'dark' : 'light',
47 | });
48 | };
49 |
50 | handleChangeUtils = (utils: UtilsLib) => {
51 | this.setState({ utils });
52 | };
53 |
54 | private createMuiTheme = () => {
55 | const { theme, direction } = this.state;
56 |
57 | return createMuiTheme({
58 | direction,
59 | palette: {
60 | type: theme,
61 | },
62 | });
63 | };
64 |
65 | render() {
66 | return (
67 |
68 |
69 |
70 |
71 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | );
83 | }
84 | }
85 |
86 | export default App;
87 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/components/Day.tsx:
--------------------------------------------------------------------------------
1 | import { Theme } from '@material-ui/core';
2 | import IconButton from '@material-ui/core/IconButton';
3 | import createStyles from '@material-ui/core/styles/createStyles';
4 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
5 | import clsx from 'clsx';
6 | import * as PropTypes from 'prop-types';
7 | import * as React from 'react';
8 |
9 | export interface DayProps extends WithStyles {
10 | children: React.ReactNode;
11 | current?: boolean;
12 | disabled?: boolean;
13 | hidden?: boolean;
14 | selected?: boolean;
15 | }
16 |
17 | class Day extends React.PureComponent {
18 | public static propTypes: any = {
19 | children: PropTypes.node.isRequired,
20 | classes: PropTypes.object.isRequired,
21 | current: PropTypes.bool,
22 | disabled: PropTypes.bool,
23 | hidden: PropTypes.bool,
24 | selected: PropTypes.bool,
25 | innerRef: PropTypes.any,
26 | };
27 |
28 | public static defaultProps = {
29 | disabled: false,
30 | hidden: false,
31 | current: false,
32 | selected: false,
33 | };
34 |
35 | public render() {
36 | const { children, classes, disabled, hidden, current, selected, ...other } = this.props;
37 |
38 | const className = clsx(classes.day, {
39 | [classes.hidden]: hidden,
40 | [classes.current]: current,
41 | [classes.isSelected]: selected,
42 | [classes.isDisabled]: disabled,
43 | });
44 |
45 | return (
46 |
47 | {children}
48 |
49 | );
50 | }
51 | }
52 |
53 | export const styles = (theme: Theme) =>
54 | createStyles({
55 | day: {
56 | width: 36,
57 | height: 36,
58 | fontSize: theme.typography.caption.fontSize,
59 | margin: '0 2px',
60 | color: theme.palette.text.primary,
61 | fontWeight: theme.typography.fontWeightMedium,
62 | padding: 0,
63 | },
64 | hidden: {
65 | opacity: 0,
66 | pointerEvents: 'none',
67 | },
68 | current: {
69 | color: theme.palette.primary.main,
70 | fontWeight: 600,
71 | },
72 | isSelected: {
73 | color: theme.palette.common.white,
74 | backgroundColor: theme.palette.primary.main,
75 | fontWeight: theme.typography.fontWeightMedium,
76 | '&:hover': {
77 | backgroundColor: theme.palette.primary.main,
78 | },
79 | },
80 | isDisabled: {
81 | pointerEvents: 'none',
82 | color: theme.palette.text.hint,
83 | },
84 | });
85 |
86 | export default withStyles(styles, { name: 'MuiPickersDay' })(Day as React.ComponentType);
87 |
--------------------------------------------------------------------------------
/lib/src/_helpers/date-utils.ts:
--------------------------------------------------------------------------------
1 | import { IUtils } from '@date-io/core/IUtils';
2 | import { DatePickerViewType } from '../constants/DatePickerView';
3 | import { DateType } from '../constants/prop-types';
4 | import { MaterialUiPickersDate } from '../typings/date';
5 |
6 | interface FindClosestDateParams {
7 | date: MaterialUiPickersDate;
8 | utils: IUtils;
9 | minDate: DateType;
10 | maxDate: DateType;
11 | disableFuture: boolean;
12 | disablePast: boolean;
13 | shouldDisableDate: (date: MaterialUiPickersDate) => boolean;
14 | }
15 |
16 | export const findClosestEnabledDate = ({
17 | date,
18 | utils,
19 | minDate,
20 | maxDate,
21 | disableFuture,
22 | disablePast,
23 | shouldDisableDate,
24 | }: FindClosestDateParams) => {
25 | const today = utils.startOfDay(utils.date());
26 |
27 | minDate = minDate && utils.date(minDate);
28 | maxDate = maxDate && utils.date(maxDate);
29 |
30 | if (disablePast && utils.isBefore(minDate, today)) {
31 | minDate = today;
32 | }
33 |
34 | if (disableFuture && utils.isAfter(maxDate, today)) {
35 | maxDate = today;
36 | }
37 |
38 | let forward = date;
39 | let backward = date;
40 | if (utils.isBefore(date, minDate)) {
41 | forward = utils.date(minDate);
42 | backward = null;
43 | }
44 |
45 | if (utils.isAfter(date, maxDate)) {
46 | if (backward) {
47 | backward = utils.date(maxDate);
48 | }
49 |
50 | forward = null;
51 | }
52 |
53 | while (forward || backward) {
54 | if (forward && utils.isAfter(forward, maxDate)) {
55 | forward = null;
56 | }
57 | if (backward && utils.isBefore(backward, minDate)) {
58 | backward = null;
59 | }
60 |
61 | if (forward) {
62 | if (!shouldDisableDate(forward)) {
63 | return forward;
64 | }
65 | forward = utils.addDays(forward, 1);
66 | }
67 |
68 | if (backward) {
69 | if (!shouldDisableDate(backward)) {
70 | return backward;
71 | }
72 | backward = utils.addDays(backward, -1);
73 | }
74 | }
75 |
76 | return null;
77 | };
78 |
79 | export const isYearOnlyView = (views: DatePickerViewType[]) =>
80 | views.length === 1 && views[0] === 'year';
81 |
82 | export const isYearAndMonthViews = (views: DatePickerViewType[]) =>
83 | views.length === 2 && views.includes('month') && views.includes('year');
84 |
85 | export const getFormatByViews = (
86 | views: DatePickerViewType[],
87 | utils: IUtils
88 | ) => {
89 | if (isYearOnlyView(views)) {
90 | return utils.yearFormat;
91 | }
92 |
93 | if (isYearAndMonthViews(views)) {
94 | return utils.yearMonthFormat;
95 | }
96 |
97 | return utils.dateFormat;
98 | };
99 |
--------------------------------------------------------------------------------
/docs/src/Pages/Localization/Date-fns/DateFnsLocalization.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import DateFnsUtils from '@date-io/date-fns';
3 | import MoreIcon from '@material-ui/icons/MoreVert';
4 | import { IconButton, Menu, MenuItem } from '@material-ui/core';
5 | import { DatePicker, MuiPickersUtilsProvider } from 'material-ui-pickers';
6 |
7 | import frLocale from 'date-fns/locale/fr';
8 | import ruLocale from 'date-fns/locale/ru';
9 | import enLocale from 'date-fns/locale/en-US';
10 |
11 | const localeMap = {
12 | en: enLocale,
13 | fr: frLocale,
14 | ru: ruLocale,
15 | };
16 |
17 | export default class DateFnsLocalizationExample extends PureComponent {
18 | state = {
19 | selectedDate: new Date(),
20 | anchorEl: null,
21 | currentLocale: 'fr',
22 | };
23 |
24 | handleDateChange = date => {
25 | this.setState({ selectedDate: date });
26 | };
27 |
28 | handleMenuOpen = event => {
29 | event.stopPropagation();
30 | this.setState({ anchorEl: event.currentTarget });
31 | };
32 |
33 | handleMenuClose = () => {
34 | this.setState({ anchorEl: null });
35 | };
36 |
37 | selectLocale = selectedLocale => {
38 | this.setState({
39 | currentLocale: selectedLocale,
40 | anchorEl: null,
41 | });
42 | };
43 |
44 | render() {
45 | const { selectedDate } = this.state;
46 | const locale = localeMap[this.state.currentLocale];
47 |
48 | return (
49 |
50 |
51 |
61 |
62 |
63 | ),
64 | }}
65 | />
66 |
67 |
68 |
84 |
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/docs/src/Pages/Localization/Moment/MomentLocalization.example.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import moment from 'moment';
3 | import 'moment/locale/fr';
4 | import 'moment/locale/ru';
5 | import MomentUtils from '@date-io/moment';
6 | import MoreIcon from '@material-ui/icons/MoreVert';
7 | import { IconButton, Menu, MenuItem } from '@material-ui/core';
8 | import { DatePicker, MuiPickersUtilsProvider } from 'material-ui-pickers';
9 |
10 | moment.locale('fr');
11 |
12 | const localeMap = {
13 | en: 'en',
14 | fr: 'fr',
15 | ru: 'ru',
16 | };
17 |
18 | export default class MomentLocalizationExample extends PureComponent {
19 | state = {
20 | selectedDate: new Date(),
21 | anchorEl: null,
22 | currentLocale: 'fr',
23 | };
24 |
25 | handleDateChange = date => {
26 | this.setState({ selectedDate: date.toDate() });
27 | };
28 |
29 | handleMenuOpen = event => {
30 | event.stopPropagation();
31 | this.setState({ anchorEl: event.currentTarget });
32 | };
33 |
34 | handleMenuClose = () => {
35 | this.setState({ anchorEl: null });
36 | };
37 |
38 | selectLocale = selectedLocale => {
39 | moment.locale(selectedLocale);
40 | this.setState({
41 | currentLocale: selectedLocale,
42 | anchorEl: null,
43 | });
44 | };
45 |
46 | render() {
47 | const { selectedDate } = this.state;
48 | const locale = localeMap[this.state.currentLocale];
49 |
50 | return (
51 |
52 |
53 |
63 |
64 |
65 | ),
66 | }}
67 | />
68 |
69 |
70 |
86 |
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/docs/src/_shared/SourcablePanel.jsx:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import Code from './Code';
5 | import { withUtilsService } from './UtilsServiceContext';
6 | import CodeIcon from '@material-ui/icons/Code';
7 | import { Typography, IconButton, withStyles, Collapse } from '@material-ui/core';
8 |
9 | class SourcablePanel extends PureComponent {
10 | static propTypes = {
11 | classes: PropTypes.object.isRequired,
12 | title: PropTypes.string.isRequired,
13 | description: PropTypes.node,
14 | sourceFile: PropTypes.string.isRequired,
15 | };
16 |
17 | state = {
18 | sourceExpanded: false,
19 | };
20 |
21 | getSource = () => require(`!raw-loader!../Pages/${this.props.sourceFile}`);
22 |
23 | getComponent = () => require(`../Pages/${this.props.sourceFile}`).default;
24 |
25 | toggleSource = () => {
26 | this.setState({ sourceExpanded: !this.state.sourceExpanded });
27 | };
28 |
29 | render() {
30 | const { sourceExpanded } = this.state;
31 | const { classes, title, description } = this.props;
32 | // make each component rerender on utils change
33 | const Component = withUtilsService(this.getComponent());
34 |
35 | return (
36 |
37 |
38 | {title}
39 |
40 |
41 | {description}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | const styles = theme => ({
59 | exampleTitle: {
60 | marginTop: '40px',
61 | marginBottom: '20px',
62 | '@media(max-width: 600px)': {
63 | marginLeft: 5,
64 | },
65 | },
66 | pickers: {
67 | display: 'flex',
68 | justifyContent: 'space-around',
69 | alignItems: 'center',
70 | flexWrap: 'wrap',
71 | minHeight: 160,
72 | paddingTop: 40,
73 | width: '100%',
74 | margin: '0 auto 50px',
75 | position: 'relative',
76 | backgroundColor:
77 | theme.palette.type === 'light' ? theme.palette.grey[200] : theme.palette.grey[900],
78 |
79 | [theme.breakpoints.down('sm')]: {
80 | flexDirection: 'column',
81 | alignItems: 'center',
82 |
83 | '& > div': {
84 | marginBottom: 32,
85 | },
86 | },
87 | },
88 | sourceBtn: {
89 | position: 'absolute',
90 | top: 10,
91 | right: 5,
92 | },
93 | source: {
94 | marginBottom: 0,
95 | },
96 | });
97 |
98 | export default withStyles(styles)(SourcablePanel);
99 |
--------------------------------------------------------------------------------
/lib/src/__tests__/DatePicker/Calendar.test.tsx:
--------------------------------------------------------------------------------
1 | import { ShallowWrapper } from 'enzyme';
2 | import * as React from 'react';
3 | import { Calendar, CalendarProps } from '../../DatePicker/components/Calendar';
4 | import { shallowRender, utilsToUse } from '../test-utils';
5 |
6 | describe('Calendar', () => {
7 | let component: ShallowWrapper;
8 |
9 | beforeEach(() => {
10 | component = shallowRender(props => (
11 |
12 | ));
13 | });
14 |
15 | it('Should renders', () => {
16 | // console.log(component.debug());
17 | expect(component).toBeTruthy();
18 | });
19 | });
20 |
21 | describe('Calendar - disabled selected date on mount', () => {
22 | let component: ShallowWrapper;
23 |
24 | beforeEach(() => {
25 | component = shallowRender(props => (
26 |
33 | ));
34 | });
35 |
36 | it('Should dispatch onDateSelect with isFinish = false on mount', () => {
37 | const { onChange } = component.instance().props;
38 | if (process.env.UTILS === 'moment') {
39 | return expect(onChange).toHaveBeenCalled();
40 | }
41 |
42 | expect(onChange).toHaveBeenCalledWith(utilsToUse.date('01-01-2018'), false);
43 | });
44 | });
45 |
46 | describe('Calendar - keyboard control', () => {
47 | let component: ShallowWrapper;
48 | const onChangeMock = jest.fn();
49 |
50 | beforeEach(() => {
51 | component = shallowRender(props => (
52 |
60 | ));
61 | });
62 |
63 | it('Should render go to prev week on up', () => {
64 | component.find('EventListener').simulate('keyDown', { keyCode: 38, preventDefault: jest.fn() });
65 | expect(onChangeMock).toHaveBeenCalled();
66 | });
67 |
68 | it('Should render go to next week on down', () => {
69 | component.find('EventListener').simulate('keyDown', { keyCode: 40, preventDefault: jest.fn() });
70 | expect(onChangeMock).toHaveBeenCalled();
71 | });
72 |
73 | it('Should render go to prev week on up', () => {
74 | component.find('EventListener').simulate('keyDown', { keyCode: 37, preventDefault: jest.fn() });
75 | expect(onChangeMock).toHaveBeenCalled();
76 | });
77 |
78 | it('Should render go to prev week on up', () => {
79 | component.find('EventListener').simulate('keyDown', { keyCode: 39, preventDefault: jest.fn() });
80 | expect(onChangeMock).toHaveBeenCalled();
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/lib/src/DateTimePicker/components/DateTimePickerTabs.tsx:
--------------------------------------------------------------------------------
1 | import * as PropTypes from 'prop-types';
2 | import * as React from 'react';
3 |
4 | import { Theme } from '@material-ui/core';
5 | import Paper from '@material-ui/core/Paper';
6 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
7 | import Tab from '@material-ui/core/Tab';
8 | import Tabs from '@material-ui/core/Tabs';
9 | import { DateRangeIcon } from '../../_shared/icons/DateRangeIcon';
10 | import { TimeIcon } from '../../_shared/icons/TimeIcon';
11 | import DateTimePickerView, { DateTimePickerViewType } from '../../constants/DateTimePickerView';
12 |
13 | const viewToTabIndex = (openView: DateTimePickerViewType) => {
14 | if (openView === DateTimePickerView.DATE || openView === DateTimePickerView.YEAR) {
15 | return 'date';
16 | }
17 |
18 | return 'time';
19 | };
20 |
21 | const tabIndexToView = (tab: DateTimePickerView) => {
22 | if (tab === 'date') {
23 | return DateTimePickerView.DATE;
24 | }
25 |
26 | return DateTimePickerView.HOUR;
27 | };
28 |
29 | export interface DateTimePickerTabsProps extends WithStyles {
30 | view: DateTimePickerViewType;
31 | onChange: (view: DateTimePickerView) => void;
32 | dateRangeIcon: React.ReactNode;
33 | timeIcon: React.ReactNode;
34 | }
35 |
36 | export const DateTimePickerTabs: React.SFC = props => {
37 | const { view, onChange, classes, theme, dateRangeIcon, timeIcon } = props;
38 |
39 | const indicatorColor = theme.palette.type === 'light' ? 'secondary' : 'primary';
40 | const handleChange = (e: React.ChangeEvent<{}>, value: DateTimePickerView) => {
41 | if (value !== viewToTabIndex(view)) {
42 | onChange(tabIndexToView(value));
43 | }
44 | };
45 |
46 | return (
47 |
48 |
55 | {dateRangeIcon}>} />
56 | {timeIcon!}>} />
57 |
58 |
59 | );
60 | };
61 |
62 | (DateTimePickerTabs as any).propTypes = {
63 | view: PropTypes.string.isRequired,
64 | dateRangeIcon: PropTypes.node.isRequired,
65 | timeIcon: PropTypes.node.isRequired,
66 | };
67 |
68 | DateTimePickerTabs.defaultProps = {
69 | dateRangeIcon: ,
70 | timeIcon: ,
71 | };
72 |
73 | export const styles = (theme: Theme) => ({
74 | tabs: {
75 | color: theme.palette.common.white,
76 | backgroundColor:
77 | theme.palette.type === 'light'
78 | ? theme.palette.primary.main
79 | : theme.palette.background.default,
80 | },
81 | });
82 |
83 | export default withStyles(styles, { name: 'MuiPickerDTTabs', withTheme: true })(DateTimePickerTabs);
84 |
--------------------------------------------------------------------------------
/lib/src/typings/overrides.ts:
--------------------------------------------------------------------------------
1 | import { StyleRules, StyleRulesCallback } from '@material-ui/core/styles/withStyles';
2 | import { styles as ModalDialogStyles } from '../_shared/ModalDialog';
3 | import { styles as PickerToolbarStyles } from '../_shared/PickerToolbar';
4 | import { styles as ToolbarButtonStyles } from '../_shared/ToolbarButton';
5 | import { styles as CalendarStyles } from '../DatePicker/components/Calendar';
6 | import { styles as CalendarHeaderStyles } from '../DatePicker/components/CalendarHeader';
7 | import { styles as DayStyles } from '../DatePicker/components/Day';
8 | import { styles as MuiPickersMonthStyles } from '../DatePicker/components/Month';
9 | import { styles as MuiPickersMonthSelectionStyles } from '../DatePicker/components/MonthSelection';
10 | import { styles as SlideTransitionStyles } from '../DatePicker/components/SlideTransition';
11 | import { styles as MuiPickersYearStyles } from '../DatePicker/components/Year';
12 | import { styles as MuiPickersYearSelectionStyles } from '../DatePicker/components/YearSelection';
13 | import { styles as DTHeaderStyles } from '../DateTimePicker/components/DateTimePickerHeader';
14 | import { styles as DTTabsStyles } from '../DateTimePicker/components/DateTimePickerTabs';
15 | import { styles as ClockStyles } from '../TimePicker/components/Clock';
16 | import { styles as ClockNumberStyles } from '../TimePicker/components/ClockNumber';
17 | import { styles as ClockPointerStyles } from '../TimePicker/components/ClockPointer';
18 | import { styles as TimePickerStyles } from '../TimePicker/TimePicker';
19 |
20 | type Classes = Partial<
21 | StyleRules<
22 | T extends string
23 | ? T
24 | : T extends StyleRulesCallback ? K : T extends StyleRules ? D : never
25 | >
26 | >;
27 |
28 | export interface MuiPickersOverrides {
29 | MuiPickersDay?: Classes;
30 | MuiPickerDTHeader?: Classes;
31 | MuiPickerDTTabs?: Classes;
32 | MuiPickersCalendar?: Classes;
33 | MuiPickersCalendarHeader?: Classes;
34 | MuiPickersSlideTransition?: Classes;
35 | MuiPickersYearSelectionStyles?: Classes;
36 | MuiPickersYear?: Classes;
37 | MuiPickersMonthSelection?: Classes;
38 | MuiPickersMonth?: Classes;
39 | MuiPickersTimePicker?: Classes;
40 | MuiPickersClock?: Classes;
41 | MuiPickersClockNumber?: Classes;
42 | MuiPickersClockPointer?: Classes;
43 | MuiPickersModal?: Classes;
44 | MuiPickersToolbar?: Classes;
45 | MuiPickersToolbarButton?: Classes;
46 | }
47 |
--------------------------------------------------------------------------------
/docs/src/layout/NavItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { ListItem, withStyles, Collapse } from '@material-ui/core';
4 |
5 | import Button from '@material-ui/core/Button';
6 | import { NavLink } from 'react-router-dom';
7 |
8 | const styles = theme => ({
9 | listItem: {
10 | display: 'block',
11 | paddingTop: 0,
12 | paddingBottom: 0,
13 | },
14 | bold: {
15 | fontWeight: 500,
16 | },
17 | button: {
18 | justifyContent: 'flex-start',
19 | textTransform: 'none',
20 | width: '100%',
21 | ...theme.typography.body1,
22 | },
23 | selected: {
24 | color: theme.palette.primary.main,
25 | fontWeight: 500,
26 | },
27 | collapse: {
28 | padding: 0,
29 | margin: 0,
30 | },
31 | });
32 |
33 | class NavItem extends React.Component {
34 | constructor(props) {
35 | super(props);
36 | this.state = {
37 | open: this.props.open,
38 | };
39 | }
40 |
41 | handleClick = e => {
42 | if (this.props.depth === 0) {
43 | e.stopPropagation();
44 | }
45 |
46 | this.setState({ open: !this.state.open });
47 | };
48 |
49 | render() {
50 | const { href, title, children, classes, depth, ...props } = this.props;
51 |
52 | const style = { paddingLeft: `${(depth + 1) * 16}px` };
53 |
54 | if (depth === 0) {
55 | style.fontWeight = 500;
56 | }
57 |
58 | if (href) {
59 | return (
60 |
61 |
74 |
75 | );
76 | }
77 |
78 | return (
79 |
80 |
89 |
90 | {children}
91 |
92 |
93 | );
94 | }
95 | }
96 |
97 | NavItem.propTypes = {
98 | classes: PropTypes.object.isRequired,
99 | open: PropTypes.bool.isRequired,
100 | href: PropTypes.string,
101 | title: PropTypes.string.isRequired,
102 | children: PropTypes.arrayOf(PropTypes.object),
103 | depth: PropTypes.number,
104 | };
105 |
106 | NavItem.defaultProps = {
107 | depth: 0,
108 | };
109 |
110 | export default withStyles(styles)(NavItem);
111 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/DatePickerInline.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Omit } from '@material-ui/core';
4 | import BasePicker, { BasePickerProps } from '../_shared/BasePicker';
5 | import { ExtendWrapper } from '../wrappers/ExtendWrapper';
6 | import InlineWrapper, { OuterInlineWrapperProps } from '../wrappers/InlineWrapper';
7 | import Calendar from './components/Calendar';
8 | import DatePicker, { BaseDatePickerProps } from './DatePicker';
9 |
10 | export interface DatePickerInlineProps
11 | extends Omit,
12 | BaseDatePickerProps,
13 | ExtendWrapper {
14 | onlyCalendar?: boolean;
15 | }
16 |
17 | export const DatePickerInline: React.SFC = props => {
18 | const {
19 | allowKeyboardControl,
20 | animateYearScrolling,
21 | disableFuture,
22 | disablePast,
23 | format,
24 | forwardedRef,
25 | labelFunc,
26 | leftArrowIcon,
27 | maxDate,
28 | minDate,
29 | initialFocusedDate,
30 | onChange,
31 | openToYearSelection,
32 | renderDay,
33 | rightArrowIcon,
34 | shouldDisableDate,
35 | value,
36 | autoOk,
37 | onlyCalendar,
38 | ...other
39 | } = props;
40 |
41 | const ComponentToShow: any = onlyCalendar ? Calendar : DatePicker;
42 |
43 | return (
44 |
45 | {({
46 | date,
47 | utils,
48 | isAccepted,
49 | handleChange,
50 | handleClear,
51 | handleTextFieldChange,
52 | handleAccept,
53 | }) => (
54 |
69 |
84 |
85 | )}
86 |
87 | );
88 | };
89 |
90 | DatePickerInline.defaultProps = {
91 | views: ['year', 'day'],
92 | };
93 |
94 | export default React.forwardRef((props: DatePickerInlineProps, ref) => (
95 |
96 | ));
97 |
--------------------------------------------------------------------------------
/docs/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
36 | Material-UI Pickers
37 |
38 |
39 |
42 |
43 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/DatePickerModal.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { getFormatByViews } from '../_helpers/date-utils';
3 | import BasePicker, { BasePickerProps } from '../_shared/BasePicker';
4 | import { ExtendWrapper } from '../wrappers/ExtendWrapper';
5 | import ModalWrapper, { ModalWrapperProps } from '../wrappers/ModalWrapper';
6 | import DatePicker, { BaseDatePickerProps } from './DatePicker';
7 |
8 | export interface DatePickerModalProps
9 | extends BasePickerProps,
10 | BaseDatePickerProps,
11 | ExtendWrapper {}
12 |
13 | export const DatePickerModal: React.SFC = props => {
14 | const {
15 | allowKeyboardControl,
16 | animateYearScrolling,
17 | autoOk,
18 | disableFuture,
19 | disablePast,
20 | format,
21 | forwardedRef,
22 | labelFunc,
23 | leftArrowIcon,
24 | maxDate,
25 | minDate,
26 | initialFocusedDate,
27 | onChange,
28 | openToYearSelection,
29 | renderDay,
30 | rightArrowIcon,
31 | shouldDisableDate,
32 | value,
33 | views,
34 | openTo,
35 | ...other
36 | } = props;
37 |
38 | return (
39 |
40 | {({
41 | date,
42 | utils,
43 | handleAccept,
44 | handleChange,
45 | handleClear,
46 | handleDismiss,
47 | handleSetTodayDate,
48 | handleTextFieldChange,
49 | isAccepted,
50 | }) => (
51 |
68 |
85 |
86 | )}
87 |
88 | );
89 | };
90 |
91 | DatePickerModal.defaultProps = {
92 | views: ['year', 'day'],
93 | };
94 |
95 | export default React.forwardRef((props: DatePickerModalProps, ref) => (
96 |
97 | ));
98 |
--------------------------------------------------------------------------------
/lib/src/DatePicker/components/MonthSelection.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { createStyles, WithStyles, withStyles } from '@material-ui/core/styles';
4 | import { withUtils, WithUtilsProps } from '../../_shared/WithUtils';
5 | import { DateType } from '../../constants/prop-types';
6 | import { MaterialUiPickersDate } from '../../typings/date';
7 | import Month from './Month';
8 |
9 | export interface MonthSelectionProps extends WithUtilsProps, WithStyles {
10 | date: MaterialUiPickersDate;
11 | minDate?: DateType;
12 | maxDate?: DateType;
13 | onChange: (date: MaterialUiPickersDate) => void;
14 | disablePast?: boolean | null | undefined;
15 | disableFuture?: boolean | null | undefined;
16 | }
17 |
18 | export class MonthSelection extends React.PureComponent {
19 | public static defaultProps = {
20 | minDate: new Date('1900-01-01'),
21 | maxDate: new Date('2100-01-01'),
22 | };
23 |
24 | public onMonthSelect = (month: number) => {
25 | const { date, onChange, utils } = this.props;
26 |
27 | const newDate = utils.setMonth(date, month);
28 | onChange(newDate);
29 | };
30 |
31 | public shouldDisableMonth = (month: Date) => {
32 | const { utils, disablePast, disableFuture, minDate, maxDate } = this.props;
33 | const now = utils.date();
34 | const utilMinDate = utils.date(minDate);
35 | const utilMaxDate = utils.date(maxDate);
36 |
37 | const firstEnabledMonth = utils.startOfMonth(
38 | disablePast && utils.isAfter(now, utilMinDate) ? now : utilMinDate
39 | );
40 |
41 | const lastEnabledMonth = utils.startOfMonth(
42 | disableFuture && utils.isBefore(now, utilMaxDate) ? now : utilMaxDate
43 | );
44 |
45 | const isBeforeFirstEnabled = utils.isBefore(month, firstEnabledMonth);
46 | const isAfterLastEnabled = utils.isAfter(month, lastEnabledMonth);
47 |
48 | return isBeforeFirstEnabled || isAfterLastEnabled;
49 | };
50 |
51 | public render() {
52 | const { date, classes, utils } = this.props;
53 | const currentMonth = utils.getMonth(date);
54 |
55 | return (
56 |
57 | {utils.getMonthArray(date).map(month => {
58 | const monthNumber = utils.getMonth(month);
59 | const monthText = utils.format(month, 'MMM');
60 |
61 | return (
62 |
69 | {monthText}
70 |
71 | );
72 | })}
73 |
74 | );
75 | }
76 | }
77 |
78 | export const styles = createStyles({
79 | container: {
80 | width: 310,
81 | display: 'flex',
82 | flexWrap: 'wrap',
83 | alignContent: 'stretch',
84 | },
85 | });
86 |
87 | export default withStyles(styles, { name: 'MuiPickersMonthSelection' })(
88 | withUtils()(MonthSelection)
89 | );
90 |
--------------------------------------------------------------------------------
/lib/src/DateTimePicker/DateTimePickerInline.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import BasePicker, { BasePickerProps } from '../_shared/BasePicker';
4 | import { ExtendWrapper } from '../wrappers/ExtendWrapper';
5 | import InlineWrapper, { OuterInlineWrapperProps } from '../wrappers/InlineWrapper';
6 | import DateTimePicker, { BaseDateTimePickerProps } from './DateTimePicker';
7 |
8 | export interface DateTimePickerInlineProps
9 | extends BasePickerProps,
10 | BaseDateTimePickerProps,
11 | ExtendWrapper {}
12 |
13 | export const DateTimePickerInline: React.SFC = props => {
14 | const {
15 | value,
16 | format,
17 | autoOk,
18 | openTo,
19 | minDate,
20 | maxDate,
21 | initialFocusedDate,
22 | showTabs,
23 | autoSubmit,
24 | disablePast,
25 | disableFuture,
26 | leftArrowIcon,
27 | rightArrowIcon,
28 | dateRangeIcon,
29 | timeIcon,
30 | renderDay,
31 | ampm,
32 | minutesStep,
33 | shouldDisableDate,
34 | animateYearScrolling,
35 | forwardedRef,
36 | allowKeyboardControl,
37 | ...other
38 | } = props;
39 |
40 | return (
41 |
42 | {({
43 | date,
44 | utils,
45 | handleChange,
46 | handleTextFieldChange,
47 | isAccepted,
48 | pick12hOr24hFormat,
49 | handleClear,
50 | handleAccept,
51 | }) => (
52 |
67 |
88 |
89 | )}
90 |
91 | );
92 | };
93 |
94 | export default React.forwardRef((props: DateTimePickerInlineProps, ref) => (
95 |
96 | ));
97 |
--------------------------------------------------------------------------------
/e2e/integration/DatePicker.spec.ts:
--------------------------------------------------------------------------------
1 | const ids = {
2 | basic: '#basic-datepicker',
3 | clearable: '#clearable-datepicker',
4 | keyboard: '#keyboard-datepicker',
5 | maskedKeyboard: '#keyboard-mask-datepicker',
6 | };
7 |
8 | describe('DatePicker', () => {
9 | before(() => {
10 | cy.visit('/regression');
11 | });
12 |
13 | it('Should open date picker on 01-01-2018', () => {
14 | cy.get(`input${ids.basic}`).should('have.value', 'January 1st');
15 |
16 | cy.get(ids.basic).click();
17 | cy.get('[data-day="21/01/2019"]').click();
18 |
19 | cy.get('h4').should('have.text', 'Mon, Jan 21');
20 | });
21 |
22 | it('Should close datepicker and accept value', () => {
23 | cy.get('button')
24 | .contains('OK')
25 | .click();
26 |
27 | cy.get(`input${ids.basic}`).should('have.value', 'January 21st');
28 | cy.get(`input${ids.clearable}`).should('have.value', 'January 21st');
29 | });
30 |
31 | it('Should change the value to the next month', () => {
32 | cy.get(ids.basic).click();
33 | // return back in 2 month
34 | cy.get('svg[data-arrow="left"]')
35 | .click()
36 | .click();
37 | // go to the next month
38 | cy.get('svg[data-arrow="right"]')
39 | .click()
40 | .click()
41 | .click();
42 |
43 | cy.get('[data-day="11/02/2019"]').click();
44 | cy.get('h4').should('have.text', 'Mon, Feb 11');
45 |
46 | cy.get('button')
47 | .contains('OK')
48 | .click();
49 | cy.get(`input${ids.basic}`).should('have.value', 'February 11th');
50 | });
51 |
52 | it('Should clear the input by clear button', () => {
53 | cy.get(ids.clearable).click();
54 |
55 | cy.get('button')
56 | .contains('Clear')
57 | .click();
58 | cy.get(`input${ids.clearable}`).should('have.value', '');
59 | });
60 |
61 | it('Should not accept invalid date format', () => {
62 | cy.get(ids.keyboard).type('Januar');
63 | cy.get(`${ids.keyboard}-helper-text`).should('have.text', 'Invalid Date Format');
64 |
65 | cy.get(ids.keyboard).clear();
66 | });
67 |
68 | it('Should accept date entered from keyboard', () => {
69 | cy.get(ids.keyboard).type('January 27');
70 | cy.get(`${ids.keyboard}-helper-text`).should('not.have.text', 'Invalid Date Format');
71 |
72 | cy.get(ids.keyboard).blur();
73 |
74 | cy.get(`input${ids.basic}`).should('have.value', 'January 27th');
75 | cy.get(`input${ids.clearable}`).should('have.value', 'January 27th');
76 | });
77 |
78 | it('Should accept date entered from keyboard', () => {
79 | cy.get(ids.maskedKeyboard)
80 | .clear()
81 | .type('03/02/2019')
82 | .blur();
83 |
84 | cy.get(`input${ids.basic}`).should('have.value', 'March 2nd');
85 | });
86 |
87 | it('Should open calendar by the keyboard icon', () => {
88 | cy.get('.keyboard-btn')
89 | .first()
90 | .click();
91 | cy.get(`[data-day="19/03/2019"]`).click();
92 | cy.get('button')
93 | .contains('OK')
94 | .click();
95 |
96 | cy.get(ids.keyboard).should('have.value', 'March 19th');
97 | });
98 | });
99 |
--------------------------------------------------------------------------------
/docs/src/Pages/GettingStarted/Installation.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Code from '_shared/Code';
3 | import { Typography } from '@material-ui/core';
4 |
5 | // eslint-disable-next-line
6 | import muiPickerProviderCode from '!raw-loader!./MuiPickersProvider.example.jsx';
7 |
8 | const installLibCode = `npm i @date-io/date-fns date-fns@2.0.0-alpha.25
9 | // or
10 | npm i @date-io/moment moment
11 | // or
12 | npm i -s @date-io/luxon luxon`;
13 |
14 | const Installation = () => (
15 |
16 |
17 | Installation
18 |
19 |
20 | Available as{' '}
21 |
22 | npm package
23 |
24 |
25 |
26 |
27 |
28 |
29 | Peer Library
30 |
31 |
32 | Material-ui-pickers was designed to use that date management library that you need.
33 |
34 |
35 |
36 | We are providing interfaces for{' '}
37 |
38 | moment
39 |
40 | ,{' '}
41 |
42 | date-fns 2{' '}
43 |
44 | and{' '}
45 |
46 | luxon
47 |
48 | P.S. If you are not using moment in the project (or dont have it in the bundle already) we
49 | suggest using date-fns, because it is much more lightweight and will be correctly tree-shaken
50 | from the bundle.
51 |
52 |
53 |
54 | Note, that we support only 2.0.0-alpha versions of date-fns for now.
55 |
56 |
57 |
58 |
59 | Teach pickers how to use one of that library using
60 | MuiPickersUtilsProvider. This component takes an utils
61 | property, and makes it available down the React tree thanks to React context. It should
62 | preferably be used at the root of your component tree.
63 |
64 |
65 |
66 |
67 | Font Icons
68 |
69 |
70 | We are using material-ui-icons icon font to display icons. In order if you can override with a
71 | help of corresponding props. Just add this to your html
72 |
73 |
74 |
79 |
80 | );
81 |
82 | export default Installation;
83 |
--------------------------------------------------------------------------------
/lib/src/TimePicker/components/ClockNumber.tsx:
--------------------------------------------------------------------------------
1 | import { Theme } from '@material-ui/core';
2 | import createStyles from '@material-ui/core/styles/createStyles';
3 | import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
4 | import Typography from '@material-ui/core/Typography';
5 | import clsx from 'clsx';
6 | import * as PropTypes from 'prop-types';
7 | import * as React from 'react';
8 |
9 | const positions = {
10 | 0: [0, 40],
11 | 1: [55, 19.6],
12 | 2: [94.4, 59.5],
13 | 3: [109, 114],
14 | 4: [94.4, 168.5],
15 | 5: [54.5, 208.4],
16 | 6: [0, 223],
17 | 7: [-54.5, 208.4],
18 | 8: [-94.4, 168.5],
19 | 9: [-109, 114],
20 | 10: [-94.4, 59.5],
21 | 11: [-54.5, 19.6],
22 | 12: [0, 5],
23 | 13: [36.9, 49.9],
24 | 14: [64, 77],
25 | 15: [74, 114],
26 | 16: [64, 151],
27 | 17: [37, 178],
28 | 18: [0, 188],
29 | 19: [-37, 178],
30 | 20: [-64, 151],
31 | 21: [-74, 114],
32 | 22: [-64, 77],
33 | 23: [-37, 50],
34 | };
35 |
36 | export interface ClockNumberProps extends WithStyles {
37 | index: number;
38 | label: string;
39 | selected: boolean;
40 | isInner?: boolean;
41 | }
42 |
43 | export class ClockNumber extends React.Component {
44 | public static propTypes: any = {
45 | index: PropTypes.number.isRequired,
46 | label: PropTypes.string.isRequired,
47 | selected: PropTypes.bool.isRequired,
48 | classes: PropTypes.object.isRequired,
49 | isInner: PropTypes.bool,
50 | innerRef: PropTypes.any,
51 | };
52 |
53 | public static defaultProps = {
54 | isInner: false,
55 | };
56 |
57 | public getTransformStyle = (index: number) => {
58 | const position = positions[index];
59 |
60 | return {
61 | transform: `translate(${position[0]}px, ${position[1]}px`,
62 | };
63 | };
64 |
65 | public render() {
66 | const { selected, label, index, classes, isInner } = this.props;
67 |
68 | const className = clsx(classes.clockNumber, {
69 | [classes.selected]: selected,
70 | });
71 |
72 | return (
73 |
79 | {label}
80 |
81 | );
82 | }
83 | }
84 |
85 | export const styles = (theme: Theme) => {
86 | const size = theme.spacing.unit * 4;
87 |
88 | return createStyles({
89 | clockNumber: {
90 | width: size,
91 | height: size,
92 | userSelect: 'none',
93 | position: 'absolute',
94 | left: `calc(50% - ${size / 2}px)`,
95 | display: 'inline-flex',
96 | justifyContent: 'center',
97 | alignItems: 'center',
98 | borderRadius: '50%',
99 | color: theme.palette.type === 'light' ? theme.palette.text.primary : theme.palette.text.hint,
100 | },
101 | selected: {
102 | color: theme.palette.common.white,
103 | },
104 | });
105 | };
106 |
107 | export default withStyles(styles, {
108 | name: 'MuiPickersClockNumber',
109 | })(ClockNumber as React.ComponentType);
110 |
--------------------------------------------------------------------------------