Autocomplete - How to put all values in one row - reactjs

I need to put all the values and input field for multi autocomplete on one line. But the input field sliding down.
My code:
const { filter, classes, options } = this.props;
const style = filter && filter.value !== '' ? {backgroundColor: 'lavender'} : {};
return (
<TableFilterRow.Cell { ...this.props } className={ classes.cell } style={ style }>
<Autocomplete
options={options}
value={options.filter(option => filter.includes(option.value)) || []}
getOptionLabel={option => option.label}
multiple={true}
fullWidth
disableClearable={true}
onChange={this.handleFilter}
renderOption={option => (
<React.Fragment>
<Checkbox
color="primary"
checked={filter.includes(option.value) || false}
/>
{option.label}
</React.Fragment>
)}
renderTags={values => values.map(option => option.label).join(', ')}
renderInput={(params) => (
<TextField
{...params}
fullWidth
margin="dense"
/>
)}
/>
</TableFilterRow.Cell>
Result:
How can I put all the values and input field for multi autocomplete on one line?

Use style hook API to select input and apply flexWrap and overflow CSS properties:
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
root: {
"& > .MuiAutocomplete-root .MuiFormControl-root .MuiInputBase-root": {
flexWrap: "nowrap",
overflowX: "scroll" // or "hidden"
}
}
}));
then, on component use on className:
...
const classes = useStyles();
return (
<div className={classes.root}>
<Autocomplete
...
See this working on codesandbox here.

Related

Not rendering exsitance tags in mui autocomplte tags

I have make input tag from mui autocomplete but I am unable to render the tags which are comming from db in array form. It's been two days I am working on it but unable to render existance tags which comming from db.
Let say extistance tags are like
const [existanceTags,setExistanceTags] = useState(
['town','home','province','country']
)
const [tags11, setTags11] = useState([]);
const [selected, setSelected] = useState([]);
const [tags, setTags] = useState([]);
<Autocomplete
multiple
size="small"
id="tags-outlined"
options={tags11}
// defaultValue={obj3?.map((option) => option?.title)}
// getOptionLabel={obj3?.map((option) => option?.title)}
freeSolo
onChange={(event, value) => setSelected([...selected, value])}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
sx={{
"& .MuiChip-deleteIcon": {
color: "#c8175d",
},
"& .MuiChip-deleteIcon:hover": {
color: "#c8175d",
},
}}
variant="outlined"
style={{ borderColor: "#c8175d" }}
label={option}
{...getTagProps({ index })}
/>
))
}
renderInput={(params) => (
<TextField
{...params}
label=" "
InputLabelProps={{ shrink: false }}
placeholder="e.g. hard massage"
value={tags}
onChange={(e) => setTags([...tags, e.target.value.split(",")])}
/>
)}
/>
Read mui docs and alot of other articles but didn't fix my issue

How to stop modal from closing when clicking on a select option?

