terriajs
Version:
Geospatial data visualization platform.
143 lines (129 loc) • 4.4 kB
JSX
"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;