diff options
author | David Yip <yipdw@member.fsf.org> | 2018-03-30 11:29:26 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-30 11:29:26 -0500 |
commit | 257146c9608b38846c9c8431c2b293b7c3cf1c6a (patch) | |
tree | d8a8d11acbff500f566cb2d543f81e02e6363858 /app/javascript/flavours/glitch/components | |
parent | 9248d1e3a73ca97eeb5d177ef2ffcb7d4d9f614c (diff) | |
parent | 48bcf4d6e89658abac5405683e823c68f27248f0 (diff) |
Merge pull request #406 from ThibG/glitch-soc/fixes/menu-fixes
Backport dropdown menu fixes from Mastodon
Diffstat (limited to 'app/javascript/flavours/glitch/components')
-rw-r--r-- | app/javascript/flavours/glitch/components/dropdown_menu.js | 62 |
1 files changed, 30 insertions, 32 deletions
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.js index 7ba7fb22b..245bebef3 100644 --- a/app/javascript/flavours/glitch/components/dropdown_menu.js +++ b/app/javascript/flavours/glitch/components/dropdown_menu.js @@ -8,6 +8,7 @@ import spring from 'react-motion/lib/spring'; import detectPassiveEvents from 'detect-passive-events'; const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; +let id = 0; class DropdownMenu extends React.PureComponent { @@ -29,6 +30,10 @@ class DropdownMenu extends React.PureComponent { placement: 'bottom', }; + state = { + mounted: false, + }; + handleDocumentClick = e => { if (this.node && !this.node.contains(e.target)) { this.props.onClose(); @@ -38,6 +43,7 @@ class DropdownMenu extends React.PureComponent { componentDidMount () { document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); + this.setState({ mounted: true }); } componentWillUnmount () { @@ -82,11 +88,15 @@ class DropdownMenu extends React.PureComponent { render () { const { items, style, placement, arrowOffsetLeft, arrowOffsetTop } = this.props; + const { mounted } = this.state; return ( <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}> {({ opacity, scaleX, scaleY }) => ( - <div className='dropdown-menu' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}> + // It should not be transformed when mounting because the resulting + // size will be used to determine the coordinate of the menu by + // react-overlays + <div className='dropdown-menu' style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}> <div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} /> <ul> @@ -115,8 +125,10 @@ export default class Dropdown extends React.PureComponent { status: ImmutablePropTypes.map, isUserTouching: PropTypes.func, isModalOpen: PropTypes.bool.isRequired, - onModalOpen: PropTypes.func, - onModalClose: PropTypes.func, + onOpen: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + dropdownPlacement: PropTypes.string, + openDropdownId: PropTypes.number, }; static defaultProps = { @@ -124,42 +136,28 @@ export default class Dropdown extends React.PureComponent { }; state = { - expanded: false, + id: id++, }; - handleClick = () => { - if (!this.state.expanded && this.props.isUserTouching() && this.props.onModalOpen) { - const { status, items } = this.props; - - this.props.onModalOpen({ - status, - actions: items.map( - (item, i) => item ? { - ...item, - name: `${item.text}-${i}`, - onClick: this.handleItemClick.bind(this, i), - } : null - ), - }); - - return; - } + handleClick = ({ target }) => { + if (this.state.id === this.props.openDropdownId) { + this.handleClose(); + } else { + const { top } = target.getBoundingClientRect(); + const placement = top * 2 < innerHeight ? 'bottom' : 'top'; - this.setState({ expanded: !this.state.expanded }); + this.props.onOpen(this.state.id, this.handleItemClick, placement); + } } handleClose = () => { - if (this.props.onModalClose) { - this.props.onModalClose(); - } - - this.setState({ expanded: false }); + this.props.onClose(this.state.id); } handleKeyDown = e => { switch(e.key) { case 'Enter': - this.handleClick(); + this.handleClick(e); break; case 'Escape': this.handleClose(); @@ -190,22 +188,22 @@ export default class Dropdown extends React.PureComponent { } render () { - const { icon, items, size, ariaLabel, disabled } = this.props; - const { expanded } = this.state; + const { icon, items, size, ariaLabel, disabled, dropdownPlacement, openDropdownId } = this.props; + const open = this.state.id === openDropdownId; return ( <div onKeyDown={this.handleKeyDown}> <IconButton icon={icon} title={ariaLabel} - active={expanded} + active={open} disabled={disabled} size={size} ref={this.setTargetRef} onClick={this.handleClick} /> - <Overlay show={expanded} placement='bottom' target={this.findTarget}> + <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}> <DropdownMenu items={items} onClose={this.handleClose} /> </Overlay> </div> |