UNPKG

@atlaskit/renderer

Version:
235 lines (225 loc) 11.3 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.BreakoutSSRInlineScript = BreakoutSSRInlineScript; exports.calcLineLength = exports.breakoutInlineScriptContext = void 0; exports.createBreakoutInlineScript = createBreakoutInlineScript; var _react = _interopRequireDefault(require("react")); var _utils = require("@atlaskit/editor-common/utils"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _style = require("./style"); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); /* eslint-disable jsdoc/require-jsdoc -- SSR inline script helpers */ /** * Inline Script that updates breakout node width on client side, * before main JavaScript bundle is ready. * More info: https://product-fabric.atlassian.net/wiki/spaces/E/pages/1216218119/Renderer+SSR+for+Breakout+Nodes */ function BreakoutSSRInlineScript(_ref) { var noOpSSRInlineScript = _ref.noOpSSRInlineScript; /** * Should only inline this script while SSR, * not needed on the client side. */ // For hydrateRoot there is a mismatch on client for this script. // So we want to add the script on client side but guard it with check to // not execute logic if (typeof window !== 'undefined' && !window.navigator.userAgent.includes('jsdom')) { if (!noOpSSRInlineScript) { return null; } else { window.__RENDERER_BYPASS_BREAKOUT_SSR__ = true; } } var id = Math.floor(Math.random() * (9999999999 - 9999 + 1)) + 9999; var shouldSkipScript = { table: (0, _platformFeatureFlags.fg)('platform-ssr-table-resize') }; return /*#__PURE__*/_react.default.createElement("script", { "data-breakout-script-id": id // To investigate if we can replace this. // eslint-disable-next-line react/no-danger , dangerouslySetInnerHTML: { __html: createBreakoutInlineScript(id, shouldSkipScript) }, "data-testid": "breakout-ssr-inline-script" }); } function createBreakoutInlineScript(id, shouldSkipScript) { var flags = { platform_editor_fix_media_in_renderer: (0, _platformFeatureFlags.fg)('platform_editor_fix_media_in_renderer'), platform_editor_renderer_extension_width_fix: (0, _expValEquals.expValEquals)('platform_editor_renderer_extension_width_fix', 'isEnabled', true) }; return "(function(window){\nif(typeof window !== 'undefined' && window.__RENDERER_BYPASS_BREAKOUT_SSR__) { return; }\n".concat(breakoutInlineScriptContext, ";\n(").concat(applyBreakoutAfterSSR.toString(), ")(\"").concat(id, "\", breakoutConsts, ").concat(JSON.stringify(shouldSkipScript), ", ").concat(JSON.stringify(flags), ");\n})(window);\n"); } var breakoutInlineScriptContext = exports.breakoutInlineScriptContext = "\n var breakoutConsts = ".concat(JSON.stringify(_utils.breakoutConsts), ";\n breakoutConsts.mapBreakpointToLayoutMaxWidth = ").concat(_utils.breakoutConsts.mapBreakpointToLayoutMaxWidth.toString(), ";\n breakoutConsts.getBreakpoint = ").concat(_utils.breakoutConsts.getBreakpoint.toString(), ";\n breakoutConsts.calcBreakoutWidth = ").concat(_utils.breakoutConsts.calcBreakoutWidth.toString(), ";\n breakoutConsts.calcLineLength = ").concat(_utils.breakoutConsts.calcLineLength.toString(), ";\n breakoutConsts.calcWideWidth = ").concat(_utils.breakoutConsts.calcWideWidth.toString(), ";\n breakoutConsts.FullPagePadding = ").concat(_style.FullPagePadding.toString(), ";\n"); /** * WARNING: NO EXTERNAL FUNCTION CALL IN THIS FUNCTION * This function will be put to DOM as an inline script. * It can not have any external function dependency. * All required data must be passed in as serializable parameters. */ // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any function applyBreakoutAfterSSR(id, breakoutConsts, shouldSkipBreakoutScript, flags) { var MEDIA_NODE_TYPE = 'mediaSingle'; var WIDE_LAYOUT_MODES = ['full-width', 'wide', 'custom']; function findUp(element, condition) { if (!element) { return; } while (element.parentElement) { if (condition(element)) { return element.parentElement; } element = element.parentElement; } } var renderer = findUp( // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- inline script runs in browser document context document.querySelector("[data-breakout-script-id=\"".concat(id, "\"]")), function (elem) { var _elem$parentElement; return !!((_elem$parentElement = elem.parentElement) !== null && _elem$parentElement !== void 0 && _elem$parentElement.classList.contains('ak-renderer-wrapper')); }); if (!renderer) { return; } var observer = new MutationObserver(function (mutationsList) { mutationsList.forEach(function (item) { if (item.target.nodeType !== Node.ELEMENT_NODE) { return; } // Remove with feature gate 'platform-ssr-table-resize' // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting if (item.target.classList.contains('ak-renderer-document')) { item.addedNodes.forEach(function (maybeNode) { // maybeNode may contain comments which doesn't have a dataset property if (maybeNode.nodeType !== Node.ELEMENT_NODE) { return; } var width; // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting var node = maybeNode; var mode = node.dataset.mode || node.dataset.layout || ''; var nodeType = node.dataset.nodeType; var widthType = node.dataset.widthType; var isMediaSingleWithPixelWidth = nodeType === 'mediaSingle' && widthType === 'pixel'; var isExtension = nodeType === 'extension' || nodeType === 'bodiedExtension'; if (!mode || !WIDE_LAYOUT_MODES.includes(mode) || // skip apply width styling to mediaSingle node with pixel width to avoid image size changing isMediaSingleWithPixelWidth && flags['platform_editor_fix_media_in_renderer'] || isExtension && flags['platform_editor_renderer_extension_width_fix']) { return; } // When flag is on we are using CSS to calculate the table width thus don't need logic below to set the width and left. if (shouldSkipBreakoutScript.table && node.classList.contains('pm-table-container')) { return; } if (node.classList.contains('fabric-editor-breakout-mark')) { return; } // use breakout script for all other types of nodes if (node.classList.contains('pm-table-container') && mode === 'custom') { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion var rendererWidth = renderer.offsetWidth; var effectiveWidth = rendererWidth - breakoutConsts.padding; width = "".concat(Math.min(parseInt(node.style.width), effectiveWidth), "px"); } else { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion width = breakoutConsts.calcBreakoutWidth(breakoutConsts)(mode, renderer.offsetWidth); } if (node.style.width === width) { return; } node.style.width = width; // Tables require some special logic, as they are not using common css transform approach, // because it breaks with sticky headers. This logic is copied from a table node: // https://bitbucket.org/atlassian/atlassian-frontend/src/77938aee0c140d02ff99b98a03849be1236865b4/packages/editor/renderer/src/react/nodes/table.tsx#table.tsx-235:245 if (node.classList.contains('pm-table-container') && // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion !renderer.classList.contains('is-full-width')) { var lineLength = breakoutConsts.calcLineLength(breakoutConsts)(); var left = lineLength / 2 - parseInt(width) / 2; if (left < 0 && parseInt(width) > lineLength) { node.style.left = left + 'px'; } else { node.style.left = ''; } } }); } else if ( /** * The mutation observer is only called once per added node. * The above condition only deals with direct children of <div class="ak-renderer-document" /> * When it is initially called on the direct children, not all the sub children have loaded. * So nested media elements which are not immediately loaded as sub children are not available in the above conditional. * Thus adding this conditional to deal with all media elements directly. */ // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting item.target.dataset.nodeType === MEDIA_NODE_TYPE) { // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting applyMediaBreakout(item.target, flags); } }); }); var applyMediaBreakout = function applyMediaBreakout(card, flags) { // width was already set by another breakout script if (card.style.width) { return; } var tableParent = findUp(card, function (elem) { return elem instanceof HTMLTableCellElement; }); // only apply the breakout to media elements not nested inside table // table sizing is not based on percentage width if (tableParent) { return; } var mode = card.dataset.mode || card.dataset.layout || ''; var width = card.dataset.width; var isPixelBasedResizing = card.dataset.widthType === 'pixel'; // Pixel based resizing has width set in pixels based on its width attribute // Thus, no need to override width if (isPixelBasedResizing) { return; } if (WIDE_LAYOUT_MODES.includes(mode)) { card.style.width = '100%'; } else if (width) { card.style.width = "".concat(width, "%"); } }; observer.observe(renderer, { childList: true, subtree: true }); /** * Using window load event to unsubscribe from mutation observer, as at this stage document is fully rendered. * Experiment with DOMContentLoaded showed that some of the blocks were not processed at all. * That's why window load is necessary. * * More info: * – https://html.spec.whatwg.org/multipage/parsing.html#the-end * – https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event * – https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event */ var _disconnect = function disconnect() { observer.disconnect(); // Ignored via go/ees005 // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners window.removeEventListener('load', _disconnect); }; // Ignored via go/ees005 // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners window.addEventListener('load', _disconnect); } var calcLineLength = exports.calcLineLength = _utils.breakoutConsts.calcLineLength(_utils.breakoutConsts);