react-select: Support inline-block / sizes

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.

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 66
  • Comments: 40 (4 by maintainers)

Most upvoted comments

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)?

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

@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.

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

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

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.

@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;
}

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’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

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

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;
}

+1

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

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

+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?

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

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.

+1

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?