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