@jsonforms/material-renderers 3.0.0-beta.4 → 3.0.0-rc.1
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/docs/assets/js/search.json +1 -1
- package/docs/globals.html +167 -40
- package/docs/index.html +15 -0
- package/docs/interfaces/categorizationstate.html +1 -1
- package/docs/interfaces/categorizationstepperstate.html +1 -1
- package/docs/interfaces/inputref.html +168 -0
- package/docs/interfaces/materialcategorizationlayoutrendererprops.html +49 -4
- package/docs/interfaces/materialcategorizationstepperlayoutrendererprops.html +46 -1
- package/docs/interfaces/materiallabelablelayoutrendererprops.html +328 -0
- package/docs/interfaces/materiallayoutrendererprops.html +5 -0
- package/docs/interfaces/withoptionlabel.html +3 -3
- package/lib/additional/MaterialLabelRenderer.d.ts +3 -3
- package/lib/cells/MaterialEnumCell.d.ts +2 -1
- package/lib/cells/MaterialOneOfEnumCell.d.ts +2 -1
- package/lib/controls/MaterialEnumControl.d.ts +2 -1
- package/lib/controls/MaterialOneOfEnumControl.d.ts +2 -1
- package/lib/controls/index.d.ts +2 -2
- package/lib/jsonforms-react-material.cjs.js +143 -65
- package/lib/jsonforms-react-material.cjs.js.map +1 -1
- package/lib/jsonforms-react-material.esm.js +137 -57
- package/lib/jsonforms-react-material.esm.js.map +1 -1
- package/lib/layouts/MaterialCategorizationLayout.d.ts +4 -3
- package/lib/layouts/MaterialCategorizationStepperLayout.d.ts +4 -3
- package/lib/layouts/MaterialGroupLayout.d.ts +2 -2
- package/lib/layouts/MaterialHorizontalLayout.d.ts +1 -1
- package/lib/layouts/MaterialVerticalLayout.d.ts +1 -1
- package/lib/mui-controls/MuiAutocomplete.d.ts +2 -2
- package/lib/mui-controls/MuiSelect.d.ts +2 -1
- package/lib/util/datejs.d.ts +17 -1
- package/lib/util/i18nDefaults.d.ts +3 -0
- package/lib/util/index.d.ts +1 -0
- package/lib/util/layout.d.ts +3 -0
- package/package.json +9 -9
- package/src/additional/MaterialLabelRenderer.tsx +5 -7
- package/src/additional/MaterialListWithDetailRenderer.tsx +4 -0
- package/src/cells/MaterialEnumCell.tsx +4 -3
- package/src/cells/MaterialOneOfEnumCell.tsx +3 -3
- package/src/controls/MaterialDateControl.tsx +30 -12
- package/src/controls/MaterialDateTimeControl.tsx +32 -13
- package/src/controls/MaterialEnumControl.tsx +12 -5
- package/src/controls/MaterialOneOfEnumControl.tsx +13 -5
- package/src/controls/MaterialRadioGroup.tsx +1 -1
- package/src/controls/MaterialTimeControl.tsx +31 -13
- package/src/layouts/MaterialCategorizationLayout.tsx +18 -9
- package/src/layouts/MaterialCategorizationStepperLayout.tsx +19 -12
- package/src/layouts/MaterialGroupLayout.tsx +6 -5
- package/src/mui-controls/MuiAutocomplete.tsx +81 -37
- package/src/mui-controls/MuiInputText.tsx +4 -1
- package/src/mui-controls/MuiSelect.tsx +10 -5
- package/src/util/datejs.tsx +73 -0
- package/src/util/i18nDefaults.ts +3 -0
- package/src/util/index.ts +1 -0
- package/src/util/layout.tsx +4 -0
- package/stats.html +1 -1
- package/test/renderers/MaterialArrayLayout.test.tsx +4 -4
- package/test/renderers/MaterialCategorizationLayout.test.tsx +17 -7
- package/test/renderers/MaterialCategorizationStepperLayout.test.tsx +21 -11
- package/test/renderers/MaterialDateControl.test.tsx +27 -0
- package/test/renderers/MaterialDateTimeControl.test.tsx +29 -2
- package/test/renderers/MaterialGroupLayout.test.tsx +4 -1
- package/test/renderers/MaterialInputControl.test.tsx +4 -0
- package/test/renderers/MaterialLabelRenderer.test.tsx +2 -1
- package/test/renderers/MaterialTimeControl.test.tsx +28 -1
- package/test/renderers/util.ts +5 -0
- package/src/util/datejs.ts +0 -32
|
@@ -22,12 +22,13 @@
|
|
|
22
22
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
23
|
THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
|
-
import React, {useState} from 'react';
|
|
25
|
+
import React, {useState, useMemo} from 'react';
|
|
26
26
|
import { AppBar, Hidden, Tab, Tabs } from '@mui/material';
|
|
27
27
|
import {
|
|
28
28
|
and,
|
|
29
29
|
Categorization,
|
|
30
30
|
Category,
|
|
31
|
+
deriveLabelForUISchemaElement,
|
|
31
32
|
isVisible,
|
|
32
33
|
RankedTester,
|
|
33
34
|
rankWith,
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
UISchemaElement,
|
|
37
38
|
uiTypeIs
|
|
38
39
|
} from '@jsonforms/core';
|
|
39
|
-
import { withJsonFormsLayoutProps } from '@jsonforms/react';
|
|
40
|
+
import { TranslateProps, withJsonFormsLayoutProps, withTranslateProps } from '@jsonforms/react';
|
|
40
41
|
import {
|
|
41
42
|
AjvProps,
|
|
42
43
|
MaterialLayoutRenderer,
|
|
@@ -68,7 +69,7 @@ export interface CategorizationState {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
export interface MaterialCategorizationLayoutRendererProps
|
|
71
|
-
extends StatePropsOfLayout, AjvProps {
|
|
72
|
+
extends StatePropsOfLayout, AjvProps, TranslateProps {
|
|
72
73
|
selected?: number;
|
|
73
74
|
ownState?: boolean;
|
|
74
75
|
data?: any;
|
|
@@ -87,13 +88,14 @@ export const MaterialCategorizationLayoutRenderer = (props: MaterialCategorizati
|
|
|
87
88
|
enabled,
|
|
88
89
|
selected,
|
|
89
90
|
onChange,
|
|
90
|
-
ajv
|
|
91
|
+
ajv,
|
|
92
|
+
t
|
|
91
93
|
} = props;
|
|
92
94
|
const categorization = uischema as Categorization;
|
|
93
95
|
const [activeCategory, setActiveCategory]= useState<number|undefined>(selected??0);
|
|
94
|
-
const categories = categorization.elements.filter((category: Category) =>
|
|
96
|
+
const categories = useMemo(() => categorization.elements.filter((category: Category) =>
|
|
95
97
|
isVisible(category, data, undefined, ajv)
|
|
96
|
-
);
|
|
98
|
+
),[categorization, data, ajv]);
|
|
97
99
|
const childProps: MaterialLayoutRendererProps = {
|
|
98
100
|
elements: categories[activeCategory].elements,
|
|
99
101
|
schema,
|
|
@@ -110,12 +112,19 @@ export const MaterialCategorizationLayoutRenderer = (props: MaterialCategorizati
|
|
|
110
112
|
}
|
|
111
113
|
setActiveCategory(value);
|
|
112
114
|
};
|
|
115
|
+
|
|
116
|
+
const tabLabels = useMemo(() => {
|
|
117
|
+
return categories.map((e: Category) =>
|
|
118
|
+
deriveLabelForUISchemaElement(e, t)
|
|
119
|
+
)
|
|
120
|
+
}, [categories, t])
|
|
121
|
+
|
|
113
122
|
return (
|
|
114
123
|
<Hidden xsUp={!visible}>
|
|
115
124
|
<AppBar position='static'>
|
|
116
125
|
<Tabs value={activeCategory} onChange={onTabChange} textColor='inherit' indicatorColor='secondary' variant='scrollable'>
|
|
117
|
-
{categories.map((
|
|
118
|
-
<Tab key={idx} label={
|
|
126
|
+
{categories.map((_, idx: number) => (
|
|
127
|
+
<Tab key={idx} label={tabLabels[idx]} />
|
|
119
128
|
))}
|
|
120
129
|
</Tabs>
|
|
121
130
|
</AppBar>
|
|
@@ -126,4 +135,4 @@ export const MaterialCategorizationLayoutRenderer = (props: MaterialCategorizati
|
|
|
126
135
|
);
|
|
127
136
|
};
|
|
128
137
|
|
|
129
|
-
export default withJsonFormsLayoutProps(
|
|
138
|
+
export default withAjvProps(withTranslateProps(withJsonFormsLayoutProps(MaterialCategorizationLayoutRenderer)));
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
23
|
THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
|
-
import React, {useState} from 'react';
|
|
25
|
+
import React, {useState, useMemo} from 'react';
|
|
26
26
|
import merge from 'lodash/merge';
|
|
27
27
|
import { Button, Hidden, Step, StepButton, Stepper } from '@mui/material';
|
|
28
28
|
import {
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
Categorization,
|
|
31
31
|
categorizationHasCategory,
|
|
32
32
|
Category,
|
|
33
|
+
deriveLabelForUISchemaElement,
|
|
33
34
|
isVisible,
|
|
34
35
|
optionIs,
|
|
35
36
|
RankedTester,
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
StatePropsOfLayout,
|
|
38
39
|
uiTypeIs
|
|
39
40
|
} from '@jsonforms/core';
|
|
40
|
-
import { withJsonFormsLayoutProps } from '@jsonforms/react';
|
|
41
|
+
import { TranslateProps, withJsonFormsLayoutProps, withTranslateProps } from '@jsonforms/react';
|
|
41
42
|
import {
|
|
42
43
|
AjvProps,
|
|
43
44
|
MaterialLayoutRenderer,
|
|
@@ -59,7 +60,7 @@ export interface CategorizationStepperState {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
export interface MaterialCategorizationStepperLayoutRendererProps
|
|
62
|
-
extends StatePropsOfLayout, AjvProps {
|
|
63
|
+
extends StatePropsOfLayout, AjvProps, TranslateProps {
|
|
63
64
|
data: any;
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -79,7 +80,8 @@ export const MaterialCategorizationStepperLayoutRenderer = (props: MaterialCateg
|
|
|
79
80
|
visible,
|
|
80
81
|
cells,
|
|
81
82
|
config,
|
|
82
|
-
ajv
|
|
83
|
+
ajv,
|
|
84
|
+
t
|
|
83
85
|
} = props;
|
|
84
86
|
const categorization = uischema as Categorization;
|
|
85
87
|
const appliedUiSchemaOptions = merge({}, config, uischema.options);
|
|
@@ -94,9 +96,9 @@ export const MaterialCategorizationStepperLayoutRenderer = (props: MaterialCateg
|
|
|
94
96
|
const buttonStyle = {
|
|
95
97
|
marginRight: '1em'
|
|
96
98
|
};
|
|
97
|
-
const categories = categorization.elements.filter((category: Category) =>
|
|
99
|
+
const categories = useMemo(() => categorization.elements.filter((category: Category) =>
|
|
98
100
|
isVisible(category, data, undefined, ajv)
|
|
99
|
-
);
|
|
101
|
+
),[categorization, data, ajv]);
|
|
100
102
|
const childProps: MaterialLayoutRendererProps = {
|
|
101
103
|
elements: categories[activeCategory].elements,
|
|
102
104
|
schema,
|
|
@@ -106,13 +108,18 @@ export const MaterialCategorizationStepperLayoutRenderer = (props: MaterialCateg
|
|
|
106
108
|
renderers,
|
|
107
109
|
cells
|
|
108
110
|
};
|
|
111
|
+
const tabLabels = useMemo(() => {
|
|
112
|
+
return categories.map((e: Category) =>
|
|
113
|
+
deriveLabelForUISchemaElement(e, t)
|
|
114
|
+
)
|
|
115
|
+
}, [categories, t])
|
|
109
116
|
return (
|
|
110
117
|
<Hidden xsUp={!visible}>
|
|
111
118
|
<Stepper activeStep={activeCategory} nonLinear>
|
|
112
|
-
{categories.map((
|
|
113
|
-
<Step key={
|
|
119
|
+
{categories.map((_: Category, idx: number) => (
|
|
120
|
+
<Step key={tabLabels[idx]}>
|
|
114
121
|
<StepButton onClick={() => handleStep(idx)}>
|
|
115
|
-
{
|
|
122
|
+
{tabLabels[idx]}
|
|
116
123
|
</StepButton>
|
|
117
124
|
</Step>
|
|
118
125
|
))}
|
|
@@ -144,6 +151,6 @@ export const MaterialCategorizationStepperLayoutRenderer = (props: MaterialCateg
|
|
|
144
151
|
);
|
|
145
152
|
};
|
|
146
153
|
|
|
147
|
-
export default
|
|
148
|
-
MaterialCategorizationStepperLayoutRenderer
|
|
149
|
-
));
|
|
154
|
+
export default withAjvProps(withTranslateProps(
|
|
155
|
+
withJsonFormsLayoutProps(MaterialCategorizationStepperLayoutRenderer
|
|
156
|
+
)));
|
|
@@ -34,21 +34,21 @@ import {
|
|
|
34
34
|
withIncreasedRank,
|
|
35
35
|
} from '@jsonforms/core';
|
|
36
36
|
import {
|
|
37
|
+
MaterialLabelableLayoutRendererProps,
|
|
37
38
|
MaterialLayoutRenderer,
|
|
38
|
-
MaterialLayoutRendererProps
|
|
39
39
|
} from '../util/layout';
|
|
40
40
|
import { withJsonFormsLayoutProps } from '@jsonforms/react';
|
|
41
41
|
|
|
42
42
|
export const groupTester: RankedTester = rankWith(1, uiTypeIs('Group'));
|
|
43
43
|
const style: { [x: string]: any } = { marginBottom: '10px' };
|
|
44
44
|
|
|
45
|
-
const GroupComponent = React.memo(({ visible, enabled, uischema, ...props }:
|
|
45
|
+
const GroupComponent = React.memo(({ visible, enabled, uischema, label, ...props }: MaterialLabelableLayoutRendererProps) => {
|
|
46
46
|
const groupLayout = uischema as GroupLayout;
|
|
47
47
|
return (
|
|
48
48
|
<Hidden xsUp={!visible}>
|
|
49
49
|
<Card style={style}>
|
|
50
|
-
{!isEmpty(
|
|
51
|
-
<CardHeader title={
|
|
50
|
+
{!isEmpty(label) && (
|
|
51
|
+
<CardHeader title={label} />
|
|
52
52
|
)}
|
|
53
53
|
<CardContent>
|
|
54
54
|
<MaterialLayoutRenderer {...props} visible={visible} enabled={enabled} elements={groupLayout.elements} />
|
|
@@ -58,7 +58,7 @@ const GroupComponent = React.memo(({ visible, enabled, uischema, ...props }: Mat
|
|
|
58
58
|
);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
export const MaterializedGroupLayoutRenderer = ({ uischema, schema, path, visible, enabled, renderers, cells, direction }: LayoutProps) => {
|
|
61
|
+
export const MaterializedGroupLayoutRenderer = ({ uischema, schema, path, visible, enabled, renderers, cells, direction, label }: LayoutProps) => {
|
|
62
62
|
const groupLayout = uischema as GroupLayout;
|
|
63
63
|
|
|
64
64
|
return (
|
|
@@ -72,6 +72,7 @@ export const MaterializedGroupLayoutRenderer = ({ uischema, schema, path, visibl
|
|
|
72
72
|
uischema={uischema}
|
|
73
73
|
renderers={renderers}
|
|
74
74
|
cells={cells}
|
|
75
|
+
label={label}
|
|
75
76
|
/>
|
|
76
77
|
);
|
|
77
78
|
};
|
|
@@ -23,15 +23,18 @@
|
|
|
23
23
|
THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
25
|
import React, { ReactNode } from 'react';
|
|
26
|
-
import { EnumCellProps, EnumOption, WithClassname } from '@jsonforms/core';
|
|
26
|
+
import { ControlProps, EnumCellProps, EnumOption, isDescriptionHidden, WithClassname } from '@jsonforms/core';
|
|
27
27
|
|
|
28
28
|
import {
|
|
29
29
|
Autocomplete,
|
|
30
30
|
AutocompleteRenderOptionState,
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
FilterOptionsState,
|
|
32
|
+
FormHelperText,
|
|
33
|
+
Hidden,
|
|
34
|
+
TextField
|
|
33
35
|
} from '@mui/material';
|
|
34
36
|
import merge from 'lodash/merge';
|
|
37
|
+
import { useFocus } from '../util/focus';
|
|
35
38
|
|
|
36
39
|
export interface WithOptionLabel {
|
|
37
40
|
getOptionLabel?(option: EnumOption) : string;
|
|
@@ -39,8 +42,13 @@ export interface WithOptionLabel {
|
|
|
39
42
|
filterOptions?(options: EnumOption[], state: FilterOptionsState<EnumOption>) : EnumOption[];
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
export const MuiAutocomplete = (props: EnumCellProps & WithClassname & WithOptionLabel) => {
|
|
45
|
+
export const MuiAutocomplete = (props: ControlProps & EnumCellProps & WithClassname & WithOptionLabel) => {
|
|
43
46
|
const {
|
|
47
|
+
description,
|
|
48
|
+
errors,
|
|
49
|
+
visible,
|
|
50
|
+
required,
|
|
51
|
+
label,
|
|
44
52
|
data,
|
|
45
53
|
className,
|
|
46
54
|
id,
|
|
@@ -52,44 +60,80 @@ export const MuiAutocomplete = (props: EnumCellProps & WithClassname & WithOptio
|
|
|
52
60
|
config,
|
|
53
61
|
getOptionLabel,
|
|
54
62
|
renderOption,
|
|
55
|
-
filterOptions
|
|
63
|
+
filterOptions,
|
|
64
|
+
isValid
|
|
56
65
|
} = props;
|
|
57
66
|
const appliedUiSchemaOptions = merge({}, config, uischema.options);
|
|
58
67
|
const [inputValue, setInputValue] = React.useState(data ?? '');
|
|
68
|
+
const [focused, onFocus, onBlur] = useFocus();
|
|
59
69
|
|
|
60
70
|
const findOption = options.find(o => o.value === data) ?? null;
|
|
71
|
+
|
|
72
|
+
const showDescription = !isDescriptionHidden(
|
|
73
|
+
visible,
|
|
74
|
+
description,
|
|
75
|
+
focused,
|
|
76
|
+
appliedUiSchemaOptions.showUnfocusedDescription
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const firstFormHelperText = showDescription
|
|
80
|
+
? description
|
|
81
|
+
: !isValid
|
|
82
|
+
? errors
|
|
83
|
+
: null;
|
|
84
|
+
const secondFormHelperText = showDescription && !isValid ? errors : null;
|
|
85
|
+
|
|
61
86
|
return (
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
<Hidden xsUp={!visible}>
|
|
88
|
+
<Autocomplete
|
|
89
|
+
className={className}
|
|
90
|
+
id={id}
|
|
91
|
+
disabled={!enabled}
|
|
92
|
+
value={findOption}
|
|
93
|
+
onChange={(_event: any, newValue: EnumOption | null) => {
|
|
94
|
+
handleChange(path, newValue?.value);
|
|
95
|
+
}}
|
|
96
|
+
inputValue={inputValue}
|
|
97
|
+
onInputChange={(_event, newInputValue) => {
|
|
98
|
+
setInputValue(newInputValue);
|
|
99
|
+
}}
|
|
100
|
+
autoHighlight
|
|
101
|
+
autoSelect
|
|
102
|
+
autoComplete
|
|
103
|
+
fullWidth
|
|
104
|
+
options={options}
|
|
105
|
+
getOptionLabel={getOptionLabel || (option => option?.label)}
|
|
106
|
+
freeSolo={false}
|
|
107
|
+
renderInput={params => {
|
|
108
|
+
return(
|
|
109
|
+
<TextField
|
|
110
|
+
label={label}
|
|
111
|
+
variant={'standard'}
|
|
112
|
+
type='text'
|
|
113
|
+
inputProps={params.inputProps}
|
|
114
|
+
inputRef={params.InputProps.ref}
|
|
115
|
+
autoFocus={appliedUiSchemaOptions.focus}
|
|
116
|
+
disabled={!enabled}
|
|
117
|
+
{...params}
|
|
118
|
+
id={id + '-input'}
|
|
119
|
+
required={required && !appliedUiSchemaOptions.hideRequiredAsterisk}
|
|
120
|
+
error={!isValid}
|
|
121
|
+
fullWidth={!appliedUiSchemaOptions.trim}
|
|
122
|
+
InputLabelProps={data ? { shrink: true } : undefined}
|
|
123
|
+
onFocus={onFocus}
|
|
124
|
+
onBlur={onBlur}
|
|
125
|
+
focused={focused}
|
|
126
|
+
/>
|
|
127
|
+
)}}
|
|
128
|
+
renderOption={renderOption}
|
|
129
|
+
filterOptions={filterOptions}
|
|
130
|
+
/>
|
|
131
|
+
<FormHelperText error={!isValid && !showDescription}>
|
|
132
|
+
{firstFormHelperText}
|
|
133
|
+
</FormHelperText>
|
|
134
|
+
<FormHelperText error={!isValid}>
|
|
135
|
+
{secondFormHelperText}
|
|
136
|
+
</FormHelperText>
|
|
137
|
+
</Hidden>
|
|
94
138
|
);
|
|
95
139
|
};
|
|
@@ -40,6 +40,9 @@ interface MuiTextInputProps {
|
|
|
40
40
|
muiInputProps?: InputProps['inputProps'];
|
|
41
41
|
inputComponent?: InputProps['inputComponent'];
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
const eventToValue = (ev:any) => ev.target.value === '' ? undefined : ev.target.value;
|
|
45
|
+
|
|
43
46
|
export const MuiInputText = React.memo((props: CellProps & WithClassname & MuiTextInputProps) => {
|
|
44
47
|
const [showAdornment, setShowAdornment] = useState(false);
|
|
45
48
|
const {
|
|
@@ -71,7 +74,7 @@ export const MuiInputText = React.memo((props: CellProps & WithClassname & MuiTe
|
|
|
71
74
|
inputProps.size = maxLength;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
|
-
const [inputText, onChange, onClear] = useDebouncedChange(handleChange, '', data, path);
|
|
77
|
+
const [inputText, onChange, onClear] = useDebouncedChange(handleChange, '', data, path, eventToValue);
|
|
75
78
|
const onPointerEnter = () => setShowAdornment(true);
|
|
76
79
|
const onPointerLeave = () => setShowAdornment(false);
|
|
77
80
|
|
|
@@ -22,25 +22,30 @@
|
|
|
22
22
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
23
23
|
THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
|
-
import React from 'react';
|
|
25
|
+
import React, { useMemo } from 'react';
|
|
26
26
|
import { EnumCellProps, WithClassname } from '@jsonforms/core';
|
|
27
27
|
|
|
28
28
|
import { MenuItem, Select } from '@mui/material';
|
|
29
29
|
import merge from 'lodash/merge';
|
|
30
|
+
import { TranslateProps } from '@jsonforms/react';
|
|
31
|
+
import { i18nDefaults } from '../util';
|
|
30
32
|
|
|
31
|
-
export const MuiSelect = React.memo((props: EnumCellProps & WithClassname) => {
|
|
33
|
+
export const MuiSelect = React.memo((props: EnumCellProps & WithClassname & TranslateProps) => {
|
|
32
34
|
const {
|
|
33
35
|
data,
|
|
34
36
|
className,
|
|
35
37
|
id,
|
|
36
38
|
enabled,
|
|
39
|
+
schema,
|
|
37
40
|
uischema,
|
|
38
41
|
path,
|
|
39
42
|
handleChange,
|
|
40
43
|
options,
|
|
41
|
-
config
|
|
44
|
+
config,
|
|
45
|
+
t
|
|
42
46
|
} = props;
|
|
43
47
|
const appliedUiSchemaOptions = merge({}, config, uischema.options);
|
|
48
|
+
const noneOptionLabel = useMemo(() => t('enum.none', i18nDefaults['enum.none'], { schema, uischema, path}), [t, schema, uischema, path]);
|
|
44
49
|
|
|
45
50
|
return (
|
|
46
51
|
<Select
|
|
@@ -49,11 +54,11 @@ export const MuiSelect = React.memo((props: EnumCellProps & WithClassname) => {
|
|
|
49
54
|
disabled={!enabled}
|
|
50
55
|
autoFocus={appliedUiSchemaOptions.focus}
|
|
51
56
|
value={data !== undefined ? data : ''}
|
|
52
|
-
onChange={ev =>
|
|
57
|
+
onChange={ev =>handleChange(path, ev.target.value || undefined)}
|
|
53
58
|
fullWidth={true}
|
|
54
59
|
variant={'standard'}
|
|
55
60
|
>
|
|
56
|
-
{[<MenuItem value='' key=
|
|
61
|
+
{[<MenuItem value={''} key='jsonforms.enum.none'><em>{noneOptionLabel}</em></MenuItem>].concat(
|
|
57
62
|
options.map(optionValue => (
|
|
58
63
|
<MenuItem value={optionValue.value} key={optionValue.value}>
|
|
59
64
|
{optionValue.label}
|
|
@@ -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
|
+
}
|
package/src/util/index.ts
CHANGED
package/src/util/layout.tsx
CHANGED
|
@@ -110,3 +110,7 @@ export const withAjvProps = <P extends {}>(Component: ComponentType<AjvProps & P
|
|
|
110
110
|
|
|
111
111
|
return (<Component {...props} ajv={ajv} />);
|
|
112
112
|
};
|
|
113
|
+
|
|
114
|
+
export interface MaterialLabelableLayoutRendererProps extends MaterialLayoutRendererProps {
|
|
115
|
+
label?: string;
|
|
116
|
+
}
|