UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

272 lines (270 loc) • 14.7 kB
define(["require", "exports", "tslib", "react", "../../common/DirectionalHint", "../../Utilities", "../../utilities/positioning", "../../Popup", "./Callout.scss", "../../Styling"], function (require, exports, tslib_1, React, DirectionalHint_1, Utilities_1, positioning_1, Popup_1, stylesImport, Styling_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var styles = stylesImport; var BEAK_ORIGIN_POSITION = { top: 0, left: 0 }; var OFF_SCREEN_STYLE = { opacity: 0 }; var BORDER_WIDTH = 1; var CalloutContent = (function (_super) { tslib_1.__extends(CalloutContent, _super); function CalloutContent(props) { var _this = _super.call(this, props) || this; _this._warnDeprecations({ 'beakStyle': 'beakWidth' }); _this._didSetInitialFocus = false; _this.state = { positions: null, slideDirectionalClassName: null, calloutElementRect: null, heightOffset: 0 }; _this._positionAttempts = 0; return _this; } CalloutContent.prototype.componentDidUpdate = function () { this._setInitialFocus(); this._updatePosition(); }; CalloutContent.prototype.componentWillMount = function () { var target = this.props.targetElement ? this.props.targetElement : this.props.target; this._setTargetWindowAndElement(target); }; CalloutContent.prototype.componentWillUpdate = function (newProps) { if (newProps.targetElement !== this.props.targetElement || newProps.target !== this.props.target) { var newTarget = newProps.targetElement ? newProps.targetElement : newProps.target; this._maxHeight = undefined; this._setTargetWindowAndElement(newTarget); } if (newProps.gapSpace !== this.props.gapSpace || this.props.beakWidth !== newProps.beakWidth) { this._maxHeight = undefined; } }; CalloutContent.prototype.componentDidMount = function () { this._onComponentDidMount(); }; CalloutContent.prototype.render = function () { // If there is no target window then we are likely in server side rendering and we should not render anything. if (!this._targetWindow) { return null; } var _a = this.props, role = _a.role, ariaLabel = _a.ariaLabel, ariaDescribedBy = _a.ariaDescribedBy, ariaLabelledBy = _a.ariaLabelledBy, className = _a.className, target = _a.target, targetElement = _a.targetElement, isBeakVisible = _a.isBeakVisible, beakStyle = _a.beakStyle, children = _a.children, beakWidth = _a.beakWidth, finalHeight = _a.finalHeight, backgroundColor = _a.backgroundColor; var positions = this.state.positions; var beakStyleWidth = beakWidth; // This is here to support the old way of setting the beak size until version 1.0.0. // beakStyle is now deprecated and will be be removed at version 1.0.0 if (beakStyle === 'ms-Callout-smallbeak') { beakStyleWidth = 16; } var beakReactStyle = { top: positions && positions.beakPosition ? positions.beakPosition.top : BEAK_ORIGIN_POSITION.top, left: positions && positions.beakPosition ? positions.beakPosition.left : BEAK_ORIGIN_POSITION.left, height: beakStyleWidth, width: beakStyleWidth, backgroundColor: backgroundColor, }; var directionalClassName = (positions && positions.directionalClassName) ? Styling_1.AnimationClassNames[positions.directionalClassName] : ''; var contentMaxHeight = this._getMaxHeight() + this.state.heightOffset; var beakVisible = isBeakVisible && (!!targetElement || !!target); var content = (React.createElement("div", { ref: this._resolveRef('_hostElement'), className: Utilities_1.css('ms-Callout-container', styles.container) }, React.createElement("div", { className: Utilities_1.css('ms-Callout', styles.root, className, directionalClassName), style: positions ? positions.calloutPosition : OFF_SCREEN_STYLE, ref: this._resolveRef('_calloutElement') }, beakVisible && (React.createElement("div", { className: Utilities_1.css('ms-Callout-beak', styles.beak), style: beakReactStyle })), beakVisible && (React.createElement("div", { className: Utilities_1.css('ms-Callout-beakCurtain', styles.beakCurtain) })), React.createElement(Popup_1.Popup, { role: role, ariaLabel: ariaLabel, ariaDescribedBy: ariaDescribedBy, ariaLabelledBy: ariaLabelledBy, className: Utilities_1.css('ms-Callout-main', styles.main, (_b = {}, _b[styles.overFlowYHidden] = finalHeight, _b)), onDismiss: this.dismiss, shouldRestoreFocus: true, style: { maxHeight: contentMaxHeight, backgroundColor: backgroundColor } }, children)))); return content; var _b; }; CalloutContent.prototype.dismiss = function (ev) { var onDismiss = this.props.onDismiss; if (onDismiss) { onDismiss(ev); } }; CalloutContent.prototype._dismissOnScroll = function (ev) { var preventDismissOnScroll = this.props.preventDismissOnScroll; if (this.state.positions && !preventDismissOnScroll) { this._dismissOnLostFocus(ev); } }; CalloutContent.prototype._dismissOnLostFocus = function (ev) { var target = ev.target; var clickedOutsideCallout = this._hostElement && !Utilities_1.elementContains(this._hostElement, target); if ((!this._target && clickedOutsideCallout) || ev.target !== this._targetWindow && clickedOutsideCallout && (this._target.stopPropagation || (!this._target || (target !== this._target && !Utilities_1.elementContains(this._target, target))))) { this.dismiss(ev); } }; CalloutContent.prototype._setInitialFocus = function () { if (this.props.setInitialFocus && !this._didSetInitialFocus && this.state.positions) { this._didSetInitialFocus = true; Utilities_1.focusFirstChild(this._calloutElement); } }; CalloutContent.prototype._onComponentDidMount = function () { var _this = this; // This is added so the callout will dismiss when the window is scrolled // but not when something inside the callout is scrolled. The delay seems // to be required to avoid React firing an async focus event in IE from // the target changing focus quickly prior to rendering the callout. this._async.setTimeout(function () { _this._events.on(_this._targetWindow, 'scroll', _this._dismissOnScroll, true); _this._events.on(_this._targetWindow, 'resize', _this.dismiss, true); _this._events.on(_this._targetWindow, 'focus', _this._dismissOnLostFocus, true); _this._events.on(_this._targetWindow, 'click', _this._dismissOnLostFocus, true); }, 0); if (this.props.onLayerMounted) { this.props.onLayerMounted(); } this._updatePosition(); this._setHeightOffsetEveryFrame(); }; CalloutContent.prototype._updatePosition = function () { var positions = this.state.positions; var hostElement = this._hostElement; var calloutElement = this._calloutElement; if (hostElement && calloutElement) { var currentProps = void 0; currentProps = Utilities_1.assign(currentProps, this.props); currentProps.bounds = this._getBounds(); // Temporary to be removed when targetElement is removed. Currently deprecated. if (this.props.targetElement) { currentProps.targetElement = this._target; } else { currentProps.target = this._target; } var newPositions = positioning_1.getRelativePositions(currentProps, hostElement, calloutElement); // Set the new position only when the positions are not exists or one of the new callout positions are different. // The position should not change if the position is within 2 decimal places. if ((!positions && newPositions) || (positions && newPositions && !this._arePositionsEqual(positions, newPositions) && this._positionAttempts < 5)) { // We should not reposition the callout more than a few times, if it is then the content is likely resizing // and we should stop trying to reposition to prevent a stack overflow. this._positionAttempts++; this.setState({ positions: newPositions }); } else { this._positionAttempts = 0; if (this.props.onPositioned) { this.props.onPositioned(); } } } }; CalloutContent.prototype._getBounds = function () { if (!this._bounds) { var currentBounds = this.props.bounds; if (!currentBounds) { currentBounds = { top: 0 + this.props.minPagePadding, left: 0 + this.props.minPagePadding, right: this._targetWindow.innerWidth - this.props.minPagePadding, bottom: this._targetWindow.innerHeight - this.props.minPagePadding, width: this._targetWindow.innerWidth - this.props.minPagePadding * 2, height: this._targetWindow.innerHeight - this.props.minPagePadding * 2 }; } this._bounds = currentBounds; } return this._bounds; }; CalloutContent.prototype._getMaxHeight = function () { if (!this._maxHeight) { if (this.props.directionalHintFixed && this._target) { var beakWidth = this.props.isBeakVisible ? this.props.beakWidth : 0; var gapSpace = this.props.gapSpace ? this.props.gapSpace : 0; this._maxHeight = positioning_1.getMaxHeight(this._target, this.props.directionalHint, beakWidth + gapSpace, this._getBounds()); } else { this._maxHeight = this._getBounds().height - BORDER_WIDTH * 2; } } return this._maxHeight; }; CalloutContent.prototype._arePositionsEqual = function (positions, newPosition) { if (positions.calloutPosition.top.toFixed(2) !== newPosition.calloutPosition.top.toFixed(2)) { return false; } if (positions.calloutPosition.left.toFixed(2) !== newPosition.calloutPosition.left.toFixed(2)) { return false; } if (positions.beakPosition.top.toFixed(2) !== newPosition.beakPosition.top.toFixed(2)) { return false; } if (positions.beakPosition.top.toFixed(2) !== newPosition.beakPosition.top.toFixed(2)) { return false; } return true; }; CalloutContent.prototype._setTargetWindowAndElement = function (target) { if (target) { if (typeof target === 'string') { var currentDoc = Utilities_1.getDocument(); this._target = currentDoc ? currentDoc.querySelector(target) : null; this._targetWindow = Utilities_1.getWindow(); } else if (target.stopPropagation) { this._target = target; this._targetWindow = Utilities_1.getWindow(target.toElement); } else { var targetElement = target; this._target = target; this._targetWindow = Utilities_1.getWindow(targetElement); } } else { this._targetWindow = Utilities_1.getWindow(); } }; CalloutContent.prototype._setHeightOffsetEveryFrame = function () { var _this = this; if (this._calloutElement && this.props.finalHeight) { this._setHeightOffsetTimer = this._async.requestAnimationFrame(function () { var calloutMainElem = _this._calloutElement.firstChild; var cardScrollHeight = calloutMainElem.scrollHeight; var cardCurrHeight = calloutMainElem.offsetHeight; var scrollDiff = cardScrollHeight - cardCurrHeight; _this.setState({ heightOffset: _this.state.heightOffset + scrollDiff }); if (calloutMainElem.offsetHeight < _this.props.finalHeight) { _this._setHeightOffsetEveryFrame(); } else { _this._async.cancelAnimationFrame(_this._setHeightOffsetTimer); } }); } }; return CalloutContent; }(Utilities_1.BaseComponent)); CalloutContent.defaultProps = { preventDismissOnScroll: false, isBeakVisible: true, beakWidth: 16, gapSpace: 0, minPagePadding: 8, directionalHint: DirectionalHint_1.DirectionalHint.bottomAutoEdge }; tslib_1.__decorate([ Utilities_1.autobind ], CalloutContent.prototype, "dismiss", null); tslib_1.__decorate([ Utilities_1.autobind ], CalloutContent.prototype, "_setInitialFocus", null); tslib_1.__decorate([ Utilities_1.autobind ], CalloutContent.prototype, "_onComponentDidMount", null); exports.CalloutContent = CalloutContent; }); //# sourceMappingURL=CalloutContent.js.map