UNPKG

@sap-ux/ui-components

Version:
283 lines 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UIDialog = exports.UIDialogScrollArea = exports.DIALOG_STYLES = exports.DIALOG_MAX_HEIGHT_OFFSET = void 0; const react_1 = __importDefault(require("react")); const react_2 = require("@fluentui/react"); const UIButton_1 = require("../UIButton"); const DeepMerge_1 = require("../../utilities/DeepMerge"); require("../../styles/_shadows.scss"); exports.DIALOG_MAX_HEIGHT_OFFSET = 32; exports.DIALOG_STYLES = { background: 'var(--vscode-editorWidget-background)', boxShadow: 'var(--ui-box-shadow-medium)', borderColor: 'var(--vscode-editorWidget-border)', borderRadius: 4, vPadding: 20, vPaddingHalf: 10, hPadding: 45, title: { color: 'var(--vscode-panelTitle-activeForeground)' }, contentText: { fontSize: 13, color: 'var(--vscode-foreground)', fontFamily: 'var(--vscode-font-family)' } }; var UIDialogScrollArea; (function (UIDialogScrollArea) { // Header and footer freezed and content is scrollable UIDialogScrollArea["Content"] = "Content"; // Whole dialog is scrollable - default FluentUI behaviour, but it is buggy with large content UIDialogScrollArea["Dialog"] = "Dialog"; })(UIDialogScrollArea || (exports.UIDialogScrollArea = UIDialogScrollArea = {})); /** * UIDialog component. * based on https://developer.microsoft.com/en-us/fluentui#/controls/web/dialog * and https://app.abstract.com/projects/42046ab5-889d-4a59-88e6-1089e98cba67/branches/master/commits/f1fcd77447bb2b32aab3498311c1c39517e47e49/files/1455D538-559B-48EF-8D9F-1C2884ED540B/layers/AFDAAFD3-BFA6-4E96-BB6D-057DD13753E5 * * @exports * @class UIDialog * @extends {React.Component<IDialogProps, {}>} */ class UIDialog extends react_1.default.Component { /** * Initializes component properties. * * @param {DialogProps} props */ constructor(props) { super(props); this.onResize = this.onResize.bind(this); this.onModalLayerMount = this.onModalLayerMount.bind(this); this.onModalLayerUnmount = this.onModalLayerUnmount.bind(this); this.attachResize = this.attachResize.bind(this); this.detachResize = this.detachResize.bind(this); this.onHeaderMouseDown = this.onHeaderMouseDown.bind(this); this.state = { resizeMaxHeight: this.getResizeMaxHeight(), isMounted: false }; } /** * Called when component is rerendered. * * @param {Readonly<DialogProps>} prevProps */ componentDidUpdate(prevProps) { const { scrollArea } = this.props; if (prevProps.scrollArea !== scrollArea) { if (scrollArea === UIDialogScrollArea.Content) { this.attachResize(); } else { this.detachResize(); } } } /** * Method handles modal dialog mount event. */ onModalLayerMount() { this.attachResize(); this.setState({ isMounted: true }); } /** * Method handles modal dialog unmount event. */ onModalLayerUnmount() { this.detachResize(); this.setState({ isMounted: false }); } /** * Method attaches to window resize event. */ attachResize() { // Window resize handled window.addEventListener('resize', this.onResize); this.onResize(); } /** * Method detaches from window resize event. */ detachResize() { window.removeEventListener('resize', this.onResize); } /** * Method handles window resizer event to update calculation for content scrollarea size. */ onResize() { this.setState({ resizeMaxHeight: this.getResizeMaxHeight() }); } /** * Method returns maximal height for dialog content "scrollArea=Content" mode. * * @returns {number} Max size of dialog. */ getResizeMaxHeight() { return window.innerHeight - exports.DIALOG_MAX_HEIGHT_OFFSET; } /** * Method returns footer element depending in component props. * Three states. * 1. Accept/Decline buttons. * 2. Custom content of footer. * 3. No footer. * * @returns {JSX.Element | null} Footer element to render. */ getFooter() { let element; const { acceptButtonText, cancelButtonText, onAccept, onCancel, acceptButtonId, cancelButtonId, footer } = this.props; const dialogFooterProps = { styles: { actionsRight: { width: '100%', textAlign: 'center', justifyContent: 'center', margin: '0px' }, actions: { margin: `${exports.DIALOG_STYLES.vPaddingHalf}px 0 ${exports.DIALOG_STYLES.vPadding}px`, lineHeight: 'auto' }, action: { margin: '0px 5px' } } }; if (footer) { element = react_1.default.createElement(react_2.DialogFooter, { ...dialogFooterProps }, footer); } else if (onAccept || onCancel) { element = (react_1.default.createElement(react_2.DialogFooter, { ...dialogFooterProps }, onAccept ? (react_1.default.createElement(UIButton_1.UIDefaultButton, { onClick: onAccept, primary: true, style: { height: '26px' }, id: acceptButtonId }, acceptButtonText)) : (''), onCancel ? (react_1.default.createElement(UIButton_1.UIDefaultButton, { onClick: onCancel, style: { height: '26px' }, id: cancelButtonId }, cancelButtonText)) : (''))); } return element; } /** * Method handles mousedown event for dialog header area. * Method added to handle scenario when we open dropdown menu and move dialog. * Code in method sets focus to focuszone's placeholder - it triggers close for opened dropdown menu. * * @param {React.MouseEvent} event Mousedown event */ onHeaderMouseDown(event) { if (!this.props.modalProps?.dragOptions) { // No need to handle non draggable return; } const dialogFocusZone = event.target.closest('.ms-Dialog-main'); if (dialogFocusZone?.firstChild) { const focusPlaceholder = dialogFocusZone.firstChild; focusPlaceholder.focus(); } } /** * @returns {JSX.Element} */ render() { const { acceptButtonText, cancelButtonText, onAccept, onCancel, acceptButtonId, cancelButtonId, footer, closeButtonVisible, multiLineTitle, scrollArea = UIDialogScrollArea.Content, children, ...rest } = this.props; const dialogProps = { minWidth: '460px', modalProps: { layerProps: { onLayerDidMount: this.onModalLayerMount, onLayerWillUnmount: this.onModalLayerUnmount } }, styles: { root: { opacity: !this.props.isOpenAnimated || this.state.isMounted ? undefined : 0 }, main: { backgroundColor: exports.DIALOG_STYLES.background, border: `1px solid ${exports.DIALOG_STYLES.borderColor}`, boxShadow: exports.DIALOG_STYLES.boxShadow, borderRadius: exports.DIALOG_STYLES.borderRadius, minHeight: 100, ...(scrollArea === UIDialogScrollArea.Content && { overflow: 'hidden', '.ms-Modal-scrollableContent': { overflow: 'hidden', height: '100%' } }) } }, dialogContentProps: { title: this.props.title, titleProps: { className: 'ui-medium-header', style: { padding: `${exports.DIALOG_STYLES.vPadding}px 0 5px`, textAlign: 'center', color: exports.DIALOG_STYLES.title.color, ...(!multiLineTitle && { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', padding: `${exports.DIALOG_STYLES.vPadding}px ${exports.DIALOG_STYLES.hPadding}px 5px` }) }, onMouseDown: this.onHeaderMouseDown }, styles: { button: { display: !closeButtonVisible ? 'none' : undefined }, inner: { padding: '0', ...(scrollArea === UIDialogScrollArea.Content && { height: '100%', overflow: 'hidden', display: 'flex', flexDirection: 'column' }) }, subText: { margin: '0', lineHeight: 18, ...exports.DIALOG_STYLES.contentText }, content: { ...exports.DIALOG_STYLES.contentText, ...(scrollArea === UIDialogScrollArea.Content && { height: '100%', display: 'flex', flexDirection: 'column', maxHeight: this.state.resizeMaxHeight }) }, innerContent: { padding: `${exports.DIALOG_STYLES.vPaddingHalf}px ${exports.DIALOG_STYLES.hPadding}px`, boxSizing: 'border-box', ...(scrollArea === UIDialogScrollArea.Content && { height: '100%', overflow: 'auto', boxSizing: 'border-box' }) } } } }; const props = (0, DeepMerge_1.deepMerge)(dialogProps, rest); return (react_1.default.createElement(react_2.Dialog, { ...props }, children, this.getFooter())); } } exports.UIDialog = UIDialog; // Default values for public component properties UIDialog.defaultProps = { isOpenAnimated: true }; //# sourceMappingURL=UIDialog.js.map