From 030370cf6255d1185ecb3deeaab7de1fe76e058b Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 28 Aug 2017 11:41:51 +0200 Subject: add branches help popups (#2420) --- .../js/components/common/BubblePopupHelper.tsx | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 server/sonar-web/src/main/js/components/common/BubblePopupHelper.tsx (limited to 'server/sonar-web/src/main/js/components/common/BubblePopupHelper.tsx') diff --git a/server/sonar-web/src/main/js/components/common/BubblePopupHelper.tsx b/server/sonar-web/src/main/js/components/common/BubblePopupHelper.tsx new file mode 100644 index 00000000000..f24365adf57 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/BubblePopupHelper.tsx @@ -0,0 +1,105 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import * as classNames from 'classnames'; + +interface Props { + className?: string; + children?: React.ReactNode; + isOpen: boolean; + offset?: { vertical: number; horizontal: number }; + popup: React.ReactElement; + position: 'bottomleft' | 'bottomright'; + togglePopup: (show: boolean) => void; +} + +interface State { + position: { top: number; left?: number; right?: number }; +} + +export default class BubblePopupHelper extends React.PureComponent { + container: HTMLElement; + popupContainer: HTMLElement | null; + state: State = { + position: { top: 0, right: 0 } + }; + + componentDidMount() { + this.setState({ position: this.getPosition(this.props) }); + } + + componentWillReceiveProps(nextProps: Props) { + if (!this.props.isOpen && nextProps.isOpen) { + window.addEventListener('keydown', this.handleKey, false); + window.addEventListener('click', this.handleOutsideClick, false); + } else if (this.props.isOpen && !nextProps.isOpen) { + window.removeEventListener('keydown', this.handleKey); + window.removeEventListener('click', this.handleOutsideClick); + } + } + + handleKey = (event: KeyboardEvent) => { + // Escape key + if (event.keyCode === 27) { + this.props.togglePopup(false); + } + }; + + handleOutsideClick = (event: MouseEvent) => { + if (!this.popupContainer || !this.popupContainer.contains(event.target as Node)) { + this.props.togglePopup(false); + } + }; + + handleClick(event: React.SyntheticEvent) { + event.stopPropagation(); + } + + getPosition(props: Props) { + const containerPos = this.container.getBoundingClientRect(); + const { position } = props; + const offset = props.offset || { vertical: 0, horizontal: 0 }; + if (position === 'bottomleft') { + return { top: containerPos.height + offset.vertical, left: offset.horizontal }; + } else { + // if (position === 'bottomright') + return { top: containerPos.height + offset.vertical, right: offset.horizontal }; + } + } + + render() { + return ( +
(this.container = container as HTMLElement)} + onClick={this.handleClick} + tabIndex={0} + role="tooltip"> + {this.props.children} + {this.props.isOpen && +
(this.popupContainer = popupContainer)}> + {React.cloneElement(this.props.popup, { + popupPosition: this.state.position + })} +
} +
+ ); + } +} -- cgit v1.2.3