@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.
- package/.size-snapshot.json +11 -11
- package/9db1cba1a61cb58db53f38425bbd1f95.7z +0 -0
- package/build/DatePicker/DatePicker.d.ts +2 -2
- package/build/DatePicker/DatePicker.js +1 -1
- package/build/DateTimePicker/DateTimePicker.js +1 -1
- package/build/Picker/Picker.js +1 -1
- package/build/TimePicker/TimePicker.js +1 -1
- package/build/_helpers/text-field-helper.d.ts +1 -1
- package/build/_shared/KeyboardDateInput.js +1 -1
- package/build/_shared/ModalDialog.d.ts +2 -2
- package/build/_shared/PureDateInput.js +1 -1
- package/build/_shared/ToolbarButton.d.ts +2 -2
- package/build/_shared/hooks/useKeyboardPickerState.js +2 -2
- package/build/constants/prop-types.js +2 -2
- package/build/dist/material-ui-pickers.esm.js +12 -12
- package/build/dist/material-ui-pickers.esm.js.map +1 -1
- package/build/dist/material-ui-pickers.js +62 -62
- package/build/dist/material-ui-pickers.js.map +1 -1
- package/build/dist/material-ui-pickers.umd.js +5790 -4511
- package/build/dist/material-ui-pickers.umd.min.js +1 -1
- package/build/package.json +1 -1
- package/build/views/Calendar/Calendar.js +2 -2
- package/build/views/Clock/Clock.d.ts +1 -1
- package/build/views/Clock/ClockPointer.d.ts +1 -1
- package/package.json +1 -1
- package/src/DatePicker/DatePicker.tsx +91 -0
- package/src/DatePicker/DatePickerToolbar.tsx +75 -0
- package/src/DatePicker/index.ts +7 -0
- package/src/DateTimePicker/DateTimePicker.tsx +72 -0
- package/src/DateTimePicker/DateTimePickerTabs.tsx +86 -0
- package/src/DateTimePicker/DateTimePickerToolbar.tsx +123 -0
- package/src/DateTimePicker/index.ts +7 -0
- package/src/MuiPickersUtilsProvider.tsx +39 -0
- package/src/Picker/Picker.tsx +275 -0
- package/src/Picker/WrappedKeyboardPicker.tsx +136 -0
- package/src/Picker/WrappedPurePicker.tsx +128 -0
- package/src/Rifm/Rifm.ts +213 -0
- package/src/TimePicker/TimePicker.tsx +67 -0
- package/src/TimePicker/TimePickerToolbar.tsx +180 -0
- package/src/TimePicker/index.tsx +7 -0
- package/src/__tests__/DatePicker/Calendar.test.tsx +51 -0
- package/src/__tests__/DatePicker/Month.test.tsx +36 -0
- package/src/__tests__/DatePicker/MonthSelection.test.tsx +37 -0
- package/src/__tests__/MuiPickersUtilsProvider.test.tsx +21 -0
- package/src/__tests__/TimePicker/Clock.test.tsx +68 -0
- package/src/__tests__/_helpers/date-utils.test.tsx +197 -0
- package/src/__tests__/_helpers/text-field-helper.test.tsx +34 -0
- package/src/__tests__/_shared/ModalDialog.test.tsx +109 -0
- package/src/__tests__/e2e/DatePicker.test.tsx +233 -0
- package/src/__tests__/e2e/DatePickerProps.test.tsx +18 -0
- package/src/__tests__/e2e/DatePickerRoot.test.tsx +154 -0
- package/src/__tests__/e2e/DateTimePicker.test.tsx +86 -0
- package/src/__tests__/e2e/DateTimePickerRoot.test.tsx +72 -0
- package/src/__tests__/e2e/KeyboardDatePicker.test.tsx +140 -0
- package/src/__tests__/e2e/KeyboardDateTimePicker.test.tsx +48 -0
- package/src/__tests__/e2e/Theme.test.tsx +30 -0
- package/src/__tests__/e2e/TimePicker.test.tsx +121 -0
- package/src/__tests__/e2e/commands.tsx +8 -0
- package/src/__tests__/setup.js +26 -0
- package/src/__tests__/test-utils.tsx +45 -0
- package/src/__tests__/tsconfig.json +5 -0
- package/src/_helpers/date-utils.ts +92 -0
- package/src/_helpers/text-field-helper.ts +239 -0
- package/src/_helpers/time-utils.ts +83 -0
- package/src/_helpers/utils.ts +8 -0
- package/src/_shared/KeyboardDateInput.tsx +161 -0
- package/src/_shared/ModalDialog.tsx +114 -0
- package/src/_shared/PickerToolbar.tsx +52 -0
- package/src/_shared/PureDateInput.tsx +45 -0
- package/src/_shared/ToolbarButton.tsx +63 -0
- package/src/_shared/ToolbarText.tsx +49 -0
- package/src/_shared/WithUtils.tsx +19 -0
- package/src/_shared/hooks/useIsLandscape.tsx +27 -0
- package/src/_shared/hooks/useKeyDown.ts +34 -0
- package/src/_shared/hooks/useKeyboardPickerState.ts +72 -0
- package/src/_shared/hooks/useOpenState.ts +22 -0
- package/src/_shared/hooks/usePickerState.ts +134 -0
- package/src/_shared/hooks/useUtils.ts +20 -0
- package/src/_shared/hooks/useViews.tsx +31 -0
- package/src/_shared/icons/ArrowLeftIcon.tsx +11 -0
- package/src/_shared/icons/ArrowRightIcon.tsx +11 -0
- package/src/_shared/icons/DateRangeIcon.tsx +11 -0
- package/src/_shared/icons/KeyboardIcon.tsx +11 -0
- package/src/_shared/icons/TimeIcon.tsx +12 -0
- package/src/constants/ClockType.ts +11 -0
- package/src/constants/dimensions.ts +5 -0
- package/src/constants/prop-types.ts +37 -0
- package/src/index.ts +48 -0
- package/src/typings/BasePicker.tsx +60 -0
- package/src/typings/date.ts +3 -0
- package/src/typings/extendMui.ts +10 -0
- package/src/typings/overrides.ts +44 -0
- package/src/views/Calendar/Calendar.tsx +361 -0
- package/src/views/Calendar/CalendarHeader.tsx +141 -0
- package/src/views/Calendar/Day.tsx +97 -0
- package/src/views/Calendar/DayWrapper.tsx +33 -0
- package/src/views/Calendar/SlideTransition.tsx +100 -0
- package/src/views/Clock/Clock.tsx +172 -0
- package/src/views/Clock/ClockNumber.tsx +91 -0
- package/src/views/Clock/ClockNumbers.tsx +76 -0
- package/src/views/Clock/ClockPointer.tsx +104 -0
- package/src/views/Clock/ClockView.tsx +105 -0
- package/src/views/Month/Month.tsx +76 -0
- package/src/views/Month/MonthView.tsx +94 -0
- package/src/views/Year/Year.tsx +77 -0
- package/src/views/Year/YearView.tsx +91 -0
- package/src/wrappers/InlineWrapper.tsx +80 -0
- package/src/wrappers/ModalWrapper.tsx +113 -0
- package/src/wrappers/StaticWrapper.tsx +22 -0
- package/src/wrappers/Wrapper.tsx +75 -0
- package/coverage/clover.xml +0 -601
- package/coverage/coverage-final.json +0 -22
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -79
- package/coverage/lcov-report/index.html +0 -201
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -170
- package/coverage/lcov-report/src/MuiPickersUtilsProvider.tsx.html +0 -186
- package/coverage/lcov-report/src/_helpers/date-utils.ts.html +0 -345
- package/coverage/lcov-report/src/_helpers/index.html +0 -123
- package/coverage/lcov-report/src/_helpers/time-utils.ts.html +0 -318
- package/coverage/lcov-report/src/_helpers/utils.ts.html +0 -93
- package/coverage/lcov-report/src/_shared/ModalDialog.tsx.html +0 -411
- package/coverage/lcov-report/src/_shared/WithUtils.tsx.html +0 -126
- package/coverage/lcov-report/src/_shared/hooks/index.html +0 -110
- package/coverage/lcov-report/src/_shared/hooks/useKeyDown.ts.html +0 -171
- package/coverage/lcov-report/src/_shared/hooks/useUtils.ts.html +0 -129
- package/coverage/lcov-report/src/_shared/icons/ArrowLeftIcon.tsx.html +0 -102
- package/coverage/lcov-report/src/_shared/icons/ArrowRightIcon.tsx.html +0 -102
- package/coverage/lcov-report/src/_shared/icons/index.html +0 -110
- package/coverage/lcov-report/src/_shared/index.html +0 -110
- package/coverage/lcov-report/src/constants/ClockType.ts.html +0 -102
- package/coverage/lcov-report/src/constants/dimensions.ts.html +0 -84
- package/coverage/lcov-report/src/constants/index.html +0 -110
- package/coverage/lcov-report/src/index.html +0 -97
- package/coverage/lcov-report/src/views/Calendar/Calendar.tsx.html +0 -1152
- package/coverage/lcov-report/src/views/Calendar/CalendarHeader.tsx.html +0 -492
- package/coverage/lcov-report/src/views/Calendar/Day.tsx.html +0 -360
- package/coverage/lcov-report/src/views/Calendar/DayWrapper.tsx.html +0 -168
- package/coverage/lcov-report/src/views/Calendar/SlideTransition.tsx.html +0 -369
- package/coverage/lcov-report/src/views/Calendar/index.html +0 -149
- package/coverage/lcov-report/src/views/Clock/Clock.tsx.html +0 -585
- package/coverage/lcov-report/src/views/Clock/ClockPointer.tsx.html +0 -381
- package/coverage/lcov-report/src/views/Clock/index.html +0 -110
- package/coverage/lcov-report/src/views/Month/Month.tsx.html +0 -297
- package/coverage/lcov-report/src/views/Month/MonthView.tsx.html +0 -351
- package/coverage/lcov-report/src/views/Month/index.html +0 -110
- package/coverage/lcov.info +0 -1094
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as enzyme from 'enzyme';
|
|
3
|
+
import LuxonUtils from '@date-io/luxon';
|
|
4
|
+
import MomentUtils from '@date-io/moment';
|
|
5
|
+
import DateFnsUtils from '@date-io/date-fns';
|
|
6
|
+
import MuiPickersUtilsProvider from '../MuiPickersUtilsProvider';
|
|
7
|
+
import { WithUtilsProps } from '../_shared/WithUtils';
|
|
8
|
+
|
|
9
|
+
const getUtilClass = () => {
|
|
10
|
+
switch (process.env.UTILS) {
|
|
11
|
+
case 'moment':
|
|
12
|
+
return MomentUtils;
|
|
13
|
+
case 'date-fns':
|
|
14
|
+
return DateFnsUtils;
|
|
15
|
+
case 'luxon':
|
|
16
|
+
return LuxonUtils;
|
|
17
|
+
default:
|
|
18
|
+
return DateFnsUtils;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const UtilClassToUse: any = getUtilClass();
|
|
23
|
+
export const utilsToUse = new UtilClassToUse();
|
|
24
|
+
|
|
25
|
+
const getComponentWithUtils = <P extends WithUtilsProps>(element: React.ReactElement<P>) =>
|
|
26
|
+
React.cloneElement(element, { utils: utilsToUse } as any);
|
|
27
|
+
|
|
28
|
+
export const shallow = <P extends WithUtilsProps>(element: React.ReactElement<P>) =>
|
|
29
|
+
enzyme.shallow(getComponentWithUtils(element));
|
|
30
|
+
|
|
31
|
+
export const mount = <P extends WithUtilsProps>(element: React.ReactElement<P>) =>
|
|
32
|
+
enzyme.mount(<MuiPickersUtilsProvider utils={UtilClassToUse}>{element}</MuiPickersUtilsProvider>);
|
|
33
|
+
|
|
34
|
+
export const shallowRender = (render: (props: any) => React.ReactElement<any>) => {
|
|
35
|
+
return enzyme.shallow(render({ utils: utilsToUse, classes: {} as any, theme: {} as any }));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// toHaveBeenCalledWith doesn't work with moment because of changing some internal props
|
|
39
|
+
export const toHaveBeenCalledExceptMoment = (mock: jest.Mock<any, any>, params: any[]) => {
|
|
40
|
+
if (process.env.UTILS === 'moment') {
|
|
41
|
+
return expect(mock).toHaveBeenCalled();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return expect(mock).toHaveBeenCalledWith(...params);
|
|
45
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { arrayIncludes } from './utils';
|
|
2
|
+
import { IUtils } from '@date-io/core/IUtils';
|
|
3
|
+
import { MaterialUiPickersDate } from '../typings/date';
|
|
4
|
+
import { DatePickerView } from '../DatePicker/DatePicker';
|
|
5
|
+
|
|
6
|
+
interface FindClosestDateParams {
|
|
7
|
+
date: MaterialUiPickersDate;
|
|
8
|
+
utils: IUtils<MaterialUiPickersDate>;
|
|
9
|
+
minDate: MaterialUiPickersDate;
|
|
10
|
+
maxDate: MaterialUiPickersDate;
|
|
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
|
+
if (disablePast && utils.isBefore(minDate!, today)) {
|
|
28
|
+
minDate = today;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (disableFuture && utils.isAfter(maxDate, today)) {
|
|
32
|
+
maxDate = today;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let forward = date;
|
|
36
|
+
let backward = date;
|
|
37
|
+
if (utils.isBefore(date, minDate)) {
|
|
38
|
+
forward = utils.date(minDate);
|
|
39
|
+
backward = null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (utils.isAfter(date, maxDate)) {
|
|
43
|
+
if (backward) {
|
|
44
|
+
backward = utils.date(maxDate);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
forward = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
while (forward || backward) {
|
|
51
|
+
if (forward && utils.isAfter(forward, maxDate)) {
|
|
52
|
+
forward = null;
|
|
53
|
+
}
|
|
54
|
+
if (backward && utils.isBefore(backward, minDate)) {
|
|
55
|
+
backward = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (forward) {
|
|
59
|
+
if (!shouldDisableDate(forward)) {
|
|
60
|
+
return forward;
|
|
61
|
+
}
|
|
62
|
+
forward = utils.addDays(forward, 1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (backward) {
|
|
66
|
+
if (!shouldDisableDate(backward)) {
|
|
67
|
+
return backward;
|
|
68
|
+
}
|
|
69
|
+
backward = utils.addDays(backward, -1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const isYearOnlyView = (views: DatePickerView[]) =>
|
|
77
|
+
views.length === 1 && views[0] === 'year';
|
|
78
|
+
|
|
79
|
+
export const isYearAndMonthViews = (views: DatePickerView[]) =>
|
|
80
|
+
views.length === 2 && arrayIncludes(views, 'month') && arrayIncludes(views, 'year');
|
|
81
|
+
|
|
82
|
+
export const getFormatByViews = (views: DatePickerView[], utils: IUtils<MaterialUiPickersDate>) => {
|
|
83
|
+
if (isYearOnlyView(views)) {
|
|
84
|
+
return utils.yearFormat;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (isYearAndMonthViews(views)) {
|
|
88
|
+
return utils.yearMonthFormat;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return utils.dateFormat;
|
|
92
|
+
};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { DatePickerProps } from '..';
|
|
2
|
+
import { Omit } from '@material-ui/core';
|
|
3
|
+
import { IUtils } from '@date-io/core/IUtils';
|
|
4
|
+
import { ParsableDate } from '../constants/prop-types';
|
|
5
|
+
import { BasePickerProps } from '../typings/BasePicker';
|
|
6
|
+
|
|
7
|
+
const replaceAt = (str: string, index: number, replacement: string) => {
|
|
8
|
+
replacement = replacement || '';
|
|
9
|
+
return str.substr(0, index) + replacement + str.substr(index + replacement.length);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getDisplayDate = (
|
|
13
|
+
value: ParsableDate,
|
|
14
|
+
format: string,
|
|
15
|
+
utils: IUtils<any>,
|
|
16
|
+
isEmpty: boolean,
|
|
17
|
+
{ invalidLabel, emptyLabel, labelFunc }: Omit<BasePickerProps, 'value' | 'onChange'>
|
|
18
|
+
) => {
|
|
19
|
+
const date = utils.date(value);
|
|
20
|
+
if (labelFunc) {
|
|
21
|
+
return labelFunc(isEmpty ? null : date, invalidLabel!);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (isEmpty) {
|
|
25
|
+
return emptyLabel || '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return utils.isValid(date) ? utils.format(date, format) : invalidLabel!;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export interface BaseValidationProps {
|
|
32
|
+
/**
|
|
33
|
+
* Message, appearing when date cannot be parsed
|
|
34
|
+
* @default 'Invalid Date Format'
|
|
35
|
+
*/
|
|
36
|
+
invalidDateMessage?: React.ReactNode;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DateValidationProps extends BaseValidationProps {
|
|
40
|
+
/**
|
|
41
|
+
* Error message, shown if date is less then minimal date
|
|
42
|
+
* @default 'Date should not be before minimal date'
|
|
43
|
+
*/
|
|
44
|
+
minDateMessage?: React.ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* Error message, shown if date is more then maximal date
|
|
47
|
+
* @default 'Date should not be after maximal date'
|
|
48
|
+
*/
|
|
49
|
+
maxDateMessage?: React.ReactNode;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const getComparisonMaxDate = (utils: IUtils<any>, strictCompareDates: boolean, date: Date) => {
|
|
53
|
+
if (strictCompareDates) {
|
|
54
|
+
return date;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return utils.endOfDay(date);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const getComparisonMinDate = (utils: IUtils<any>, strictCompareDates: boolean, date: Date) => {
|
|
61
|
+
if (strictCompareDates) {
|
|
62
|
+
return date;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return utils.startOfDay(date);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const validate = (
|
|
69
|
+
value: ParsableDate,
|
|
70
|
+
utils: IUtils<any>,
|
|
71
|
+
{
|
|
72
|
+
maxDate,
|
|
73
|
+
minDate,
|
|
74
|
+
disablePast,
|
|
75
|
+
disableFuture,
|
|
76
|
+
maxDateMessage,
|
|
77
|
+
minDateMessage,
|
|
78
|
+
invalidDateMessage,
|
|
79
|
+
strictCompareDates,
|
|
80
|
+
}: Omit<DatePickerProps, 'views' | 'openTo'> // DateTimePicker doesn't support
|
|
81
|
+
): React.ReactNode => {
|
|
82
|
+
const parsedValue = utils.date(value);
|
|
83
|
+
|
|
84
|
+
// if null - do not show error
|
|
85
|
+
if (value === null) {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!utils.isValid(value)) {
|
|
90
|
+
return invalidDateMessage;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
maxDate &&
|
|
95
|
+
utils.isAfter(
|
|
96
|
+
parsedValue,
|
|
97
|
+
getComparisonMaxDate(utils, !!strictCompareDates, utils.date(maxDate))
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
return maxDateMessage;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
disableFuture &&
|
|
105
|
+
utils.isAfter(parsedValue, getComparisonMaxDate(utils, !!strictCompareDates, utils.date()))
|
|
106
|
+
) {
|
|
107
|
+
return maxDateMessage;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
minDate &&
|
|
112
|
+
utils.isBefore(
|
|
113
|
+
parsedValue,
|
|
114
|
+
getComparisonMinDate(utils, !!strictCompareDates, utils.date(minDate))
|
|
115
|
+
)
|
|
116
|
+
) {
|
|
117
|
+
return minDateMessage;
|
|
118
|
+
}
|
|
119
|
+
if (
|
|
120
|
+
disablePast &&
|
|
121
|
+
utils.isBefore(parsedValue, getComparisonMinDate(utils, !!strictCompareDates, utils.date()))
|
|
122
|
+
) {
|
|
123
|
+
return minDateMessage;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return '';
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export function pick12hOr24hFormat(
|
|
130
|
+
userFormat: string | undefined,
|
|
131
|
+
ampm: boolean | undefined = true,
|
|
132
|
+
formats: { '12h': string; '24h': string }
|
|
133
|
+
) {
|
|
134
|
+
if (userFormat) {
|
|
135
|
+
return userFormat;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return ampm ? formats['12h'] : formats['24h'];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function makeMaskFromFormat(format: string, numberMaskChar: string) {
|
|
142
|
+
return format.replace(/[a-z]/gi, numberMaskChar);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const maskedDateFormatter = (mask: string, numberMaskChar: string) => (
|
|
146
|
+
value: string,
|
|
147
|
+
oldValue: string,
|
|
148
|
+
currentCaretPosition?: number
|
|
149
|
+
) => {
|
|
150
|
+
const refuse = /[^\d\w_]+/gi;
|
|
151
|
+
|
|
152
|
+
if (!value || value === '') {
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!oldValue) {
|
|
157
|
+
oldValue = '';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (value == oldValue) {
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (value.length < oldValue.length && currentCaretPosition != null) {
|
|
165
|
+
value =
|
|
166
|
+
value.slice(0, currentCaretPosition) +
|
|
167
|
+
'_' +
|
|
168
|
+
value.slice(currentCaretPosition, oldValue.length);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let refusedOldValue = oldValue.replace(refuse, '');
|
|
172
|
+
let refusedNewValue = value.replace(refuse, '');
|
|
173
|
+
let refusedMaskSymbols = mask.replace(refuse, '');
|
|
174
|
+
|
|
175
|
+
const minLength =
|
|
176
|
+
refusedOldValue.length > refusedNewValue.length
|
|
177
|
+
? refusedNewValue.length
|
|
178
|
+
: refusedOldValue.length;
|
|
179
|
+
|
|
180
|
+
let startPos = 0;
|
|
181
|
+
let endPosShift = 0;
|
|
182
|
+
while (startPos < minLength) {
|
|
183
|
+
if (refusedNewValue[startPos] == refusedOldValue[startPos]) {
|
|
184
|
+
startPos++;
|
|
185
|
+
} else {
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
while (
|
|
191
|
+
endPosShift < minLength &&
|
|
192
|
+
startPos < refusedNewValue.length - endPosShift &&
|
|
193
|
+
startPos < refusedOldValue.length - endPosShift
|
|
194
|
+
) {
|
|
195
|
+
if (
|
|
196
|
+
refusedNewValue[refusedNewValue.length - endPosShift - 1] ==
|
|
197
|
+
refusedOldValue[refusedOldValue.length - endPosShift - 1]
|
|
198
|
+
) {
|
|
199
|
+
endPosShift++;
|
|
200
|
+
} else {
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let endStartPos = refusedNewValue.length - endPosShift;
|
|
206
|
+
endStartPos = endStartPos < startPos ? startPos : endStartPos;
|
|
207
|
+
|
|
208
|
+
let firstPart = refusedNewValue.slice(0, startPos);
|
|
209
|
+
let addedPart = refusedNewValue.slice(startPos, endStartPos);
|
|
210
|
+
let endPart = refusedNewValue.slice(endStartPos, refusedNewValue.length);
|
|
211
|
+
|
|
212
|
+
let maxSymbolsInEnd = refusedMaskSymbols.length - firstPart.length - addedPart.length;
|
|
213
|
+
while (maxSymbolsInEnd > endPart.length) {
|
|
214
|
+
addedPart += numberMaskChar;
|
|
215
|
+
maxSymbolsInEnd--;
|
|
216
|
+
}
|
|
217
|
+
let entireRefusedString =
|
|
218
|
+
firstPart + addedPart + endPart.slice(endPart.length - maxSymbolsInEnd, endPart.length);
|
|
219
|
+
|
|
220
|
+
// Do nothing ?...
|
|
221
|
+
entireRefusedString = entireRefusedString.slice(0, refusedMaskSymbols.length);
|
|
222
|
+
|
|
223
|
+
let i = 0;
|
|
224
|
+
let n = 0;
|
|
225
|
+
let result = '';
|
|
226
|
+
while (i < mask.length) {
|
|
227
|
+
const maskChar = mask[i];
|
|
228
|
+
if (maskChar === numberMaskChar && n < entireRefusedString.length) {
|
|
229
|
+
const parsedChar = entireRefusedString[n];
|
|
230
|
+
result += parsedChar;
|
|
231
|
+
n += 1;
|
|
232
|
+
} else {
|
|
233
|
+
result += maskChar;
|
|
234
|
+
}
|
|
235
|
+
i += 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result;
|
|
239
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { IUtils } from '@date-io/core/IUtils';
|
|
2
|
+
import { MaterialUiPickersDate } from '../typings/date';
|
|
3
|
+
|
|
4
|
+
const center = {
|
|
5
|
+
x: 260 / 2,
|
|
6
|
+
y: 260 / 2,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const basePoint = {
|
|
10
|
+
x: center.x,
|
|
11
|
+
y: 0,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const cx = basePoint.x - center.x;
|
|
15
|
+
const cy = basePoint.y - center.y;
|
|
16
|
+
|
|
17
|
+
const rad2deg = (rad: number) => rad * 57.29577951308232;
|
|
18
|
+
|
|
19
|
+
const getAngleValue = (step: number, offsetX: number, offsetY: number) => {
|
|
20
|
+
const x = offsetX - center.x;
|
|
21
|
+
const y = offsetY - center.y;
|
|
22
|
+
|
|
23
|
+
const atan = Math.atan2(cx, cy) - Math.atan2(x, y);
|
|
24
|
+
|
|
25
|
+
let deg = rad2deg(atan);
|
|
26
|
+
deg = Math.round(deg / step) * step;
|
|
27
|
+
deg %= 360;
|
|
28
|
+
|
|
29
|
+
const value = Math.floor(deg / step) || 0;
|
|
30
|
+
const delta = Math.pow(x, 2) + Math.pow(y, 2);
|
|
31
|
+
const distance = Math.sqrt(delta);
|
|
32
|
+
|
|
33
|
+
return { value, distance };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getHours = (offsetX: number, offsetY: number, ampm: boolean) => {
|
|
37
|
+
let { value, distance } = getAngleValue(30, offsetX, offsetY);
|
|
38
|
+
value = value || 12;
|
|
39
|
+
|
|
40
|
+
if (!ampm) {
|
|
41
|
+
if (distance < 90) {
|
|
42
|
+
value += 12;
|
|
43
|
+
value %= 24;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
value %= 12;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return value;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const getMinutes = (offsetX: number, offsetY: number, step = 1) => {
|
|
53
|
+
const angleStep = step * 6;
|
|
54
|
+
let { value } = getAngleValue(angleStep, offsetX, offsetY);
|
|
55
|
+
value = (value * step) % 60;
|
|
56
|
+
|
|
57
|
+
return value;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const getMeridiem = (
|
|
61
|
+
date: MaterialUiPickersDate,
|
|
62
|
+
utils: IUtils<MaterialUiPickersDate>
|
|
63
|
+
): 'am' | 'pm' => {
|
|
64
|
+
return utils.getHours(date) >= 12 ? 'pm' : 'am';
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const convertToMeridiem = (
|
|
68
|
+
time: MaterialUiPickersDate,
|
|
69
|
+
meridiem: 'am' | 'pm',
|
|
70
|
+
ampm: boolean,
|
|
71
|
+
utils: IUtils<MaterialUiPickersDate>
|
|
72
|
+
) => {
|
|
73
|
+
if (ampm) {
|
|
74
|
+
const currentMeridiem = utils.getHours(time) >= 12 ? 'pm' : 'am';
|
|
75
|
+
if (currentMeridiem !== meridiem) {
|
|
76
|
+
const hours = meridiem === 'am' ? utils.getHours(time) - 12 : utils.getHours(time) + 12;
|
|
77
|
+
|
|
78
|
+
return utils.setHours(time, hours);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return time;
|
|
83
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Use it instead of .includes method for IE support */
|
|
2
|
+
export function arrayIncludes<T>(array: T[], itemOrItems: T | T[]) {
|
|
3
|
+
if (Array.isArray(itemOrItems)) {
|
|
4
|
+
return itemOrItems.every(item => array.indexOf(item) !== -1);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return array.indexOf(itemOrItems) !== -1;
|
|
8
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import InputAdornment, { InputAdornmentProps } from '@material-ui/core/InputAdornment';
|
|
3
|
+
import TextField, { BaseTextFieldProps, TextFieldProps } from '@material-ui/core/TextField';
|
|
4
|
+
import { IconButton } from '@material-ui/core';
|
|
5
|
+
import { ExtendMui } from '../typings/extendMui';
|
|
6
|
+
import { KeyboardIcon } from './icons/KeyboardIcon';
|
|
7
|
+
import { IconButtonProps } from '@material-ui/core/IconButton';
|
|
8
|
+
import { makeMaskFromFormat, maskedDateFormatter } from '../_helpers/text-field-helper';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
import { Rifm } from '../Rifm/Rifm';
|
|
11
|
+
|
|
12
|
+
export interface KeyboardDateInputProps
|
|
13
|
+
extends ExtendMui<BaseTextFieldProps, 'variant' | 'onError' | 'onChange' | 'value'> {
|
|
14
|
+
format: string;
|
|
15
|
+
onChange: (value: string | null) => void;
|
|
16
|
+
onBlur?: () => void;
|
|
17
|
+
onClick?: () => void;
|
|
18
|
+
validationError?: React.ReactNode;
|
|
19
|
+
inputValue: string;
|
|
20
|
+
InputProps?: TextFieldProps['InputProps'];
|
|
21
|
+
/** Override input component */
|
|
22
|
+
TextFieldComponent?: React.ComponentType<TextFieldProps>;
|
|
23
|
+
/** Icon displaying for open picker button */
|
|
24
|
+
keyboardIcon?: React.ReactNode;
|
|
25
|
+
/** Pass material-ui text field variant down, bypass internal variant prop */
|
|
26
|
+
inputVariant?: TextFieldProps['variant'];
|
|
27
|
+
/**
|
|
28
|
+
* Custom mask. Can be used to override generate from format. (e.g. __/__/____ __:__)
|
|
29
|
+
*/
|
|
30
|
+
mask?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Char string that will be replaced with number (for "_" mask will be "__/__/____")
|
|
33
|
+
* @default '_'
|
|
34
|
+
*/
|
|
35
|
+
maskChar?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Refuse values regexp
|
|
38
|
+
* @default /[^\d]+/gi
|
|
39
|
+
*/
|
|
40
|
+
refuse?: RegExp;
|
|
41
|
+
/**
|
|
42
|
+
* Props to pass to keyboard input adornment
|
|
43
|
+
* @type {Partial<InputAdornmentProps>}
|
|
44
|
+
*/
|
|
45
|
+
InputAdornmentProps?: Partial<InputAdornmentProps>;
|
|
46
|
+
/**
|
|
47
|
+
* Props to pass to keyboard adornment button
|
|
48
|
+
* @type {Partial<IconButtonProps>}
|
|
49
|
+
*/
|
|
50
|
+
KeyboardButtonProps?: Partial<IconButtonProps>;
|
|
51
|
+
/** Custom formatter to be passed into Rifm component */
|
|
52
|
+
rifmFormatter?: (str: string) => string;
|
|
53
|
+
/**
|
|
54
|
+
* Show errors only when input blur
|
|
55
|
+
*/
|
|
56
|
+
showErrorOnBlur?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const KeyboardDateInput: React.FunctionComponent<KeyboardDateInputProps> = ({
|
|
60
|
+
inputValue,
|
|
61
|
+
showErrorOnBlur = false,
|
|
62
|
+
inputVariant,
|
|
63
|
+
validationError,
|
|
64
|
+
KeyboardButtonProps,
|
|
65
|
+
InputAdornmentProps,
|
|
66
|
+
onClick,
|
|
67
|
+
onChange,
|
|
68
|
+
onBlur,
|
|
69
|
+
onFocus,
|
|
70
|
+
InputProps,
|
|
71
|
+
mask,
|
|
72
|
+
maskChar = '_',
|
|
73
|
+
refuse = /[^\d_]+/gi,
|
|
74
|
+
format,
|
|
75
|
+
keyboardIcon,
|
|
76
|
+
disabled,
|
|
77
|
+
rifmFormatter,
|
|
78
|
+
TextFieldComponent = TextField,
|
|
79
|
+
...other
|
|
80
|
+
}) => {
|
|
81
|
+
const inputMask = mask || makeMaskFromFormat(format, maskChar);
|
|
82
|
+
const letterPosition = format.indexOf('a');
|
|
83
|
+
// prettier-ignore
|
|
84
|
+
const formatter = React.useCallback(
|
|
85
|
+
maskedDateFormatter(inputMask, maskChar),
|
|
86
|
+
[mask, maskChar]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const position =
|
|
90
|
+
InputAdornmentProps && InputAdornmentProps.position ? InputAdornmentProps.position : 'end';
|
|
91
|
+
|
|
92
|
+
const handleChange = (text: string) => {
|
|
93
|
+
const finalString = text === '' || text === inputMask ? null : text;
|
|
94
|
+
onChange(finalString);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const replace = (text: string): string => {
|
|
98
|
+
return text;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const [showErrorFlag, handleBlur] = useState(showErrorOnBlur ? true : false);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Rifm
|
|
105
|
+
value={inputValue}
|
|
106
|
+
onChange={handleChange}
|
|
107
|
+
format={rifmFormatter || formatter}
|
|
108
|
+
replace={replace}
|
|
109
|
+
mask={true}
|
|
110
|
+
maskChar={maskChar}
|
|
111
|
+
letterPosition={letterPosition}
|
|
112
|
+
>
|
|
113
|
+
{({ onChange, value }) => (
|
|
114
|
+
<TextFieldComponent
|
|
115
|
+
disabled={disabled}
|
|
116
|
+
error={Boolean(validationError) && !showErrorFlag}
|
|
117
|
+
helperText={!showErrorFlag && validationError}
|
|
118
|
+
{...other}
|
|
119
|
+
value={value}
|
|
120
|
+
onChange={onChange}
|
|
121
|
+
// onInput={changeInputStringValue}
|
|
122
|
+
// onKeyUp={handleKeyUp}
|
|
123
|
+
// onMouseUp={handleClick}
|
|
124
|
+
onBlur={() => {
|
|
125
|
+
if (onBlur) {
|
|
126
|
+
onBlur();
|
|
127
|
+
}
|
|
128
|
+
if (showErrorOnBlur) {
|
|
129
|
+
handleBlur(false);
|
|
130
|
+
}
|
|
131
|
+
}}
|
|
132
|
+
onFocus={(ev: any) => {
|
|
133
|
+
if (onFocus) {
|
|
134
|
+
onFocus(ev);
|
|
135
|
+
}
|
|
136
|
+
if (showErrorOnBlur) {
|
|
137
|
+
handleBlur(true);
|
|
138
|
+
}
|
|
139
|
+
}}
|
|
140
|
+
variant={inputVariant as any}
|
|
141
|
+
InputProps={{
|
|
142
|
+
...InputProps,
|
|
143
|
+
[`${position}Adornment`]: (
|
|
144
|
+
<InputAdornment position={position} {...InputAdornmentProps}>
|
|
145
|
+
<IconButton disabled={disabled} {...KeyboardButtonProps} onClick={onClick}>
|
|
146
|
+
{keyboardIcon}
|
|
147
|
+
</IconButton>
|
|
148
|
+
</InputAdornment>
|
|
149
|
+
),
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
)}
|
|
153
|
+
</Rifm>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
KeyboardDateInput.defaultProps = {
|
|
158
|
+
keyboardIcon: <KeyboardIcon />,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default KeyboardDateInput;
|