UNPKG

@shopify/polaris

Version:

Shopify’s product component library

351 lines (319 loc) • 11.9 kB
import { objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js'; import React$1, { PureComponent, createRef } from 'react'; import { durationSlow } from '@shopify/polaris-tokens'; import { useFeatures } from '../../utilities/features/hooks.js'; import { useMediaQuery } from '../../utilities/media-query/hooks.js'; import { EventListener as EventListener$1 } from '../EventListener/EventListener.js'; import { useI18n } from '../../utilities/i18n/hooks.js'; import { layer, dataPolarisTopBar } from '../shared.js'; import { classNames } from '../../utilities/css.js'; import { MobileCancelMajor } from '@shopify/polaris-icons'; import { Icon as Icon$1 } from '../Icon/Icon.js'; import { Button as Button$1 } from '../Button/Button.js'; import { Backdrop as Backdrop$1 } from '../Backdrop/Backdrop.js'; import { CSSTransition } from 'react-transition-group'; import { FrameContext } from '../../utilities/frame/context.js'; import { TrapFocus as TrapFocus$1 } from '../TrapFocus/TrapFocus.js'; import { setRootProperty as setRootProperty$1 } from '../../utilities/set-root-property.js'; import { ToastManager as ToastManager$1 } from './components/ToastManager/ToastManager.js'; import { Loading as Loading$1 } from './components/Loading/Loading.js'; import { ContextualSaveBar as ContextualSaveBar$1 } from './components/ContextualSaveBar/ContextualSaveBar.js'; import { CSSAnimation as CSSAnimation$1 } from './components/CSSAnimation/CSSAnimation.js'; import styles from './Frame.scss.js'; var GLOBAL_RIBBON_CUSTOM_PROPERTY = '--global-ribbon-height'; var APP_FRAME_MAIN = 'AppFrameMain'; var APP_FRAME_MAIN_ANCHOR_TARGET = 'AppFrameMainContent'; var APP_FRAME_NAV = 'AppFrameNav'; var APP_FRAME_TOP_BAR = 'AppFrameTopBar'; var APP_FRAME_LOADING_BAR = 'AppFrameLoadingBar'; var _ref = /*#__PURE__*/React$1.createElement(Icon$1, { source: MobileCancelMajor }); var _ref2 = /*#__PURE__*/React$1.createElement(Loading$1, null); class FrameInner extends PureComponent { constructor(...args) { super(...args); this.state = { skipFocused: false, globalRibbonHeight: 0, loadingStack: 0, toastMessages: [], showContextualSaveBar: false }; this.contextualSaveBar = null; this.globalRibbonContainer = null; this.navigationNode = /*#__PURE__*/createRef(); this.skipToMainContentTargetNode = this.props.skipToContentTarget || /*#__PURE__*/createRef(); this.setGlobalRibbonHeight = () => { var { globalRibbonContainer } = this; if (globalRibbonContainer) { this.setState({ globalRibbonHeight: globalRibbonContainer.offsetHeight }, this.setGlobalRibbonRootProperty); } }; this.setGlobalRibbonRootProperty = () => { var { globalRibbonHeight } = this.state; setRootProperty$1(GLOBAL_RIBBON_CUSTOM_PROPERTY, "".concat(globalRibbonHeight, "px"), null); }; this.showToast = toast => { this.setState(({ toastMessages }) => { var hasToastById = toastMessages.find(({ id }) => id === toast.id) != null; return { toastMessages: hasToastById ? toastMessages : [...toastMessages, toast] }; }); }; this.hideToast = ({ id }) => { this.setState(({ toastMessages }) => { return { toastMessages: toastMessages.filter(({ id: toastId }) => id !== toastId) }; }); }; this.setContextualSaveBar = props => { var { showContextualSaveBar } = this.state; this.contextualSaveBar = _objectSpread2({}, props); if (showContextualSaveBar === true) { this.forceUpdate(); } else { this.setState({ showContextualSaveBar: true }); } }; this.removeContextualSaveBar = () => { this.contextualSaveBar = null; this.setState({ showContextualSaveBar: false }); }; this.startLoading = () => { this.setState(({ loadingStack }) => ({ loadingStack: loadingStack + 1 })); }; this.stopLoading = () => { this.setState(({ loadingStack }) => ({ loadingStack: Math.max(0, loadingStack - 1) })); }; this.handleResize = () => { if (this.props.globalRibbon) { this.setGlobalRibbonHeight(); } }; this.handleFocus = () => { this.setState({ skipFocused: true }); }; this.handleBlur = () => { this.setState({ skipFocused: false }); }; this.handleClick = () => { this.skipToMainContentTargetNode.current && this.skipToMainContentTargetNode.current.focus(); }; this.handleNavigationDismiss = () => { var { onNavigationDismiss } = this.props; if (onNavigationDismiss != null) { onNavigationDismiss(); } }; this.setGlobalRibbonContainer = node => { this.globalRibbonContainer = node; }; this.handleNavKeydown = event => { var { key } = event; var { mediaQuery: { isNavigationCollapsed }, showMobileNavigation } = this.props; var mobileNavShowing = isNavigationCollapsed && showMobileNavigation; if (mobileNavShowing && key === 'Escape') { this.handleNavigationDismiss(); } }; } componentDidMount() { this.handleResize(); if (this.props.globalRibbon) { return; } this.setGlobalRibbonRootProperty(); } componentDidUpdate(prevProps) { if (this.props.globalRibbon !== prevProps.globalRibbon) { this.setGlobalRibbonHeight(); } } render() { var { skipFocused, loadingStack, toastMessages, showContextualSaveBar } = this.state; var { children, navigation, topBar, globalRibbon, showMobileNavigation = false, skipToContentTarget, i18n, mediaQuery: { isNavigationCollapsed }, features: { newDesignLanguage } } = this.props; var navClassName = classNames(styles.Navigation, showMobileNavigation && styles['Navigation-visible'], newDesignLanguage && styles['Navigation-newDesignLanguage']); var mobileNavHidden = isNavigationCollapsed && !showMobileNavigation; var mobileNavShowing = isNavigationCollapsed && showMobileNavigation; var tabIndex = mobileNavShowing ? 0 : -1; var navigationMarkup = navigation ? /*#__PURE__*/React$1.createElement(TrapFocus$1, { trapping: mobileNavShowing }, /*#__PURE__*/React$1.createElement(CSSTransition, { nodeRef: this.navigationNode, appear: isNavigationCollapsed, exit: isNavigationCollapsed, in: showMobileNavigation, timeout: durationSlow, classNames: navTransitionClasses }, /*#__PURE__*/React$1.createElement("div", { ref: this.navigationNode, className: navClassName, onKeyDown: this.handleNavKeydown, id: APP_FRAME_NAV, key: "NavContent", hidden: mobileNavHidden }, navigation, /*#__PURE__*/React$1.createElement("button", { type: "button", className: styles.NavigationDismiss, onClick: this.handleNavigationDismiss, "aria-hidden": mobileNavHidden || !isNavigationCollapsed && !showMobileNavigation, "aria-label": i18n.translate('Polaris.Frame.Navigation.closeMobileNavigationLabel'), tabIndex: tabIndex }, _ref)))) : null; var loadingMarkup = loadingStack > 0 ? /*#__PURE__*/React$1.createElement("div", { className: styles.LoadingBar, id: APP_FRAME_LOADING_BAR }, _ref2) : null; var contextualSaveBarMarkup = /*#__PURE__*/React$1.createElement(CSSAnimation$1, { in: showContextualSaveBar, className: styles.ContextualSaveBar, type: "fade" }, /*#__PURE__*/React$1.createElement(ContextualSaveBar$1, this.contextualSaveBar)); var topBarClassName = classNames(styles.TopBar, newDesignLanguage && styles['TopBar-newDesignLanguage']); var topBarMarkup = topBar ? /*#__PURE__*/React$1.createElement("div", Object.assign({ className: topBarClassName }, layer.props, dataPolarisTopBar.props, { id: APP_FRAME_TOP_BAR }), topBar) : null; var globalRibbonClassName = classNames(styles.GlobalRibbonContainer, newDesignLanguage && styles['GlobalRibbonContainer-newDesignLanguage']); var globalRibbonMarkup = globalRibbon ? /*#__PURE__*/React$1.createElement("div", { className: globalRibbonClassName, ref: this.setGlobalRibbonContainer }, globalRibbon) : null; var skipClassName = classNames(styles.Skip, skipFocused && styles.focused); var skipTarget = skipToContentTarget ? skipToContentTarget.current && skipToContentTarget.current.id || '' : APP_FRAME_MAIN_ANCHOR_TARGET; var skipMarkup = /*#__PURE__*/React$1.createElement("div", { className: skipClassName }, /*#__PURE__*/React$1.createElement(Button$1, { url: "#".concat(skipTarget), onFocus: this.handleFocus, onBlur: this.handleBlur, onClick: this.handleClick }, i18n.translate('Polaris.Frame.skipToContent'))); var navigationAttributes = navigation ? { 'data-has-navigation': true } : {}; var frameClassName = classNames(styles.Frame, navigation && styles.hasNav, topBar && styles.hasTopBar); var mainClassName = classNames(styles.Main, newDesignLanguage && styles['Main-newDesignLanguage']); var navigationOverlayMarkup = showMobileNavigation && isNavigationCollapsed ? /*#__PURE__*/React$1.createElement(Backdrop$1, { belowNavigation: true, onClick: this.handleNavigationDismiss, onTouchStart: this.handleNavigationDismiss }) : null; var skipToMainContentTarget = skipToContentTarget ? null : /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/anchor-is-valid React$1.createElement("a", { id: APP_FRAME_MAIN_ANCHOR_TARGET, ref: this.skipToMainContentTargetNode, tabIndex: -1 }); var context = { showToast: this.showToast, hideToast: this.hideToast, startLoading: this.startLoading, stopLoading: this.stopLoading, setContextualSaveBar: this.setContextualSaveBar, removeContextualSaveBar: this.removeContextualSaveBar }; return /*#__PURE__*/React$1.createElement(FrameContext.Provider, { value: context }, /*#__PURE__*/React$1.createElement("div", Object.assign({ className: frameClassName }, layer.props, navigationAttributes), skipMarkup, topBarMarkup, navigationMarkup, contextualSaveBarMarkup, loadingMarkup, navigationOverlayMarkup, /*#__PURE__*/React$1.createElement("main", { className: mainClassName, id: APP_FRAME_MAIN, "data-has-global-ribbon": Boolean(globalRibbon) }, skipToMainContentTarget, /*#__PURE__*/React$1.createElement("div", { className: styles.Content }, children)), /*#__PURE__*/React$1.createElement(ToastManager$1, { toastMessages: toastMessages }), globalRibbonMarkup, /*#__PURE__*/React$1.createElement(EventListener$1, { event: "resize", handler: this.handleResize }))); } } var navTransitionClasses = { enter: classNames(styles['Navigation-enter']), enterActive: classNames(styles['Navigation-enterActive']), enterDone: classNames(styles['Navigation-enterActive']), exit: classNames(styles['Navigation-exit']), exitActive: classNames(styles['Navigation-exitActive']) }; function Frame(props) { var i18n = useI18n(); var mediaQuery = useMediaQuery(); var features = useFeatures(); return /*#__PURE__*/React$1.createElement(FrameInner, Object.assign({}, props, { i18n: i18n, mediaQuery: mediaQuery, features: features })); } export { Frame };