Skip to content

Commit 12ec9fe

Browse files
authored
MUI Support multi select autocmplete (#475)
* MUI Support multi select autocmplete * Fix styles. Added configsdefaultSelectWidth, defaultSearchWidth, defaultMaxRows * lint * fix non-autocomplete mui selects by passing object in customProps * showCheckboxes * nit * 4.4.2 * lint
1 parent 32aa3bb commit 12ec9fe

File tree

15 files changed

+163
-38
lines changed

15 files changed

+163
-38
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# Changelog
2+
- 4.4.2
3+
- Added support of autocomplete for multiselect widget in MUI (PR #475)
24
- 4.4.1
35
- feat: possibility to add custom operators for groups (PR #462)
46
- 4.4.0

CONFIG.adoc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,14 @@ Render settings:
335335
|customFieldSelectProps |`{}` |You can pass props to `Select` field widget. Example: `{showSearch: true}`
336336
|groupActionsPosition |`topRight` |You can change the position of the group actions to the following: +
337337
`topLeft, topCenter, topRight, bottomLeft, bottomCenter, bottomRight`
338-
|renderBeforeWidget| |
339-
|renderAfterWidget| |
340-
|renderBeforeActions| |
341-
|renderAfterActions| |
342-
|defaultSliderWidth|"200px" |Width for slider
338+
|renderBeforeWidget | |
339+
|renderAfterWidget | |
340+
|renderBeforeActions | |
341+
|renderAfterActions | |
342+
|defaultSliderWidth |`200px` |Width for slider
343+
|defaultSelectWidth |`200px` |Width for select
344+
|defaultSearchWidth |`100px` |Width for search in autocomplete
345+
|defaultMaxRows |5 | Max rows for textarea
343346
|===
344347

345348
Other settings:

examples/demo/config.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ export default (skin: string) => {
131131
showSearch: true
132132
}
133133
},
134+
select: {
135+
...InitialConfig.widgets.select,
136+
},
137+
multiselect: {
138+
...InitialConfig.widgets.multiselect,
139+
customProps: {
140+
//showCheckboxes: false,
141+
width: "200px",
142+
input: {
143+
width: "100px"
144+
}
145+
}
146+
},
134147
treeselect: {
135148
...InitialConfig.widgets.treeselect,
136149
customProps: {
@@ -202,6 +215,9 @@ export default (skin: string) => {
202215
...localeSettings,
203216

204217
defaultSliderWidth: "200px",
218+
defaultSelectWidth: "200px",
219+
defaultSearchWidth: "100px",
220+
defaultMaxRows: 5,
205221

206222
valueSourcesInfo: {
207223
value: {
@@ -454,6 +470,7 @@ export default (skin: string) => {
454470
label: "Colors",
455471
type: "multiselect",
456472
fieldSettings: {
473+
showSearch: true,
457474
listValues: {
458475
yellow: "Yellow",
459476
green: "Green",

modules/components/widgets/antd/value/MultiSelect.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Select } from "antd";
44
import {calcTextWidth, SELECT_WIDTH_OFFSET_RIGHT} from "../../../../utils/domUtils";
55
import {mapListValues} from "../../../../utils/stuff";
66
import {useOnPropsChanged} from "../../../../utils/reactUtils";
7+
import omit from "lodash/omit";
78
const Option = Select.Option;
89

910
export default class MultiSelectWidget extends PureComponent {
@@ -59,6 +60,7 @@ export default class MultiSelectWidget extends PureComponent {
5960
const aValue = value && value.length ? value : undefined;
6061
const width = aValue ? null : placeholderWidth + SELECT_WIDTH_OFFSET_RIGHT;
6162
const dropdownWidth = this.optionsMaxWidth + SELECT_WIDTH_OFFSET_RIGHT;
63+
const customSelectProps = omit(customProps, ["showCheckboxes"]);
6264

6365
return (
6466
<Select
@@ -78,7 +80,7 @@ export default class MultiSelectWidget extends PureComponent {
7880
value={aValue}
7981
onChange={this.handleChange}
8082
filterOption={this.filterOption}
81-
{...customProps}
83+
{...customSelectProps}
8284
>{this.options}
8385
</Select>
8486
);

modules/components/widgets/antd/value/Select.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {calcTextWidth, SELECT_WIDTH_OFFSET_RIGHT} from "../../../../utils/domUti
44
import {mapListValues} from "../../../../utils/stuff";
55
import {useOnPropsChanged} from "../../../../utils/reactUtils";
66
import { Select } from "antd";
7+
import omit from "lodash/omit";
78
const Option = Select.Option;
89

910
export default class SelectWidget extends PureComponent {
@@ -55,6 +56,7 @@ export default class SelectWidget extends PureComponent {
5556
const dropdownWidth = this.optionsMaxWidth + SELECT_WIDTH_OFFSET_RIGHT;
5657
const width = value ? dropdownWidth : placeholderWidth + SELECT_WIDTH_OFFSET_RIGHT;
5758
const aValue = value != undefined ? value+"" : undefined;
59+
const customSelectProps = omit(customProps, [""]);
5860

5961
return (
6062
<Select
@@ -67,7 +69,7 @@ export default class SelectWidget extends PureComponent {
6769
value={aValue}
6870
onChange={this.handleChange}
6971
filterOption={this.filterOption}
70-
{...customProps}
72+
{...customSelectProps}
7173
>{this.options}
7274
</Select>
7375
);

modules/components/widgets/antd/value/TextArea.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, { PureComponent } from "react";
22
import PropTypes from "prop-types";
33
import { Input, Col } from "antd";
44
const { TextArea } = Input;
5-
const defaultMaxRows = 5;
65

76
export default class TextAreaWidget extends PureComponent {
87
static propTypes = {
@@ -25,7 +24,7 @@ export default class TextAreaWidget extends PureComponent {
2524

2625
render() {
2726
const {config, placeholder, customProps, value, readonly, maxLength, maxRows, fullWidth} = this.props;
28-
const {renderSize} = config.settings;
27+
const {renderSize, defaultMaxRows} = config.settings;
2928
const aValue = value != undefined ? value : null;
3029

3130
return (

modules/components/widgets/material/value/MaterialAutocomplete.jsx

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@ import TextField from "@material-ui/core/TextField";
44
import FormControl from "@material-ui/core/FormControl";
55
import Autocomplete, { createFilterOptions } from "@material-ui/lab/Autocomplete";
66
import CircularProgress from "@material-ui/core/CircularProgress";
7+
import Chip from "@material-ui/core/Chip";
8+
import Checkbox from "@material-ui/core/Checkbox";
9+
import { makeStyles } from "@material-ui/core/styles";
10+
import CheckBoxOutlineBlankIcon from "@material-ui/icons/CheckBoxOutlineBlank";
11+
import CheckBoxIcon from "@material-ui/icons/CheckBox";
712

813
import useListValuesAutocomplete from "../../../../hooks/useListValuesAutocomplete";
914

15+
const nonCheckedIcon = <CheckBoxOutlineBlankIcon fontSize="small" />;
16+
const checkedIcon = <CheckBoxIcon fontSize="small" />;
1017
const defaultFilterOptions = createFilterOptions();
18+
const emptyArray = [];
1119

1220
export default (props) => {
1321
const {
14-
allowCustomValues,
22+
allowCustomValues, multiple,
1523
value: selectedValue, customProps, readonly, config
1624
} = props;
17-
const hasValue = selectedValue != null;
1825

1926
// hook
2027
const {
@@ -33,22 +40,58 @@ export default (props) => {
3340
getOptionDisabled,
3441
getOptionLabel,
3542
} = useListValuesAutocomplete(props, {
36-
debounceTimeout: 100
43+
debounceTimeout: 100,
44+
multiple
3745
});
3846

3947
// setings
40-
const {defaultSliderWidth} = config.settings;
41-
const {width, ...rest} = customProps || {};
42-
const customInputProps = rest.input || {};
43-
const customAutocompleteProps = omit(rest.autocomplete || rest, ["showSearch"]);
48+
const {defaultSelectWidth, defaultSearchWidth} = config.settings;
49+
const {width, showCheckboxes, ...rest} = customProps || {};
50+
let customInputProps = rest.input || {};
51+
const inputWidth = customInputProps.width || defaultSearchWidth;
52+
customInputProps = omit(customInputProps, ["width"]);
53+
const customAutocompleteProps = omit(rest, ["showSearch", "showCheckboxes"]);
4454

55+
const fullWidth = true;
56+
const minWidth = width || defaultSelectWidth;
57+
const style = {
58+
width: (multiple ? undefined : minWidth),
59+
minWidth: minWidth
60+
};
61+
const placeholder = !readonly ? aPlaceholder : "";
62+
const hasValue = selectedValue != null;
63+
// should be simple value to prevent re-render!s
64+
const value = hasValue ? selectedValue : (multiple ? emptyArray : null);
65+
4566
const filterOptions = (options, params) => {
4667
const filtered = defaultFilterOptions(options, params);
4768
const extended = extendOptions(filtered, params);
4869
return extended;
4970
};
5071

51-
// Render
72+
// styles
73+
const useStyles = makeStyles((theme) => ({
74+
// fix too small width
75+
input: {
76+
minWidth: inputWidth + " !important",
77+
}
78+
}));
79+
80+
const useStylesChip = makeStyles((theme) => ({
81+
// fix height
82+
root: {
83+
height: "auto"
84+
},
85+
label: {
86+
marginTop: "3px",
87+
marginBottom: "3px",
88+
}
89+
}));
90+
91+
const classesChip = useStylesChip();
92+
const classes = useStyles();
93+
94+
// render
5295
const renderInput = (params) => {
5396
return (
5497
<TextField
@@ -64,35 +107,65 @@ export default (props) => {
64107
),
65108
}}
66109
disabled={readonly}
67-
placeholder={!readonly ? aPlaceholder : ""}
110+
placeholder={placeholder}
68111
//onChange={onInputChange}
69112
{...customInputProps}
70113
/>
71114
);
72115
};
73116

117+
const renderTags = (value, getTagProps) => value.map((option, index) => {
118+
return <Chip
119+
key={index}
120+
classes={classesChip}
121+
label={getOptionLabel(option)}
122+
{...getTagProps({ index })}
123+
/>;
124+
});
125+
126+
const renderOption = (option, { selected }) => {
127+
if (multiple && showCheckboxes != false) {
128+
return <React.Fragment>
129+
<Checkbox
130+
icon={nonCheckedIcon}
131+
checkedIcon={checkedIcon}
132+
style={{ marginRight: 8 }}
133+
checked={selected}
134+
/>
135+
{option.title}
136+
</React.Fragment>;
137+
} else {
138+
return <React.Fragment>{option.title}</React.Fragment>;
139+
}
140+
};
141+
74142
return (
75-
<FormControl>
143+
<FormControl fullWidth={fullWidth}>
76144
<Autocomplete
77-
fullWidth
78-
style={{ width: width || defaultSliderWidth }}
145+
disableCloseOnSelect={multiple}
146+
fullWidth={fullWidth}
147+
multiple={multiple}
148+
style={style}
149+
classes={classes}
79150
freeSolo={allowCustomValues}
80151
loading={isInitialLoading}
81152
open={open}
82153
onOpen={onOpen}
83154
onClose={onClose}
84155
inputValue={inputValue}
85156
onInputChange={onInputChange}
86-
label={!readonly ? aPlaceholder : ""}
157+
label={placeholder}
87158
onChange={onChange}
88-
value={hasValue ? selectedValue : null} // should be simple value to prevent re-render!
159+
value={value}
89160
getOptionSelected={getOptionSelected}
90161
disabled={readonly}
91162
readOnly={readonly}
92163
options={options}
93164
getOptionLabel={getOptionLabel}
94165
getOptionDisabled={getOptionDisabled}
95166
renderInput={renderInput}
167+
renderTags={renderTags}
168+
renderOption={renderOption}
96169
filterOptions={filterOptions}
97170
{...customAutocompleteProps}
98171
></Autocomplete>

modules/components/widgets/material/value/MaterialMultiSelect.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default ({listValues, value, setValue, allowCustomValues, readonly, place
4646
disabled={readonly}
4747
readOnly={readonly}
4848
renderValue={renderValue}
49-
{...omit(customProps, ["showSearch"])}
49+
{...omit(customProps, ["showSearch", "input", "showCheckboxes"])}
5050
>
5151
{renderOptions(hasValue ? value : [])}
5252
</Select>

modules/components/widgets/material/value/MaterialSelect.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default ({listValues, value, setValue, allowCustomValues, readonly, place
4343
disabled={readonly}
4444
readOnly={readonly}
4545
renderValue={renderValue}
46-
{...omit(customProps, ["showSearch"])}
46+
{...omit(customProps, ["showSearch", "input"])}
4747
>
4848
{renderOptions()}
4949
</Select>

modules/components/widgets/material/value/MaterialTextArea.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React from "react";
22
import TextField from "@material-ui/core/TextField";
33
import FormControl from "@material-ui/core/FormControl";
4-
const defaultMaxRows = 5;
54

65
export default (props) => {
76
const {value, setValue, config, readonly, placeholder, customProps, maxLength, maxRows, fullWidth} = props;
7+
const {defaultMaxRows} = config.settings;
88

99
const onChange = e => {
1010
let val = e.target.value;

0 commit comments

Comments
 (0)