UNPKG

terriajs

Version:

Geospatial data visualization platform.

184 lines (166 loc) 5.59 kB
import React from "react"; import PropTypes from "prop-types"; import createReactClass from "create-react-class"; import classNames from "classnames"; import defined from "terriajs-cesium/Source/Core/defined"; import { withTranslation } from "react-i18next"; import Styles from "./panel.scss"; import Icon from "../../../Styled/Icon"; const InnerPanel = createReactClass({ propTypes: { /** * A property that will be looked for on window click events - if it's set, the click event will not cause the * panel to close. */ doNotCloseFlag: PropTypes.string, /** Disable closing on loss of focus and only allow with close button */ disableCloseOnFocusLoss: PropTypes.bool, /** Will be called when the panel has finished hiding */ onDismissed: PropTypes.func, /** Animate as modal instead of dropdown */ showDropdownAsModal: PropTypes.bool, /** show panel centered instead of offset toward the left */ showDropdownInCenter: PropTypes.bool, /** Relative width to draw from */ modalWidth: PropTypes.number, /** Theme to style components */ theme: PropTypes.object, /** How far the caret at the top of the panel should be from its left, as CSS (so #px or #% are both valid) */ caretOffset: PropTypes.string, /** Will be passed as "ref" to the outermost element */ innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** How far the dropdown should be offset from the left of the container. */ dropdownOffset: PropTypes.string, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.element), PropTypes.element ]), t: PropTypes.func.isRequired }, getDefaultProps() { return { onDismissed: () => {} }; }, getInitialState() { return {}; }, componentDidMount() { this.escKeyListener = (e) => { if (e.keyCode === 27) { this.close(e); } }; window.addEventListener("click", this.close); window.addEventListener("keydown", this.escKeyListener, true); setTimeout(() => this.setState({ isOpenCss: true })); }, componentWillUnmount() { this.cleanListeners(); }, cleanListeners() { window.removeEventListener("click", this.close); window.removeEventListener("keydown", this.escKeyListener, true); }, forceClose() { this.cleanListeners(); // If we're closing we want to immediately change the css class to cause it to animate shut, then when it's // finished actually stop it rendering with isOpen = false this.setState({ isOpenCss: false }); setTimeout(() => { this.props.onDismissed(); }, 200); // TODO: Determine when it stops animating instead of duplicating the 200ms timeout? }, close(e) { // Only close if this wasn't a click on an open/close button. if ( (!this.props.doNotCloseFlag || !e[this.props.doNotCloseFlag]) && !this.props.disableCloseOnFocusLoss ) { this.forceClose(); } }, render() { const { t } = this.props; return ( <div className={classNames( // Until we break these few components out of sass, we'll use regular ol classnames "tjs-sc-InnerPanel", Styles.inner, this.props.theme.inner, { [Styles.isOpen]: this.state.isOpenCss }, { [Styles.showDropdownAsModal]: this.props.showDropdownAsModal }, { [Styles.showDropdownInCenter]: this.props.showDropdownInCenter } )} css={` background: ${(p) => p.theme.dark}; `} ref={this.props.innerRef} onClick={(e) => e.stopPropagation()} style={{ width: this.props.modalWidth, left: this.props.dropdownOffset, // the modal should be right-aligned with the button right: "0px", transformOrigin: this.props.showDropdownInCenter ? "0 top" : this.props.caretOffset && `${this.props.caretOffset} top` }} > <button type="button" className={classNames( // Until we break these few components out of sass, we'll use regular ol classnames "tjs-sc-InnerPanelCloseButton", Styles.innerCloseBtn, { [Styles.innerCloseBtnForModal]: this.props.showDropdownAsModal } )} onClick={this.forceClose} title={t("general.close")} aria-label={t("general.close")} showDropdownAsModal={this.props.showDropdownAsModal} css={` svg { fill: ${(p) => p.theme.textLight}; } &:hover, &:focus { svg { fill: ${(p) => p.theme.colorPrimary}; } } ${(p) => p.showDropdownAsModal && ` svg { fill: ${p.theme.grey}; } `} `} > <Icon glyph={Icon.GLYPHS.close} /> </button> <If condition={ defined(this.props.caretOffset) && !this.props.showDropdownAsModal } > <span className={classNames(Styles.caret, "tjs-sc-InnerPanel__caret")} style={{ left: this.props.caretOffset }} css={` background: ${(p) => p.theme.dark}; `} /> </If> <div className={Styles.content}>{this.props.children}</div> </div> ); } }); export default withTranslation()(InnerPanel);