@entryscape/rdforms 10.3.0 → 10.5.0
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/dist/rdforms.bmd.js +10 -10
- package/dist/rdforms.bootstrap.js +16 -16
- package/dist/rdforms.jquery.js +3 -3
- package/dist/rdforms.node.js +1 -1
- package/dist/rdforms.react.js +234 -42
- package/package.json +17 -10
- package/src/model/ChoiceBinding.js +0 -8
- package/src/model/engine.js +9 -12
- package/src/template/Item.js +4 -0
- package/src/utils.js +1 -1
- package/src/view/Editor.js +16 -9
- package/src/view/View.js +45 -22
- package/src/view/jquery/text.js +17 -20
- package/src/view/jquery/util.js +170 -0
- package/src/view/react/buttons.js +18 -7
- package/src/view/react/choiceEditors/CheckBoxesEditor.js +87 -0
- package/src/view/react/choiceEditors/ChoiceLookup.js +1 -1
- package/src/view/react/choiceEditors/ChoiceLookupAndInlineSearch.js +43 -30
- package/src/view/react/choiceEditors/ChoiceSelector.js +30 -17
- package/src/view/react/choiceEditors/RadioButtonsEditor.js +26 -12
- package/src/view/react/choiceEditors/ShowButton.js +3 -3
- package/src/view/react/choiceEditors/index.js +9 -0
- package/src/view/react/components.js +38 -10
- package/src/view/react/date.js +161 -119
- package/src/view/react/duration.js +1 -1
- package/src/view/react/labels.js +48 -29
- package/src/view/react/textEditors.js +7 -8
- package/src/view/resources/nls.json +2 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
4
|
+
import Checkbox from '@mui/material/Checkbox';
|
|
5
|
+
import { FormGroup } from '@mui/material';
|
|
6
|
+
import { useLocalizedSortedChoices, useName } from '../hooks';
|
|
7
|
+
import * as engine from '../../../model/engine';
|
|
8
|
+
|
|
9
|
+
const CheckOption = (props) => {
|
|
10
|
+
const { choice, binding, onChoiceChange } = props;
|
|
11
|
+
const [checked, setChecked] = React.useState(choice.value === binding.getValue());
|
|
12
|
+
|
|
13
|
+
const handleChange = (evt) => {
|
|
14
|
+
binding.setChoice(evt.target.checked ? choice.original : null);
|
|
15
|
+
setChecked(evt.target.checked);
|
|
16
|
+
onChoiceChange();
|
|
17
|
+
};
|
|
18
|
+
return <FormControlLabel
|
|
19
|
+
label={choice.label} control={<Checkbox checked={checked} onChange={handleChange}/>}
|
|
20
|
+
{...(choice.mismatch ? { className: 'mismatch' } : {})}
|
|
21
|
+
title={choice.description || choice.seeAlso || choice.value}/>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default function CheckBoxesEditor(props) {
|
|
25
|
+
const [resetCount, setResetCount] = React.useState(0);
|
|
26
|
+
const binding = props.binding;
|
|
27
|
+
const item = binding.getItem();
|
|
28
|
+
const choices = useLocalizedSortedChoices(binding);
|
|
29
|
+
const choiceBindingPairs = useMemo(() => {
|
|
30
|
+
const parentBinding = binding.getParent();
|
|
31
|
+
const val2binding = {};
|
|
32
|
+
// eslint-disable-next-line no-return-assign
|
|
33
|
+
parentBinding.getChildBindingsFor(item).forEach(b => (val2binding[b.getValue()] = b));
|
|
34
|
+
const pairs = choices.map((c) => {
|
|
35
|
+
const existingbinding = val2binding[c.value];
|
|
36
|
+
if (existingbinding) {
|
|
37
|
+
delete val2binding[c.value];
|
|
38
|
+
return [c, existingbinding];
|
|
39
|
+
}
|
|
40
|
+
return [c, engine.create(parentBinding, item)];
|
|
41
|
+
});
|
|
42
|
+
// Add checks for values that are non-conforming (should have mismatch on their choices).
|
|
43
|
+
Object.values(val2binding).forEach((b) => {
|
|
44
|
+
const choice = b.getChoice();
|
|
45
|
+
if (choice) {
|
|
46
|
+
pairs.push([choice, b]);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return pairs;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const [error, setError] = useState(binding.getChoice()?.mismatch === true);
|
|
53
|
+
const row = item.hasStyle('verticalCheckboxes') ? {} : { row: true };
|
|
54
|
+
|
|
55
|
+
const onChoiceChange = () => {
|
|
56
|
+
const newError = choiceBindingPairs.find(pair => pair[1].getChoice()
|
|
57
|
+
&& pair[1].getChoice().mismatch) !== undefined;
|
|
58
|
+
if (newError !== error) {
|
|
59
|
+
setError(newError);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
props.field.toggleClass('mismatchReport', error);
|
|
65
|
+
}, [error]);
|
|
66
|
+
|
|
67
|
+
props.context.clear = () => {
|
|
68
|
+
choiceBindingPairs.forEach(pair => pair[1].setChoice(null));
|
|
69
|
+
setError(false);
|
|
70
|
+
setResetCount(resetCount + 1);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<><FormGroup {...row}>
|
|
75
|
+
{choiceBindingPairs.map(pair => (
|
|
76
|
+
<CheckOption key={`${resetCount}-${pair[1].getHash()}`}
|
|
77
|
+
choice={pair[0]}
|
|
78
|
+
binding={pair[1]}
|
|
79
|
+
onChoiceChange={onChoiceChange} />
|
|
80
|
+
))}
|
|
81
|
+
</FormGroup>{error && (
|
|
82
|
+
<div key="warning" className="rdformsWarning">
|
|
83
|
+
{props.context.view.messages.wrongValueField}
|
|
84
|
+
</div>
|
|
85
|
+
)}</>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import React, { useState, useEffect } from 'react';
|
|
3
|
-
import TextField from '@material
|
|
3
|
+
import TextField from '@mui/material/TextField';
|
|
4
4
|
import renderingContext from '../../renderingContext';
|
|
5
5
|
import { loadLocalizedChoice } from '../hooks';
|
|
6
6
|
import utils from '../../../utils';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import React, { useState, useEffect } from 'react';
|
|
3
|
-
import TextField from '@material
|
|
4
|
-
import Autocomplete from '@material
|
|
5
|
-
import BuildIcon from '@
|
|
6
|
-
import IconButton from '@material
|
|
3
|
+
import TextField from '@mui/material/TextField';
|
|
4
|
+
import Autocomplete from '@mui/material/Autocomplete';
|
|
5
|
+
import BuildIcon from '@mui/icons-material/Build';
|
|
6
|
+
import IconButton from '@mui/material/IconButton';
|
|
7
7
|
import renderingContext from '../../renderingContext';
|
|
8
8
|
import { loadLocalizedChoice, localizedChoice } from '../hooks';
|
|
9
9
|
import ShowButton from './ShowButton';
|
|
@@ -70,7 +70,7 @@ export default (props) => {
|
|
|
70
70
|
<TextField
|
|
71
71
|
aria-labelledby={labelledBy}
|
|
72
72
|
{...params}
|
|
73
|
-
{
|
|
73
|
+
{...(value && value.mismatch ? { error: true } : {})}
|
|
74
74
|
onKeyDown={({ key, keyCode }) => {
|
|
75
75
|
const isEnterKey = key === 'Enter' || keyCode === 13;
|
|
76
76
|
if (isEnterKey && !open) {
|
|
@@ -96,29 +96,42 @@ export default (props) => {
|
|
|
96
96
|
});
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
-
return
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
onClick={
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
<Autocomplete
|
|
102
|
+
className="rdformsSearch"
|
|
103
|
+
disableClearable={true}
|
|
104
|
+
fullWidth={false}
|
|
105
|
+
value={value}
|
|
106
|
+
options={options}
|
|
107
|
+
filterOptions={(fopts) => fopts}
|
|
108
|
+
open={open}
|
|
109
|
+
onOpen={() => setOpen(true)}
|
|
110
|
+
onClose={() => setOpen(false)}
|
|
111
|
+
onInputChange={(event, newInputValue) => setInputValue(newInputValue)}
|
|
112
|
+
onChange={onChange}
|
|
113
|
+
isOptionEqualToValue={(option, choice) => option.value === choice.value}
|
|
114
|
+
getOptionLabel={(choice) =>
|
|
115
|
+
choice === null ? '' : choice.label || choice.value
|
|
116
|
+
}
|
|
117
|
+
getOptionDisabled={(option) => option.mismatch === true}
|
|
118
|
+
renderInput={renderInput}
|
|
119
|
+
/>
|
|
120
|
+
<ShowButton {...props} onClick={showHandler} />
|
|
121
|
+
{value && value.original.upgrade && (
|
|
122
|
+
<IconButton
|
|
123
|
+
aria-label={props.context.view.messages.edit_upgrade}
|
|
124
|
+
title={props.context.view.messages.edit_upgrade}
|
|
125
|
+
onClick={upgradeHandler}
|
|
126
|
+
>
|
|
127
|
+
<BuildIcon />
|
|
128
|
+
</IconButton>
|
|
129
|
+
)}
|
|
130
|
+
{error && (
|
|
131
|
+
<div key="warning" className="rdformsWarning">
|
|
132
|
+
{props.context.view.messages.wrongValueField}
|
|
133
|
+
</div>
|
|
134
|
+
)}
|
|
135
|
+
</>
|
|
136
|
+
);
|
|
124
137
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import React, { useState, useEffect } from 'react';
|
|
3
|
-
import TextField from '@material
|
|
4
|
-
import Autocomplete from '@material
|
|
3
|
+
import TextField from '@mui/material/TextField';
|
|
4
|
+
import Autocomplete from '@mui/material/Autocomplete';
|
|
5
5
|
import renderingContext from '../../renderingContext';
|
|
6
6
|
import { useLocalizedSortedChoices, useLocalizedChoice } from '../hooks';
|
|
7
7
|
|
|
@@ -35,8 +35,13 @@ export default (props) => {
|
|
|
35
35
|
const renderInput = (params) => {
|
|
36
36
|
params.inputProps = params.inputProps || {};
|
|
37
37
|
params.inputProps['aria-labelledby'] = labelledBy;
|
|
38
|
-
return (
|
|
39
|
-
|
|
38
|
+
return (
|
|
39
|
+
<TextField
|
|
40
|
+
{...params}
|
|
41
|
+
{...(value && value.mismatch ? { error: true } : {})}
|
|
42
|
+
variant={renderingContext.materialVariant}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
40
45
|
};
|
|
41
46
|
const handleChange = (event, newChoice) => {
|
|
42
47
|
binding.setChoice(newChoice.original);
|
|
@@ -44,17 +49,25 @@ export default (props) => {
|
|
|
44
49
|
setError(newChoice.original.mismatch === true);
|
|
45
50
|
};
|
|
46
51
|
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<Autocomplete
|
|
55
|
+
className="rdformsSearch"
|
|
56
|
+
disableClearable={true}
|
|
57
|
+
value={value}
|
|
58
|
+
options={choices}
|
|
59
|
+
onChange={handleChange}
|
|
60
|
+
isOptionEqualToValue={(option, choice) => option.value === choice.value}
|
|
61
|
+
getOptionLabel={(choice) => (choice === null ? '' : choice.label)}
|
|
62
|
+
getOptionDisabled={(option) => option.mismatch === true}
|
|
63
|
+
filterSelectedOptions
|
|
64
|
+
renderInput={renderInput}
|
|
65
|
+
/>
|
|
66
|
+
{error && (
|
|
67
|
+
<div key="warning" className="rdformsWarning">
|
|
68
|
+
{props.context.view.messages.wrongValueField}
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
60
73
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import React, { useState, useEffect } from 'react';
|
|
3
|
-
import Radio from '@material
|
|
4
|
-
import RadioGroup from '@material
|
|
5
|
-
import FormControlLabel from '@material
|
|
6
|
-
import FormControl from '@material
|
|
3
|
+
import Radio from '@mui/material/Radio';
|
|
4
|
+
import RadioGroup from '@mui/material/RadioGroup';
|
|
5
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
6
|
+
import FormControl from '@mui/material/FormControl';
|
|
7
7
|
import { useLocalizedSortedChoices, useName } from '../hooks';
|
|
8
8
|
|
|
9
9
|
const ChoiceOption = props => <FormControlLabel
|
|
@@ -46,13 +46,27 @@ export default function RadioButtonsEditor(props) {
|
|
|
46
46
|
};
|
|
47
47
|
}, []);
|
|
48
48
|
|
|
49
|
-
return (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<FormControl component="fieldset">
|
|
52
|
+
<RadioGroup
|
|
53
|
+
{...row}
|
|
54
|
+
key="radio"
|
|
55
|
+
aria-label={item.getLabel()}
|
|
56
|
+
name={name}
|
|
57
|
+
value={value}
|
|
58
|
+
onChange={handleChange}
|
|
59
|
+
>
|
|
60
|
+
{choices.map((choice) => (
|
|
61
|
+
<ChoiceOption key={choice.value} choice={choice} />
|
|
62
|
+
))}
|
|
63
|
+
</RadioGroup>
|
|
64
|
+
</FormControl>
|
|
65
|
+
{error && (
|
|
66
|
+
<div key="warning" className="rdformsWarning">
|
|
67
|
+
{props.context.view.messages.wrongValueField}
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</>
|
|
57
71
|
);
|
|
58
72
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars,max-len */
|
|
2
2
|
import React, { useMemo } from 'react';
|
|
3
|
-
import SearchIcon from '@
|
|
4
|
-
import IconButton from '@material
|
|
5
|
-
import SvgIcon from '@material
|
|
3
|
+
import SearchIcon from '@mui/icons-material/Search';
|
|
4
|
+
import IconButton from '@mui/material/IconButton';
|
|
5
|
+
import SvgIcon from '@mui/material/SvgIcon';
|
|
6
6
|
|
|
7
7
|
function SearchAndCreateIcon(props) {
|
|
8
8
|
return (
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import renderingContext from '../../renderingContext';
|
|
4
4
|
import RadioButtonsEditor from './RadioButtonsEditor';
|
|
5
|
+
import CheckBoxesEditor from './CheckBoxesEditor';
|
|
5
6
|
import ChoiceSelector from './ChoiceSelector';
|
|
6
7
|
import ChoiceLookup from './ChoiceLookup';
|
|
7
8
|
import ChoiceLookupAndInlineSearch from './ChoiceLookupAndInlineSearch';
|
|
@@ -27,6 +28,14 @@ editors.itemtype('choice').choices().check(radioCheck).register((fieldDiv, bindi
|
|
|
27
28
|
field={fieldDiv}/>);
|
|
28
29
|
});
|
|
29
30
|
|
|
31
|
+
const checkBoxComponent = (fieldDiv, binding, context) => {
|
|
32
|
+
// eslint-disable-next-line no-new
|
|
33
|
+
fieldDiv.appendChild(<CheckBoxesEditor key={binding.getHash()} binding={binding} context={context}
|
|
34
|
+
field={fieldDiv}/>);
|
|
35
|
+
};
|
|
36
|
+
editors.itemtype('choice').choices().style('verticalCheckBoxes').register(checkBoxComponent);
|
|
37
|
+
editors.itemtype('choice').choices().style('horizontalCheckBoxes').register(checkBoxComponent);
|
|
38
|
+
|
|
30
39
|
editors.itemtype('choice').choices().register((fieldDiv, binding, context) => {
|
|
31
40
|
fieldDiv.appendChild(<ChoiceSelector key={binding.getHash()} binding={binding} context={context} field={fieldDiv}/>);
|
|
32
41
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import WarningIcon from '@
|
|
3
|
-
import ErrorIcon from '@
|
|
4
|
-
import OfflineBoltIcon from '@
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import WarningIcon from '@mui/icons-material/Warning';
|
|
3
|
+
import ErrorIcon from '@mui/icons-material/Error';
|
|
4
|
+
import OfflineBoltIcon from '@mui/icons-material/OfflineBolt';
|
|
5
5
|
import renderingContext from '../renderingContext';
|
|
6
6
|
import './labels';
|
|
7
7
|
import './text';
|
|
@@ -13,6 +13,30 @@ import './date';
|
|
|
13
13
|
import './duration';
|
|
14
14
|
import '../bootstrap/auto';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* A wrapper for adding a dom element to
|
|
18
|
+
* a react component.
|
|
19
|
+
*/
|
|
20
|
+
const DOMElementWrapper = ({ element }) => {
|
|
21
|
+
const ref = useRef();
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
ref.current.appendChild(element);
|
|
25
|
+
}, [element]);
|
|
26
|
+
|
|
27
|
+
return <div ref={ref} />;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* If child is not a react component, it creates one.
|
|
32
|
+
*/
|
|
33
|
+
const getReactComponent = (child, index) => {
|
|
34
|
+
// In case child is a dom element
|
|
35
|
+
if (child instanceof Node) return <DOMElementWrapper key={index} element={child} />;
|
|
36
|
+
// In case child is a struct
|
|
37
|
+
if (child.component) return React.createElement(child.component, { key: child.id });
|
|
38
|
+
return child; // Assumes child a react component
|
|
39
|
+
};
|
|
16
40
|
|
|
17
41
|
/**
|
|
18
42
|
* Utility to toggle a set of classes potentially separated by spaces in a set.
|
|
@@ -71,11 +95,11 @@ const newStruct = (Tag, parent) => {
|
|
|
71
95
|
// -- START: Initial methods used before react kicks in.
|
|
72
96
|
toggleClass: (clsStr, addOrNot) => toggleClass(firstClsSet, clsStr, addOrNot),
|
|
73
97
|
domQuery: selector => (selectorInClasses(selector, firstClsSet) ? ext : findStruct(selector, firstChildArr)),
|
|
74
|
-
appendChild: struct => {
|
|
98
|
+
appendChild: (struct) => {
|
|
75
99
|
firstChildArr.push(struct);
|
|
76
100
|
},
|
|
77
101
|
appendAfter: (struct, sibling) => {
|
|
78
|
-
firstChildArr.splice(firstChildArr.indexOf(sibling) + 1, 0, struct)
|
|
102
|
+
firstChildArr.splice(firstChildArr.indexOf(sibling) + 1, 0, struct);
|
|
79
103
|
},
|
|
80
104
|
text: (str) => {
|
|
81
105
|
firstTextStr = str;
|
|
@@ -128,9 +152,12 @@ const newStruct = (Tag, parent) => {
|
|
|
128
152
|
setAttrs(oldAttrs => updateObjAttr(Object.assign({}, oldAttrs), attr, value));
|
|
129
153
|
};
|
|
130
154
|
// -- END
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
155
|
+
return (
|
|
156
|
+
<Tag className={clsSet.join(' ')}>
|
|
157
|
+
{childArr.map((child, index) => getReactComponent(child, index))}
|
|
158
|
+
{text}
|
|
159
|
+
</Tag>
|
|
160
|
+
);
|
|
134
161
|
},
|
|
135
162
|
};
|
|
136
163
|
|
|
@@ -188,7 +215,6 @@ renderingContext.destroyDomNode = (struct /* , view */) => {
|
|
|
188
215
|
}
|
|
189
216
|
};
|
|
190
217
|
|
|
191
|
-
|
|
192
218
|
renderingContext.prePresenterViewRenderer = () => {};
|
|
193
219
|
|
|
194
220
|
renderingContext.materialVariant = 'filled';
|
|
@@ -222,3 +248,5 @@ renderingContext.renderValidationMessage = (fieldDiv, type, message) => {
|
|
|
222
248
|
fieldDiv.appendChild(<div className="rdformsValidationMessageWrapper" key={ `rdforms_valcount_${validationCounter}`}
|
|
223
249
|
><ValidationIcon/><span className="rdformsValidationMessage">{message}</span></div>);
|
|
224
250
|
};
|
|
251
|
+
|
|
252
|
+
renderingContext.multiValueSupport = true;
|