React Select auto size width

2020-06-12 04:19发布

问题:

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

回答1:

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. :)



回答2:

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>


回答3:

if you're using react-select v3 you can use customStyles object:

const customStyles = {
  container: provided => ({
    ...provided,
    width: 150
  })
};

<Select
    styles={customStyles}
    {...otherProps}
/>


回答4:

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}
    />
  )