React Select auto size width - reactjs

When using react-select it is not auto sizing by option value, but using width:100% as you can see in picture:
Options are short:
getOptions() {
return [
{ value: 'AND', label: 'AND' },
{ value: 'OR', label: 'OR' }
]
}
And code which produces it:
<Select
options={this.getOptions()}
value={value}
autosize={true}
clearable={false}
simpleValue
/>
Is there any way to make react-select to show these values with auto sizing, so select box would be the same as option length, and I could, for example, center this select box in <div>?
Updated 14.11.2017
Full example can be seen in this jsFiddle

Inline styles did not work for me.
I just wrapped the Select component in a div and gave the div the width I wanted.
<div style={{width: '300px'}}>
<Select
menuPlacement="auto"
menuPosition="fixed"
etc, etc..
/>
</div>

SOLUTION 1
You can leverage React's inline styles by updating the components' width based on the length of the selected option.
Let me explain further: Say the selected value is HelloWorld. This string is of length 10. We could guess that each character accounts for say 8px each on average (total guess I have no clue at all). Thus, the width of this word is around 8*10=80px, right ? Also, there are some controls after the word (the carret and the cross) and we need some minimum padding: together they may be of 100px width. Then here you have it: your div's width should be ( 8px * 10 letters ) + 100px = 180px.
More precisely, the correct formula is something like:
(average_letter_size * selected_value.length) + other_elements_sizes
When selected_value changes, so does its length, and therefore the width of the div gets updated with the new total.
Example: if the selected value is now Lorem Ipsum dolor sit amet, the length is now 26. By applying the formula we get a larger width of : (8px * 26 letters) + 100px = 308px.
For this to work in react, here is a snippet:
<Select
style={{width: `${(8*this.state.selectedOption2.length) + 100}px`}}
className="select-custom-class"
name="form-field-name"
value={this.state.selectedOption2}
options={options2}
onChange={(value) => { this.setState({ selectedOption2: value.value }); }}
/>
As you can see I added :
style={{width: `${(8*this.state.selectedOption2.length) + 100}px`}}
to your component. Whenever the state gets updated, everything is propagated including the width of the component.
See a working example in this fiddle.
Eventually, you want to fine-tune the rules and averages to your needs. I also suggest you apply a letter size depending on the number of capital and lowercase letters in the selected value.
SOLUTION 2 (edit)
I came up with a pure CSS solution if you want. It should be better tested against your design, but this should work:
/* .Select-value comes with an absolute position to stack it below .Select-input */
/* we need to scratch that in order for us to be able to let the div grow depending on its content size */
.Select-placeholder, .Select--single > .Select-control .Select-value {
position: relative;
padding-left: 0;
}
/* All these 3 classes come with ugly "table" display...*/
.Select-control, .Select-clear-zone, .Select-arrow-zone {
display: inherit;
}
/* here is the trick: we display the wrapper as flex in order to make it fit in height*/
/* we flip positions of .Select-value and .Select-input using row-reverse in order to have a nice input to the left and no to the right */
.select-custom-class .Select-multi-value-wrapper {
display: flex;
flex-direction: row-reverse;
}
/*we put our controls back to a better center position */
.Select-clear-zone {
position: absolute;
top: 8px;
right: 20px;
}
.Select-arrow-zone {
position: absolute;
top: 8px;
right: 0px;
}
See a working fiddle (I changed some of the examples for better illustration)
Tell me what you think. :)

I borrowed this from aidan-keay on the repo thread, but adding this to the custom styles prop worked for me:
menu: (base) => ({
...base,
width: "max-content",
minWidth: "100%"
}),

if you're using react-select v3 you can use customStyles object:
const customStyles = {
container: provided => ({
...provided,
width: 150
})
};
<Select
styles={customStyles}
{...otherProps}
/>

9/2020
Hey guys :) the solution is so simple than that workarounds !
the problem in these classes __placeholder, __single-value
just add this css to both of them and you will get auto sized react-select
.CUSTOM_PREFIX__single-value,
.CUSTOM_PREFIX__placeholder {
position: static;
transform: none;
max-width: none;
}
In the above example the prop classNamePrefix will equal CUSTOM_PREFIX
classNamePrefix="CUSTOM_PREFIX"

09/2021 (react-select v4)
Adding position: static and transform: none will scale the select-container accordingly.
placeholder: (provided) => ({
...provided,
position: 'static',
transform: 'none',
}),
singleValue: (provided) => ({
...provided,
position: 'static',
transform: 'none',
}),

