@docsvision/material-ui-pickers 3.2.0-beta.24 → 3.2.0-beta.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.size-snapshot.json +11 -11
  2. package/9db1cba1a61cb58db53f38425bbd1f95.7z +0 -0
  3. package/build/DatePicker/DatePicker.d.ts +2 -2
  4. package/build/DatePicker/DatePicker.js +1 -1
  5. package/build/DateTimePicker/DateTimePicker.js +1 -1
  6. package/build/Picker/Picker.js +1 -1
  7. package/build/TimePicker/TimePicker.js +1 -1
  8. package/build/_helpers/text-field-helper.d.ts +1 -1
  9. package/build/_shared/KeyboardDateInput.js +1 -1
  10. package/build/_shared/ModalDialog.d.ts +2 -2
  11. package/build/_shared/PureDateInput.js +1 -1
  12. package/build/_shared/ToolbarButton.d.ts +2 -2
  13. package/build/_shared/hooks/useKeyboardPickerState.js +2 -2
  14. package/build/constants/prop-types.js +2 -2
  15. package/build/dist/material-ui-pickers.esm.js +12 -12
  16. package/build/dist/material-ui-pickers.esm.js.map +1 -1
  17. package/build/dist/material-ui-pickers.js +62 -62
  18. package/build/dist/material-ui-pickers.js.map +1 -1
  19. package/build/dist/material-ui-pickers.umd.js +5790 -4511
  20. package/build/dist/material-ui-pickers.umd.min.js +1 -1
  21. package/build/package.json +1 -1
  22. package/build/views/Calendar/Calendar.js +2 -2
  23. package/build/views/Clock/Clock.d.ts +1 -1
  24. package/build/views/Clock/ClockPointer.d.ts +1 -1
  25. package/package.json +1 -1
  26. package/src/DatePicker/DatePicker.tsx +91 -0
  27. package/src/DatePicker/DatePickerToolbar.tsx +75 -0
  28. package/src/DatePicker/index.ts +7 -0
  29. package/src/DateTimePicker/DateTimePicker.tsx +72 -0
  30. package/src/DateTimePicker/DateTimePickerTabs.tsx +86 -0
  31. package/src/DateTimePicker/DateTimePickerToolbar.tsx +123 -0
  32. package/src/DateTimePicker/index.ts +7 -0
  33. package/src/MuiPickersUtilsProvider.tsx +39 -0
  34. package/src/Picker/Picker.tsx +275 -0
  35. package/src/Picker/WrappedKeyboardPicker.tsx +136 -0
  36. package/src/Picker/WrappedPurePicker.tsx +128 -0
  37. package/src/Rifm/Rifm.ts +213 -0
  38. package/src/TimePicker/TimePicker.tsx +67 -0
  39. package/src/TimePicker/TimePickerToolbar.tsx +180 -0
  40. package/src/TimePicker/index.tsx +7 -0
  41. package/src/__tests__/DatePicker/Calendar.test.tsx +51 -0
  42. package/src/__tests__/DatePicker/Month.test.tsx +36 -0
  43. package/src/__tests__/DatePicker/MonthSelection.test.tsx +37 -0
  44. package/src/__tests__/MuiPickersUtilsProvider.test.tsx +21 -0
  45. package/src/__tests__/TimePicker/Clock.test.tsx +68 -0
  46. package/src/__tests__/_helpers/date-utils.test.tsx +197 -0
  47. package/src/__tests__/_helpers/text-field-helper.test.tsx +34 -0
  48. package/src/__tests__/_shared/ModalDialog.test.tsx +109 -0
  49. package/src/__tests__/e2e/DatePicker.test.tsx +233 -0
  50. package/src/__tests__/e2e/DatePickerProps.test.tsx +18 -0
  51. package/src/__tests__/e2e/DatePickerRoot.test.tsx +154 -0
  52. package/src/__tests__/e2e/DateTimePicker.test.tsx +86 -0
  53. package/src/__tests__/e2e/DateTimePickerRoot.test.tsx +72 -0
  54. package/src/__tests__/e2e/KeyboardDatePicker.test.tsx +140 -0
  55. package/src/__tests__/e2e/KeyboardDateTimePicker.test.tsx +48 -0
  56. package/src/__tests__/e2e/Theme.test.tsx +30 -0
  57. package/src/__tests__/e2e/TimePicker.test.tsx +121 -0
  58. package/src/__tests__/e2e/commands.tsx +8 -0
  59. package/src/__tests__/setup.js +26 -0
  60. package/src/__tests__/test-utils.tsx +45 -0
  61. package/src/__tests__/tsconfig.json +5 -0
  62. package/src/_helpers/date-utils.ts +92 -0
  63. package/src/_helpers/text-field-helper.ts +239 -0
  64. package/src/_helpers/time-utils.ts +83 -0
  65. package/src/_helpers/utils.ts +8 -0
  66. package/src/_shared/KeyboardDateInput.tsx +161 -0
  67. package/src/_shared/ModalDialog.tsx +114 -0
  68. package/src/_shared/PickerToolbar.tsx +52 -0
  69. package/src/_shared/PureDateInput.tsx +45 -0
  70. package/src/_shared/ToolbarButton.tsx +63 -0
  71. package/src/_shared/ToolbarText.tsx +49 -0
  72. package/src/_shared/WithUtils.tsx +19 -0
  73. package/src/_shared/hooks/useIsLandscape.tsx +27 -0
  74. package/src/_shared/hooks/useKeyDown.ts +34 -0
  75. package/src/_shared/hooks/useKeyboardPickerState.ts +72 -0
  76. package/src/_shared/hooks/useOpenState.ts +22 -0
  77. package/src/_shared/hooks/usePickerState.ts +134 -0
  78. package/src/_shared/hooks/useUtils.ts +20 -0
  79. package/src/_shared/hooks/useViews.tsx +31 -0
  80. package/src/_shared/icons/ArrowLeftIcon.tsx +11 -0
  81. package/src/_shared/icons/ArrowRightIcon.tsx +11 -0
  82. package/src/_shared/icons/DateRangeIcon.tsx +11 -0
  83. package/src/_shared/icons/KeyboardIcon.tsx +11 -0
  84. package/src/_shared/icons/TimeIcon.tsx +12 -0
  85. package/src/constants/ClockType.ts +11 -0
  86. package/src/constants/dimensions.ts +5 -0
  87. package/src/constants/prop-types.ts +37 -0
  88. package/src/index.ts +48 -0
  89. package/src/typings/BasePicker.tsx +60 -0
  90. package/src/typings/date.ts +3 -0
  91. package/src/typings/extendMui.ts +10 -0
  92. package/src/typings/overrides.ts +44 -0
  93. package/src/views/Calendar/Calendar.tsx +361 -0
  94. package/src/views/Calendar/CalendarHeader.tsx +141 -0
  95. package/src/views/Calendar/Day.tsx +97 -0
  96. package/src/views/Calendar/DayWrapper.tsx +33 -0
  97. package/src/views/Calendar/SlideTransition.tsx +100 -0
  98. package/src/views/Clock/Clock.tsx +172 -0
  99. package/src/views/Clock/ClockNumber.tsx +91 -0
  100. package/src/views/Clock/ClockNumbers.tsx +76 -0
  101. package/src/views/Clock/ClockPointer.tsx +104 -0
  102. package/src/views/Clock/ClockView.tsx +105 -0
  103. package/src/views/Month/Month.tsx +76 -0
  104. package/src/views/Month/MonthView.tsx +94 -0
  105. package/src/views/Year/Year.tsx +77 -0
  106. package/src/views/Year/YearView.tsx +91 -0
  107. package/src/wrappers/InlineWrapper.tsx +80 -0
  108. package/src/wrappers/ModalWrapper.tsx +113 -0
  109. package/src/wrappers/StaticWrapper.tsx +22 -0
  110. package/src/wrappers/Wrapper.tsx +75 -0
  111. package/coverage/clover.xml +0 -601
  112. package/coverage/coverage-final.json +0 -22
  113. package/coverage/lcov-report/base.css +0 -224
  114. package/coverage/lcov-report/block-navigation.js +0 -79
  115. package/coverage/lcov-report/index.html +0 -201
  116. package/coverage/lcov-report/prettify.css +0 -1
  117. package/coverage/lcov-report/prettify.js +0 -2
  118. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  119. package/coverage/lcov-report/sorter.js +0 -170
  120. package/coverage/lcov-report/src/MuiPickersUtilsProvider.tsx.html +0 -186
  121. package/coverage/lcov-report/src/_helpers/date-utils.ts.html +0 -345
  122. package/coverage/lcov-report/src/_helpers/index.html +0 -123
  123. package/coverage/lcov-report/src/_helpers/time-utils.ts.html +0 -318
  124. package/coverage/lcov-report/src/_helpers/utils.ts.html +0 -93
  125. package/coverage/lcov-report/src/_shared/ModalDialog.tsx.html +0 -411
  126. package/coverage/lcov-report/src/_shared/WithUtils.tsx.html +0 -126
  127. package/coverage/lcov-report/src/_shared/hooks/index.html +0 -110
  128. package/coverage/lcov-report/src/_shared/hooks/useKeyDown.ts.html +0 -171
  129. package/coverage/lcov-report/src/_shared/hooks/useUtils.ts.html +0 -129
  130. package/coverage/lcov-report/src/_shared/icons/ArrowLeftIcon.tsx.html +0 -102
  131. package/coverage/lcov-report/src/_shared/icons/ArrowRightIcon.tsx.html +0 -102
  132. package/coverage/lcov-report/src/_shared/icons/index.html +0 -110
  133. package/coverage/lcov-report/src/_shared/index.html +0 -110
  134. package/coverage/lcov-report/src/constants/ClockType.ts.html +0 -102
  135. package/coverage/lcov-report/src/constants/dimensions.ts.html +0 -84
  136. package/coverage/lcov-report/src/constants/index.html +0 -110
  137. package/coverage/lcov-report/src/index.html +0 -97
  138. package/coverage/lcov-report/src/views/Calendar/Calendar.tsx.html +0 -1152
  139. package/coverage/lcov-report/src/views/Calendar/CalendarHeader.tsx.html +0 -492
  140. package/coverage/lcov-report/src/views/Calendar/Day.tsx.html +0 -360
  141. package/coverage/lcov-report/src/views/Calendar/DayWrapper.tsx.html +0 -168
  142. package/coverage/lcov-report/src/views/Calendar/SlideTransition.tsx.html +0 -369
  143. package/coverage/lcov-report/src/views/Calendar/index.html +0 -149
  144. package/coverage/lcov-report/src/views/Clock/Clock.tsx.html +0 -585
  145. package/coverage/lcov-report/src/views/Clock/ClockPointer.tsx.html +0 -381
  146. package/coverage/lcov-report/src/views/Clock/index.html +0 -110
  147. package/coverage/lcov-report/src/views/Month/Month.tsx.html +0 -297
  148. package/coverage/lcov-report/src/views/Month/MonthView.tsx.html +0 -351
  149. package/coverage/lcov-report/src/views/Month/index.html +0 -110
  150. package/coverage/lcov.info +0 -1094
