UNPKG

@atlaskit/renderer

Version:
200 lines (198 loc) • 10.1 kB
import _extends from "@babel/runtime/helpers/extends"; /** * @jsxRuntime classic * @jsx jsx */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 import { jsx, css } from '@emotion/react'; import React, { useEffect, useRef } from 'react'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import ExtensionRenderer from '../../ui/ExtensionRenderer'; import { overflowShadow, WidthConsumer } from '@atlaskit/editor-common/ui'; import { calcBreakoutWidth } from '@atlaskit/editor-common/utils'; import { RendererCssClassName } from '../../consts'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { calcBreakoutWidthCss } from '../utils/breakout'; import { fg } from '@atlaskit/platform-feature-flags'; const viewportSizes = ['small', 'medium', 'default', 'large', 'xlarge']; // Mirrors sizes from https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/forge/xen-editor-provider/src/render/renderers/ForgeUIExtension.tsx const macroHeights = { small: '112px', medium: '262px', default: '262px', large: '524px', xlarge: '1048px' }; const getViewportSize = (extensionId, extensionViewportSizes) => { if (!Array.isArray(extensionViewportSizes) || !extensionId) { return; } const extension = extensionViewportSizes.find(extension => extension.extensionId === extensionId); if (extension) { const viewportSize = extension.viewportSize && viewportSizes.includes(extension.viewportSize) ? extension.viewportSize : 'default'; return macroHeights[viewportSize]; } }; const containerStyle = css({ containerType: 'inline-size' }); /** * Fires an analytics event once on mount when a bodied extension is rendered as an inline element. * The node is stored in a ref to avoid re-firing if the node reference changes. */ const FireExtensionAsInlineAnalytics = ({ fireAnalyticsEvent, node }) => { const nodeRef = useRef(node); useEffect(() => { fireAnalyticsEvent({ action: ACTION.RENDERED, actionSubject: ACTION_SUBJECT.EXTENSION_AS_INLINE, actionSubjectId: ACTION_SUBJECT_ID.EXTENSION_BODIED, attributes: { extensionKey: nodeRef.current.extensionKey, extensionType: nodeRef.current.extensionType }, eventType: EVENT_TYPE.OPERATIONAL }); }, [fireAnalyticsEvent]); return null; }; export const renderExtension = (content, layout, options = {}, removeOverflow, extensionId, extensionViewportSizes, nodeHeight, localId, shouldDisplayExtensionAsInline, node, isInsideOfInlineExtension) => { const overflowContainerClass = !removeOverflow ? RendererCssClassName.EXTENSION_OVERFLOW_CONTAINER : ''; // by default, we assume the extension is at top level, (direct child of doc node) const { isTopLevel = true, rendererAppearance, fireAnalyticsEvent } = options || {}; // we should only use custom layout for full-page appearance const canUseCustomLayout = expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? rendererAppearance === 'full-page' : true; const isCustomLayout = isTopLevel && ['wide', 'full-width'].includes(layout) && canUseCustomLayout; const centerAlignClass = isCustomLayout ? RendererCssClassName.EXTENSION_CENTER_ALIGN : ''; /** * To reduce cumulative layout shift, we check installed manifest values (viewportSize) for Forge and extension node parameters * for Connect (legacy). As Connect is being phased out, we want Forge to also start to store its expected height * in node parameters, especially for dynamic content macros. LegacyMacroStyledElements implements logic similar to here * as the extension handler in CFE for legacy macros and Connect. */ const viewportSize = getViewportSize(extensionId, extensionViewportSizes); const extensionHeight = nodeHeight || viewportSize; const isInline = (shouldDisplayExtensionAsInline === null || shouldDisplayExtensionAsInline === void 0 ? void 0 : shouldDisplayExtensionAsInline(node)) && expValEquals('platform_editor_render_bodied_extension_as_inline', 'isEnabled', true); const inlineClassName = isInline ? RendererCssClassName.EXTENSION_AS_INLINE : ''; const asInlineAnalytics = isInline && fireAnalyticsEvent && node && fg('platform_editor_render_inline_extension_analytics') ? jsx(FireExtensionAsInlineAnalytics, { fireAnalyticsEvent: fireAnalyticsEvent, node: node }) : null; if (expValEquals('platform_editor_renderer_extension_width_fix', 'isEnabled', true)) { const extensionDiv = jsx("div", { ref: options.handleRef // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: `${RendererCssClassName.EXTENSION} ${inlineClassName} ${options.shadowClassNames} ${centerAlignClass}`, style: { width: isInline ? undefined : (expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? isCustomLayout : isTopLevel) ? // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values calcBreakoutWidthCss(layout) : expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? undefined : '100%', // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop minHeight: isInline ? undefined : extensionHeight && `${extensionHeight}px` }, "data-layout": layout, "data-local-id": localId, "data-testid": "extension--wrapper", "data-node-type": "extension" }, jsx("div", { tabIndex: options.tabIndex // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop , className: `${RendererCssClassName.EXTENSION_INNER_WRAPPER} ${overflowContainerClass}`, css: [!isInsideOfInlineExtension && fg('platform_fix_macro_renders_in_layouts') && containerStyle] }, asInlineAnalytics, content)); return centerAlignClass && expValEquals('platform_editor_flex_based_centering', 'isEnabled', true) ? jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop className: RendererCssClassName.STICKY_SAFE_CENTER_WRAPPER + ' ' + RendererCssClassName.FLEX_CENTER_WRAPPER }, extensionDiv) : extensionDiv; } return jsx(WidthConsumer, null, ({ width }) => { const extensionDiv = jsx("div", { ref: options.handleRef // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , className: `${RendererCssClassName.EXTENSION} ${inlineClassName} ${options.shadowClassNames} ${centerAlignClass}`, style: { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 width: isInline ? undefined : (expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? isCustomLayout : isTopLevel) ? // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 calcBreakoutWidth(layout, width) : expValEquals('platform_editor_remove_important_in_render_ext', 'isEnabled', true) ? undefined : '100%', // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop minHeight: isInline ? undefined : `${extensionHeight}px` }, "data-layout": layout, "data-local-id": localId }, jsx("div", { tabIndex: options.tabIndex // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop , className: `${RendererCssClassName.EXTENSION_INNER_WRAPPER} ${overflowContainerClass}`, css: [!isInsideOfInlineExtension && fg('platform_fix_macro_renders_in_layouts') && containerStyle] }, asInlineAnalytics, content)); return centerAlignClass && expValEquals('platform_editor_flex_based_centering', 'isEnabled', true) ? jsx("div", { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop className: RendererCssClassName.STICKY_SAFE_CENTER_WRAPPER + ' ' + RendererCssClassName.FLEX_CENTER_WRAPPER }, extensionDiv) : extensionDiv; }); }; const Extension = props => { const { text, layout = 'default', handleRef, shadowClassNames, path = [], extensionViewportSizes, parameters, nodeHeight, localId, isInsideOfInlineExtension } = props; return jsx(ExtensionRenderer // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , _extends({}, props, { type: "extension" }), ({ result }) => { try { // Return the result directly if it's a valid JSX.Element if (result && /*#__PURE__*/React.isValidElement(result)) { return renderExtension(result, layout, { isTopLevel: path.length < 1, handleRef, shadowClassNames, tabIndex: props.tabIndex, rendererAppearance: props.rendererAppearance }, undefined, parameters === null || parameters === void 0 ? void 0 : parameters.extensionId, extensionViewportSizes, nodeHeight, localId, undefined, undefined, isInsideOfInlineExtension); } // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { /** We don't want this error to block renderer */ /** We keep rendering the default content */ } // Always return default content if anything goes wrong return renderExtension(text || 'extension', layout, { isTopLevel: path.length < 1, handleRef, shadowClassNames, tabIndex: props.tabIndex, rendererAppearance: props.rendererAppearance }, undefined, parameters === null || parameters === void 0 ? void 0 : parameters.extensionId, extensionViewportSizes, nodeHeight, localId, undefined, undefined, isInsideOfInlineExtension); }); }; const _default_1 = overflowShadow(Extension, { overflowSelector: `.${RendererCssClassName.EXTENSION_OVERFLOW_CONTAINER}` }); export default _default_1;