React-select: Support inline-block / sizes

Created on 19 Jul 2015  Â·  40Comments  Â·  Source: JedWatson/react-select

react-select only seems to work as a full-width block component. It would be nice if it would also support use as an inline-block component (like a standard <select>) with either an auto-calculated width or a specified width.

Most helpful comment

Is there a way to make react-select be auto-sized (by content size) and not take up the full width of its parent (or having to be sized to a fixed width that would have to be calculated somehow from the content)?

All 40 comments

+1

@dantman can't you just put the Select inside an inline-block?

@dantman can't you just put the Select inside an inline-block?

That is what I'm doing now. But A) it should not be necessary and B) automatic sizing (like how normal selects work) cannot be done that way.

Is there a way to make react-select be auto-sized (by content size) and not take up the full width of its parent (or having to be sized to a fixed width that would have to be calculated somehow from the content)?

ping @jossmac

I was going to say wrap it, but that's been covered by @mik01aj

@dantman because I haven't needed this personally I'm having trouble imagining how it would be used. Could you please provide a use case, preferably with screenshots?

I've created a jsfiddle demonstrating different use cases
https://jsfiddle.net/Lp51Ldu8/1/
I included notes directly in the fiddle. Defining a fixed width would be ok e.g. for measurements units but for localized names of levels (different length in different languages), it would not be the best solution.

In simple terms, I would need a react select component that can behave the same as the standard html select input control in terms of being an inline element sized by its content width. Even just react-select component having the width - both when open and closed - as the longest option, would be sufficient for me

+1

+1

+1

+1

Update: the reason we haven't gone ahead with this feature is two fold;

  1. To get the desired width of the component we would have to loop through each of the children finding the option with the greatest character length, which would decrease performance
  2. After arriving at this number we'll have to add some fuzzy logic by getting the current font-size and multiplying from there to arrive at the width. This could go horribly wrong in a few cases

If using a fixed-width works for your application, I recommend wrapping the react-select component with your own and handling the inline-block and max-width declarations inside of it. Something like this:

displayName: 'MySelect',
render () {
  return (
    <div style={{ display: 'inline-block', maxWidth: this.props.maxWidth }}>
      <ReactSelect {...this.props} />
    </div>
  )
}
<MySelect ... />

_ping_ @JedWatson safe to close?

Hey @jossmac, it's actually way less complicated than that. It basically involves changing .Select-control to width: auto and having the .Select-value not be absolutely-positioned (plus a few other css cleanups).

@tonybaroneee sounds good man, would you mind submitting a PR?

Sure, I'll try to get to it soon.

that would be pretty useful, can't wait to see the PR :+1:

Hey, I really need this feature. I tried to change what you said @tonybaroneee but it was not as simple as you said because we need to handle the search feature and the multi feature. So I hope you found the solution :)

+1

@tonybaroneee any word on that PR?

Sorry for the delay, been super busy. I'm home on break now, so will try to
work on it this week!
On Dec 23, 2015 8:56 PM, "Joss Mackison" [email protected] wrote:

@tonybaroneee https://github.com/tonybaroneee any word on that PR?

—
Reply to this email directly or view it on GitHub
https://github.com/JedWatson/react-select/issues/323#issuecomment-167022696
.

@cedricdelpoux you're right, this is turning out to be quite a bear based on the combination of elements within a .Select at any given time :stuck_out_tongue:

Is it possible to get @jossmac's solution added to the docs? Or added as an example?

Hey, I have a working solution (not fit to be merged at the moment) in my branch here. Live example here.

Basically I've made it so the collapsed menu isn't actually removed, it just renders with 0 height. This lets us measure the width and keep it in sync with the width of the main element.

There's obviously a lot more changes that need to be made before this can be merged (such as a prop to turn this on or off), but I'd love some feedback on whether or not people think this is a viable route to go.

I just tried @jossmac 's solution and unfortunately, it doesn't seem to be working for me - anyone else got it to work and can provide a working example?

I couldn't get @jossmac 's solution to work, but this worked for me:

.Select-input > input {
    max-width: X - 50px; // where X is the max width that you want
}

To dynamically adjust the height as user pick from the options, I spent a full day using js ways to dynamically adjust the Select width based on the option length.
Then a CSS ninja showed me this, and it works:

.Select-input {
    position: absolute;
    top: 0
}
.Select-value-label {
    display: block;
}
.Select-value {
    position: relative;
    white-space: normal;
}

Will this be implemented in the 1.0 version?

+1

Everyone who's +1'd this, @jossmac and I think we might be able to implement this, but can you tell us more about the use-case please? we're not convinced this isn't easier to handle with a wrapper around react-select, and continue to have the control take up 100% of the _available_ space by default.

my use-case is: show several elements in the same row:
<field_name>: <react-select-component-here> <field_value>

e.g.

Parameter1 <Select: "equals, not equal, contains, etc"> Text Edit box for Parameter1 value here