@@ -0,0 +1,114 @@
1
+ import * as React from 'react';
2
+ import clsx from 'clsx';
3
+ import Button from '@material-ui/core/Button';
4
+ import DialogActions from '@material-ui/core/DialogActions';
5
+ import DialogContent from '@material-ui/core/DialogContent';
6
+ import Dialog, { DialogProps } from '@material-ui/core/Dialog';
7
+ import { DIALOG_WIDTH } from '../constants/dimensions';
8
+ import { createStyles, WithStyles, withStyles } from '@material-ui/core/styles';
9
+
10
+ export interface ModalDialogProps extends DialogProps {
11
+ onAccept: () => void;
12
+ onDismiss: () => void;
13
+ onClear: () => void;
14
+ onSetToday: () => void;
15
+ okLabel?: React.ReactNode;
16
+ cancelLabel?: React.ReactNode;
17
+ clearLabel?: React.ReactNode;
18
+ todayLabel?: React.ReactNode;
19
+ clearable?: boolean;
20
+ showTodayButton?: boolean;
21
+ showTabs?: boolean;
22
+ wider?: boolean;
23
+ }
24
+
25
+ export const ModalDialog: React.SFC<ModalDialogProps & WithStyles<typeof styles>> = ({
26
+ children,
27
+ classes,
28
+ onAccept,
29
+ onDismiss,
30
+ onClear,
31
+ onSetToday,
32
+ okLabel,
33
+ cancelLabel,
34
+ clearLabel,
35
+ todayLabel,
36
+ clearable,
37
+ showTodayButton,
38
+ showTabs,
39
+ wider,
40
+ ...other
41
+ }) => (
42
+ <Dialog
43
+ role="dialog"
44
+ onClose={onDismiss}
45
+ classes={{
46
+ paper: clsx(classes.dialogRoot, {
47
+ [classes.dialogRootWider]: wider,
48
+ }),
49
+ }}
50
+ {...other}
51
+ >
52
+ <DialogContent children={children} className={classes.dialog} />
53
+
54
+ <DialogActions
55
+ classes={{
56
+ root: clsx({
57
+ [classes.withAdditionalAction]: clearable || showTodayButton,
58
+ }),
59
+ }}
60
+ >
61
+ {clearable && (
62
+ <Button color="primary" onClick={onClear}>
63
+ {clearLabel}
64
+ </Button>
65
+ )}
66
+
67
+ {!clearable && showTodayButton && (
68
+ <Button color="primary" onClick={onSetToday}>
69
+ {todayLabel}
70
+ </Button>
71
+ )}
72
+
73
+ {cancelLabel && (
74
+ <Button color="primary" onClick={onDismiss}>
75
+ {cancelLabel}
76
+ </Button>
77
+ )}
78
+
79
+ {okLabel && (
80
+ <Button color="primary" onClick={onAccept}>
81
+ {okLabel}
82
+ </Button>
83
+ )}
84
+ </DialogActions>
85
+ </Dialog>
86
+ );
87
+
88
+ ModalDialog.displayName = 'ModalDialog';
89
+
90
+ export const styles = createStyles({
91
+ dialogRoot: {
92
+ minWidth: DIALOG_WIDTH,
93
+ // maxWidth: DIALOG_WIDTH_WIDER,
94
+ },
95
+ dialogRootWider: {
96
+ // minWidth: DIALOG_WIDTH_WIDER,
97
+ },
98
+ dialog: {
99
+ '&:first-child': {
100
+ padding: 0,
101
+ },
102
+ },
103
+ withAdditionalAction: {
104
+ // set justifyContent to default value to fix IE11 layout bug
105
+ // see https://github.com/dmtrKovalenko/material-ui-pickers/pull/267
106
+ justifyContent: 'flex-start',
107
+
108
+ '& > *:first-child': {
109
+ marginRight: 'auto',
110
+ },
111
+ },
112
+ });
113
+
114
+ export default withStyles(styles, { name: 'MuiPickersModal' })(ModalDialog);
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import clsx from 'clsx';
3
+ import Toolbar, { ToolbarProps } from '@material-ui/core/Toolbar';
4
+ import { ExtendMui } from '../typings/extendMui';
5
+ import { makeStyles } from '@material-ui/core/styles';
6
+
7
+ export const useStyles = makeStyles(
8
+ theme => ({
9
+ toolbar: {
10
+ display: 'flex',
11
+ flexDirection: 'row',
12
+ alignItems: 'center',
13
+ justifyContent: 'center',
14
+ height: 100,
15
+ backgroundColor:
16
+ theme.palette.type === 'light'
17
+ ? theme.palette.primary.main
18
+ : theme.palette.background.default,
19
+ },
20
+ toolbarLandscape: {
21
+ height: 'auto',
22
+ maxWidth: 150,
23
+ padding: 8,
24
+ justifyContent: 'flex-start',
25
+ },
26
+ }),
27
+ { name: 'MuiPickersToolbar' }
28
+ );
29
+
30
+ interface PickerToolbarProps extends ExtendMui<ToolbarProps> {
31
+ isLandscape: boolean;
32
+ }
33
+
34
+ const PickerToolbar: React.SFC<PickerToolbarProps> = ({
35
+ children,
36
+ isLandscape,
37
+ className = null,
38
+ ...other
39
+ }) => {
40
+ const classes = useStyles();
41
+
42
+ return (
43
+ <Toolbar
44
+ className={clsx(classes.toolbar, { [classes.toolbarLandscape]: isLandscape }, className)}
45
+ {...other}
46
+ >
47
+ {children}
48
+ </Toolbar>
49
+ );
50
+ };
51
+
52
+ export default PickerToolbar;
@@ -0,0 +1,45 @@
1
+ import * as React from 'react';
2
+ import TextField, { BaseTextFieldProps, TextFieldProps } from '@material-ui/core/TextField';
3
+ import { ExtendMui } from '../typings/extendMui';
4
+
5
+ export interface PureDateInputProps
6
+ extends ExtendMui<BaseTextFieldProps, 'variant' | 'onError' | 'onChange' | 'value'> {
7
+ /** Pass material-ui text field variant down, bypass internal variant prop */
8
+ inputVariant?: TextFieldProps['variant'];
9
+ /** Override input component */
10
+ TextFieldComponent?: React.ComponentType<TextFieldProps>;
11
+ InputProps?: TextFieldProps['InputProps'];
12
+ inputValue: string;
13
+ validationError?: React.ReactNode;
14
+ }
15
+
16
+ export const PureDateInput: React.FC<PureDateInputProps> = ({
17
+ inputValue,
18
+ inputVariant,
19
+ validationError,
20
+ InputProps,
21
+ TextFieldComponent = TextField,
22
+ ...other
23
+ }) => {
24
+ const PureDateInputProps = React.useMemo(
25
+ () => ({
26
+ ...InputProps,
27
+ readOnly: true,
28
+ }),
29
+ [InputProps]
30
+ );
31
+
32
+ return (
33
+ <TextFieldComponent
34
+ error={Boolean(validationError)}
35
+ helperText={validationError}
36
+ {...other}
37
+ // do not overridable
38
+ value={inputValue}
39
+ variant={inputVariant as any}
40
+ InputProps={PureDateInputProps}
41
+ />
42
+ );
43
+ };
44
+
45
+ PureDateInput.displayName = 'PureDateInput';
@@ -0,0 +1,63 @@
1
+ import * as React from 'react';
2
+ import * as PropTypes from 'prop-types';
3
+ import clsx from 'clsx';
4
+ import ToolbarText from './ToolbarText';
5
+ import Button, { ButtonProps } from '@material-ui/core/Button';
6
+ import { ExtendMui } from '../typings/extendMui';
7
+ import { TypographyProps } from '@material-ui/core/Typography';
8
+ import { createStyles, withStyles, WithStyles } from '@material-ui/core/styles';
9
+
10
+ export interface ToolbarButtonProps
11
+ extends ExtendMui<ButtonProps, 'variant'>,
12
+ WithStyles<typeof styles> {
13
+ variant: TypographyProps['variant'];
14
+ selected: boolean;
15
+ label: string;
16
+ align?: TypographyProps['align'];
17
+ typographyClassName?: string;
18
+ }
19
+
20
+ const ToolbarButton: React.FunctionComponent<ToolbarButtonProps> = ({
21
+ classes,
22
+ className = null,
23
+ label,
24
+ selected,
25
+ variant,
26
+ align,
27
+ typographyClassName,
28
+ ...other
29
+ }) => {
30
+ return (
31
+ <Button variant="text" className={clsx(classes.toolbarBtn, className)} {...other}>
32
+ <ToolbarText
33
+ align={align}
34
+ className={typographyClassName}
35
+ variant={variant}
36
+ label={label}
37
+ selected={selected}
38
+ />
39
+ </Button>
40
+ );
41
+ };
42
+
43
+ (ToolbarButton as any).propTypes = {
44
+ selected: PropTypes.bool.isRequired,
45
+ label: PropTypes.string.isRequired,
46
+ classes: PropTypes.any.isRequired,
47
+ className: PropTypes.string,
48
+ innerRef: PropTypes.any,
49
+ };
50
+
51
+ ToolbarButton.defaultProps = {
52
+ className: '',
53
+ };
54
+
55
+ export const styles = createStyles({
56
+ toolbarBtn: {
57
+ padding: 0,
58
+ minWidth: '16px',
59
+ textTransform: 'none',
60
+ },
61
+ });
62
+
63
+ export default withStyles(styles, { name: 'MuiPickersToolbarButton' })(ToolbarButton);
@@ -0,0 +1,49 @@
1
+ import * as React from 'react';
2
+ import clsx from 'clsx';
3
+ import Typography, { TypographyProps } from '@material-ui/core/Typography';
4
+ import { ExtendMui } from '../typings/extendMui';
5
+ import { makeStyles, fade } from '@material-ui/core/styles';
6
+
7
+ export interface ToolbarTextProps extends ExtendMui<TypographyProps> {
8
+ selected?: boolean;
9
+ label: string;
10
+ }
11
+
12
+ export const useStyles = makeStyles(
13
+ theme => {
14
+ const textColor =
15
+ theme.palette.type === 'light'
16
+ ? theme.palette.primary.contrastText
17
+ : theme.palette.getContrastText(theme.palette.background.default);
18
+
19
+ return {
20
+ toolbarTxt: {
21
+ color: fade(textColor, 0.54),
22
+ },
23
+ toolbarBtnSelected: {
24
+ color: textColor,
25
+ },
26
+ };
27
+ },
28
+ { name: 'MuiPickersToolbarText' }
29
+ );
30
+
31
+ const ToolbarText: React.FunctionComponent<ToolbarTextProps> = ({
32
+ selected,
33
+ label,
34
+ className = null,
35
+ ...other
36
+ }) => {
37
+ const classes = useStyles();
38
+ return (
39
+ <Typography
40
+ children={label}
41
+ className={clsx(classes.toolbarTxt, className, {
42
+ [classes.toolbarBtnSelected]: selected,
43
+ })}
44
+ {...other}
45
+ />
46
+ );
47
+ };
48
+
49
+ export default ToolbarText;
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import { Omit } from '@material-ui/core';
3
+ import { useUtils } from './hooks/useUtils';
4
+ import { IUtils } from '@date-io/core/IUtils';
5
+ import { MaterialUiPickersDate } from '../typings/date';
6
+
7
+ export interface WithUtilsProps {
8
+ utils: IUtils<MaterialUiPickersDate>;
9
+ }
10
+
11
+ export const withUtils = () => <P extends WithUtilsProps>(Component: React.ComponentType<P>) => {
12
+ const WithUtils: React.SFC<Omit<P, keyof WithUtilsProps>> = props => {
13
+ const utils = useUtils();
14
+ return <Component utils={utils} {...(props as any)} />;
15
+ };
16
+
17
+ WithUtils.displayName = `WithUtils(${Component.displayName || Component.name})`;
18
+ return WithUtils;
19
+ };
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { useIsomorphicEffect } from './useKeyDown';
3
+ import { BasePickerProps } from '../../typings/BasePicker';
4
+
5
+ const getOrientation = () =>
6
+ typeof window !== 'undefined' &&
7
+ window.screen &&
8
+ window.screen.orientation &&
9
+ Math.abs(window.screen.orientation.angle) === 90
10
+ ? 'landscape'
11
+ : 'portrait';
12
+
13
+ export function useIsLandscape(customOrientation?: BasePickerProps['orientation']) {
14
+ const [orientation, setOrientation] = React.useState<BasePickerProps['orientation']>(
15
+ getOrientation()
16
+ );
17
+
18
+ const eventHandler = React.useCallback(() => setOrientation(getOrientation()), []);
19
+
20
+ useIsomorphicEffect(() => {
21
+ window.addEventListener('orientationchange', eventHandler);
22
+ return () => window.removeEventListener('orientationchange', eventHandler);
23
+ }, [eventHandler]);
24
+
25
+ const orientationToUse = customOrientation || orientation;
26
+ return orientationToUse === 'landscape';
27
+ }
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+
3
+ export const useIsomorphicEffect =
4
+ typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
5
+
6
+ type KeyHandlers = Record<KeyboardEvent['key'], () => void>;
7
+
8
+ export function runKeyHandler(e: KeyboardEvent, keyHandlers: KeyHandlers) {
9
+ const handler = keyHandlers[e.key];
10
+ if (handler) {
11
+ handler();
12
+ // if event was handled prevent other side effects (e.g. page scroll)
13
+ e.preventDefault();
14
+ }
15
+ }
16
+
17
+ export function useKeyDown(active: boolean, keyHandlers: KeyHandlers) {
18
+ const keyHandlersRef = React.useRef(keyHandlers);
19
+ useIsomorphicEffect(() => {
20
+ keyHandlersRef.current = keyHandlers;
21
+ });
22
+
23
+ React.useEffect(() => {
24
+ if (active) {
25
+ const handleKeyDown = (event: KeyboardEvent) => {
26
+ runKeyHandler(event, keyHandlers);
27
+ };
28
+ window.addEventListener('keydown', handleKeyDown);
29
+ return () => {
30
+ window.removeEventListener('keydown', handleKeyDown);
31
+ };
32
+ }
33
+ }, [active, keyHandlers]);
34
+ }
@@ -0,0 +1,72 @@
1
+ import { useUtils } from './useUtils';
2
+ import { Omit } from '@material-ui/core';
3
+ import { IUtils } from '@date-io/core/IUtils';
4
+ import { BasePickerProps } from '../../typings/BasePicker';
5
+ import { MaterialUiPickersDate } from '../../typings/date';
6
+ import { useCallback, useEffect, useMemo, useState } from 'react';
7
+ import { getDisplayDate } from '../../_helpers/text-field-helper';
8
+ import { StateHookOptions, usePickerState } from './usePickerState';
9
+
10
+ export interface BaseKeyboardPickerProps extends Omit<BasePickerProps, 'onChange'> {
11
+ /** String value for controlling value with pure input string. Overrides value prop */
12
+ inputValue?: string;
13
+ /** Keyboard onChange callback */
14
+ onChange: (date: MaterialUiPickersDate | null, value?: string | null) => void;
15
+ }
16
+
17
+ function parseInputString(value: string, utils: IUtils<any>, format: string) {
18
+ try {
19
+ return utils.parse(value, format);
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+
25
+ export function useKeyboardPickerState(props: BaseKeyboardPickerProps, options: StateHookOptions) {
26
+ const { format = options.getDefaultFormat(), inputValue, onChange, value } = props;
27
+ const utils = useUtils();
28
+
29
+ const displayDate = getDisplayDate(value, format, utils, value === null, props);
30
+ const [innerInputValue, setInnerInputValue] = useState(displayDate);
31
+ const dateValue = inputValue ? parseInputString(inputValue, utils, format) : value;
32
+
33
+ useEffect(() => {
34
+ if (value === null || utils.isValid(value)) {
35
+ setInnerInputValue(displayDate);
36
+ }
37
+ }, [displayDate, setInnerInputValue, utils, value]);
38
+
39
+ const handleKeyboardChange = useCallback(
40
+ (date: MaterialUiPickersDate) => {
41
+ onChange(date, date === null ? null : utils.format(date, format));
42
+ },
43
+ [format, onChange, utils]
44
+ );
45
+
46
+ const { inputProps: innerInputProps, wrapperProps, pickerProps } = usePickerState(
47
+ // Extend props interface
48
+ { ...props, value: dateValue, onChange: handleKeyboardChange },
49
+ options
50
+ );
51
+
52
+ const inputProps = useMemo(
53
+ () => ({
54
+ ...innerInputProps, // reuse validation and open/close logic
55
+ format: wrapperProps.format,
56
+ inputValue: inputValue || innerInputValue,
57
+ onChange: (value: string | null) => {
58
+ setInnerInputValue(value || '');
59
+ const date = value === null ? null : utils.parse(value, wrapperProps.format);
60
+
61
+ onChange(date, value);
62
+ },
63
+ }),
64
+ [innerInputProps, innerInputValue, inputValue, onChange, utils, wrapperProps.format]
65
+ );
66
+
67
+ return {
68
+ inputProps,
69
+ wrapperProps,
70
+ pickerProps,
71
+ };
72
+ }
@@ -0,0 +1,22 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import { BasePickerProps } from '../../typings/BasePicker';
3
+ import { useCallback, useState, Dispatch, SetStateAction } from 'react';
4
+
5
+ export function useOpenState({ open, onOpen, onClose }: BasePickerProps) {
6
+ let setIsOpenState: null | Dispatch<SetStateAction<boolean>> = null;
7
+ if (open === undefined || open === null) {
8
+ // The component is uncontrolled, so we need to give it its own state.
9
+ [open, setIsOpenState] = useState<boolean>(false);
10
+ }
11
+
12
+ // prettier-ignore
13
+ const setIsOpen = useCallback((newIsOpen: boolean) => {
14
+ setIsOpenState && setIsOpenState(newIsOpen);
15
+
16
+ return newIsOpen
17
+ ? onOpen && onOpen()
18
+ : onClose && onClose();
19
+ }, [onOpen, onClose, setIsOpenState]);
20
+
21
+ return { isOpen: open, setIsOpen };
22
+ }
@@ -0,0 +1,134 @@
1
+ import { useUtils } from './useUtils';
2
+ import { IUtils } from '@date-io/core/IUtils';
3
+ import { MaterialUiPickersDate } from '../..';
4
+ import { useOpenState } from './useOpenState';
5
+ import { BasePickerProps } from '../../typings/BasePicker';
6
+ import { getDisplayDate, validate } from '../../_helpers/text-field-helper';
7
+ import { useCallback, useDebugValue, useEffect, useMemo, useState, useRef } from 'react';
8
+
9
+ export interface StateHookOptions {
10
+ getDefaultFormat: () => string;
11
+ }
12
+
13
+ const useValueToDate = (
14
+ utils: IUtils<MaterialUiPickersDate>,
15
+ { value, initialFocusedDate }: BasePickerProps
16
+ ) => {
17
+ const nowRef = useRef(utils.date());
18
+ const date = utils.date(value || initialFocusedDate || nowRef.current);
19
+
20
+ return date && utils.isValid(date) ? date : nowRef.current;
21
+ };
22
+
23
+ function useDateValues(props: BasePickerProps, options: StateHookOptions) {
24
+ const utils = useUtils();
25
+ const date = useValueToDate(utils, props);
26
+ const format = props.format || options.getDefaultFormat();
27
+
28
+ return { date, format };
29
+ }
30
+
31
+ export function usePickerState(props: BasePickerProps, options: StateHookOptions) {
32
+ const { autoOk, disabled, onAccept, onChange, onError, value, variant } = props;
33
+
34
+ const utils = useUtils();
35
+ const { isOpen, setIsOpen } = useOpenState(props);
36
+ const { date, format } = useDateValues(props, options);
37
+ const [pickerDate, setPickerDate] = useState(date);
38
+
39
+ useEffect(() => {
40
+ // if value was changed in closed state - treat it as accepted
41
+ if (!isOpen && !utils.isEqual(pickerDate, date)) {
42
+ setPickerDate(date);
43
+ }
44
+ }, [date, isOpen, pickerDate, utils]);
45
+
46
+ const acceptDate = useCallback(
47
+ (acceptedDate: MaterialUiPickersDate) => {
48
+ setIsOpen(false);
49
+ onChange(acceptedDate);
50
+
51
+ if (onAccept) {
52
+ onAccept(acceptedDate);
53
+ }
54
+ },
55
+ [onAccept, onChange, setIsOpen]
56
+ );
57
+
58
+ const wrapperProps = useMemo(
59
+ () => ({
60
+ format,
61
+ open: isOpen,
62
+ onClear: () => acceptDate(null),
63
+ onAccept: () => acceptDate(pickerDate),
64
+ onSetToday: () => setPickerDate(utils.date()),
65
+ onDismiss: () => {
66
+ setIsOpen(false);
67
+ },
68
+ }),
69
+ [acceptDate, format, isOpen, pickerDate, setIsOpen, utils]
70
+ );
71
+
72
+ const pickerProps = useMemo(
73
+ () => ({
74
+ date: pickerDate,
75
+ onChange: (newDate: MaterialUiPickersDate, isFinish = true) => {
76
+ setPickerDate(newDate);
77
+
78
+ if (variant === 'inline' || variant === 'static') {
79
+ onChange(newDate);
80
+ }
81
+
82
+ if (isFinish && autoOk) {
83
+ acceptDate(newDate);
84
+ }
85
+ },
86
+ onSetToday: () => {
87
+ let newDate = utils.date();
88
+ setPickerDate(newDate);
89
+
90
+ if (variant === 'inline' || variant === 'static') {
91
+ onChange(newDate);
92
+ }
93
+
94
+ if (autoOk) {
95
+ acceptDate(newDate);
96
+ }
97
+ },
98
+ onSetShift: (shiftDate: Date) => {
99
+ let newDate = utils.date(shiftDate);
100
+
101
+ if (variant === 'inline' || variant === 'static') {
102
+ onChange(newDate);
103
+ }
104
+
105
+ if (autoOk) {
106
+ acceptDate(newDate);
107
+ }
108
+ },
109
+ }),
110
+ [acceptDate, autoOk, onChange, pickerDate, variant]
111
+ );
112
+
113
+ const validationError = validate(value, utils, props);
114
+ useEffect(() => {
115
+ if (validationError && onError) {
116
+ onError(validationError, value);
117
+ }
118
+ }, [onError, validationError, value]);
119
+
120
+ const inputValue = getDisplayDate(date, format, utils, value === null, props);
121
+ const inputProps = useMemo(
122
+ () => ({
123
+ inputValue,
124
+ validationError,
125
+ onClick: () => !disabled && setIsOpen(true),
126
+ }),
127
+ [disabled, inputValue, setIsOpen, validationError]
128
+ );
129
+
130
+ const pickerState = { pickerProps, inputProps, wrapperProps };
131
+
132
+ useDebugValue(pickerState);
133
+ return pickerState;
134
+ }
@@ -0,0 +1,20 @@
1
+ import { useContext } from 'react';
2
+ import { IUtils } from '@date-io/core/IUtils';
3
+ import { MaterialUiPickersDate } from '../../typings/date';
4
+ import { MuiPickersContext } from '../../MuiPickersUtilsProvider';
5
+
6
+ export const checkUtils = (utils: IUtils<MaterialUiPickersDate> | null | undefined) => {
7
+ if (!utils) {
8
+ // tslint:disable-next-line
9
+ throw new Error(
10
+ '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.'
11
+ );
12
+ }
13
+ };
14
+
15
+ export function useUtils() {
16
+ const utils = useContext(MuiPickersContext);
17
+ checkUtils(utils);
18
+
19
+ return utils!;
20
+ }
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { MaterialUiPickersDate } from '../..';
3
+ import { PickerView } from '../../Picker/Picker';
4
+ import { arrayIncludes } from '../../_helpers/utils';
5
+
6
+ export function useViews(
7
+ views: PickerView[],
8
+ openTo: PickerView,
9
+ onChange: (date: MaterialUiPickersDate, isFinish?: boolean) => void
10
+ ) {
11
+ const [openView, setOpenView] = React.useState(
12
+ openTo && arrayIncludes(views, openTo) ? openTo : views[0]
13
+ );
14
+
15
+ const handleChangeAndOpenNext = React.useCallback(
16
+ (date: MaterialUiPickersDate, isFinish?: boolean) => {
17
+ const nextViewToOpen = views[views.indexOf(openView!) + 1];
18
+ if (isFinish && nextViewToOpen) {
19
+ // do not close picker if needs to show next view
20
+ onChange(date, false);
21
+ setOpenView(nextViewToOpen);
22
+ return;
23
+ }
24
+
25
+ onChange(date, Boolean(isFinish));
26
+ },
27
+ [onChange, openView, views]
28
+ );
29
+
30
+ return { handleChangeAndOpenNext, openView, setOpenView };
31
+ }