react-select: closeMenuOnSelect and blurInputOnSelect not working properly - reactjs

I have a Select component like this:
<Select
className="multiple-select"
closeMenuOnSelect={false}
blurInputOnSelect={false}
hideSelectedOptions={false}
isMulti
autoFocus
isSearchable
/>
I have also other props for handle de data, options etc, but that's not the case here, i think.
Why my component is closing every time i select an option? even with this props.
There's something related to the component re-render when I select an option? And what can I do to fix this?
UPDATE
Giving more context to the code...
I have a custom style:
const customStyles = {
control: (base, state) => ({
...base,
boxShadow: state.isFocused ? 0 : 0,
borderColor: state.isFocused ? '#FFC600' : '#9CAEC1',
'&:hover': {
borderColor: state.isFocused ? '#FFC600' : base.borderColor,
},
}),
placeholder: (base) => ({
...base,
font: 'normal 15px/24px Montserrat',
color: '#9CAEC1',
}),
option: (base, state) => ({
...base,
minHeight: '62px',
maxHeight: '62px',
width: '270px',
minWidth: '270px',
maxWidth: '270px',
lineHeight: '48px !important',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
display: 'inline-block',
verticalAlign: 'middle',
cursor: 'pointer',
font: 'normal 15px/24px Montserrat',
color: state.isSelected ? '#fff' : '#6D839A',
backgroundColor: state.isSelected ? '#029619' : null,
':hover': {
background: '#029619',
color: '#fff',
},
}),
menuList: (base) => ({
...base,
minHeight: 'auto',
maxHeight: '250px',
'::-webkit-scrollbar': {
width: '10px',
height: '10px',
},
'::-webkit-scrollbar-track': {
background: '#E4E8ED',
borderRadius: '5rem',
},
'::-webkit-scrollbar-thumb': {
background: '#6D839A',
borderRadius: '5rem',
},
}),
valueContainer: (provided, state) => ({
...provided,
textOverflow: 'ellipsis',
maxWidth: '90%',
whiteSpace: 'nowrap',
overflow: 'hidden',
display: 'initial',
font: 'normal 15px/24px Montserrat',
color: '#6D839A',
}),
};
I customized too the multivalue container:
const multiValueContainer = ({ selectProps, data }) => {
const label = data.value;
const val = `${label}, `;
return val;
};
My component looks like this: (the onClickHandler function that I receive by props just set a state in a reducer, where this state receive the options selected)
function MultipleSelect(props) {
const { onClickHandler, data, selected, label } = props;
function msg() {
if (data.length > 0) {
return `Sorry, no results :(`;
} else {
return 'Loading items...';
}
}
function onSelectHandler(e) {
if (e.length === 0) {
onClickHandler([], props);
} else {
const selected_ideas = [];
e.forEach((option_selected) => {
selected_ideas.push(
...data.filter((idea) => idea.value === option_selected.value),
);
});
if (selected_ideas.length) {
onClickHandler(selected_ideas, props);
} else {
console.warn('onSelectHandler received invalid argument -> ', e);
return;
}
}
}
return (
<Wrapper selected={selected}>
<Select
className="multiple-select"
components={{ MultiValueContainer: multiValueContainer }}
styles={customStyles}
options={data}
value={selected}
onChange={onSelectHandler}
placeholder={label}
noOptionsMessage={msg}
closeMenuOnSelect={false}
blurInputOnSelect={false}
hideSelectedOptions={false}
isMulti
autoFocus
isSearchable
/>
</Wrapper>
);
}
MultipleSelect.propTypes = {
onClickHandler: PropTypes.func.isRequired,
data: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
}),
).isRequired,
label: PropTypes.string.isRequired,
};
export default MultipleSelect;
My Wrapper component:
const Wrapper = styled('div')`
position: relative;
width: 100%;
max-width: 280px;
height: fit-content;
display: flex;
flex-direction: column;
margin: 24px auto;
justify-content: center;
align-items: center;
.multiple-select {
width: 100%;
}
.multiple-select .css-tlfecz-indicatorContainer,
.multiple-select .css-1okebmr-indicatorSeparator {
color: ${(props) => props.theme.colors.gray300};
}
.multiple-select .css-b8ldur-Input {
font: normal 15px/24px Montserrat;
border-top: ${(props) =>
props.selected.length > 0 ? `1px solid #9CAEC1` : 'none'};
}
`;

Related

Remove Checkbox Outline in Material-UI

I wonder how do I remove the orange box encapsulating the checkbox. I couldn't find a way to remove it? It does appear when my mouse is clicking on that part.
StyledGrid
export const StyledDataGrid = styled(DataGridPro)(({ theme }) => ({
backgroundColor: theme.palette.common.white,
border: 0,
"& .MuiDataGrid-columnHeader, .MuiDataGrid-cell, .MuiDataGrid-cellCheckbox":
{
border: 0,
":focus": {
outline: "none",
},
},
"& .MuiDataGrid-columnHeaders": {
borderTopLeftRadius: "8px",
borderTopRightRadius: "8px",
backgroundColor: "#fbfbfc",
textTransform: "uppercase",
"& .MuiDataGrid-menuIcon": {
display: "none",
},
"& .MuiDataGrid-columnHeaderTitle": {
color: theme.palette.text.secondary,
},
"& .MuiDataGrid-columnHeader--sorted .MuiDataGrid-columnHeaderTitle": {
color: theme.palette.text.primary,
},
},
"& .MuiDataGrid-row, & .MuiDataGrid-cell": {
maxHeight: "initial !important",
minHeight: "initial !important",
},
}));
Component
<StyledDataGrid
columns={supplierColumns}
rows={rows}
loading={rows.length === 0}
disableSelectionOnClick
checkboxSelection
onSelectionModelChange={(ids) => setSelectedRowIds(ids)}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
rowsPerPageOptions={[5, 10, 20]}
pagination
/>
I think you have to add outline directly , or try pesudo focus-within selector
export const StyledDataGrid = styled(DataGridPro)(({ theme }) => ({
backgroundColor: theme.palette.common.white,
border: 0,
"& .MuiDataGrid-columnHeader, .MuiDataGrid-cell, .MuiDataGrid-cellCheckbox":
{
border: 0,
outline: "none"
},
}));
export const StyledDataGrid = styled(DataGridPro)(({ theme }) => ({
backgroundColor: theme.palette.common.white,
border: 0,
"& .MuiDataGrid-columnHeader, .MuiDataGrid-cell, .MuiDataGrid-cellCheckbox":
{
border: 0,
"& :focus-within": {
outline: "none"
}
},
}));

How to pass props in to MUI styled menu to achieve certain conditional styling?

How to pass props to the already styled mui menu , I want to be able to use conditional styling on the menu so it could have 2 types of minimum width.. depending on each case problem for me is the menu has they style out side of the component that is getting the props that I want to check against its type, so how can it be able to achieve this ?
const StyledMenu = styled((props: MenuProps) => (
<Menu
elevation={0}
anchorOrigin={{
vertical: 'bottom',
horizontal: 200,
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
{...props}
/>
))(({ theme }) => ({
backgroundColor: 'rgba(255, 255, 455, 0.455)',
backdropFilter: 'blur(1px)',
'& .MuiPaper-root': {
borderRadius: 3,
//{props === 'Type' ? { minWidth: 1360 } : { minWidth: 250 }},
{props === 'Type' ? { minWidth: 1360 } : { minWidth: 250 }}
marginTop: theme.spacing(1),
color: theme.palette.mode === 'light' ? 'rgb(55, 65, 81)' : theme.palette.grey[300],
boxShadow: 'rgb(255, 255, 255) 0px 0px 0px 0px, rgba(0, 0, 0, 0.05) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px',
'& .MuiMenu-list': {
padding: '4px 0',
},
'& .MuiMenuItem-root': {
'& .MuiSvgIcon-root': {
fontSize: 18,
color: theme.palette.text.secondary,
marginRight: theme.spacing(1.5),
},
'&:active': {
backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
},
},
},
}));
export const Expandable: React.FC<Props> = ({ source, type, date, icon }) => {
const context = useContext(movementsContext);
//useEffect(() => {}, [context.StabelItems]);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<Box style={{ margin: 'auto', display: 'flex', justifyContent: 'center' }}>
<StyledMenu
id='demo-customized-menu'
MenuListProps={{
'aria-labelledby': 'demo-customized-button',
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
</Box>
</Box>
);
};
I've recently developed two different ways of conditional styling using MUI's withStyle & makeStyle
Approach #1
Make two different class styles and conditionally apply them to your element
const useStyles = React.makeStyles(theme => ({
blueStyle: {
color: 'blue'
},
redStyle: {
color: 'red'
},
}));
export default const YourComponent = () => {
const classes = useStyles();
const [condition, setCondition] = React.useState(true);
return <div className={condition ? classes.blueStyle : classes.redStyle}>Hello World!</div>
}
Approach #2
You can conditionally style a table cell (or any element of your choosing) based on screen size
const StyledDiv = withStyles((theme) => ({
root: {
color: 'blue', // default style of color is blue
[theme.breakpoints.up('sm')]: { // you can use sx, sm, md, lg or xl for different screen conditions
color: 'red', // style of color is red if screen size is sm or sx
// other styles can go here
},
}
}))(div);
export default const YourComponent = () => {
return <StyledDiv >Hello World!</StyledDiv>
}

Is there a way to style the labels in my react-select option?

I'm using react-select and and I'm sending an object as options. The first element contains a title and second contains a subtitle. Using the getOptionLabel API I'm able to populate the option in my select field. This is the code along with its associated style:
export default (props) => {
const selectStyles = {
control: (_provided, state) => {
return {
..._provided,
width: "100%",
fontSize: "1rem",
verticalAlign: "middle",
color: "#1a1a1a",
appearance: "none",
minHeight: "auto",
};
},
valueContainer: (provided) => {
return {
...provided,
minHeight: "auto",
flexWrap: "nowrap",
overflowX: "auto",
padding: "0 .5rem",
};
},
container: (provided) => {
return {
...provided,
padding: 0,
flexGrow: "1",
};
},
option: (provided, { data, isSelected, isFocused }) => {
return {
...provided,
backgroundColor: isSelected ? "#98c1ff" : provided.backgroundColor,
color: data.color ? data.color : provided.color,
fontWeight: data.color ? "800" : provided.fontWeight,
cursor: "pointer",
paddingTop: ".25rem",
paddingBottom: ".25rem",
fontSize: "0.75em",
};
},
multiValue: (provided) => {
return {
...provided,
padding: ".25rem .5rem",
background: "#EBEBEB",
minHeight: "auto",
borderRadius: "2px",
marginRight: ".25rem",
flexShrink: "0",
};
},
multiValueLabel: (provided) => {
return {
...provided,
fontSize: ".75rem",
fontWeight: "400",
color: "#292929",
padding: 0,
};
},
multiValueRemove: (provided) => {
return {
...provided,
color: "#8F8F8F",
background: "transparent",
paddingRight: 0,
":hover": {
color: "#1B4EA3",
background: "transparent",
cursor: "pointer",
},
};
},
clearIndicator: (provided) => {
return {
...provided,
paddingTop: 0,
paddingBottom: 0,
minHeight: "auto",
};
},
dropdownIndicator: (provided) => {
return {
...provided,
paddingTop: 0,
paddingBottom: 0,
minHeight: "auto",
};
},
menu: (provided) => ({
...provided,
marginTop: ".5rem",
marginBottom: 0,
borderRadius: 0,
}),
menuList: (provided) => ({
...provided,
paddingTop: 0,
paddingBottom: 0,
}),
};
let {
name,
id,
options,
value,
} = props;
return (
<Select
{...props}
placeholder=""
id={id}
name={name}
value={value}
options={options}
getOptionLabel={(options) => `${options.title} ${options.subTitle}`}
styles={selectStyles}
/>
);
};
With this I'm able to get the following result:
However, I want the subtitle to have a different color, ideally like this:
You can override SingleValue and Option components and add custom style in your the wrapper components:
Options
const options = [
{
value: "chocolate",
title: "Chocolate",
subTitle: "Icescream"
},
{
value: "strawberry",
title: "Strawberry",
subTitle: "Icescream"
},
{
value: "vanilla",
title: "Vanilla",
subTitle: "Icescream"
}
];
Component
Option component: displays each option in the menu.
SingleValue component: displays the selected value in the input for a single select.
const SingleValue = (props) => {
const { title, subTitle } = props.getValue()[0];
return (
<components.SingleValue {...props}>
<span>{title}</span> <span style={{ color: "darkgray" }}>{subTitle}</span>
</components.SingleValue>
);
};
const Option = (props) => {
const { title, subTitle } = props.data;
return (
<components.Option {...props}>
<span>{title}</span> <span style={{ color: "darkgray" }}>{subTitle}</span>
</components.Option>
);
};
Usage
<Select
options={options}
getOptionLabel={(options) => `${options.title} ${options.subTitle}`}
components={{ SingleValue, Option }}
/>
Live Demo

How can i pass the props to change the width of react-select component

This is how I am calling the component and I am passing the props here and want to use it but don't know to use these props in the code.
<Selector
key={index}
placeholder={"Select an option."}
defaultMenuIsOpen={true}
isSearchable={false}
options={options}
width={"600px"} //this is the props i want to use for width
color={"red"} // this props I want to use for color
onChange={(e: string | any) => {
const { value } = e;
this.handleCondition(value, index, "condition");
}}
/>
Here is a styling part for the react-select and I want to use the props here
export const customStyles: StylesConfig = {
indicatorSeparator: () => ({ display: 'none' }),
menu: () => ({
width: '250px',
marginTop: '4px',
border: 'solid 1px #dfe3e9',
borderRadius: '4px!important',
boxShadow: '0 0 8px 0 #e5e5ea',
position: 'absolute',
zIndex: 999999,
backgroundColor: 'white',
}),
control: (props) => ({
display: 'flex',
// width: props.width ? `${props.width}` : '250px',
width: props.width ? props.width : '250px',
height: '49px!important',
borderRadius: '4px!important',
border: 'solid 1px #dfe3e9',
color: '#1e58c9',
}),
singleValue: () => ({
color: '#1e58c9',
flexWrap: 'nowrap'
})
}
This is React part, this is what I am rendering
<ReactSelect
options={this.props.options}
styles={customStyles}
{...this.props}
/>
{allowClosing && (
<CloseWidget place={dpdown} allowClosing={allowClosing} id={id} />
)}
You could e.g. keep that config object as the class variable to have access to props as well as to state:
state = {
width: 250,
}
customStyles: StylesConfig = {
indicatorSeparator: () => ({ display: 'none' }),
menu: () => ({
width: this.state.width + 'px',
}),
}
You will also have to check if props have changed:
static getDerivedStateFromProps(props, state) {
if (state.width !== props.width)
return {
width: props.width,
};
}
};
And then in your component:
<ReactSelect
options={this.props.options}
styles={this.customStyles}
{...this.props}
/>

update a value using useRef and useEffect

I need to get the left position of an element after elements got mounted to show a progress bar. It works on clicking links however when it got mounted the progressWidth is not getting calculated. basically, the useEffect seems running before the component is mounted!!!!
export default ({ task, selectedLocal, selectedScenarioId, taskId, selectedTaskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const isBefore = taskId <= selectedTaskId;
const isActive = taskId === selectedTaskId;
const navItemWidth = 100;
const [progressWidth, setProgressWidth] = useState(0);
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (ref.current) {
console.log(ref.current.getBoundingClientRect().left);
setProgressWidth(ref.current.getBoundingClientRect().left);
}
});
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold'
},
navTitle: {
width: `${navItemWidth}px`,
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: `${navItemWidth}px`
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative',
'z-index': '3'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
activeNavIndicator: { borderColor: theme.palette.themePrimary },
activeInnerIndicator: { backgroundColor: theme.palette.themePrimary },
progress: {
marginTop: '59px',
'z-index': '2',
position: 'fixed',
left: '0',
width: `${progressWidth}px`,
borderBottom: '2px solid',
borderColor: theme.palette.themePrimary
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(isActive ? [styles.navLink, styles.navLinkActive] : styles.navLink)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div
ref={ref}
className={css(
isBefore ? [styles.navIndicator, styles.activeNavIndicator] : styles.navIndicator
)}
>
<div
className={css(
isBefore ? [styles.innerIndicator, styles.activeInnerIndicator] : styles.innerIndicator
)}
/>
</div>
</div>
</NavLink>
{isActive && <div className={css(styles.progress)} />}
</div>
);
};
So when component is getting loaded I get image 1, when I click on the component I get image 2. What I need to happen is when component is getting loaded it should look like image 2.

Resources