I've made a custom filter for MUI's datagrid, the filter has two select's which allow you to filter by the column and filter type. The selects are quite big and endup outside the modal, when clicking on an option the whole modal closes, how can I prevent this from happening?
I've used this tutorial - Detect click outside React component to detect clicks outside the filter.
The code below shows the filter and I've also made an codesandbox example here - https://codesandbox.io/s/awesome-panka-g92vhn?file=/src/DataGridCustomFilter.js:0-6708
any help would be appreciated
import React, { useState, useEffect, useRef } from "react";
import {
Button,
Stack,
FormControl,
InputLabel,
Select,
MenuItem,
Paper,
Grid,
IconButton,
TextField,
ClickAwayListener
} from "#material-ui/core";
import FilterListIcon from "#mui/icons-material/FilterList";
import AddIcon from "#mui/icons-material/Add";
import CloseIcon from "#mui/icons-material/Close";
import { useForm, useFieldArray, Controller } from "react-hook-form";
import { columns } from "./columns";
const filterTypes = {
string: ["contains", "equals", "starts with", "ends with", "is any of"],
int: ["contains", "equals", "less than", "greater than"]
};
function FilterRow({
len,
setOpen,
field,
control,
columns,
index,
handleRemoveFilter
}) {
return (
<Grid container spacing={0}>
<Grid
item
md={1}
style={{
display: "flex",
alignSelf: "flex-end",
alignItems: "center"
}}
>
<IconButton
size="small"
onClick={() => {
if (len === 1) {
setOpen(false);
} else {
console.log(index, "---");
handleRemoveFilter(index);
}
}}
>
<CloseIcon style={{ fontSize: "20px" }} />
</IconButton>
</Grid>
<Grid item md={4}>
<Controller
name={`filterForm.${index}.column`}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl variant="standard" sx={{ width: "100%" }}>
<InputLabel>Column</InputLabel>
<Select
value={value}
onChange={onChange}
label="Column"
defaultValue=""
>
{columns.map((a) => {
return a.exclude_filter === true ? null : (
<MenuItem value={a.headerName}>{a.headerName}</MenuItem>
);
})}
</Select>
</FormControl>
)}
/>
</Grid>
<Grid item md={3}>
<Controller
name={`filterForm.${index}.filter`}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl variant="standard" sx={{ width: "100%" }}>
<InputLabel>Filter</InputLabel>
<Select
value={value}
onChange={onChange}
label="Filter"
defaultValue=""
>
{filterTypes.string.map((a) => {
return <MenuItem value={a}>{a}</MenuItem>;
})}
</Select>
</FormControl>
)}
/>
</Grid>
<Grid item md={4}>
<Controller
name={`filterForm.${index}.value`}
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl>
<TextField
onChange={onChange}
value={value}
label="Value"
variant="standard"
/>
</FormControl>
)}
/>
{/* )} */}
</Grid>
</Grid>
);
}
function DataGridCustomFilter() {
const { control, handleSubmit } = useForm();
const { fields, append, remove } = useFieldArray({
control,
name: "filterForm"
});
const [open, setOpen] = useState(false);
const onSubmit = (data) => {};
useEffect(() => {
if (fields.length === 0) {
append({
column: "ID",
filter: filterTypes.string[0],
value: ""
});
}
}, [fields]);
const [clickedOutside, setClickedOutside] = useState(false);
const myRef = useRef();
const handleClickOutside = (e) => {
if (myRef.current && !myRef.current.contains(e.target)) {
setClickedOutside(true);
setOpen(!open);
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
});
return (
<>
<Button
startIcon={<FilterListIcon />}
size="small"
onClick={() => {
setOpen(!open);
}}
// disabled={isDisabled}
>
FILTERS
</Button>
{open ? (
<div ref={myRef}>
<Paper
style={{
width: 550,
padding: 10,
zIndex: 1300,
position: "absolute",
inset: "0px auto auto 0px",
margin: 0,
display: "block"
// transform: "translate3d(160.556px, 252.222px, 0px)",
}}
variant="elevation"
elevation={5}
>
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={0.5}>
<div style={{ maxHeight: 210, overflow: "scroll" }}>
{fields.map((field, index) => {
return (
<div style={{ paddingBottom: 5 }}>
<FilterRow
len={fields.length}
control={control}
setOpen={setOpen}
field={field}
columns={columns}
handleRemoveFilter={() => remove(index)}
{...{ control, index, field }}
// handleClickAway={handleClickAway}
/>
</div>
);
})}
</div>
<div style={{ marginTop: 10, paddingLeft: 40 }}>
<Stack direction="row" spacing={1}>
<Button size="small" startIcon={<AddIcon />}>
ADD FILTER
</Button>
<Button size="small" type="submit">
{fields.length > 1 ? "APPLY FILTERS" : "APPLY FILTER"}
</Button>
</Stack>
</div>
</Stack>
</form>
</Paper>
</div>
) : null}
</>
);
}
export default DataGridCustomFilter;
So far I've tried MUI's ClickAwayListener and the example above, both seem to give the same result
DataGrid component uses NativeSelect. I have checked your codesandbox and tried replacing Select to NativeSelect and MenuItem to Option. filter is working properly. below is sample code for update.
...
<NativeSelect
value={value}
onChange={onChange}
label="Column"
defaultValue=""
>
{columns.map((a) => {
return a.exclude_filter === true ? null : (
<option value={a.headerName}>{a.headerName}</option >
);
})}
</NativeSelect>
...

MUI AUTOCOMPLETE : How to render two result lists based on option properties?

