@jsonforms/material-renderers 3.0.0-beta.2 → 3.0.0-beta.5
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/README.md +71 -13
- package/docs/assets/js/search.json +1 -1
- package/docs/globals.html +103 -52
- package/docs/index.html +79 -13
- package/docs/interfaces/inputref.html +168 -0
- package/lib/additional/MaterialListWithDetailRenderer.d.ts +1 -1
- package/lib/jsonforms-react-material.cjs.js +47 -19
- package/lib/jsonforms-react-material.cjs.js.map +1 -1
- package/lib/jsonforms-react-material.esm.js +50 -15
- package/lib/jsonforms-react-material.esm.js.map +1 -1
- package/lib/layouts/MaterialArrayLayoutRenderer.d.ts +1 -1
- package/lib/util/datejs.d.ts +17 -1
- package/package.json +7 -7
- package/src/additional/MaterialListWithDetailRenderer.tsx +5 -3
- package/src/complex/MaterialTableControl.tsx +1 -1
- package/src/controls/MaterialDateControl.tsx +23 -6
- package/src/controls/MaterialDateTimeControl.tsx +23 -6
- package/src/controls/MaterialTimeControl.tsx +24 -7
- package/src/layouts/ExpandPanelRenderer.tsx +2 -1
- package/src/layouts/MaterialArrayLayoutRenderer.tsx +3 -25
- package/src/mui-controls/MuiAutocomplete.tsx +1 -0
- package/src/util/datejs.tsx +73 -0
- package/stats.html +1 -1
- package/test/renderers/MaterialArrayLayout.test.tsx +32 -1
- package/test/renderers/MaterialDateControl.test.tsx +27 -0
- package/test/renderers/MaterialDateTimeControl.test.tsx +27 -0
- package/test/renderers/MaterialTimeControl.test.tsx +27 -0
- package/src/util/datejs.ts +0 -32
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ArrayLayoutProps, RankedTester } from '@jsonforms/core';
|
|
3
|
-
export declare const MaterialArrayLayoutRenderer: ({ visible,
|
|
3
|
+
export declare const MaterialArrayLayoutRenderer: ({ visible, addItem, ...props }: ArrayLayoutProps) => JSX.Element;
|
|
4
4
|
export declare const materialArrayLayoutTester: RankedTester;
|
|
5
5
|
declare const _default: React.ComponentType<import("@jsonforms/core").OwnPropsOfControl>;
|
|
6
6
|
export default _default;
|
package/lib/util/datejs.d.ts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
import { TextFieldProps } from '@mui/material';
|
|
1
2
|
import dayjs from 'dayjs';
|
|
2
|
-
|
|
3
|
+
import React from 'react';
|
|
4
|
+
export declare const createOnChangeHandler: (path: string, handleChange: (path: string, value: any) => void, saveFormat: string | undefined) => (time: dayjs.Dayjs, textInputValue: string) => void;
|
|
3
5
|
export declare const getData: (data: any, saveFormat: string | undefined) => dayjs.Dayjs | null;
|
|
6
|
+
declare type ResettableTextFieldProps = TextFieldProps & {
|
|
7
|
+
rawValue: any;
|
|
8
|
+
dayjsValueIsValid: boolean;
|
|
9
|
+
valueInInputFormat: string;
|
|
10
|
+
focused: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The dayjs formatter/parser is very lenient and for example ignores additional digits and/or characters.
|
|
14
|
+
* In these cases the input text can look vastly different than the actual value stored in the data.
|
|
15
|
+
* The 'ResettableTextField' component adjusts the text field to reflect the actual value stored in the data
|
|
16
|
+
* once it's no longer 'focused', i.e. when the user stops editing.
|
|
17
|
+
*/
|
|
18
|
+
export declare const ResettableTextField: React.FC<ResettableTextFieldProps>;
|
|
19
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsonforms/material-renderers",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.5",
|
|
4
4
|
"description": "Material Renderer Set for JSON Forms",
|
|
5
5
|
"repository": "https://github.com/eclipsesource/jsonforms",
|
|
6
6
|
"bugs": "https://github.com/eclipsesource/jsonforms/issues",
|
|
@@ -74,10 +74,10 @@
|
|
|
74
74
|
"dayjs": "1.10.6"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
|
-
"@emotion/react": "^11.
|
|
77
|
+
"@emotion/react": "^11.4.1",
|
|
78
78
|
"@emotion/styled": "^11.3.0",
|
|
79
|
-
"@jsonforms/core": "3.0.0-beta.
|
|
80
|
-
"@jsonforms/react": "3.0.0-beta.
|
|
79
|
+
"@jsonforms/core": "3.0.0-beta.5",
|
|
80
|
+
"@jsonforms/react": "3.0.0-beta.5",
|
|
81
81
|
"@mui/icons-material": "^5.0.0",
|
|
82
82
|
"@mui/lab": "^5.0.0-alpha.54",
|
|
83
83
|
"@mui/material": "^5.0.0"
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@emotion/react": "^11.5.0",
|
|
87
87
|
"@emotion/styled": "^11.3.0",
|
|
88
|
-
"@jsonforms/core": "^3.0.0-beta.
|
|
89
|
-
"@jsonforms/react": "^3.0.0-beta.
|
|
88
|
+
"@jsonforms/core": "^3.0.0-beta.5",
|
|
89
|
+
"@jsonforms/react": "^3.0.0-beta.5",
|
|
90
90
|
"@mui/icons-material": "^5.2.0",
|
|
91
91
|
"@mui/lab": "^5.0.0-alpha.58",
|
|
92
92
|
"@mui/material": "^5.2.2",
|
|
@@ -114,5 +114,5 @@
|
|
|
114
114
|
"webpack-cli": "^3.2.1",
|
|
115
115
|
"webpack-dev-server": "^3.9.0"
|
|
116
116
|
},
|
|
117
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "b66023cf081e56baa3194ac3d6658e23fd267bf9"
|
|
118
118
|
}
|
|
@@ -60,7 +60,8 @@ export const MaterialListWithDetailRenderer = ({
|
|
|
60
60
|
data,
|
|
61
61
|
renderers,
|
|
62
62
|
cells,
|
|
63
|
-
config
|
|
63
|
+
config,
|
|
64
|
+
rootSchema
|
|
64
65
|
}: ArrayLayoutProps) => {
|
|
65
66
|
const [selectedIndex, setSelectedIndex] = useState(undefined);
|
|
66
67
|
const handleRemoveItem = useCallback(
|
|
@@ -90,9 +91,10 @@ export const MaterialListWithDetailRenderer = ({
|
|
|
90
91
|
uischema.scope,
|
|
91
92
|
path,
|
|
92
93
|
undefined,
|
|
93
|
-
uischema
|
|
94
|
+
uischema,
|
|
95
|
+
rootSchema
|
|
94
96
|
),
|
|
95
|
-
[uischemas, schema, uischema.scope, path, uischema]
|
|
97
|
+
[uischemas, schema, uischema.scope, path, uischema, rootSchema]
|
|
96
98
|
);
|
|
97
99
|
const appliedUiSchemaOptions = merge({}, config, uischema.options);
|
|
98
100
|
|
|
@@ -370,7 +370,7 @@ const TableRows = ({
|
|
|
370
370
|
moveDownCreator={moveDown}
|
|
371
371
|
enableUp={index !== 0}
|
|
372
372
|
enableDown={index !== data - 1}
|
|
373
|
-
showSortButtons={appliedUiSchemaOptions.showSortButtons}
|
|
373
|
+
showSortButtons={appliedUiSchemaOptions.showSortButtons || appliedUiSchemaOptions.showArrayTableSortButtons}
|
|
374
374
|
enabled={enabled}
|
|
375
375
|
cells={cells}
|
|
376
376
|
path={path}
|
|
@@ -32,13 +32,18 @@ import {
|
|
|
32
32
|
rankWith,
|
|
33
33
|
} from '@jsonforms/core';
|
|
34
34
|
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
35
|
-
import { FormHelperText, Hidden
|
|
35
|
+
import { FormHelperText, Hidden } from '@mui/material';
|
|
36
36
|
import {
|
|
37
37
|
DatePicker,
|
|
38
38
|
LocalizationProvider
|
|
39
39
|
} from '@mui/lab';
|
|
40
40
|
import AdapterDayjs from '@mui/lab/AdapterDayjs';
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
createOnChangeHandler,
|
|
43
|
+
getData,
|
|
44
|
+
ResettableTextField,
|
|
45
|
+
useFocus,
|
|
46
|
+
} from '../util';
|
|
42
47
|
|
|
43
48
|
export const MaterialDateControl = (props: ControlProps)=> {
|
|
44
49
|
const [focused, onFocus, onBlur] = useFocus();
|
|
@@ -68,6 +73,8 @@ export const MaterialDateControl = (props: ControlProps)=> {
|
|
|
68
73
|
const format = appliedUiSchemaOptions.dateFormat ?? 'YYYY-MM-DD';
|
|
69
74
|
const saveFormat = appliedUiSchemaOptions.dateSaveFormat ?? 'YYYY-MM-DD';
|
|
70
75
|
|
|
76
|
+
const views = appliedUiSchemaOptions.views ?? ['year', 'day'];
|
|
77
|
+
|
|
71
78
|
const firstFormHelperText = showDescription
|
|
72
79
|
? description
|
|
73
80
|
: !isValid
|
|
@@ -80,30 +87,40 @@ export const MaterialDateControl = (props: ControlProps)=> {
|
|
|
80
87
|
saveFormat
|
|
81
88
|
),[path, handleChange, saveFormat]);
|
|
82
89
|
|
|
90
|
+
const value = getData(data, saveFormat);
|
|
91
|
+
const valueInInputFormat = value ? value.format(format) : '';
|
|
92
|
+
|
|
83
93
|
return (
|
|
84
94
|
<Hidden xsUp={!visible}>
|
|
85
95
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
86
96
|
<DatePicker
|
|
87
97
|
label={label}
|
|
88
|
-
value={
|
|
98
|
+
value={value}
|
|
89
99
|
clearable
|
|
90
100
|
onChange={onChange}
|
|
91
101
|
inputFormat={format}
|
|
92
102
|
disableMaskedInput
|
|
93
|
-
views={
|
|
103
|
+
views={views}
|
|
94
104
|
disabled={!enabled}
|
|
95
105
|
cancelText={appliedUiSchemaOptions.cancelLabel}
|
|
96
106
|
clearText={appliedUiSchemaOptions.clearLabel}
|
|
97
107
|
okText={appliedUiSchemaOptions.okLabel}
|
|
98
108
|
renderInput={params => (
|
|
99
|
-
<
|
|
109
|
+
<ResettableTextField
|
|
100
110
|
{...params}
|
|
111
|
+
rawValue={data}
|
|
112
|
+
dayjsValueIsValid={value !== null}
|
|
113
|
+
valueInInputFormat={valueInInputFormat}
|
|
114
|
+
focused={focused}
|
|
101
115
|
id={id + '-input'}
|
|
102
116
|
required={required && !appliedUiSchemaOptions.hideRequiredAsterisk}
|
|
103
117
|
autoFocus={appliedUiSchemaOptions.focus}
|
|
104
118
|
error={!isValid}
|
|
105
119
|
fullWidth={!appliedUiSchemaOptions.trim}
|
|
106
|
-
inputProps={{
|
|
120
|
+
inputProps={{
|
|
121
|
+
...params.inputProps,
|
|
122
|
+
type: 'text',
|
|
123
|
+
}}
|
|
107
124
|
InputLabelProps={data ? { shrink: true } : undefined}
|
|
108
125
|
onFocus={onFocus}
|
|
109
126
|
onBlur={onBlur}
|
|
@@ -32,13 +32,18 @@ import {
|
|
|
32
32
|
rankWith
|
|
33
33
|
} from '@jsonforms/core';
|
|
34
34
|
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
35
|
-
import { FormHelperText, Hidden
|
|
35
|
+
import { FormHelperText, Hidden } from '@mui/material';
|
|
36
36
|
import {
|
|
37
37
|
DateTimePicker,
|
|
38
38
|
LocalizationProvider
|
|
39
39
|
} from '@mui/lab';
|
|
40
40
|
import AdapterDayjs from '@mui/lab/AdapterDayjs';
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
createOnChangeHandler,
|
|
43
|
+
getData,
|
|
44
|
+
ResettableTextField,
|
|
45
|
+
useFocus
|
|
46
|
+
} from '../util';
|
|
42
47
|
|
|
43
48
|
export const MaterialDateTimeControl = (props: ControlProps) => {
|
|
44
49
|
const [focused, onFocus, onBlur] = useFocus();
|
|
@@ -69,6 +74,8 @@ export const MaterialDateTimeControl = (props: ControlProps) => {
|
|
|
69
74
|
const format = appliedUiSchemaOptions.dateTimeFormat ?? 'YYYY-MM-DD HH:mm';
|
|
70
75
|
const saveFormat = appliedUiSchemaOptions.dateTimeSaveFormat ?? undefined;
|
|
71
76
|
|
|
77
|
+
const views = appliedUiSchemaOptions.views ?? ['year', 'day', 'hours', 'minutes'];
|
|
78
|
+
|
|
72
79
|
const firstFormHelperText = showDescription
|
|
73
80
|
? description
|
|
74
81
|
: !isValid
|
|
@@ -82,31 +89,41 @@ export const MaterialDateTimeControl = (props: ControlProps) => {
|
|
|
82
89
|
saveFormat
|
|
83
90
|
),[path, handleChange, saveFormat]);
|
|
84
91
|
|
|
92
|
+
const value = getData(data, saveFormat);
|
|
93
|
+
const valueInInputFormat = value ? value.format(format) : '';
|
|
94
|
+
|
|
85
95
|
return (
|
|
86
96
|
<Hidden xsUp={!visible}>
|
|
87
97
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
88
98
|
<DateTimePicker
|
|
89
99
|
label={label}
|
|
90
|
-
value={
|
|
100
|
+
value={value}
|
|
91
101
|
clearable
|
|
92
102
|
onChange={onChange}
|
|
93
103
|
inputFormat={format}
|
|
94
104
|
disableMaskedInput
|
|
95
105
|
ampm={!!appliedUiSchemaOptions.ampm}
|
|
96
|
-
views={
|
|
106
|
+
views={views}
|
|
97
107
|
disabled={!enabled}
|
|
98
108
|
cancelText={appliedUiSchemaOptions.cancelLabel}
|
|
99
109
|
clearText={appliedUiSchemaOptions.clearLabel}
|
|
100
110
|
okText={appliedUiSchemaOptions.okLabel}
|
|
101
111
|
renderInput={params => (
|
|
102
|
-
<
|
|
112
|
+
<ResettableTextField
|
|
103
113
|
{...params}
|
|
114
|
+
rawValue={data}
|
|
115
|
+
dayjsValueIsValid={value !== null}
|
|
116
|
+
valueInInputFormat={valueInInputFormat}
|
|
117
|
+
focused={focused}
|
|
104
118
|
id={id + '-input'}
|
|
105
119
|
required={required && !appliedUiSchemaOptions.hideRequiredAsterisk}
|
|
106
120
|
autoFocus={appliedUiSchemaOptions.focus}
|
|
107
121
|
error={!isValid}
|
|
108
122
|
fullWidth={!appliedUiSchemaOptions.trim}
|
|
109
|
-
inputProps={{
|
|
123
|
+
inputProps={{
|
|
124
|
+
...params.inputProps,
|
|
125
|
+
type: 'text',
|
|
126
|
+
}}
|
|
110
127
|
InputLabelProps={data ? { shrink: true } : undefined}
|
|
111
128
|
onFocus={onFocus}
|
|
112
129
|
onBlur={onBlur}
|
|
@@ -32,13 +32,18 @@ import {
|
|
|
32
32
|
rankWith
|
|
33
33
|
} from '@jsonforms/core';
|
|
34
34
|
import { withJsonFormsControlProps } from '@jsonforms/react';
|
|
35
|
-
import { FormHelperText, Hidden
|
|
35
|
+
import { FormHelperText, Hidden } from '@mui/material';
|
|
36
36
|
import {
|
|
37
37
|
TimePicker,
|
|
38
38
|
LocalizationProvider
|
|
39
39
|
} from '@mui/lab';
|
|
40
40
|
import AdapterDayjs from '@mui/lab/AdapterDayjs';
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
createOnChangeHandler,
|
|
43
|
+
getData,
|
|
44
|
+
ResettableTextField,
|
|
45
|
+
useFocus
|
|
46
|
+
} from '../util';
|
|
42
47
|
|
|
43
48
|
export const MaterialTimeControl = (props: ControlProps) => {
|
|
44
49
|
const [focused, onFocus, onBlur] = useFocus();
|
|
@@ -67,7 +72,9 @@ export const MaterialTimeControl = (props: ControlProps) => {
|
|
|
67
72
|
);
|
|
68
73
|
|
|
69
74
|
const format = appliedUiSchemaOptions.timeFormat ?? 'HH:mm';
|
|
70
|
-
|
|
75
|
+
const saveFormat = appliedUiSchemaOptions.timeSaveFormat ?? 'HH:mm:ss';
|
|
76
|
+
|
|
77
|
+
const views = appliedUiSchemaOptions.views ?? ['hours', 'minutes'];
|
|
71
78
|
|
|
72
79
|
const firstFormHelperText = showDescription
|
|
73
80
|
? description
|
|
@@ -82,31 +89,41 @@ export const MaterialTimeControl = (props: ControlProps) => {
|
|
|
82
89
|
saveFormat
|
|
83
90
|
),[path, handleChange, saveFormat]);
|
|
84
91
|
|
|
92
|
+
const value = getData(data, saveFormat);
|
|
93
|
+
const valueInInputFormat = value ? value.format(format) : '';
|
|
94
|
+
|
|
85
95
|
return (
|
|
86
96
|
<Hidden xsUp={!visible}>
|
|
87
97
|
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
|
88
98
|
<TimePicker
|
|
89
99
|
label={label}
|
|
90
|
-
value={
|
|
100
|
+
value={value}
|
|
91
101
|
clearable
|
|
92
102
|
onChange={onChange}
|
|
93
103
|
inputFormat={format}
|
|
94
104
|
disableMaskedInput
|
|
95
105
|
ampm={!!appliedUiSchemaOptions.ampm}
|
|
96
|
-
views={
|
|
106
|
+
views={views}
|
|
97
107
|
disabled={!enabled}
|
|
98
108
|
cancelText={appliedUiSchemaOptions.cancelLabel}
|
|
99
109
|
clearText={appliedUiSchemaOptions.clearLabel}
|
|
100
110
|
okText={appliedUiSchemaOptions.okLabel}
|
|
101
111
|
renderInput={params => (
|
|
102
|
-
<
|
|
112
|
+
<ResettableTextField
|
|
103
113
|
{...params}
|
|
114
|
+
rawValue={data}
|
|
115
|
+
dayjsValueIsValid={value !== null}
|
|
116
|
+
valueInInputFormat={valueInInputFormat}
|
|
117
|
+
focused={focused}
|
|
104
118
|
id={id + '-input'}
|
|
105
119
|
required={required && !appliedUiSchemaOptions.hideRequiredAsterisk}
|
|
106
120
|
autoFocus={appliedUiSchemaOptions.focus}
|
|
107
121
|
error={!isValid}
|
|
108
122
|
fullWidth={!appliedUiSchemaOptions.trim}
|
|
109
|
-
inputProps={{
|
|
123
|
+
inputProps={{
|
|
124
|
+
...params.inputProps,
|
|
125
|
+
type: 'text'
|
|
126
|
+
}}
|
|
110
127
|
InputLabelProps={data ? { shrink: true } : undefined}
|
|
111
128
|
onFocus={onFocus}
|
|
112
129
|
onBlur={onBlur}
|
|
@@ -119,6 +119,7 @@ const ExpandPanelRendererComponent = (props: ExpandPanelProps) => {
|
|
|
119
119
|
);
|
|
120
120
|
|
|
121
121
|
const appliedUiSchemaOptions = merge({}, config, uischema.options);
|
|
122
|
+
const showSortButtons = appliedUiSchemaOptions.showSortButtons || appliedUiSchemaOptions.showArrayLayoutSortButtons;
|
|
122
123
|
|
|
123
124
|
return (
|
|
124
125
|
<Accordion
|
|
@@ -147,7 +148,7 @@ const ExpandPanelRendererComponent = (props: ExpandPanelProps) => {
|
|
|
147
148
|
justifyContent='center'
|
|
148
149
|
alignItems='center'
|
|
149
150
|
>
|
|
150
|
-
{
|
|
151
|
+
{showSortButtons ? (
|
|
151
152
|
<Fragment>
|
|
152
153
|
<Grid item>
|
|
153
154
|
<IconButton
|
|
@@ -36,19 +36,8 @@ import { withJsonFormsArrayLayoutProps } from '@jsonforms/react';
|
|
|
36
36
|
|
|
37
37
|
export const MaterialArrayLayoutRenderer = ({
|
|
38
38
|
visible,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
uischema,
|
|
42
|
-
schema,
|
|
43
|
-
label,
|
|
44
|
-
rootSchema,
|
|
45
|
-
renderers,
|
|
46
|
-
cells,
|
|
47
|
-
data,
|
|
48
|
-
path,
|
|
49
|
-
errors,
|
|
50
|
-
uischemas,
|
|
51
|
-
addItem
|
|
39
|
+
addItem,
|
|
40
|
+
...props
|
|
52
41
|
}: ArrayLayoutProps) => {
|
|
53
42
|
const addItemCb = useCallback((p: string, value: any) => addItem(p, value), [
|
|
54
43
|
addItem
|
|
@@ -56,20 +45,9 @@ export const MaterialArrayLayoutRenderer = ({
|
|
|
56
45
|
return (
|
|
57
46
|
<Hidden xsUp={!visible}>
|
|
58
47
|
<MaterialArrayLayout
|
|
59
|
-
label={label}
|
|
60
|
-
uischema={uischema}
|
|
61
|
-
schema={schema}
|
|
62
|
-
id={id}
|
|
63
|
-
rootSchema={rootSchema}
|
|
64
|
-
errors={errors}
|
|
65
|
-
enabled={enabled}
|
|
66
48
|
visible={visible}
|
|
67
|
-
data={data}
|
|
68
|
-
path={path}
|
|
69
49
|
addItem={addItemCb}
|
|
70
|
-
|
|
71
|
-
cells={cells}
|
|
72
|
-
uischemas={uischemas}
|
|
50
|
+
{...props}
|
|
73
51
|
/>
|
|
74
52
|
</Hidden>
|
|
75
53
|
);
|
|
@@ -77,6 +77,7 @@ export const MuiAutocomplete = (props: EnumCellProps & WithClassname & WithOptio
|
|
|
77
77
|
fullWidth
|
|
78
78
|
options={options}
|
|
79
79
|
getOptionLabel={getOptionLabel || (option => option?.label)}
|
|
80
|
+
freeSolo={false}
|
|
80
81
|
style={{ marginTop: 16 }}
|
|
81
82
|
renderInput={params => (
|
|
82
83
|
<Input
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TextField, TextFieldProps } from '@mui/material';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import customParsing from 'dayjs/plugin/customParseFormat';
|
|
4
|
+
import React, { useRef} from 'react';
|
|
5
|
+
|
|
6
|
+
// required for the custom save formats in the date, time and date-time pickers
|
|
7
|
+
dayjs.extend(customParsing);
|
|
8
|
+
|
|
9
|
+
export const createOnChangeHandler = (
|
|
10
|
+
path: string,
|
|
11
|
+
handleChange: (path: string, value: any) => void,
|
|
12
|
+
saveFormat: string | undefined
|
|
13
|
+
) => (time: dayjs.Dayjs, textInputValue: string) => {
|
|
14
|
+
if (!time) {
|
|
15
|
+
handleChange(path, undefined);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const result = dayjs(time).format(saveFormat);
|
|
19
|
+
handleChange(path, result === 'Invalid Date' ? textInputValue : result);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getData = (
|
|
23
|
+
data: any,
|
|
24
|
+
saveFormat: string | undefined
|
|
25
|
+
): dayjs.Dayjs | null => {
|
|
26
|
+
if (!data) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const dayjsData = dayjs(data, saveFormat);
|
|
30
|
+
if (dayjsData.toString() === 'Invalid Date') {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return dayjsData;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
interface InputRef {
|
|
38
|
+
lastInput: string;
|
|
39
|
+
toShow: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type ResettableTextFieldProps = TextFieldProps & {
|
|
43
|
+
rawValue: any;
|
|
44
|
+
dayjsValueIsValid: boolean;
|
|
45
|
+
valueInInputFormat: string;
|
|
46
|
+
focused: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The dayjs formatter/parser is very lenient and for example ignores additional digits and/or characters.
|
|
51
|
+
* In these cases the input text can look vastly different than the actual value stored in the data.
|
|
52
|
+
* The 'ResettableTextField' component adjusts the text field to reflect the actual value stored in the data
|
|
53
|
+
* once it's no longer 'focused', i.e. when the user stops editing.
|
|
54
|
+
*/
|
|
55
|
+
export const ResettableTextField: React.FC<ResettableTextFieldProps> = ({ rawValue, dayjsValueIsValid, valueInInputFormat, focused, inputProps, ...props }) => {
|
|
56
|
+
const value = useRef<InputRef>({ lastInput: inputProps?.value, toShow: inputProps?.value });
|
|
57
|
+
if (!focused) {
|
|
58
|
+
// The input text is not focused, therefore let's show the value actually stored in the data
|
|
59
|
+
if (!dayjsValueIsValid) {
|
|
60
|
+
// pass through the "raw" value in case it can't be formatted by dayjs
|
|
61
|
+
value.current.toShow = typeof rawValue === 'string' || rawValue === null || rawValue === undefined ? rawValue : JSON.stringify(rawValue)
|
|
62
|
+
} else {
|
|
63
|
+
// otherwise use the specified format
|
|
64
|
+
value.current.toShow = valueInInputFormat;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (focused && inputProps?.value !== value.current.lastInput) {
|
|
68
|
+
// Show the current text the user is typing into the text input
|
|
69
|
+
value.current.lastInput = inputProps?.value;
|
|
70
|
+
value.current.toShow = inputProps?.value;
|
|
71
|
+
}
|
|
72
|
+
return <TextField {...props} inputProps={{ ...inputProps, value: value.current.toShow || '' }} />
|
|
73
|
+
}
|