Dropdown width that accommodates longest string in its options?

When I set the basis for my dropdown to "auto" it:

  • Changes size when the selected option changes
  • Doesn't even size to fit the whole label, but clips it with '...'

image

I could fix this by not using "auto," but then I'd have to figure out what the longest option label is, and it seems like there should be a better way (or that "auto" should just work better?)

Figuring out how to size a dropdown has to be a pretty common thing, so I'm just wondering how others go about it, or if I'm missing something that can make it work better. I'm designing for a system that will have very short labels in some cases, and much longer ones in others, so setting it to a fixed size isn't very desirable.

You could bind the dropdown's basis property to this.props.options and use a script transform to iterate through the options, find the longest one, translate the character length into a pixel value, and return a string in the form "###px".

Or, set the basis to a very high pixel value that can accommodate most of your possible options, and just set the shrink value to 1. Not sure if that would solve the problem of the selected value getting cut off at all.

That's what I've decided to do, except using 'em' instead of pixels since that's a unit relative to the font size. Seems to work fine, just feels like overkill, and that "auto" should just work that way...

Huh, I never knew you could use 'em' as a unit here. I learned something new today.

I did mess around with one of my dropdowns set to 'Auto' and it is working as you would expect -- not clipping my labels or anything. Weird that yours is exhibiting that behavior.

image

image

Yeah that's strange, I thought at first maybe something in my stylesheet was causing the '...' but when I removed it, it still was doing it.

Does yours change size when options of different lengths are chosen?

Yeah, it changes size to match the selected value. What are your grow and shrink values set to? Mine are at 0 and 1, respectively.

Same, 0 and 1. Thought maybe it was unique to the browser, but I see the same thing in Chrome and Edge.

As a workaround, I would use this expression:

concat(len(max({this.props.options})) * 12.045, 'em')
2 Likes

max({this.props.options}) returns null, so that doesn't seem to work, but it's essentially what I've done with scripting. I made it a library function as I anticipate reusing it in the future:

def calculateDropdownBasisForLongestOption(options):
	widest = 0
	for val in options:
		if len(val.label) > widest:
			widest = len(val.label)
	return str(float(widest) * 0.8) + 'em'

It's not a perfect solution unless using a monospace font, which may be why we each came up with different multipliers...

I know that expressions are more efficient than scripts, but I'm assuming that's not the case when using the runScript() expression...?

You can get the widest value in one line using list comprehension. Saves you from checking each value individually.

widest = max([len(options[i].label) for i in range(len(options))])

1 Like

I would have to default to @PGriffith or @pturmel on this, I believe that there is a different penalty that you pay when using the runScript() expression.

max() won't work here, now that I'm looking because the objects in the array are all the same length. That's what I get for not testing.

If you have Phil's Integration Toolkit Module, then you could do this:

concat(
    round(
        max(
            forEach(
                {this.props.options},
                len(it()['label'])
            )
        ) * 12.45,
    2),
"em")

This one is tested. :rofl:

3 Likes

Or even more simply:

widest = max([len(opt.label) for opt in options])
2 Likes