@alexeyOnGitHub we have exactly same usecase in our app. I have written some css, which is not ready for all uses of a component (for example we don't use prop clearable=true). There is some custom styles also (padding of loading zone).

It's works nice in a row, but is not exactly a inline-block usage (options will overflow selected value when are wider).

This is our "condensed" stylesheet

.Select-container.condensed .Select .Select-control {
    width: auto;
}
.Select-container.condensed:not(.multi) .Select .Select-value,
.Select-container.condensed .Select .Select-placeholder {
    position: static;
    display: table-cell;
    padding-right: 0;
}
.Select-container.condensed .Select .Select-loading-zone {
    padding-left: 5px;
}
.Select-container.condensed:not(.multi) .Select .Select-value ~ .Select-input,
.Select-container.condensed .Select .Select-placeholder ~ .Select-input {
    position: absolute;
    left: 0;
}
.Select-container.condensed .Select .Select-menu-outer {
    width: auto;
    min-width: 100%;
}
.Select-container.condensed .Select .Select-menu {
    overflow-y: auto;
    overflow-x: hidden;
}
.Select-container.condensed:not(.multi) .Select .Select-option {
    white-space: nowrap;
    padding-right: 25px; /* absolute positioning scrollbar fix */
}
.Select-container.condensed.multi .Select .Select-multi-value-wrapper {
    display: initial;
    position: initial;
}

If anyone happened to run into an issue where they had elements neighboring their dynamic sizing react-select component and it was not pushing the neighboring elements to the side until you gave react-select focus again I have found a way to fix it. This fix expands upon on the CSS @shanSyapse posted.

Here is the CSS I used to allow for dynamically resizing react-select and covers the se-case of the component expanding while hiding behind/over neighboring components and not pushing neighboring elements to the side instead

.Select-input {
  position: absolute !important;
  top: 0 !important;
}

.Select-value-label {
  display: block !important;
}

.Select-value {
  position: relative !important;
}

.Select-menu-outer {
  width: auto;
  min-width: 100%;
}

.Select-option {
  white-space: nowrap;
}

.Select-control {
  min-width: 100%;
  width: auto;
}

Hope this helps

Since there isn't any fast way of doing this I used

<div className="col-sm-4">
<Select ... />
</div>

and therefore have 3 elements inlined.

Wrapping the component inside a div with style="display: inline-flex;" can make it inline

I absolutely had to do this for a project as a requirement for the design for one specific select and I came up with this solution.

Make a ref on your select like so:

<Select ref={(select) => this.yourSelect = select} />

In your component's componentDidUpdate lifecycle method update the size of your select using the ref you just made like so:

componentDidUpdate() {

    if (this.yourSelect) { //check to make sure your thing has rendered
        var selectWrapper = this.yourSelect.wrapper; //get the html element, which you'll set the width of
        var selectLabel = this.yourSelect.wrapper.querySelector('.Select-value-label'); //find the actual text that goes into the select element
        //set the width (plus an abitrary number which I had to do for my project. You could also get the padding, margins etc from other objects if needed.)
        selectWrapper.style.maxWidth = selectLabel.offsetWidth + 30 + "px";
    }
}

@shanSyapse css works well to make it take up the size of the currently selected value. In my case I'd like it to work like a normal select element though (taking up size of longest option).

I can see how this might lead to performance issues when having tons of elements though, and how it wouldn't make sense at all in the context of asynchronous options.

Given the many use cases the component accommodates I would not fault the authors for choosing to point users towards just deciding on a max width manually.

Lodash omit for some singleValue properties helped me to fit Select's width by value:

styles={{
    ...otherProps.styles,
    singleValue: styles => _.omit(styles, ['maxWidth', 'position', 'top', 'transform']),
}}

Maybe, it might help.

This is the closest I can get with native style extending functionality:

  singleValue: styles => _.omit(styles, ['maxWidth', 'position', 'transform']),
  menu: styles => ({
    ...styles,
    whiteSpace: 'nowrap',
    width: 'auto',
    right: 0
  })

This is the closest I can get with native style extending functionality:

  singleValue: styles => _.omit(styles, ['maxWidth', 'position', 'transform']),
  menu: styles => ({
    ...styles,
    whiteSpace: 'nowrap',
    width: 'auto',
    right: 0
  })

Awesome, this worked for me! For people like me who don't want to use lodash just for this:

const _objectWithoutProperties = (obj, keys) => {
  var target = {};
  for (var i in obj) {
    if (keys.indexOf(i) >= 0) continue;
    if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
    target[i] = obj[i];
  }
  return target;
};

styles={{
  singleValue: styles =>
    _objectWithoutProperties(styles, [
      'maxWidth',
      'position',
      'transform',
    ]),
    menu: styles => ({
      ...styles,
      whiteSpace: 'nowrap',
      width: 'auto',
      right: 0,
    }),
  }}

For anyone who comes across this, the rest and spread operators can handle this without any need for lodash.

styles={{
    singleValue: ({ maxWidth, position, top, transform, ...otherStyles }) => ({ ...otherStyles }),
}}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

joshualimpj picture joshualimpj  Â·  3Comments

mjuopperi picture mjuopperi  Â·  3Comments

Meesam picture Meesam  Â·  3Comments

pashap picture pashap  Â·  3Comments

batusai513 picture batusai513  Â·  3Comments