Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I make the dropdown open at the top instead of bottom? #403

Closed
chirantan opened this issue Aug 24, 2015 · 23 comments
Closed

How do I make the dropdown open at the top instead of bottom? #403

chirantan opened this issue Aug 24, 2015 · 23 comments

Comments

@chirantan
Copy link

No description provided.

@aldeka
Copy link

aldeka commented Sep 9, 2015

I too have this issue, where I have a dropdown near the bottom of the page and the dropdown menu breaks things when open because there is no room at the bottom. Being able to specify the direction that the dropdown opens would be a big plus.

@vinayakpatil
Copy link

Is there an easy way to do this?

@vjpr
Copy link

vjpr commented Dec 1, 2015

+1

@medv
Copy link

medv commented Dec 9, 2015

Add this to your css somewhere:

.Select-menu-outer{top: auto; bottom: 100%}

Of course then you would have to check how far you are scrolled and decide whether to open above/below. This would make a great addition, maybe your scrollable container's node passed as a prop to react-select for it to measure its dropdown height, the container's scrollTop vs the select. Not sure what the best way to get this done in react yet!

@vitosamson
Copy link

wanted to share my approach to this, in case it helps anyone:

import { findDOMNode } from 'react-dom';
import Select from 'react-select';

const MAX_MENU_HEIGHT = 200;
const AVG_OPTION_HEIGHT = 36;

class MySelect extends Component {
  constructor(props) {
    super(props);

    this.state = {
      dropUp: false,
    };

    this.determineDropUp = this.determineDropUp.bind(this);
  }

  componentDidMount() {
    this.determineDropUp(this.props);
    window.addEventListener('resize', this.determineDropUp);
    window.addEventListener('scroll', this.determineDropUp);
  }

  componentWillReceiveProps(nextProps) {
    this.determineDropUp(nextProps);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.determineDropUp);
    window.removeEventListener('scroll', this.determineDropUp);
  }

  determineDropUp(props = {}) {
    const options = props.options || this.props.options || [];
    const node = findDOMNode(this.selectInst);

    if (!node) return;

    const windowHeight = window.innerHeight;
    const menuHeight = Math.min(MAX_MENU_HEIGHT, (options.length * AVG_OPTION_HEIGHT));
    const instOffsetWithMenu = node.getBoundingClientRect().bottom + menuHeight;

    this.setState({
      dropUp: instOffsetWithMenu >= windowHeight,
    });
  }

  render() {
    const className = this.state.dropUp ? 'drop-up' : '';
    
    return (
      <Select {...this.props} className={className} ref={inst => (this.selectInst = inst)} />
    );
  }
}
.drop-up .Select-menu-outer {
  top: auto;
  bottom: 100%;
}

the calculations may require some tweaking based on your page layout. it unfortunately doesn't work for async options since we don't have access to the loaded options via props. I also debounce the determineDropUp method to 100ms to avoid any thrashing from the resize and scroll listeners.

@hyperh
Copy link

hyperh commented Jan 22, 2017

+1

@medv
Copy link

medv commented Jan 23, 2017

@vitosamson looks good, though I would base my solution on measurement so that your css can be king in terms of determining how big something is and what are its limits (regarding your pixel constants at the top of the file).

@vitosamson
Copy link

@medv I don't think that's possible, since the menu would have to already be open in order to measure its height and the height of the options.

@medv
Copy link

medv commented Jan 24, 2017

@vinayakpatil that's generally when you render a proxy somewhere off-screen or below layers with the same style directive and gather data from it. For each differently-styled react-select component that would subscribe to this thinking, you would spawn a dummy proxy menu (located in the same dom path available with the same selector you target your real component with so all css applies) that has a single item and take readings from that, then your measurements are correct.

