UNPKG

@shopify/app-bridge-host

Version:

App Bridge Host contains components and middleware to be consumed by the app's host, as well as the host itself. The middleware and `Frame` component are responsible for facilitating communication between the client and host, and used to act on actions se

153 lines (150 loc) 5.8 kB
import { __rest, __assign } from 'tslib'; import React, { useCallback, useMemo } from 'react'; import compose from '@shopify/react-compose'; import { Spinner, Modal as Modal$2 } from '@shopify/polaris-internal'; import { Context } from '@shopify/app-bridge-core'; import { Button, Modal as Modal$3 } from '@shopify/app-bridge-core/actions'; import { feature } from '../store/reducers/embeddedApp/modal/index.js'; import withFeature from '../withFeature.js'; import Frame from '../Frame.js'; import { useHostContext } from '../hooks/useHostContext.js'; import '@shopify/app-bridge-core/actions/Error'; import { buildAppUrl } from './utilities/appUrl.js'; var style = { border: 'none', width: '100%', flex: '1', display: 'flex', }; var spinnerStyle = { position: 'absolute', width: '100%', height: '100%', backgroundColor: 'white', justifyContent: 'center', alignItems: 'center', display: 'flex', }; var DEFAULT_IFRAME_CONTENT_HEIGHT = 400; var SMALL_IFRAME_CONTENT_HEIGHT = 150; var allowedProtocols = ['https:', 'http:']; /** * Renders a Polaris Modal with the Context set to `Modal` * When the `location` is defined, renders Frame component as the modal content * @public * @requires HostContext * */ function Modal(props) { var _a = useHostContext(), app = _a.app, config = _a.config; var _b = props.actions, close = _b.close, clickFooterButton = _b.clickFooterButton, _c = props.store, _d = _c.content, content = _d === void 0 ? null : _d, height = _c.height, id = _c.id, location = _c.location, open = _c.open, primaryAction = _c.primaryAction, secondaryActions = _c.secondaryActions, size = _c.size, _e = _c.title, title = _e === void 0 ? '' : _e, loading = _c.loading, extraProps = __rest(props, ["actions", "store"]); var modalRef = useCallback(function (node) { if (node) { var modalElement = document.querySelector("[class^='Polaris-Modal-Dialog__Modal']"); if (modalElement) { modalElement.style.overflow = 'hidden'; } } }, []); var modalStyles = __assign(__assign({}, style), { height: "".concat(height || getIframeHeight(size), "px"), maxHeight: loading ? '100%' : undefined }); var url = useMemo(function () { if (!location) { // needed to handle basic case of title & content return; } var apiKey = config.apiKey, handle = config.handle, url = config.url; return buildAppUrl({ handle: handle, apiKey: apiKey, url: url, pathname: location, search: takeSearch(location), }); }, [location]); try { if (!allowedProtocols.includes(new URL(location || '').protocol)) { return null; } } catch (error) { // Noop } // There is a bug in Polaris for the iframe height calculation // It returns null when the modal is not opened and is never re-calculated if (!open) { return null; } var frameContent = url ? (React.createElement(Frame, __assign({}, extraProps, { config: config, style: modalStyles, context: Context.Modal, app: app, title: title, url: url.href, onUrlChange: updateIframeUrl }))) : (content); var isSectioned = url === undefined; var isTitleHidden = (title || '').trim() === ''; var polarisPrimaryAction = primaryAction ? createButton(primaryAction) : undefined; var polarisSecondaryActions = modalSecondaryActions(secondaryActions); var polarisModalSize = getPolarisModalSize(size); function handleClose() { close({ id: id }); } var loadingSpinner = loading ? (React.createElement("div", { style: spinnerStyle }, React.createElement(Spinner, null))) : null; return (React.createElement(Modal$2, { size: polarisModalSize, title: title, open: open, onClose: handleClose, primaryAction: polarisPrimaryAction, secondaryActions: polarisSecondaryActions, sectioned: isSectioned, titleHidden: isTitleHidden }, React.createElement("div", { ref: modalRef }, loadingSpinner, frameContent))); function createButton(button) { var label = button.label, id = button.id, disabled = button.disabled, style = button.style, loading = button.loading; var onAction = function () { return clickFooterButton(id); }; return { content: label, disabled: disabled, destructive: style === Button.Style.Danger, onAction: onAction, loading: loading, }; } function modalSecondaryActions(actions) { if (!Array.isArray(actions) || !actions.length) { return; } return actions.map(createButton); } } function getPolarisModalSize(size) { switch (size) { case Modal$3.Size.Large: return 'large'; case Modal$3.Size.Small: return 'small'; default: return undefined; } } function getIframeHeight(size) { var isFullScreen = size === Modal$3.Size.Full; if (isFullScreen) { return; } if (size === Modal$3.Size.Small) { return SMALL_IFRAME_CONTENT_HEIGHT; } return DEFAULT_IFRAME_CONTENT_HEIGHT; } /** * Take the search parameters from a given URL */ function takeSearch(url) { var match = url.match(/\?[^#]+/); if (match === null) { return undefined; } return match[0]; } function updateIframeUrl(iframe, newUrl) { if (!iframe || !iframe.contentWindow) { return; } iframe.contentWindow.location.replace(newUrl); } /** * The Modal feature with its reducer, actions and UI component * @public * */ var Modal$1 = compose(withFeature(feature))(Modal); export { Modal, Modal$1 as default };