@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.
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { ArrayLayoutProps, RankedTester } from '@jsonforms/core';
3
- export declare const MaterialArrayLayoutRenderer: ({ visible, enabled, id, uischema, schema, label, rootSchema, renderers, cells, data, path, errors, uischemas, addItem }: ArrayLayoutProps) => JSX.Element;
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;
@@ -1,3 +1,19 @@
1
+ import { TextFieldProps } from '@mui/material';
1
2
  import dayjs from 'dayjs';
2
- export declare const createOnChangeHandler: (path: string, handleChange: (path: string, value: any) => void, saveFormat: string | undefined) => (time: dayjs.Dayjs) => void;
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.2",
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.5.0",
77
+ "@emotion/react": "^11.4.1",
78
78
  "@emotion/styled": "^11.3.0",
79
- "@jsonforms/core": "3.0.0-beta.2",
80
- "@jsonforms/react": "3.0.0-beta.2",
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.2",
89
- "@jsonforms/react": "^3.0.0-beta.2",
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": "8e9c14dcf049ee4b31baf9c40a86b394cba33139"
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, TextField } from '@mui/material';
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 { createOnChangeHandler, getData, useFocus } from '../util';
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={getData(data, saveFormat)}
98
+ value={value}
89
99
  clearable
90
100
  onChange={onChange}
91
101
  inputFormat={format}
92
102
  disableMaskedInput
93
- views={appliedUiSchemaOptions.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
- <TextField
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={{ ...params.inputProps, type: 'text' }}
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, TextField } from '@mui/material';
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 { createOnChangeHandler, getData, useFocus } from '../util';
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={getData(data, saveFormat)}
100
+ value={value}
91
101
  clearable
92
102
  onChange={onChange}
93
103
  inputFormat={format}
94
104
  disableMaskedInput
95
105
  ampm={!!appliedUiSchemaOptions.ampm}
96
- views={appliedUiSchemaOptions.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
- <TextField
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={{ ...params.inputProps, type: 'text' }}
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, TextField } from '@mui/material';
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 { createOnChangeHandler, getData, useFocus } from '../util';
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
- const saveFormat = appliedUiSchemaOptions.timeSaveFormat ?? 'HH:mm:ss';
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={getData(data, saveFormat)}
100
+ value={value}
91
101
  clearable
92
102
  onChange={onChange}
93
103
  inputFormat={format}
94
104
  disableMaskedInput
95
105
  ampm={!!appliedUiSchemaOptions.ampm}
96
- views={appliedUiSchemaOptions.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
- <TextField
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={{ ...params.inputProps, type: 'text' }}
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
- {appliedUiSchemaOptions.showSortButtons ? (
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
- enabled,
40
- id,
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
- renderers={renderers}
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
+ }