UNPKG

azure-devops-ui

Version:

React components for building web UI in Azure DevOps

158 lines (157 loc) 8.21 kB
import "../../CommonImports"; import "../../Core/core.css"; import "./TeachingBubble.css"; import * as React from "react"; import { TeachingBubbleCornerPlacement } from "./TeachingBubble.Props"; import { Callout } from '../../Callout'; import { FocusZoneKeyStroke } from '../../FocusZone'; import { css, KeyCode } from '../../Util'; import { ScreenSize } from '../../Core/Util/Screen'; import { Location } from '../..//Utilities/Position'; import { ScreenSizeObserver } from '../../Utilities/ScreenSize'; export class CustomTeachingBubble extends React.Component { constructor(props) { super(props); this.dismiss = () => { this.setState({ fadingOut: true }); }; this.onAnimationEnd = () => { if (this.state.fadingOut) { this.props.onDismiss && this.props.onDismiss(); } else if (this.state.newPosition) { this.setState({ anchorElement: this.props.anchorElement, anchorOrigin: this.props.anchorOrigin, children: this.props.children, cornerPlacement: this.props.cornerPlacement, newPosition: false }, this.props.onLocationChange); } }; this.getCalloutOrigin = (anchorOrigin, cornerPlacement) => { if (anchorOrigin.horizontal === Location.center) { cornerPlacement = TeachingBubbleCornerPlacement.vertical; } else if (anchorOrigin.vertical === Location.center) { cornerPlacement = TeachingBubbleCornerPlacement.horizontal; } if (cornerPlacement === TeachingBubbleCornerPlacement.horizontal) { if (anchorOrigin.horizontal === Location.start) { return { horizontal: Location.end, vertical: anchorOrigin.vertical }; } else if (anchorOrigin.horizontal === Location.end) { return { horizontal: Location.start, vertical: anchorOrigin.vertical }; } else { return { horizontal: Location.center, vertical: anchorOrigin.vertical }; } } else { if (anchorOrigin.vertical === Location.start) { return { horizontal: anchorOrigin.horizontal, vertical: Location.end }; } else if (anchorOrigin.vertical === Location.end) { return { horizontal: anchorOrigin.horizontal, vertical: Location.start }; } else { return { horizontal: anchorOrigin.horizontal, vertical: Location.center }; } } }; this.getBeakClassName = (anchorOrigin, calloutOrigin) => { let classNameHorizontal, classNameVertical; if (calloutOrigin.horizontal === Location.start) { if (anchorOrigin.horizontal === Location.end) { classNameHorizontal = "left-primary"; } else { classNameHorizontal = "left-secondary"; } } else if (calloutOrigin.horizontal === Location.end) { if (anchorOrigin.horizontal === Location.start) { classNameHorizontal = "right-primary"; } else { classNameHorizontal = "right-secondary"; } } else { classNameHorizontal = "center-h"; } if (calloutOrigin.vertical === Location.start) { if (anchorOrigin.vertical === Location.end) { classNameVertical = "top-primary"; } else { classNameVertical = "top-secondary"; } } else if (calloutOrigin.vertical === Location.end) { if (anchorOrigin.vertical === Location.start) { classNameVertical = "bottom-primary"; } else { classNameVertical = "bottom-secondary"; } } else { classNameVertical = "center-v"; } return css(classNameHorizontal, classNameVertical); }; this.state = { anchorElement: props.anchorElement, anchorOrigin: props.anchorOrigin, children: props.children, cornerPlacement: this.props.cornerPlacement }; } static getDerivedStateFromProps(props, state) { if (state.anchorElement !== props.anchorElement || state.anchorOrigin.horizontal !== props.anchorOrigin.horizontal || state.anchorOrigin.vertical !== props.anchorOrigin.vertical) { return Object.assign(Object.assign({}, state), { newPosition: true }); } else { return { children: props.children, cornerPlacement: props.cornerPlacement }; } } render() { const { anchorElement, anchorOrigin, children, cornerPlacement = TeachingBubbleCornerPlacement.vertical, fadingOut, newPosition } = this.state; const calloutOrigin = this.getCalloutOrigin(anchorOrigin, cornerPlacement); return (React.createElement(ScreenSizeObserver, null, (screenSizeProps) => { const fullScreen = screenSizeProps.screenSize === ScreenSize.xsmall; let maxHeight; if (!fullScreen) { if (anchorOrigin.vertical === Location.end) { maxHeight = window.innerHeight - anchorElement.getBoundingClientRect().bottom; } else if (anchorOrigin.vertical === Location.start) { maxHeight = anchorElement.getBoundingClientRect().top; } else { maxHeight = window.innerHeight; } } return (React.createElement(Callout, { anchorElement: fullScreen ? undefined : anchorElement, anchorPoint: fullScreen ? { x: 0, y: 0 } : undefined, anchorOrigin: anchorOrigin, ariaDescribedBy: this.props.ariaDescribedBy, ariaLabelledBy: this.props.ariaLabeledBy, blurDismiss: this.props.textOnly, calloutOrigin: calloutOrigin, className: css(fullScreen && "absolute-fill"), contentClassName: css("bolt-bubble-callout", (fadingOut || newPosition) && "fade-out", this.getBeakClassName(anchorOrigin, calloutOrigin), fullScreen ? "bolt-bubble-fullscreen absolute-fill flex-grow scroll-auto" : "relative"), contentShadow: true, escDismiss: true, fixedLayout: true, focuszoneProps: { circularNavigation: true, defaultActiveElement: this.props.defaultActiveElement || ".bolt-bubble-focusable-element", focusOnMount: true, handleTabKey: true, includeDefaults: true, postprocessKeyStroke: function (event) { // We want to prevent moving outside the bubble if there are no focusable elements in the bubble. event.which === KeyCode.tab && event.preventDefault(); return FocusZoneKeyStroke.IgnoreParents; } }, onAnimationEnd: this.onAnimationEnd, onDismiss: this.dismiss, updateLayout: true, viewportChangeDismiss: false }, React.createElement("div", { "aria-live": "polite", "aria-relevant": "text", className: "bolt-bubble-container" }, !fullScreen && React.createElement("div", { className: "bolt-bubble-beak depth-8" }), React.createElement("div", { className: "bolt-bubble-content relative scroll-auto", style: { maxHeight: maxHeight } }, React.createElement("div", { className: "bolt-bubble-focusable-element no-outline", tabIndex: -1 }), children)))); })); } }