Update: for people who are using React-Select for a "tags autocomplete" feature but are having trouble where it sets a style width that is too narrow based on the previous tag you searched, this is what works for me:
Set a style of:
.myCustomPrefix__value-container > div {
width: auto !important;
}
Set classNamePrefix="myCustomPrefix" in the component (docs).
Old answer:
See the official docs at https://react-select.com/styles#style-object
I originally thought that setting width to "auto" for option worked for me:
const customStyles = {
option: (styles, { data, isDisabled, isFocused, isSelected }) => {
return {
...styles,
fontSize: '12px',
textAlign: 'left',
width: 'auto',
}
},
}
...
return (
//https://react-select.com/props
<AsyncSelect
components={animatedComponents}
isMulti
// isDisabled={isLoading}
// isLoading={isLoading}
onChange={handleChange}
onInputChange={handleInputChange}
cacheOptions
defaultOptions
defaultMenuIsOpen={false}
closeMenuOnSelect={closeMenuOnSelect}
placeholder={placeholder}
loadOptions={promiseOptions}
value={selectedOptions}
styles={customStyles}
formatOptionLabel={formatOptionLabel}
/>
)

Related

React material-table - set row height

First of all, I have tested all possible solutions I have founded and I still didn't solve my problem.
I want to put a shorter height in my material-table rows. This is what it looks like right now.
Table
I would like my rows to have a height similar to the headers in that image. I have tried many things, one of them was the following:
options={{
headerStyle: {
whiteSpace: "nowrap",
height: 20,
maxHeight: 20,
padding: 0
},
rowStyle: {
height: 20,
maxHeight: 20,
padding: 0
},
}}
I would really appreciate it if someone can help me.
You can set options in material-table
options={{
padding: "dense",
}}
I had the same problem with React Material-Table. I fixed adding this in the global index.css of the project:
.MuiTableCell-root {
padding: 0 14px !important;
}
and then i could modify the height in the rowStyle in the options of the Material-Table component:
options = {
rowStyle: {
height: "20px"
}
}
you need to use withStyles and update the specific element class style, so it will reflect to all the elements. Check the working example as you expected here : codesandbox
import MuiTableCell from '#material-ui/core/TableCell';
const TableCell = withStyles(theme => ({
root: {
height: 10,
padding:0
}
}))(MuiTableCell);
Much more simplified, overwrite padding of 16px to smaller size...<TableCell style={{padding:5px}} ...>.
I think the link below might be helpful for you.
customizing material ui table

is it possible to animate a strikethrough with React Spring?

I'm new to React Spring and I initially tried this
const strikeProps = useSpring({
textDecoration: "line-through",
from: { textDecoration: "none" },
});
But it's not working. I think there should be a way simulate the CSS solution for this.
The problem here is, that the original CSS solution is uses pseudo element for emulating the strike trough. We can only add react-spring properties for normal html elements. So the most compact way is to create a separate strike through component for this problem. For example:
const StrikeTroughtText = ({ children, weight = 1 }) => {
const props = useSpring({
from: { width: "0%" },
to: { width: "100%" }
});
return (
<div style={{ position: "relative", display: "inline-block" }}>
{children}
<animated.div
style={{
position: "absolute",
top: "50%",
left: 0,
width: props.width,
height: `${weight}px`,
background: "black"
}}
/>
</div>
);
};
We basically animate the width of the absolutely positioned div containing a black line over the text.
You can use it like a div component:
<StrikeTroughtText>text</StrikeTroughtText>
For bigger font size the default 1 px line weight is not enough, so I added a weight property also.
Here is my example: https://codesandbox.io/s/react-strike-trought-text-component-with-react-spring-animation-86cfd?file=/src/App.js

React-select default behaviour and custom looking

I am very new in react and in react-select too. While trying to implement a custom looking component I read a lot of information, but it is not so user friendly IMHO.
I want my react-select component to show selected data in a row. Now it shows them in columns, I attached the picture.
BTW what is the purpose of name, label fields? What is the exact difference?
<Select
defaultValue={[models[0], models[1]]}
isMulti
options={models}
className="select-custom-class"
name="form-field-name"
options={models}
placeholder="Model: "
/>
I want to do some "chips style" dropdown VIA react-select. Is it possible? What style options I have to specify to round the corners like on the picture? I also want to add some static text inside the chips to be shown always: for example (regarding the pic below) "City: $here_come_the_values"
Maybe I should use another react component? The ending purpose of those dropdowns are to make filtering to the table, so I want to link them with react-bootstrap-table2.
Any help is appreciated! Thank you.
Well, actually I found how to make a nice-looking ONE LINE select component with dots in the end (like spread operator or ellipsis)
with modern react-jsx syntax without this
<Select
defaultValue={defaultValues}
closeMenuOnSelect={false}
isMulti
options={defaultValues}
components={{
IndicatorSeparator: () => null,
DropdownIndicator,
ClearIndicator,
MultiValueContainer: multiValueContainer
}}
and here we return all the values
const multiValueContainer = (props) => {
const label = props.data.label;
const allSelected = props.selectProps.value;
const index = allSelected.findIndex(selected => selected.label === label);
const isLastSelected = index === allSelected.length - 1;
const labelSuffix = isLastSelected ? ` (${allSelected.length})` : ", ";
const val = `${label}${labelSuffix}`;
return val;
};
and the style
const styles = {
control: (base, state) => ({
...base,
borderRadius: '16px',
border: '1px solid #E5F7FF',
boxShadow: 'none',
boxSizing: 'border - box',
wordWrap: "break-word",
'&:hover':
{
border: '1px solid #0679A8',
}
}),
valueContainer: (provided, state) => ({
...provided,
textOverflow: "ellipsis",
maxWidth: "90%",
whiteSpace: "nowrap",
overflow: "hidden",
display: "initial"
})
};