i'm trying to display results with the MUI Autocomplete component but i need to
separate them in two different list side by side.
I currently have a functionnal searchbar with one list, but i want to separate the results based on a specific property from each option who will refer as their type.
Here's what i have as a beginner :
<Autocomplete
freeSolo={true}
id="equipment-searchbar"
onOpen={() => {
setOpen(true);
}}
onClose={() => {
setOpen(false);
}}
clearOnBlur={false}
isOptionEqualToValue={(option, value) => option.name === value.name}
getOptionLabel={(option) => option.title || ''}
filterOptions={x => x}
options={options}
groupBy={(option) => option.type}
onInputChange={onInputChange}
open={open}
onChange={(event: any, option: any) => {
let searchData = (document.getElementById("equipment-searchbar") as HTMLInputElement).value
if (option !== null && option.globalSearch !== true) {
switch (option.type) {
case bikeSmooveboxType:
redirect("bike/" + option.title)
break;
case stationTransmiterType:
redirect("station/" + option.code)
break;
}
}
}}
loading={loading}
renderInput={(params) => (
<TextField
className="inputRounded"
variant="outlined"
label={"Rechercher dans les équipements"}
{...params}
InputProps={{
...params.InputProps,
endAdornment: (
<React.Fragment>
<SearchIcon/>
{params.InputProps.endAdornment}
</React.Fragment>
),
}}
/>
)}
/>
Any suggestions for solving this problem ?
Thank's a lot !
Basically what you need to do is:
use renderGroup prop and put your jsx for columns.
use ListboxProps prop to apply display:flex for the results.
if needed you can check out PaperComponent and PopperComponent for further customization.
This is a snippet of what probably you need:
<Autocomplete
renderGroup={(item) => {
const { group, children } = item;
if (group === "type1")
return (
<Box
sx={{ width: "50%" }}
/*custom props for type 1*/
>
{children}
</Box>
);
return (
<Box
sx={{ width: "50%" }}
/*custom props for type 2*/
>
{children}
</Box>
);
}}
ListboxProps={{
sx: { display: "flex" },
}}
/>

React js click outside of the component does not work

I have a component in my react s application where i use ClickAwayListener from #mui/material/ClickAwayListener.
export default function MultipleSelectChip() {
const theme = useTheme();
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value }
} = event;
setPersonName(
// On autofill we get a the stringified value.
typeof value === "string" ? value.split(",") : value
);
};
return (
<div>
{[1, 2, 3].map(() => {
return (
<div>
<ClickAwayListener onClickAway={() => console.log("click outside")}>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-chip-label">Chip</InputLabel>
<Select
labelId="demo-multiple-chip-label"
id="demo-multiple-chip"
multiple
value={personName}
onChange={handleChange}
input={
<OutlinedInput id="select-multiple-chip" label="Chip" />
}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</ClickAwayListener>
</div>
);
})}
</div>
);
}
I expect to trigger onClickAway function only when i click outside a dropdown. But when i click the dropdown plus when i select an option the function also is triggered and i don't understand why. Hw to get the expected behaviour when click outside one of the dropdown? NOTE: the functionality works if i don't use map(), meaning without many dropdowns. demo: https://codesandbox.io/s/multipleselectchip-material-demo-forked-n03vq?file=/demo.js:1080-2905

React JS Material UI Select IconComponent (Dropdown Icon) avoid rotating

By default, in React JS Material UI's Select component, when we provide a custom IconComponent, it gets turned upside down when user has selected the dropdown / Select component.
Sample code:
<Select
multiple
variant="outlined"
MenuProps={CustomMenuProps}
IconComponent={Search}
renderValue={(selected) => (selected as string[]).join(', ')}
{...props}
>
...
I did a sneaky thing to remove "MuiSelect-iconOpen" from the className when calling IconComponent.
Sample Code after my fix:
<Select
multiple
variant="outlined"
MenuProps={CustomMenuProps}
IconComponent={({ className }) => {
className = className.replace("MuiSelect-iconOpen", "")
return <Search className={className} />
}}
renderValue={(selected) => (selected as string[]).join(', ')}
{...props}
>
....
Now is there a better way to do this without replacing the className?
My current solution is to overwrite the original iconOpen class provided by the Material-UI Select.
....
import { makeStyles } from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
iconOpen: {
transform: 'rotate(0deg)',
},
}));
....
export const MyCompo: FC<> = () => {
const classes = useStyles();
return (
<Select
multiple
variant="outlined"
MenuProps={CustomMenuProps}
IconComponent={Search}
classes={{
iconOpen: classes.iconOpen,
}}
renderValue={(selected) => (selected as string[]).join(', ')}
{...props}
>
....
<Select
value={values.phoneCode}
onChange={handleChange("phoneCode")}
inputProps={{ "aria-label": "Without label" }}
IconComponent={(_props) => {
const rotate = _props.className.toString().includes("iconOpen");
return (
<div
style={{
position: "absolute",
cursor: "pointer",
pointerEvents: "none",
right: 10,
height: "15px",
width: "15px",
transform: rotate ? "rotate(180deg)" : "none",
}}
>
<ArrowDown />
</div>
);
}}
>
....
It does not rotate if you use arrow function: IconComponent={()=> <YourIcon/>}

Resources