It introduces a little performance loss due to this process (keeping in mind window-resize etc), but it solves a lot of front-end questions for those of us that can use css well and depend on it doing its job. Specific media-query directives defined in your css will save you a lot of time and issues - I have not used react-select in a project that didn't have the same component drawn in at least 2-3 ways depending on matching media query. Not a pre-set "breakpoint", since screen sizes are varied these days. It's better to use something like jeet.gs or lost-grid to write specific rules for your components from mobile-first up to wherever they feel like they don't belong anymore, at which point you bump them up to the next presentation rules.

@alanqthomas
Copy link

Thanks @vitosamson! Here's the CSS I used in conjunction with what he already used to achieve a bit more visual consistency, i.e. margin fix for positioning, corner radii, remove box-shadow. Hope it helps.

.drop-up .Select-menu-outer {
	top: auto;
	bottom: 100%;
	margin-top: 0px;
	margin-bottom: -1px;
	border-bottom-right-radius: 0px;
	border-bottom-left-radius: 0px;
	border-top-left-radius: 4px;
	border-top-right-radius: 4px;
	box-shadow: none;
}

.drop-up.is-open > .Select-control {
	border-bottom-right-radius: 4px;
	border-bottom-left-radius: 4px;
	border-top-left-radius: 0px;
	border-top-right-radius: 0px;
}

@dmr
Copy link

dmr commented Apr 27, 2017

In buildo/react-components#800 they integrated an option for this behavior

menuPosition="top"

--> How do you feel about this option for react-select?

@vkmita
Copy link

vkmita commented Jul 21, 2017

+1

@geminiyellow
Copy link

+1 hope this issue can be close tomorrow.

@VD17593
Copy link

VD17593 commented Feb 19, 2018

added to Select :

menuContainerStyle={{top: 'auto', bottom: '100%'}}

@adccb
Copy link

adccb commented Sep 5, 2018

bump!

hey @JedWatson, i'd love to see some kind of response to this. this should be done by the library, not left to individual apps to hack together a solution. thanks!

@vinayakpatil
Copy link

@JedWatson Many libraries use https://popper.js.org/ for this desired intelligent popover positioning

@xiao-lix
Copy link

xiao-lix commented Dec 9, 2018

add to Select :

menuPlacement = "top"

@nguyenvanphuc2203
Copy link

nguyenvanphuc2203 commented Jan 11, 2019

add to Select :

menuPlacement = "top"

@cathylollipop it work!

@jossmac
Copy link
Collaborator

jossmac commented Feb 14, 2019

Ref #1602

@jossmac
Copy link
Collaborator

jossmac commented Feb 14, 2019

Use menuPlacement. Docs for the props API. Thanks @cathylollipop

@zahramemar
Copy link

Add this props to your component menuPlacement="auto" .

@jayarjo
Copy link

jayarjo commented Dec 8, 2022

menuPlacement="auto" doesn't work for me, menu is still showing up below the component, even when it renders outside of the screen.

@AlreNux
Copy link

AlreNux commented Aug 14, 2023

const [isOpenAbove, setIsOpenAbove] = useState<boolean | null>(null)

  const refSelectField = useRef<HTMLDivElement | null>(null)
  const refSelectDropdownField = useRef<HTMLDivElement | null>(null)

  useLayoutEffect(() => {
    function positionDropdown() {
      if (refSelectField.current) {
        const selectRect = refSelectField.current.getBoundingClientRect()
        const spaceBelow = window.innerHeight - selectRect.bottom
        if (refSelectDropdownField.current) {
          if (spaceBelow < refSelectDropdownField.current.offsetHeight) {
            setIsOpenAbove(true)
          } else {
            setIsOpenAbove(false)
          }
        }
      }
    }
    window.addEventListener('resize', positionDropdown)
    positionDropdown()

    return () => {
      window.removeEventListener('resize', positionDropdown)
    }
  }, [onFocus])
<div
          className={clsx(
            'absolute left-0',
            isOpenAbove
              ? 'bottom-auto top-0 -translate-y-full'
              : ' bottom-0 translate-y-full'
          )}
          ref={refSelectDropdownField}>{...custom dropdown}</div>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests