UNPKG

terriajs

Version:

Geospatial data visualization platform.

143 lines (129 loc) 4.4 kB
"use strict"; // proptypes are in mixin /* eslint react/prop-types:0*/ import React from "react"; import createReactClass from "create-react-class"; import classNames from "classnames"; import Icon from "../../../Styled/Icon"; import InnerPanel from "./InnerPanel"; import BaseOuterPanel from "./BaseOuterPanel"; import Styles from "./panel.scss"; import defined from "terriajs-cesium/Source/Core/defined"; const DropdownPanel = createReactClass({ displayName: "DropdownPanel", mixins: [BaseOuterPanel], getInitialState() { return { localIsOpen: false, caretOffset: undefined, dropdownOffset: undefined }; }, onInnerMounted(innerElement) { const centerInnerDropdown = this.props.showDropdownInCenter; if (centerInnerDropdown) { this.setState({ caretOffset: "50%", dropdownOffset: "50%" }); } else if (innerElement) { const btnRef = this.props.btnRef; const buttonElementOffsetLeft = btnRef?.current?.offsetLeft || this.buttonElement?.offsetLeft || 0; const buttonElementClientWidth = btnRef?.current?.clientWidth || this.buttonElement?.clientWidth || 0; // how much further right the panel is from the button const offset = buttonElementOffsetLeft - innerElement.offsetLeft; // if the panel is left of the button leave its offset as is, otherwise move it right so it's level with the button. const dropdownOffset = offset < innerElement.offsetLeft ? offset : innerElement.offsetLeft; // offset the caret to line up with the middle of the button - note that the caret offset is relative to the panel, whereas // the offsets for the button/panel are relative to their container. const caretOffset = Math.max( buttonElementClientWidth / 2 - 10 - (dropdownOffset - buttonElementOffsetLeft), 0 ); this.setState({ caretOffset: caretOffset >= 0 && caretOffset + "px", dropdownOffset: dropdownOffset + "px" }); } else { this.setState({ caretOffset: undefined, dropdownOffset: undefined }); } }, /* eslint-disable-next-line camelcase */ UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.forceClosed) { this.onDismissed(); } }, openWithUserClick(e) { if (this.props.userOnClick) { this.props.userOnClick(); } this.openPanel(e); }, render() { let iconGlyph; if (defined(Icon.GLYPHS[this.props.theme.icon])) { iconGlyph = Icon.GLYPHS[this.props.theme.icon]; } else { iconGlyph = this.props.theme.icon; } return ( <div className={classNames(Styles.panel, this.props.theme.outer)}> <button onClick={this.openWithUserClick} type="button" className={classNames(Styles.button, this.props.theme.btn, { [Styles.buttonForModalDropdown]: this.props.showDropdownAsModal })} title={this.props.btnTitle} ref={ this.props.btnRef || ((element) => (this.buttonElement = element)) } isOpen={this.isOpen()} css={` ${(p) => p.isOpen && `&:not(.foo) { background: ${p.theme.colorPrimary}; svg { fill: ${p.theme.textLight}; } }`} `} > <If condition={this.props.theme.icon}> <Icon glyph={iconGlyph} /> </If> <If condition={this.props.btnText}> <span>{this.props.btnText}</span> </If> </button> <If condition={this.isOpen()}> <InnerPanel showDropdownInCenter={this.props.showDropdownInCenter} showDropdownAsModal={this.props.showDropdownAsModal} modalWidth={this.props.modalWidth} onDismissed={this.onDismissed} innerRef={this.onInnerMounted} doNotCloseFlag={this.getDoNotCloseFlag()} theme={this.props.theme} caretOffset={this.state.caretOffset} dropdownOffset={this.state.dropdownOffset} disableCloseOnFocusLoss={this.props.disableCloseOnFocusLoss} > {this.props.children} </InnerPanel> </If> </div> ); } }); export default DropdownPanel;