@scenid/react-formulator 0.4.4 → 0.5.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/dist/index.cjs.js +221 -114
- package/dist/index.esm.js +220 -112
- package/package.json +2 -2
- package/src/Editable/FormRepeater.jsx +75 -48
- package/src/FormulatorFormSection.jsx +12 -5
- package/stories/Forms.stories.jsx +9 -1
- package/stories/forms/types.schemas.js +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scenid/react-formulator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"module": "dist/index.esm.js",
|
|
6
6
|
"repository": "https://dennykoch@bitbucket.org/scenid/react-formulator.git",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@rollup/plugin-commonjs": "^22.0.0",
|
|
47
47
|
"@rollup/plugin-node-resolve": "^13.2.1",
|
|
48
48
|
"@scenid/cloud-icons": "^2.6.0",
|
|
49
|
-
"@scenid/formulator": "^2.0
|
|
49
|
+
"@scenid/formulator": "^2.2.0",
|
|
50
50
|
"@storybook/addon-actions": "^6.4.22",
|
|
51
51
|
"@storybook/addon-essentials": "^6.4.22",
|
|
52
52
|
"@storybook/addon-interactions": "^6.4.22",
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
IconButton,
|
|
13
13
|
InputAdornment,
|
|
14
14
|
FormControl,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Typography,
|
|
16
|
+
MenuItem,
|
|
17
|
+
ClickAwayListener
|
|
18
18
|
} from '@material-ui/core'
|
|
19
19
|
|
|
20
20
|
import {
|
|
@@ -28,10 +28,11 @@ const getValue = (value, catalog) => {
|
|
|
28
28
|
return value
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const FormRepeater = ({ variant, name, value,
|
|
31
|
+
const FormRepeater = ({ variant, name, label, value, options, onChange }) => {
|
|
32
32
|
const [entries, setEntries] = useState(value || [])
|
|
33
33
|
const [inEdit, setInEdit] = useState(false)
|
|
34
34
|
const [inputValue, setInputValue] = useState('')
|
|
35
|
+
const [catalogSelectValue, setCatalogSelectValue] = useState('')
|
|
35
36
|
|
|
36
37
|
const handleEntryAdd = () => {
|
|
37
38
|
if (inputValue.length > 0) {
|
|
@@ -43,6 +44,7 @@ const FormRepeater = ({ variant, name, value, catalog, onChange }) => {
|
|
|
43
44
|
const handleCancelNewEntry = () => {
|
|
44
45
|
setInEdit(false)
|
|
45
46
|
setInputValue('')
|
|
47
|
+
setCatalogSelectValue('')
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
const handleEntryDel = delIndex => {
|
|
@@ -57,18 +59,31 @@ const FormRepeater = ({ variant, name, value, catalog, onChange }) => {
|
|
|
57
59
|
}, [entries])
|
|
58
60
|
|
|
59
61
|
let blockedOptions = []
|
|
60
|
-
if (
|
|
61
|
-
blockedOptions = Object.entries(
|
|
62
|
+
if (options) {
|
|
63
|
+
blockedOptions = Object.entries(options).filter(e => Array.isArray(value) && value.includes(e[0])).map(e => e[0])
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
return (
|
|
65
67
|
<Box>
|
|
68
|
+
<Typography variant="body1">
|
|
69
|
+
{label}
|
|
70
|
+
</Typography>
|
|
71
|
+
{
|
|
72
|
+
(!entries || entries?.length === 0)
|
|
73
|
+
&& (
|
|
74
|
+
<Box mt={1} pl={2}>
|
|
75
|
+
<Typography variant="body2" color="textSecondary">
|
|
76
|
+
Keine Einträge vorhanden
|
|
77
|
+
</Typography>
|
|
78
|
+
</Box>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
66
81
|
<List dense>
|
|
67
82
|
{
|
|
68
83
|
entries.map((entry, index) => (
|
|
69
84
|
// eslint-disable-next-line react/no-array-index-key
|
|
70
85
|
<ListItem key={`entry-${index}`}>
|
|
71
|
-
<ListItemText primary={getValue(entry,
|
|
86
|
+
<ListItemText primary={getValue(entry, options)} />
|
|
72
87
|
<ListItemSecondaryAction>
|
|
73
88
|
<IconButton
|
|
74
89
|
edge="end"
|
|
@@ -92,53 +107,61 @@ const FormRepeater = ({ variant, name, value, catalog, onChange }) => {
|
|
|
92
107
|
variant="outlined"
|
|
93
108
|
onClick={() => setInEdit(true)}
|
|
94
109
|
>
|
|
95
|
-
|
|
110
|
+
Einträge hinzufügen
|
|
96
111
|
</Button>
|
|
97
112
|
</Box>
|
|
98
113
|
)
|
|
99
114
|
}
|
|
100
115
|
{
|
|
101
|
-
(inEdit && !
|
|
116
|
+
(inEdit && !options)
|
|
102
117
|
&& (
|
|
103
118
|
<Box
|
|
104
119
|
width="100%"
|
|
105
120
|
display="flex"
|
|
106
121
|
alignItems="center"
|
|
107
122
|
>
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
<ClickAwayListener
|
|
124
|
+
onClickAway={() => {
|
|
125
|
+
if (inputValue === '') {
|
|
126
|
+
setInEdit(false)
|
|
127
|
+
}
|
|
128
|
+
}}
|
|
111
129
|
>
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
<FormControl
|
|
131
|
+
variant="filled"
|
|
132
|
+
fullWidth
|
|
133
|
+
>
|
|
134
|
+
<TextField
|
|
135
|
+
label="Neuer Eintrag"
|
|
136
|
+
variant={variant}
|
|
137
|
+
value={inputValue}
|
|
138
|
+
InputProps={{
|
|
139
|
+
endAdornment: (
|
|
140
|
+
<InputAdornment position="end">
|
|
141
|
+
<Box mr={1}>
|
|
142
|
+
<IconButton size="small" onClick={handleEntryAdd}>
|
|
143
|
+
<SaveIcon fontSize="small" />
|
|
144
|
+
</IconButton>
|
|
145
|
+
</Box>
|
|
146
|
+
<IconButton size="small" onClick={handleCancelNewEntry}>
|
|
147
|
+
<ClearIcon fontSize="small" />
|
|
122
148
|
</IconButton>
|
|
123
|
-
</
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
onChange={e => setInputValue(e.target.value)}
|
|
135
|
-
/>
|
|
136
|
-
</FormControl>
|
|
149
|
+
</InputAdornment>
|
|
150
|
+
)
|
|
151
|
+
}}
|
|
152
|
+
onKeyDown={e => {
|
|
153
|
+
if (e.key === 'Enter') handleEntryAdd()
|
|
154
|
+
if (e.key === 'Escape') handleCancelNewEntry()
|
|
155
|
+
}}
|
|
156
|
+
onChange={e => setInputValue(e.target.value)}
|
|
157
|
+
/>
|
|
158
|
+
</FormControl>
|
|
159
|
+
</ClickAwayListener>
|
|
137
160
|
</Box>
|
|
138
161
|
)
|
|
139
162
|
}
|
|
140
163
|
{
|
|
141
|
-
(inEdit &&
|
|
164
|
+
(inEdit && options)
|
|
142
165
|
&& (
|
|
143
166
|
<Box
|
|
144
167
|
width="100%"
|
|
@@ -149,17 +172,20 @@ const FormRepeater = ({ variant, name, value, catalog, onChange }) => {
|
|
|
149
172
|
variant="filled"
|
|
150
173
|
fullWidth
|
|
151
174
|
>
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
175
|
+
<TextField
|
|
176
|
+
label="Eintrag auswählen"
|
|
177
|
+
select
|
|
178
|
+
variant={variant}
|
|
179
|
+
value={catalogSelectValue}
|
|
180
|
+
onBlur={handleCancelNewEntry}
|
|
181
|
+
onChange={e => {
|
|
182
|
+
setCatalogSelectValue('')
|
|
183
|
+
setEntries([...entries, e.target.value])
|
|
184
|
+
}}
|
|
156
185
|
>
|
|
157
|
-
<MenuItem value="please-select" disabled>
|
|
158
|
-
Eintrag hinzufügen
|
|
159
|
-
</MenuItem>
|
|
160
186
|
{
|
|
161
187
|
Object
|
|
162
|
-
.entries(
|
|
188
|
+
.entries(options)
|
|
163
189
|
.sort((a, b) => a[1].localeCompare(b[1]))
|
|
164
190
|
.map(e => (
|
|
165
191
|
<MenuItem
|
|
@@ -171,7 +197,7 @@ const FormRepeater = ({ variant, name, value, catalog, onChange }) => {
|
|
|
171
197
|
</MenuItem>
|
|
172
198
|
))
|
|
173
199
|
}
|
|
174
|
-
</
|
|
200
|
+
</TextField>
|
|
175
201
|
</FormControl>
|
|
176
202
|
</Box>
|
|
177
203
|
)
|
|
@@ -182,9 +208,10 @@ const FormRepeater = ({ variant, name, value, catalog, onChange }) => {
|
|
|
182
208
|
|
|
183
209
|
FormRepeater.propTypes = {
|
|
184
210
|
variant: PropTypes.oneOf(['standard', 'filled', 'outlined']),
|
|
185
|
-
name: PropTypes.string
|
|
211
|
+
name: PropTypes.string,
|
|
212
|
+
label: PropTypes.string,
|
|
186
213
|
value: PropTypes.any,
|
|
187
|
-
|
|
214
|
+
options: PropTypes.object,
|
|
188
215
|
onChange: PropTypes.func.isRequired
|
|
189
216
|
}
|
|
190
217
|
|
|
@@ -110,14 +110,21 @@ class FormulatorFormSection extends React.Component {
|
|
|
110
110
|
const mapEntry = isRender ? renderComponentMap[field] : componentMap[mapKey]
|
|
111
111
|
let component = mapEntry, props = {}
|
|
112
112
|
|
|
113
|
+
console.log(schema.properties, fieldEntry)
|
|
114
|
+
if (mapKey === 'array' && schema.properties[fieldEntry].options) {
|
|
115
|
+
props.options = (
|
|
116
|
+
schema.properties[fieldEntry].options
|
|
117
|
+
.reduce((l, r) => ({
|
|
118
|
+
...l,
|
|
119
|
+
[r]: translations?.[fieldEntry]?.[r] || r
|
|
120
|
+
}), {})
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
113
124
|
if (mapKey === 'select') {
|
|
114
125
|
props.options = (
|
|
115
126
|
type.map(value => ({
|
|
116
|
-
label:
|
|
117
|
-
translations[field]
|
|
118
|
-
&& translations[field][value]
|
|
119
|
-
)
|
|
120
|
-
|| value,
|
|
127
|
+
label: translations?.field?.value || value,
|
|
121
128
|
value
|
|
122
129
|
}))
|
|
123
130
|
)
|
|
@@ -135,7 +135,15 @@ Types.args = {
|
|
|
135
135
|
/>
|
|
136
136
|
)
|
|
137
137
|
},
|
|
138
|
-
translations:
|
|
138
|
+
translations: {
|
|
139
|
+
...typesTranslations,
|
|
140
|
+
arrayCatalogRepeater: {
|
|
141
|
+
cat: 'Mieze',
|
|
142
|
+
dog: 'Doggo',
|
|
143
|
+
fish: 'Fishy',
|
|
144
|
+
horse: 'Horsy'
|
|
145
|
+
}
|
|
146
|
+
},
|
|
139
147
|
data: {
|
|
140
148
|
hiddenDataInput: 'Data we do not want to show in Video Calls',
|
|
141
149
|
hiddenGroupInput1: 'sensitive data',
|
|
@@ -171,6 +171,21 @@ const inputs = [
|
|
|
171
171
|
render: ['arrayRepeater'],
|
|
172
172
|
validation: { type: 'array' }
|
|
173
173
|
},
|
|
174
|
+
{
|
|
175
|
+
label: 'Repeater with Catalog',
|
|
176
|
+
key: 'arrayCatalogRepeater',
|
|
177
|
+
render: ['arrayCatalogRepeater'],
|
|
178
|
+
validation: {
|
|
179
|
+
type: 'array',
|
|
180
|
+
validator: 'In',
|
|
181
|
+
options: [
|
|
182
|
+
'cat',
|
|
183
|
+
'dog',
|
|
184
|
+
'fish',
|
|
185
|
+
'horse'
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
},
|
|
174
189
|
{
|
|
175
190
|
label: 'Autocomplete Field',
|
|
176
191
|
desc: 'You can supply an array of options or an async function to fetch options on the fly.',
|