@atlaskit/renderer
Version:
Renderer component
200 lines (198 loc) • 10.1 kB
JavaScript
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;