Skip to content

Commit eebb172

Browse files
authored
MUI - Support showSearch (autocomplete) for field select widget (#563)
1 parent 067ce7f commit eebb172

File tree

11 files changed

+86
-9
lines changed

11 files changed

+86
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- Added read-only mode switch for rules and groups. See `showLock` and `canDeleteLocked` config options, custom JsonLogic op `locked`, `setLock` action, `lockLabel` and `lockedLabel`. Added Switch components, see `renderSwitch`. (issue #377) (PR #490)
55
- Fixed issue with frozen config (`Object.freeze`) by using `clone` (issue #345) (PR #490)
66
- Fix: Filter value sources for func args correctly. LHS field can be used as arg in RHS function. (PR #490)
7+
- MUI - Support showSearch (autocomplete) for field select widget (issue #479 #521) (PR #563)
78
- 4.7.2
89
- Fixed import of rule_group with `not` (issue #548) (PR #559)
910
- 4.7.1

examples/demo/config.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ export default (skin: string) => {
245245
canLeaveEmptyGroup: true,
246246
shouldCreateEmptyGroup: false,
247247
showErrorMessage: true,
248+
customFieldSelectProps: {
249+
showSearch: true
250+
},
248251
// renderField: (props) => <FieldCascader {...props} />,
249252
// renderOperator: (props) => <FieldDropdown {...props} />,
250253
// renderFunc: (props) => <FieldSelect {...props} />,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import React from "react";
2+
import MaterialAutocomplete from "../value/MaterialAutocomplete";
3+
4+
const itemsToListValues = (items, level = 0) => (
5+
items.map(item => {
6+
const {items, path, label, disabled, grouplabel} = item;
7+
const prefix = "\u00A0\u00A0".repeat(level);
8+
if (items) {
9+
return itemsToListValues(items, level+1);
10+
} else {
11+
return {
12+
title: label,
13+
renderTitle: prefix+label,
14+
value: path,
15+
disabled,
16+
groupTitle: level > 0 ? prefix+grouplabel : null,
17+
};
18+
}
19+
}).flat(Infinity)
20+
);
21+
22+
const filterOptionsConfig = {
23+
stringify: (option) => {
24+
const keysForFilter = ["title", "value", "grouplabel", "label"];
25+
const valueForFilter = keysForFilter
26+
.map(k => (typeof option[k] == "string" ? option[k] : ""))
27+
.join("\0");
28+
return valueForFilter;
29+
}
30+
};
31+
32+
const fieldAdapter = ({items, selectedKey, setField, ...rest}) => {
33+
const listValues = itemsToListValues(items);
34+
const groupBy = (option) => option.groupTitle;
35+
const value = selectedKey;
36+
const setValue = (value, _asyncValues) => {
37+
if (!value) return undefined;
38+
return setField(value);
39+
};
40+
41+
return {
42+
...rest,
43+
listValues,
44+
setValue,
45+
groupBy,
46+
filterOptionsConfig,
47+
allowCustomValues: false,
48+
multiple: false,
49+
value,
50+
};
51+
};
52+
53+
export default (props) => {
54+
return <MaterialAutocomplete {...fieldAdapter(props)} />;
55+
};

modules/components/widgets/material/core/MaterialFieldSelect.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ export default ({items, setField, selectedKey, readonly, placeholder}) => {
1111
const prefix = "\u00A0\u00A0".repeat(level);
1212
if (items) {
1313
return [
14-
<ListSubheader disabled={disabled} key={path} disableSticky={true}>{label}</ListSubheader>,
14+
<ListSubheader disabled={disabled} key={path} disableSticky={true}>
15+
{prefix && <span>{prefix}</span>}
16+
{label}
17+
</ListSubheader>,
1518
renderOptions(items, level+1),
1619
];
1720
} else {

modules/components/widgets/material/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import MaterialAutocompleteWidget from "./value/MaterialAutocomplete";
2121

2222
// field select widgets
2323
import MaterialFieldSelect from "./core/MaterialFieldSelect";
24+
import MaterialFieldAutocomplete from "./core/MaterialFieldAutocomplete";
2425

2526
// core components
2627
import MaterialButton from "./core/MaterialButton";
@@ -72,6 +73,7 @@ export default {
7273
MaterialAutocompleteWidget,
7374

7475
MaterialFieldSelect,
76+
MaterialFieldAutocomplete,
7577

7678
MaterialButton,
7779
MaterialButtonGroup,

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ const emptyArray = [];
2020
export default (props) => {
2121
const {
2222
allowCustomValues, multiple,
23-
value: selectedValue, customProps, readonly, config
23+
value: selectedValue, customProps, readonly, config, groupBy, filterOptionsConfig
2424
} = props;
25+
const filterOptionsFn = filterOptionsConfig ? createFilterOptions(filterOptionsConfig) : defaultFilterOptions;
2526

2627
// hook
2728
const {
@@ -64,7 +65,7 @@ export default (props) => {
6465
const value = hasValue ? selectedValue : (multiple ? emptyArray : null);
6566

6667
const filterOptions = (options, params) => {
67-
const filtered = defaultFilterOptions(options, params);
68+
const filtered = filterOptionsFn(options, params);
6869
const extended = extendOptions(filtered, params);
6970
return extended;
7071
};
@@ -135,7 +136,7 @@ export default (props) => {
135136
{option.title}
136137
</React.Fragment>;
137138
} else {
138-
return <React.Fragment>{option.title}</React.Fragment>;
139+
return <React.Fragment>{option.renderTitle || option.title}</React.Fragment>;
139140
}
140141
};
141142

@@ -161,6 +162,7 @@ export default (props) => {
161162
disabled={readonly}
162163
readOnly={readonly}
163164
options={options}
165+
groupBy={groupBy}
164166
getOptionLabel={getOptionLabel}
165167
getOptionDisabled={getOptionDisabled}
166168
renderInput={renderInput}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default (props) => {
1919
<FormControl fullWidth={fullWidth}>
2020
<TextField
2121
fullWidth={fullWidth}
22-
rowsMax={maxRows || defaultMaxRows}
22+
maxRows={maxRows || defaultMaxRows}
2323
multiline={true}
2424
value={textValue}
2525
placeholder={!readonly ? placeholder : ""}

modules/config/material/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const {
1919
MaterialAutocompleteWidget,
2020

2121
MaterialFieldSelect,
22+
MaterialFieldAutocomplete,
2223
MaterialConjs,
2324
MaterialSwitch,
2425
MaterialButton,
@@ -34,7 +35,9 @@ const {
3435
const settings = {
3536
...BasicConfig.settings,
3637

37-
renderField: (props) => <MaterialFieldSelect {...props} />,
38+
renderField: (props) => props?.customProps?.showSearch
39+
? <MaterialFieldAutocomplete {...props} />
40+
: <MaterialFieldSelect {...props} />,
3841
renderOperator: (props) => <MaterialFieldSelect {...props} />,
3942
renderFunc: (props) => <MaterialFieldSelect {...props} />,
4043
renderConjs: (props) => <MaterialConjs {...props} />,

modules/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,7 @@ interface ReadyWidgets extends VanillaWidgets {
947947
export interface MaterialWidgets {
948948
// material core widgets
949949
MaterialFieldSelect: ElementType<FieldProps>,
950+
MaterialFieldAutocomplete: ElementType<FieldProps>,
950951
MaterialConjs: ElementType<ConjsProps>,
951952
MaterialSwitch: ElementType<SwitchProps>,
952953
MaterialButton: ElementType<ButtonProps>,

modules/utils/autocomplete.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,15 @@ export const mergeListValues = (values, newValues, toStart = false) => {
3131

3232
export const listValueToOption = (lv) => {
3333
if (lv == null) return null;
34-
const {title, value} = lv;
35-
return {title, value};
34+
const {title, value, disabled, groupTitle, renderTitle} = lv;
35+
let option = {title, value};
36+
if (disabled)
37+
option.disabled = disabled;
38+
if (groupTitle)
39+
option.groupTitle = groupTitle;
40+
if (renderTitle)
41+
option.renderTitle = renderTitle;
42+
return option;
3643
};
3744

3845
export const getListValue = (selectedValue, listValues) =>

0 commit comments

Comments
 (0)