UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

288 lines • 14.7 kB
import * as tslib_1 from "tslib"; import * as React from 'react'; import { BaseComponent, classNamesFunction, getId, allowScrollOnElement, KeyCodes, elementContains } from '../../Utilities'; import { FocusTrapZone } from '../FocusTrapZone/index'; import { animationDuration } from './Modal.styles'; import { Overlay } from '../../Overlay'; import { Layer } from '../../Layer'; import { Popup } from '../Popup/index'; import { withResponsiveMode, ResponsiveMode } from '../../utilities/decorators/withResponsiveMode'; import { DirectionalHint } from '../Callout/index'; import { Icon } from '../Icon/index'; import { DraggableZone } from '../../utilities/DraggableZone/index'; // @TODO - need to change this to a panel whenever the breakpoint is under medium (verify the spec) var DefaultLayerProps = { eventBubblingEnabled: false }; var getClassNames = classNamesFunction(); var ModalBase = /** @class */ (function (_super) { tslib_1.__extends(ModalBase, _super); function ModalBase(props) { var _this = _super.call(this, props) || this; _this._focusTrapZone = React.createRef(); // Allow the user to scroll within the modal but not on the body _this._allowScrollOnModal = function (elt) { if (elt) { allowScrollOnElement(elt, _this._events); } else { _this._events.off(_this._scrollableContent); } _this._scrollableContent = elt; }; _this._onModalContextMenuClose = function () { _this.setState({ isModalMenuOpen: false }); }; _this._onModalClose = function () { _this._lastSetX = 0; _this._lastSetY = 0; _this.setState({ isModalMenuOpen: false, isInKeyboardMoveMode: false, isOpen: false, x: 0, y: 0 }); if (_this.props.dragOptions) { _this._events.off(window, 'keyup', _this._onKeyUp, true /* useCapture */); } // Call the onDismiss callback if (_this.props.onDismissed) { _this.props.onDismissed(); } }; _this._onDragStart = function () { _this.setState({ isModalMenuOpen: false, isInKeyboardMoveMode: false }); }; _this._onDrag = function (_, ui) { var _a = _this.state, x = _a.x, y = _a.y; _this.setState({ x: x + ui.delta.x, y: y + ui.delta.y }); }; _this._onDragStop = function () { _this.focus(); }; _this._onKeyUp = function (event) { // Need to handle the CTRL + ALT + SPACE key during keyup due to FireFox bug: // https://bugzilla.mozilla.org/show_bug.cgi?id=1220143 // Otherwise it would continue to fire a click even if the event was cancelled // during mouseDown. if (event.altKey && event.ctrlKey && event.keyCode === KeyCodes.space) { // Since this is a global handler, we should make sure the target is within the dialog // before opening the dropdown if (elementContains(_this._scrollableContent, event.target)) { _this.setState({ isModalMenuOpen: !_this.state.isModalMenuOpen }); event.preventDefault(); event.stopPropagation(); } } }; // We need a global onKeyDown event when we are in the move mode so that we can // handle the key presses and the components inside the modal do not get the events _this._onKeyDown = function (event) { if (event.altKey && event.ctrlKey && event.keyCode === KeyCodes.space) { // CTRL + ALT + SPACE is handled during keyUp event.preventDefault(); event.stopPropagation(); return; } if (_this.state.isModalMenuOpen && (event.altKey || event.keyCode === KeyCodes.escape)) { _this.setState({ isModalMenuOpen: false }); } if (_this.state.isInKeyboardMoveMode && (event.keyCode === KeyCodes.escape || event.keyCode === KeyCodes.enter)) { _this.setState({ isInKeyboardMoveMode: false }); event.preventDefault(); event.stopPropagation(); } if (_this.state.isInKeyboardMoveMode) { var handledEvent = true; var delta = _this._getMoveDelta(event); switch (event.keyCode) { case KeyCodes.escape: _this.setState({ x: _this._lastSetX, y: _this._lastSetY }); case KeyCodes.enter: { _this._lastSetX = 0; _this._lastSetY = 0; _this.setState({ isInKeyboardMoveMode: false }); break; } case KeyCodes.up: { _this.setState({ y: _this.state.y - delta }); break; } case KeyCodes.down: { _this.setState({ y: _this.state.y + delta }); break; } case KeyCodes.left: { _this.setState({ x: _this.state.x - delta }); break; } case KeyCodes.right: { _this.setState({ x: _this.state.x + delta }); break; } default: { handledEvent = false; } } if (handledEvent) { event.preventDefault(); event.stopPropagation(); } } }; _this._onEnterKeyboardMoveMode = function () { _this._lastSetX = _this.state.x; _this._lastSetY = _this.state.y; _this.setState({ isInKeyboardMoveMode: true, isModalMenuOpen: false }); _this._events.on(window, 'keydown', _this._onKeyDown, true /* useCapture */); }; _this._onExitKeyboardMoveMode = function () { _this._lastSetX = 0; _this._lastSetY = 0; _this.setState({ isInKeyboardMoveMode: false }); _this._events.off(window, 'keydown', _this._onKeyDown, true /* useCapture */); }; _this.state = { id: getId('Modal'), isOpen: props.isOpen, isVisible: props.isOpen, hasBeenOpened: props.isOpen, x: 0, y: 0 }; _this._lastSetX = 0; _this._lastSetY = 0; _this._warnDeprecations({ onLayerDidMount: 'layerProps.onLayerDidMount' }); return _this; } // tslint:disable-next-line function-name ModalBase.prototype.UNSAFE_componentWillReceiveProps = function (newProps) { clearTimeout(this._onModalCloseTimer); // Opening the dialog if (newProps.isOpen) { if (!this.state.isOpen) { // First Open this.setState({ isOpen: true }); // Add a keyUp handler for all key up events when the dialog is open if (newProps.dragOptions) { this._events.on(window, 'keyup', this._onKeyUp, true /* useCapture */); } } else { // Modal has been opened // Reopen during closing this.setState({ hasBeenOpened: true, isVisible: true }); if (newProps.topOffsetFixed) { var dialogMain = document.getElementsByClassName('ms-Dialog-main'); var modalRectangle = void 0; if (dialogMain.length > 0) { modalRectangle = dialogMain[0].getBoundingClientRect(); this.setState({ modalRectangleTop: modalRectangle.top }); } } } } // Closing the dialog if (!newProps.isOpen && this.state.isOpen) { this._onModalCloseTimer = this._async.setTimeout(this._onModalClose, parseFloat(animationDuration) * 1000); this.setState({ isVisible: false }); } }; ModalBase.prototype.componentDidUpdate = function (prevProps, prevState) { if (!prevProps.isOpen && !prevState.isVisible) { this.setState({ isVisible: true }); } }; ModalBase.prototype.render = function () { var _a = this.props, className = _a.className, containerClassName = _a.containerClassName, scrollableContentClassName = _a.scrollableContentClassName, elementToFocusOnDismiss = _a.elementToFocusOnDismiss, firstFocusableSelector = _a.firstFocusableSelector, forceFocusInsideTrap = _a.forceFocusInsideTrap, ignoreExternalFocusing = _a.ignoreExternalFocusing, isBlocking = _a.isBlocking, isClickableOutsideFocusTrap = _a.isClickableOutsideFocusTrap, isDarkOverlay = _a.isDarkOverlay, onDismiss = _a.onDismiss, layerProps = _a.layerProps, overlay = _a.overlay, responsiveMode = _a.responsiveMode, titleAriaId = _a.titleAriaId, styles = _a.styles, subtitleAriaId = _a.subtitleAriaId, theme = _a.theme, topOffsetFixed = _a.topOffsetFixed, onLayerDidMount = _a.onLayerDidMount, isModeless = _a.isModeless, dragOptions = _a.dragOptions; var _b = this.state, isOpen = _b.isOpen, isVisible = _b.isVisible, hasBeenOpened = _b.hasBeenOpened, modalRectangleTop = _b.modalRectangleTop, x = _b.x, y = _b.y, isInKeyboardMoveMode = _b.isInKeyboardMoveMode; if (!isOpen) { return null; } var layerClassName = layerProps === undefined ? '' : layerProps.className; var classNames = getClassNames(styles, { theme: theme, className: className, containerClassName: containerClassName, scrollableContentClassName: scrollableContentClassName, isOpen: isOpen, isVisible: isVisible, hasBeenOpened: hasBeenOpened, modalRectangleTop: modalRectangleTop, topOffsetFixed: topOffsetFixed, isModeless: isModeless, layerClassName: layerClassName, isDefaultDragHandle: dragOptions && !dragOptions.dragHandleSelector }); var mergedLayerProps = tslib_1.__assign({}, DefaultLayerProps, this.props.layerProps, { onLayerDidMount: layerProps && layerProps.onLayerDidMount ? layerProps.onLayerDidMount : onLayerDidMount, insertFirst: isModeless, className: classNames.layer }); var modalContent = (React.createElement(FocusTrapZone, { componentRef: this._focusTrapZone, className: classNames.main, elementToFocusOnDismiss: elementToFocusOnDismiss, isClickableOutsideFocusTrap: isModeless || isClickableOutsideFocusTrap || !isBlocking, ignoreExternalFocusing: ignoreExternalFocusing, forceFocusInsideTrap: isModeless ? !isModeless : forceFocusInsideTrap, firstFocusableSelector: firstFocusableSelector, focusPreviouslyFocusedInnerElement: true, onBlur: isInKeyboardMoveMode ? this._onExitKeyboardMoveMode : undefined }, dragOptions && isInKeyboardMoveMode && (React.createElement("div", { className: classNames.keyboardMoveIconContainer }, dragOptions.keyboardMoveIconProps ? (React.createElement(Icon, tslib_1.__assign({}, dragOptions.keyboardMoveIconProps))) : (React.createElement(Icon, { iconName: "move", className: classNames.keyboardMoveIcon })))), React.createElement("div", { ref: this._allowScrollOnModal, className: classNames.scrollableContent, "data-is-scrollable": true }, dragOptions && this.state.isModalMenuOpen && (React.createElement(dragOptions.menu, { items: [ { key: 'move', text: dragOptions.moveMenuItemText, onClick: this._onEnterKeyboardMoveMode }, { key: 'close', text: dragOptions.closeMenuItemText, onClick: this._onModalClose } ], onDismiss: this._onModalContextMenuClose, alignTargetEdge: true, coverTarget: true, directionalHint: DirectionalHint.topLeftEdge, directionalHintFixed: true, shouldFocusOnMount: true, target: this._scrollableContent })), this.props.children))); // @temp tuatology - Will adjust this to be a panel at certain breakpoints if (responsiveMode >= ResponsiveMode.small) { return (React.createElement(Layer, tslib_1.__assign({}, mergedLayerProps), React.createElement(Popup, { role: isModeless || !isBlocking ? 'dialog' : 'alertdialog', "aria-modal": !isModeless, ariaLabelledBy: titleAriaId, ariaDescribedBy: subtitleAriaId, onDismiss: onDismiss }, React.createElement("div", { className: classNames.root }, !isModeless && React.createElement(Overlay, tslib_1.__assign({ isDarkThemed: isDarkOverlay, onClick: isBlocking ? undefined : onDismiss }, overlay)), dragOptions ? (React.createElement(DraggableZone, { handleSelector: dragOptions.dragHandleSelector || "." + classNames.main.split(' ')[0], preventDragSelector: "button", onStart: this._onDragStart, onDragChange: this._onDrag, onStop: this._onDragStop, position: { x: x, y: y } }, modalContent)) : (modalContent))))); } return null; }; ModalBase.prototype.focus = function () { if (this._focusTrapZone.current) { this._focusTrapZone.current.focus(); } }; ModalBase.prototype._getMoveDelta = function (event) { var delta = 10; if (event.shiftKey) { if (!event.ctrlKey) { delta = 50; } } else if (event.ctrlKey) { delta = 1; } return delta; }; ModalBase.defaultProps = { isOpen: false, isDarkOverlay: true, isBlocking: false, className: '', containerClassName: '' }; ModalBase = tslib_1.__decorate([ withResponsiveMode ], ModalBase); return ModalBase; }(BaseComponent)); export { ModalBase }; //# sourceMappingURL=Modal.base.js.map