react-select background color issues

Having a problem with using className prop.
What's happening for me is that only the parent div gets the class and the children divs don't. As a result, they end up having background color white instead of the override color.
<Select
className="games-dropdown-2"
defaultValue={colourOptions[0]}
name="color"
options={colourOptions}
/>
Below is the css class
.games-dropdown-2 {
background-color: #023950;
color: #FFFFFF;
padding-left: 15px;
width: 93%;
}
Another problem is that the child div seems to be inheriting border css from the grandparent div which is weird.
Attaching an image to give idea.
react-select-classname-issue
For v2 it's way easier to use style-in-JS in order to customize your select. So in your case you can try something like this:
const customStyles = {
control: (base, state) => ({
...base,
background: "#023950",
// match with the menu
borderRadius: state.isFocused ? "3px 3px 0 0" : 3,
// Overwrittes the different states of border
borderColor: state.isFocused ? "yellow" : "green",
// Removes weird border around container
boxShadow: state.isFocused ? null : null,
"&:hover": {
// Overwrittes the different states of border
borderColor: state.isFocused ? "red" : "blue"
}
}),
menu: base => ({
...base,
// override border radius to match the box
borderRadius: 0,
// kill the gap
marginTop: 0
}),
menuList: base => ({
...base,
// kill the white space on first and last option
padding: 0
})
};
<Select styles={customStyles} options={options} />
If you need to use thus select in different files I would recommend to create a custom component so you won't have to repeat the style everywhere.
By default the text will take the color define in your general CSS file.
Here the live example.
UPDATE
Following your request in comment I have updated the code above and here a new live example.
you can solve your background color issue like below and people have also faced some issue of z-index that also solved
const colourStyles = {
menuList: styles => ({
...styles,
background: 'papayawhip'
}),
option: (styles, {isFocused, isSelected}) => ({
...styles,
background: isFocused
? 'hsla(291, 64%, 42%, 0.5)'
: isSelected
? 'hsla(291, 64%, 42%, 1)'
: undefined,
zIndex: 1
}),
menu: base => ({
...base,
zIndex: 100
})
}
const options = [
{value: 'chocolate', label: 'Chocolate'},
{value: 'strawberry', label: 'Strawberry'},
]
<Select
// defaultValue={[colourOptions[2], colourOptions[3]]}
name="colors"
options={options}
className="basic-multi-select"
classNamePrefix="select"
styles={colourStyles}
/>

How can I disable multiline for autocomplete material-ui demo?

Country select of
autocomplete demo at material-ui
uses react-select and material-ui controls,
shows multiline text, select control changes it's dimensions when country doesn't fit in one line.
I see this behaviour at CodeSandbox when I decrease width of web browser.
How can I modify demo so that country will always fit in one line,
select control will not change it's dimensions?
TextField has props multiline, rows and rowsMax props that can be changed.
If that isn't what you need then you could add the following css to the text in the TextField so the text does not wrap:
overflow: hidden;
white-space: nowrap;
I managed this by mixing a few different things:
First create a class like so:
const useStyles = makeStyles((theme: Theme) =>
createStyles({
closed: {
flexWrap: "nowrap",
overflowX: "hidden",
},
// Add a linear gradient behind the buttons and over the Chips (if applies)
endAdornment: {
background:
"linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.6) 22%, rgba(255,255,255,1) 60%)",
bottom: 0,
display: "flex",
alignItems: "center",
right: "0 !important",
paddingRight: 9,
paddingLeft: theme.spacing(2),
top: 0,
},
})
);
Then in your static function add this :
const onOpenChange = (open: boolean | null) => {
setIsOpen(open);
};
const inputStyle = clsx({
[classes.closed]: !isOpen, //only when isOpen === false
});
Finally on the Autocomplete component itself use:
classes={{ inputRoot: inputStyle, endAdornment: classes.endAdornment }}
onOpen={() => onOpenChange(true)}
onClose={() => onOpenChange(false)}
If you are wondering how to make each option be displayed in just one line with ellipsis, you can do the follow:
<Autocomplete
...
getOptionLabel={(option: any) => `${option.label} (${option.code})`}
renderOption={(option) => (
<React.Fragment>
<div style={{ textOverflow: 'ellipsis', overflow: "hidden", whiteSpace: "nowrap" }}>
{option.label} ({option.code})
</div>
</React.Fragment>
)}
...
/>
For the Country Demo example, you can check what I did here: https://codesandbox.io/s/autocomplete-with-ellipsis-i8hnw

Resources