@shopify/polaris
Version:
Shopify’s product component library
351 lines (319 loc) • 11.9 kB
JavaScript
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 };