@atlaskit/renderer
Version:
Renderer component
913 lines (896 loc) • 49 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.tableCanBeSticky = exports.shouldHeaderStick = exports.shouldHeaderPinBottom = exports.orderChildren = exports.isTableResizingEnabled = exports.isStickyScrollbarEnabled = exports.isHeaderRowEnabled = exports.hasRowspan = exports.getRefTop = exports.addSortableColumn = exports.TableProcessorWithContainerStyles = exports.TableContainer = exports.RefSyncBlockFakeBorders = void 0;
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _styles = require("@atlaskit/editor-common/styles");
var _nodeWidth = require("@atlaskit/editor-common/node-width");
var _style = require("../../ui/Renderer/style");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _consts = require("../../consts");
var _utils = require("@atlaskit/editor-common/utils");
var _types = require("@atlaskit/editor-common/types");
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
var _tableCell = require("./tableCell");
var _sticky = require("./table/sticky");
var _table = require("./table/table");
var _appearance = require("../utils/appearance");
var _TableStickyScrollbar = require("./TableStickyScrollbar");
var _rendererContext = require("../../renderer-context");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _table2 = require("@atlaskit/editor-common/table");
var _contentMode = require("./table/content-mode");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* eslint-disable @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/ui-styling-standard/enforce-style-prop, @repo/internal/react/no-class-components */
var stickyContainerBaseStyles = {
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
height: "var(--ds-space-250, 20px)",
// MAX_BROWSER_SCROLLBAR_HEIGHT
// Follow editor to hide by default so it does not show empty gap in SSR
// https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/editor/editor-plugin-table/src/nodeviews/TableComponent.tsx#957
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
display: 'block',
width: '100%'
};
var stickyContainerAdditionalStyles = {
visibility: 'hidden',
overflowX: 'auto',
position: 'sticky',
bottom: "var(--ds-space-0, 0px)",
zIndex: 1
};
var isTableResizingEnabled = exports.isTableResizingEnabled = function isTableResizingEnabled(appearance) {
return (0, _appearance.isFullWidthOrFullPageAppearance)(appearance) || (0, _appearance.isCommentAppearance)(appearance);
};
var isStickyScrollbarEnabled = exports.isStickyScrollbarEnabled = function isStickyScrollbarEnabled(appearance) {
return (0, _appearance.isFullWidthOrFullPageAppearance)(appearance) && (0, _experiments.editorExperiment)('platform_renderer_table_sticky_scrollbar', true, {
exposure: true
});
};
var orderChildren = exports.orderChildren = function orderChildren(children, tableNode, smartCardStorage, tableOrderStatus) {
if (!tableOrderStatus || tableOrderStatus.order === _types.SortOrder.NO_ORDER) {
return children;
}
var order = tableOrderStatus.order,
columnIndex = tableOrderStatus.columnIndex;
var compareNodesInOrder = (0, _utils.createCompareNodes)({
getInlineCardTextFromStore: function getInlineCardTextFromStore(attrs) {
var _ref = attrs,
url = _ref.url;
if (!url) {
return null;
}
return smartCardStorage.get(url) || null;
}
}, order);
var tableArray = (0, _utils.convertProsemirrorTableNodeToArrayOfRows)(tableNode);
var tableArrayWithChildren = tableArray.map(function (rowNodes, index) {
return {
rowNodes: rowNodes,
rowReact: children[index]
};
});
var headerRow = tableArrayWithChildren.shift();
var sortedTable = tableArrayWithChildren.sort(function (rowA, rowB) {
return compareNodesInOrder(rowA.rowNodes[columnIndex], rowB.rowNodes[columnIndex]);
});
if (headerRow) {
sortedTable.unshift(headerRow);
}
return sortedTable.map(function (elem) {
return elem.rowReact;
});
};
var hasRowspan = exports.hasRowspan = function hasRowspan(row) {
var hasRowspan = false;
row.forEach(function (cell) {
return hasRowspan = hasRowspan || cell.attrs.rowspan > 1;
});
return hasRowspan;
};
var getRefTop = exports.getRefTop = function getRefTop(refElement) {
return Math.round(refElement.getBoundingClientRect().top);
};
var shouldHeaderStick = exports.shouldHeaderStick = function shouldHeaderStick(scrollTop, tableTop, tableBottom, rowHeight) {
return tableTop <= scrollTop && !(tableBottom - rowHeight <= scrollTop);
};
var shouldHeaderPinBottom = exports.shouldHeaderPinBottom = function shouldHeaderPinBottom(scrollTop, tableBottom, rowHeight) {
return tableBottom - rowHeight <= scrollTop && !(tableBottom < scrollTop);
};
var addSortableColumn = exports.addSortableColumn = function addSortableColumn(rows, tableOrderStatus, onSorting
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
return _react.default.Children.map(rows, function (row, index) {
if (index === 0) {
return /*#__PURE__*/_react.default.cloneElement(_react.default.Children.only(row), {
tableOrderStatus: tableOrderStatus,
onSorting: onSorting
});
}
return row;
});
};
var isHeaderRowEnabled = exports.isHeaderRowEnabled = function isHeaderRowEnabled(rows
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
if (!rows.length) {
return false;
}
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
var children = rows[0].props.children;
if (!children.length) {
return false;
}
if (children.length === 1) {
return children[0].type === _tableCell.TableHeader;
}
return children.every(function (node) {
return node.type === _tableCell.TableHeader;
});
};
var tableCanBeSticky = exports.tableCanBeSticky = function tableCanBeSticky(node, children
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
return isHeaderRowEnabled(children) && node && node.firstChild && !hasRowspan(node.firstChild);
};
/**
* Fake left/right borders rendered as direct children of TABLE_CONTAINER
* (the non-scrolling parent of the horizontally scrolling TABLE_NODE_WRAPPER).
*
* The visible styling for these divs lives in `tableFakeBorderStyles`
* (`renderer/src/ui/Renderer/RendererStyleContainer.tsx`), which is itself
* gated on `editorExperiment('platform_synced_block', true)` AND
* `isInsideSyncBlock`.
*
* Shared between `renderer/src/react/nodes/table.tsx` and
* `renderer/src/react/nodes/tableNew.tsx` so the two stay in sync.
*/
var TableFakeBorders = function TableFakeBorders(_ref2) {
var isNumberColumnEnabled = _ref2.isNumberColumnEnabled;
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
className: _styles.TableSharedCssClassName.TABLE_LEFT_BORDER,
"data-with-numbered-table": isNumberColumnEnabled ? 'true' : undefined,
"data-testid": "table-left-border"
}), /*#__PURE__*/_react.default.createElement("div", {
className: _styles.TableSharedCssClassName.TABLE_RIGHT_BORDER,
"data-with-numbered-table": isNumberColumnEnabled ? 'true' : undefined,
"data-testid": "table-right-border"
}));
};
/**
* Reads `nestedRendererType` from RendererContext and renders the fake left/right
* borders only when the current renderer is the nested renderer for a reference
* synced block.
*/
var RefSyncBlockFakeBorders = exports.RefSyncBlockFakeBorders = function RefSyncBlockFakeBorders(_ref3) {
var isNumberColumnEnabled = _ref3.isNumberColumnEnabled;
var _useRendererContext = (0, _rendererContext.useRendererContext)(),
nestedRendererType = _useRendererContext.nestedRendererType;
var isInsideOfRefSyncBlock = nestedRendererType === 'syncedBlock';
if (!isInsideOfRefSyncBlock || !(0, _experiments.editorExperiment)('platform_synced_block', true)) {
return null;
}
return /*#__PURE__*/_react.default.createElement(TableFakeBorders, {
isNumberColumnEnabled: isNumberColumnEnabled
});
};
/**
* TableContainer renders tables using only CSS-based rules
*/
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/react/no-class-components
/**
*
*/
var TableContainer = exports.TableContainer = /*#__PURE__*/function (_React$Component) {
function TableContainer() {
var _this;
(0, _classCallCheck2.default)(this, TableContainer);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _callSuper(this, TableContainer, [].concat(args));
(0, _defineProperty2.default)(_this, "state", {
stickyMode: 'none',
wrapperWidth: 0,
headerRowHeight: 0
});
(0, _defineProperty2.default)(_this, "tableRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)(_this, "stickyHeaderRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)(_this, "stickyScrollbarRef", /*#__PURE__*/_react.default.createRef());
// used for sync scroll + copying wrapper width to sticky header
(0, _defineProperty2.default)(_this, "stickyWrapperRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)(_this, "wrapperRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)(_this, "overflowParent", null);
(0, _defineProperty2.default)(_this, "updatedLayout", 'custom');
(0, _defineProperty2.default)(_this, "containerRef", null);
(0, _defineProperty2.default)(_this, "_isInsideNestedRenderer", null);
// Stores the last computed style values from render() for use by applyNestedRendererTableFix().
// This avoids reading from the DOM which can be stale when React removes properties between renders.
(0, _defineProperty2.default)(_this, "lastComputedStyle", {});
(0, _defineProperty2.default)(_this, "resizeObserver", null);
(0, _defineProperty2.default)(_this, "applyResizerChange", function (entries) {
var wrapperWidth = _this.state.wrapperWidth;
var headerRowHeight = _this.state.headerRowHeight;
var _iterator = _createForOfIteratorHelper(entries),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var entry = _step.value;
if (entry.target === _this.wrapperRef.current) {
wrapperWidth = entry.contentRect.width;
} else if (entry.target === _this.stickyHeaderRef.current) {
headerRowHeight = Math.round(entry.contentRect.height);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (headerRowHeight !== _this.state.headerRowHeight || wrapperWidth !== _this.state.wrapperWidth) {
_this.setState({
wrapperWidth: wrapperWidth,
headerRowHeight: headerRowHeight
});
}
});
/**
* Callback ref that captures the container DOM element and also forwards
* to the handleRef prop from the overflow shadow HOC.
*/
(0, _defineProperty2.default)(_this, "setContainerRef", function (el) {
_this.containerRef = el;
var handleRef = _this.props.handleRef;
if (typeof handleRef === 'function') {
handleRef(el);
} else if (handleRef && (0, _typeof2.default)(handleRef) === 'object') {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handleRef.current = el;
}
});
(0, _defineProperty2.default)(_this, "componentWillUnmount", function () {
if (_this.overflowParent) {
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
_this.overflowParent.removeEventListener('scroll', _this.onScroll);
}
if (_this.nextFrame) {
cancelAnimationFrame(_this.nextFrame);
}
if (_this.resizeObserver) {
_this.resizeObserver.disconnect();
}
if (_this.stickyScrollbar) {
_this.stickyScrollbar.dispose();
}
});
(0, _defineProperty2.default)(_this, "getScrollTop", function () {
var stickyHeaders = _this.props.stickyHeaders;
var offsetTop = stickyHeaders && stickyHeaders.offsetTop || 0;
return (_this.overflowParent ? _this.overflowParent.top : 0) + offsetTop;
});
(0, _defineProperty2.default)(_this, "updateSticky", function () {
var tableElem = _this.tableRef.current;
var refElem = _this.stickyHeaderRef.current;
if (!tableElem || !refElem) {
return;
}
var scrollTop = _this.getScrollTop() + _sticky.tableStickyPadding;
var tableTop = getRefTop(tableElem);
var tableBottom = tableTop + tableElem.clientHeight;
var shouldSticky = shouldHeaderStick(scrollTop, tableTop, tableBottom, refElem.clientHeight);
var shouldPin = shouldHeaderPinBottom(scrollTop, tableBottom, refElem.clientHeight);
var stickyMode = 'none';
if (shouldPin) {
stickyMode = 'pin-bottom';
} else if (shouldSticky) {
stickyMode = 'stick';
}
if (_this.state.stickyMode !== stickyMode) {
_this.setState({
stickyMode: stickyMode
});
}
_this.nextFrame = undefined;
});
(0, _defineProperty2.default)(_this, "onScroll", function () {
if (!_this.nextFrame) {
_this.nextFrame = requestAnimationFrame(_this.updateSticky);
}
});
(0, _defineProperty2.default)(_this, "onWrapperScrolled", function () {
if (!_this.wrapperRef.current || !_this.stickyWrapperRef.current) {
return;
}
_this.stickyWrapperRef.current.scrollLeft = _this.wrapperRef.current.scrollLeft;
if (_this.stickyScrollbarRef.current) {
_this.stickyScrollbarRef.current.scrollLeft = _this.wrapperRef.current.scrollLeft;
}
});
(0, _defineProperty2.default)(_this, "grabFirstRowRef", function (children) {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return _react.default.Children.map(children || false, function (child, idx) {
if (idx === 0 && /*#__PURE__*/_react.default.isValidElement(child)) {
return /*#__PURE__*/_react.default.cloneElement(child, {
innerRef: _this.stickyHeaderRef
});
}
return child;
});
});
return _this;
}
(0, _inherits2.default)(TableContainer, _React$Component);
return (0, _createClass2.default)(TableContainer, [{
key: "isInsideNestedRenderer",
value:
/**
* Checks if this table is inside a nested renderer (e.g. Include Page macro)
* by looking for multiple .ak-renderer-document ancestors in the DOM.
* The result is cached since a table's position in the DOM tree is stable after mount.
*/
function isInsideNestedRenderer() {
if (this._isInsideNestedRenderer !== null) {
return this._isInsideNestedRenderer;
}
if (!this.containerRef) {
return false;
}
var docAncestorCount = 0;
var el = this.containerRef.parentElement;
while (el) {
if (el.classList.contains(_consts.RendererCssClassName.DOCUMENT)) {
docAncestorCount++;
if (docAncestorCount >= 2) {
this._isInsideNestedRenderer = true;
return true;
}
}
el = el.parentElement;
}
this._isInsideNestedRenderer = false;
return false;
}
/**
* For tables inside nested renderers (e.g. Include Page macro), the parent
* renderer's CSS override forces width:100%!important and left:0!important
* which overrides the inline styles set by this component. Using
* style.setProperty with 'important' priority on inline styles beats
* stylesheet !important rules per the CSS cascade.
*
* Uses lastComputedStyle (populated during render) rather than reading from
* element.style, because React may remove properties from the DOM when their
* values transition to undefined between renders.
*/
}, {
key: "applyNestedRendererTableFix",
value: function applyNestedRendererTableFix() {
if (!this.containerRef || !(0, _platformFeatureFlags.fg)('platform_nested_table_style_override')) {
return;
}
if (!this.isInsideNestedRenderer()) {
return;
}
var _this$lastComputedSty = this.lastComputedStyle,
width = _this$lastComputedSty.width,
left = _this$lastComputedSty.left,
marginLeft = _this$lastComputedSty.marginLeft;
var style = this.containerRef.style;
style.setProperty('width', width || 'auto', 'important');
style.setProperty('left', left || 'auto', 'important');
style.setProperty('margin-left', marginLeft || '0', 'important');
}
}, {
key: "componentDidMount",
value:
/**
* Starts observing table dimensions and wires sticky header/scrollbar behavior after mount.
*
* @example
*/
function componentDidMount() {
this.resizeObserver = new ResizeObserver(this.applyResizerChange);
if (this.wrapperRef.current) {
this.resizeObserver.observe(this.wrapperRef.current);
}
if (this.stickyHeaderRef.current) {
this.resizeObserver.observe(this.stickyHeaderRef.current);
}
if (this.props.stickyHeaders) {
var _this$props$stickyHea;
this.overflowParent = _sticky.OverflowParent.fromElement(this.tableRef.current, (_this$props$stickyHea = this.props.stickyHeaders) === null || _this$props$stickyHea === void 0 ? void 0 : _this$props$stickyHea.defaultScrollRootId_DO_NOT_USE);
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
this.overflowParent.addEventListener('scroll', this.onScroll);
}
if (this.wrapperRef.current && isStickyScrollbarEnabled(this.props.rendererAppearance)) {
this.stickyScrollbar = new _TableStickyScrollbar.TableStickyScrollbar(this.wrapperRef.current);
}
this.applyNestedRendererTableFix();
}
/**
* Updates sticky header wiring and scroll synchronization after prop or state changes.
*
* @param prevProps
* @param prevState
* @example
*/
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
// toggling sticky headers visiblity
if (this.props.stickyHeaders && !this.overflowParent) {
var _this$props$stickyHea2;
this.overflowParent = _sticky.OverflowParent.fromElement(this.tableRef.current, (_this$props$stickyHea2 = this.props.stickyHeaders) === null || _this$props$stickyHea2 === void 0 ? void 0 : _this$props$stickyHea2.defaultScrollRootId_DO_NOT_USE);
} else if (!this.props.stickyHeaders && this.overflowParent) {
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
this.overflowParent.removeEventListener('scroll', this.onScroll);
this.overflowParent = null;
}
// offsetTop might have changed, re-position sticky header
if (this.props.stickyHeaders !== prevProps.stickyHeaders) {
this.updateSticky();
}
// sync horizontal scroll in floating div when toggling modes
if (prevState.stickyMode !== this.state.stickyMode) {
this.onWrapperScrolled();
}
// React re-applies the style prop on every render, which overwrites the
// !important priorities set at mount time. Re-apply after each update.
this.applyNestedRendererTableFix();
}
}, {
key: "pinTop",
get:
/**
* Calculates the top offset used when the sticky header is pinned to the table bottom.
*/
function get() {
if (!this.tableRef.current || !this.stickyHeaderRef.current) {
return;
}
return this.tableRef.current.offsetHeight - this.stickyHeaderRef.current.offsetHeight + _styles.tableMarginTop - _sticky.tableStickyPadding;
}
/**
* Determines whether sticky header positioning should include the default scroll root offset.
*/
}, {
key: "shouldAddOverflowParentOffsetTop_DO_NOT_USE",
get: function get() {
// IF the StickyHeaderConfig specifies that the default scroll root offsetTop should be added
// AND the StickyHeaderConfig specifies a default scroll root id
// AND the OverflowParent is the corresponding element
// THEN we should add the OverflowParent offset top (RETURN TRUE)
return this.props.stickyHeaders && !!this.props.stickyHeaders.shouldAddDefaultScrollRootOffsetTop_DO_NOT_USE && !!this.props.stickyHeaders.defaultScrollRootId_DO_NOT_USE && this.overflowParent && this.overflowParent.id === this.props.stickyHeaders.defaultScrollRootId_DO_NOT_USE;
}
/**
* Resolves the top position for the sticky header based on the current sticky mode.
*/
}, {
key: "stickyTop",
get: function get() {
switch (this.state.stickyMode) {
case 'pin-bottom':
return this.pinTop;
case 'stick':
var offsetTop = this.props.stickyHeaders && this.props.stickyHeaders.offsetTop;
if (typeof offsetTop === 'number' && this.shouldAddOverflowParentOffsetTop_DO_NOT_USE) {
var overflowParentOffsetTop = this.overflowParent ? this.overflowParent.top : 0;
return offsetTop + overflowParentOffsetTop;
} else {
return offsetTop;
}
default:
return undefined;
}
}
/**
* Renders the table container, sticky header, table content, sticky scrollbar, and synced block borders.
*
* @example
*/
}, {
key: "render",
value: function render() {
var _this$tableRef$curren;
var _this$props = this.props,
isNumberColumnEnabled = _this$props.isNumberColumnEnabled,
layout = _this$props.layout,
columnWidths = _this$props.columnWidths,
stickyHeaders = _this$props.stickyHeaders,
tableNode = _this$props.tableNode,
rendererAppearance = _this$props.rendererAppearance,
isInsideOfBlockNode = _this$props.isInsideOfBlockNode,
isInsideOfTable = _this$props.isInsideOfTable,
isinsideMultiBodiedExtension = _this$props.isinsideMultiBodiedExtension,
allowTableAlignment = _this$props.allowTableAlignment,
allowTableResizing = _this$props.allowTableResizing,
isPresentational = _this$props.isPresentational,
allowFixedColumnWidthOption = _this$props.allowFixedColumnWidthOption;
var stickyMode = this.state.stickyMode;
var lineLengthFixedWidth = _editorSharedStyles.akEditorDefaultLayoutWidth;
var updatedLayout;
var fullPageRendererWidthCSS = (0, _experiments.editorExperiment)('platform_editor_preview_panel_responsiveness', true, {
exposure: true
}) ? 'calc(100cqw - var(--ak-renderer--full-page-gutter) * 2)' : "100cqw - ".concat(_style.FullPagePadding, "px * 2");
var renderWidthCSS = rendererAppearance === 'full-page' ? fullPageRendererWidthCSS : "100cqw";
var calcDefaultLayoutWidthByAppearance = function calcDefaultLayoutWidthByAppearance(rendererAppearance, tableNode) {
if (rendererAppearance === 'max' && !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width) && ((0, _expValEquals.expValEquals)('editor_tinymce_full_width_mode', 'isEnabled', true) || (0, _expValEquals.expValEquals)('confluence_max_width_content_appearance', 'isEnabled', true))) {
return "min(".concat(_editorSharedStyles.akEditorMaxWidthLayoutWidth, "px, ").concat(renderWidthCSS, ")");
} else if (rendererAppearance === 'full-width' && !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width)) {
return "min(".concat(_editorSharedStyles.akEditorFullWidthLayoutWidth, "px, ").concat(renderWidthCSS, ")");
} else if (rendererAppearance === 'comment' && allowTableResizing && !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width)) {
return renderWidthCSS;
} else {
// custom width, or width mapped to breakpoint
var tableContainerWidth = (0, _nodeWidth.getTableContainerWidth)(tableNode);
return "min(".concat(tableContainerWidth, "px, ").concat(renderWidthCSS, ")");
}
};
var tableWidthCSS = calcDefaultLayoutWidthByAppearance(rendererAppearance, tableNode);
// Logic for table alignment in renderer
var isTableAlignStart = tableNode && tableNode.attrs && tableNode.attrs.layout === 'align-start' && allowTableAlignment;
var fullWidthLineLengthCSS = "min(".concat(_editorSharedStyles.akEditorFullWidthLayoutWidth, "px, ").concat(renderWidthCSS, ")");
var maxWidthLineLengthCSS = "min(".concat(_editorSharedStyles.akEditorMaxWidthLayoutWidth, "px, ").concat(renderWidthCSS, ")");
var isCommentAppearanceAndTableAlignmentEnabled = (0, _appearance.isCommentAppearance)(rendererAppearance) && allowTableAlignment;
var lineLengthCSS = (0, _appearance.isFullWidthAppearance)(rendererAppearance) ? fullWidthLineLengthCSS : (0, _appearance.isMaxWidthAppearance)(rendererAppearance) && ((0, _expValEquals.expValEquals)('editor_tinymce_full_width_mode', 'isEnabled', true) || (0, _expValEquals.expValEquals)('confluence_max_width_content_appearance', 'isEnabled', true)) ? maxWidthLineLengthCSS : isCommentAppearanceAndTableAlignmentEnabled ? renderWidthCSS : "".concat(lineLengthFixedWidth, "px");
var tableWidthNew = (0, _nodeWidth.getTableContainerWidth)(tableNode);
var shouldCalculateLeftForAlignment = !isInsideOfBlockNode && !isInsideOfTable && isTableAlignStart && ((0, _appearance.isFullPageAppearance)(rendererAppearance) && tableWidthNew <= lineLengthFixedWidth || (0, _appearance.isFullWidthAppearance)(rendererAppearance) || (0, _appearance.isMaxWidthAppearance)(rendererAppearance) && ((0, _expValEquals.expValEquals)('editor_tinymce_full_width_mode', 'isEnabled', true) || (0, _expValEquals.expValEquals)('confluence_max_width_content_appearance', 'isEnabled', true)) || isCommentAppearanceAndTableAlignmentEnabled);
var leftCSS;
if (shouldCalculateLeftForAlignment) {
leftCSS = "(".concat(tableWidthCSS, " - ").concat(lineLengthCSS, ") / 2");
}
if (!shouldCalculateLeftForAlignment && (0, _appearance.isFullPageAppearance)(rendererAppearance)) {
// Note tableWidthCSS here is the renderer width
// When the screen is super wide we want table to break out.
// However if screen is smaller than 760px. We want table align to left.
leftCSS = "min(0px, ".concat(lineLengthCSS, " - ").concat(tableWidthCSS, ") / 2");
}
var children = _react.default.Children.toArray(this.props.children);
// Historically, tables in the full-width renderer had their layout set to 'default' which is deceiving.
// This check caters for those tables and helps with SSR logic
var isFullWidth = !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width) && rendererAppearance === 'full-width' && layout !== 'full-width';
if (isFullWidth) {
updatedLayout = 'full-width';
// if table has width explicity set, ensure SSR is handled
} else if (tableNode !== null && tableNode !== void 0 && tableNode.attrs.width) {
updatedLayout = 'custom';
} else {
updatedLayout = layout;
}
var finalTableContainerWidth = allowTableResizing ? tableWidthNew : 'inherit';
// We can only use CSS to determine the width when we have a known width in container.
// When appearance is full-page, full-width or comment we use CSS based width calculation.
// Otherwise it's fixed table width (customized width) or inherit.
if (rendererAppearance === 'full-page' || rendererAppearance === 'full-width' || rendererAppearance === 'max' && ((0, _expValEquals.expValEquals)('editor_tinymce_full_width_mode', 'isEnabled', true) || (0, _expValEquals.expValEquals)('confluence_max_width_content_appearance', 'isEnabled', true))) {
finalTableContainerWidth = allowTableResizing ? "calc(".concat(tableWidthCSS, ")") : 'inherit';
}
if (rendererAppearance === 'comment' && allowTableResizing && !allowTableAlignment) {
// If table alignment is disabled and table width is akEditorDefaultLayoutWidth = 760,
// it is most likely a table created before "Support Table in Comments" FF was enabled
// and we would see a bug ED-24795. A table created before "Support Table in Comments",
// should inhirit the width of the renderer container.
// !NOTE: it a table resized to 760 is copied from 'full-page' editor and pasted in comment editor
// where (allowTableResizing && !allowTableAlignment), the table will loose 760px width.
finalTableContainerWidth = tableNode !== null && tableNode !== void 0 && tableNode.attrs.width && (tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.width) !== _editorSharedStyles.akEditorDefaultLayoutWidth ? "calc(".concat(tableWidthCSS, ")") : 'inherit';
}
if (rendererAppearance === 'comment' && allowTableResizing && allowTableAlignment) {
// If table alignment is enabled and layout is not 'align-start' or 'center', we are loading a table that was
// created before "Support Table in Comments" FF was enabled. So the table should have the same width as renderer container
// instead of 760 that was set on tableNode when the table had been published.
finalTableContainerWidth = ((tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.layout) === 'align-start' || (tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.layout) === 'center') && tableNode !== null && tableNode !== void 0 && tableNode.attrs.width ? "calc(".concat(tableWidthCSS, ")") : 'inherit';
}
var isContentModeTable = (0, _table2.isTableInContentMode)({
tableNode: tableNode,
isSupported: (0, _contentMode.isContentModeSupported)({
allowTableResizing: allowTableResizing,
rendererAppearance: rendererAppearance
}),
isTableNested: isInsideOfBlockNode || isInsideOfTable
}) && (0, _expValEquals.expValEquals)('platform_editor_table_fit_to_content_auto_convert', 'isEnabled', true);
var style = _objectSpread(_objectSpread({}, isContentModeTable && {
'--renderer-table-max-width': renderWidthCSS
}), {}, {
width: finalTableContainerWidth,
left: leftCSS ? "calc(".concat(leftCSS, ")") : undefined,
marginLeft: shouldCalculateLeftForAlignment && leftCSS !== undefined ? "calc(-1 * (".concat(leftCSS, "))") : undefined
});
// Store computed style values for applyNestedRendererTableFix() to use.
// Reading from props rather than the DOM ensures correctness when React
// removes properties (transitions from set to undefined) between renders.
this.lastComputedStyle = {
width: typeof style.width === 'number' ? "".concat(style.width, "px") : style.width,
left: style.left,
marginLeft: style.marginLeft
};
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
className: "".concat(_styles.TableSharedCssClassName.TABLE_CONTAINER, " ").concat(this.props.shadowClassNames || ''),
"data-layout": updatedLayout,
"data-testid": "table-container",
ref: this.setContainerRef,
style: style
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
}, isStickyScrollbarEnabled(this.props.rendererAppearance) && /*#__PURE__*/_react.default.createElement("div", {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
className: _styles.TableSharedCssClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_TOP,
"data-testid": "sticky-scrollbar-sentinel-top"
}), stickyHeaders && tableNode && !isContentModeTable && tableCanBeSticky(tableNode, children) && /*#__PURE__*/_react.default.createElement(_sticky.StickyTable, {
isNumberColumnEnabled: isNumberColumnEnabled,
tableWidth: "inherit",
renderWidth: 0,
layout: layout,
handleRef: this.props.handleRef,
shadowClassNames: this.props.shadowClassNames,
top: this.stickyTop,
mode: stickyMode,
innerRef: this.stickyWrapperRef,
wrapperWidth: this.state.wrapperWidth,
columnWidths: columnWidths,
rowHeight: this.state.headerRowHeight,
tableNode: tableNode,
rendererAppearance: rendererAppearance,
allowTableResizing: allowTableResizing,
fixTableSSRResizing: true,
allowFixedColumnWidthOption: allowFixedColumnWidthOption
}, [children && children[0]]), /*#__PURE__*/_react.default.createElement("div", {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
className: _styles.TableSharedCssClassName.TABLE_NODE_WRAPPER,
ref: this.wrapperRef,
"data-number-column": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.isNumberColumnEnabled,
"data-layout": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.layout,
"data-autosize": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.__autoSize,
"data-table-local-id": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.localId,
"data-table-width": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.width,
"data-vc": "table-node-wrapper",
onScroll: this.props.stickyHeaders && this.onWrapperScrolled
}, /*#__PURE__*/_react.default.createElement(_table.Table, {
innerRef: this.tableRef,
columnWidths: columnWidths,
layout: layout,
renderWidth: 0,
isNumberColumnEnabled: isNumberColumnEnabled,
tableNode: tableNode,
rendererAppearance: rendererAppearance,
isInsideOfBlockNode: isInsideOfBlockNode,
isInsideOfTable: isInsideOfTable,
isinsideMultiBodiedExtension: isinsideMultiBodiedExtension,
allowTableResizing: allowTableResizing,
isPresentational: isPresentational,
allowFixedColumnWidthOption: allowFixedColumnWidthOption
}, this.grabFirstRowRef(children))), isStickyScrollbarEnabled(this.props.rendererAppearance) && /*#__PURE__*/_react.default.createElement("div", {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
className: "".concat(_styles.TableSharedCssClassName.TABLE_STICKY_SCROLLBAR_CONTAINER).concat((0, _platformFeatureFlags.fg)('confluence_frontend_table_scrollbar_ttvc_fix') ? '-view-page' : ''),
ref: this.stickyScrollbarRef,
"data-vc": "table-sticky-scrollbar-container",
style: (0, _platformFeatureFlags.fg)('confluence_frontend_table_scrollbar_ttvc_fix') ? _objectSpread(_objectSpread({}, stickyContainerBaseStyles), stickyContainerAdditionalStyles) : stickyContainerBaseStyles
}, /*#__PURE__*/_react.default.createElement("div", {
style: {
width: (0, _platformFeatureFlags.fg)('confluence_frontend_table_scrollbar_ttvc_fix') ? '100%' : (_this$tableRef$curren = this.tableRef.current) === null || _this$tableRef$curren === void 0 ? void 0 : _this$tableRef$curren.clientWidth,
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
height: '100%'
}
})), isStickyScrollbarEnabled(this.props.rendererAppearance) && /*#__PURE__*/_react.default.createElement("div", {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
className: _styles.TableSharedCssClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_BOTTOM,
"data-testid": "sticky-scrollbar-sentinel-bottom"
}), /*#__PURE__*/_react.default.createElement(RefSyncBlockFakeBorders, {
isNumberColumnEnabled: isNumberColumnEnabled
})));
}
}]);
}(_react.default.Component);
var getCellEdgePropsByCellOffset = function getCellEdgePropsByCellOffset(tableNode) {
var cellEdgePropsByCellOffset = new Map();
var cellRightByCellOffset = new Map();
var occupiedGrid = [];
var tableWidth = 0;
var cellOffset = 0;
tableNode.forEach(function (rowNode, _rowOffset, rowIndex) {
var _occupiedGrid$rowInde;
occupiedGrid[rowIndex] = (_occupiedGrid$rowInde = occupiedGrid[rowIndex]) !== null && _occupiedGrid$rowInde !== void 0 ? _occupiedGrid$rowInde : [];
var columnIndex = 0;
cellOffset += 1;
rowNode.forEach(function (cellNode) {
while (occupiedGrid[rowIndex][columnIndex]) {
columnIndex += 1;
}
var colspan = cellNode.attrs.colspan || 1;
var rowspan = cellNode.attrs.rowspan || 1;
var cellLeft = columnIndex;
var cellRight = cellLeft + colspan;
var cellTop = rowIndex;
var cellBottom = cellTop + rowspan;
for (var row = cellTop; row < cellBottom; row += 1) {
var _occupiedGrid$row;
occupiedGrid[row] = (_occupiedGrid$row = occupiedGrid[row]) !== null && _occupiedGrid$row !== void 0 ? _occupiedGrid$row : [];
for (var column = cellLeft; column < cellRight; column += 1) {
occupiedGrid[row][column] = true;
}
}
tableWidth = Math.max(tableWidth, cellRight);
cellRightByCellOffset.set(cellOffset, cellRight);
cellEdgePropsByCellOffset.set(cellOffset, {
reachesBottom: cellBottom >= tableNode.childCount,
reachesLeft: cellLeft === 0,
reachesRight: false,
reachesTop: cellTop === 0
});
columnIndex = cellRight;
cellOffset += cellNode.nodeSize;
});
cellOffset += 1;
});
cellRightByCellOffset.forEach(function (cellRight, currentCellOffset) {
var edgeProps = cellEdgePropsByCellOffset.get(currentCellOffset);
if (edgeProps) {
edgeProps.reachesRight = cellRight >= tableWidth;
}
});
return cellEdgePropsByCellOffset;
};
var addTableCellEdgeProps = function addTableCellEdgeProps(rows, tableNode) {
try {
if (!tableNode) {
return rows;
}
var cellEdgePropsByCellOffset = getCellEdgePropsByCellOffset(tableNode);
var cellOffset = 0;
return _react.default.Children.map(rows, function (row, rowIndex) {
var rowNode = tableNode.child(rowIndex);
cellOffset += 1;
var cellIndex = 0;
var rowChildren = _react.default.Children.map(row.props.children, function (child) {
if (! /*#__PURE__*/_react.default.isValidElement(child)) {
return child;
}
var cellNode = rowNode.child(cellIndex);
var edgeProps = cellEdgePropsByCellOffset.get(cellOffset);
cellIndex += 1;
cellOffset += cellNode.nodeSize;
return edgeProps ? /*#__PURE__*/_react.default.cloneElement(child, edgeProps) : child;
});
cellOffset += 1;
return /*#__PURE__*/_react.default.cloneElement(row, undefined, rowChildren);
});
} catch (_unused) {
// Renderer can receive malformed historical ADF. If the table shape cannot
// be described safely, keep rendering without rounded edge metadata.
return rows;
}
};
/**
* Processes table children before passing them to the styled table container.
*/
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/react/no-class-components
var TableProcessorWithContainerStyles = exports.TableProcessorWithContainerStyles = /*#__PURE__*/function (_React$Component2) {
function TableProcessorWithContainerStyles() {
var _this2;
(0, _classCallCheck2.default)(this, TableProcessorWithContainerStyles);
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
_this2 = _callSuper(this, TableProcessorWithContainerStyles, [].concat(args));
(0, _defineProperty2.default)(_this2, "state", {
tableOrderStatus: undefined
});
// adds sortable + re-orders children
(0, _defineProperty2.default)(_this2, "addSortableColumn", function (childrenArray) {
var _this2$props = _this2.props,
tableNode = _this2$props.tableNode,
allowColumnSorting = _this2$props.allowColumnSorting,
smartCardStorage = _this2$props.smartCardStorage;
var tableOrderStatus = _this2.state.tableOrderStatus;
if (allowColumnSorting && isHeaderRowEnabled(childrenArray) && tableNode && !(0, _utils.hasMergedCell)(tableNode)) {
return addSortableColumn(orderChildren(childrenArray, tableNode, smartCardStorage, tableOrderStatus), tableOrderStatus, _this2.changeSortOrder);
}
return childrenArray;
});
(0, _defineProperty2.default)(_this2, "changeSortOrder", function (columnIndex, sortOrder) {
_this2.setState({
tableOrderStatus: {
columnIndex: columnIndex,
order: sortOrder
}
});
});
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(0, _defineProperty2.default)(_this2, "addNumberColumnIndexes", function (rows) {
var isNumberColumnEnabled = _this2.props.isNumberColumnEnabled;
var headerRowEnabled = isHeaderRowEnabled(rows);
return _react.default.Children.map(rows, function (row, index) {
return /*#__PURE__*/_react.default.cloneElement(_react.default.Children.only(row), {
isNumberColumnEnabled: isNumberColumnEnabled,
index: headerRowEnabled ? index === 0 ? '' : index : index + 1
});
});
});
return _this2;
}
(0, _inherits2.default)(TableProcessorWithContainerStyles, _React$Component2);
return (0, _createClass2.default)(TableProcessorWithContainerStyles, [{
key: "render",
value:
/**
* Renders processed table children inside the table container.
*
* @example
*/
function render() {
var _this$props2 = this.props,
allowColumnSorting = _this$props2.allowColumnSorting,
allowFixedColumnWidthOption = _this$props2.allowFixedColumnWidthOption,
allowTableAlignment = _this$props2.allowTableAlignment,
allowTableResizing = _this$props2.allowTableResizing,
children = _this$props2.children,
columnWidths = _this$props2.columnWidths,
disableTableOverflowShadow = _this$props2.disableTableOverflowShadow,
handleRef = _this$props2.handleRef,
isinsideMultiBodiedExtension = _this$props2.isinsideMultiBodiedExtension,
isInsideOfBlockNode = _this$props2.isInsideOfBlockNode,
isInsideOfTable = _this$props2.isInsideOfTable,
isNumberColumnEnabled = _this$props2.isNumberColumnEnabled,
isPresentational = _this$props2.isPresentational,
layout = _this$props2.layout,
rendererAppearance = _this$props2.rendererAppearance,
renderWidth = _this$props2.renderWidth,
shadowClassNames = _this$props2.shadowClassNames,
smartCardStorage = _this$props2.smartCardStorage,
stickyHeaders = _this$props2.stickyHeaders,
tabIndex = _this$props2.tabIndex,
tableNode = _this$props2.tableNode;
if (!children) {
return null;
}
var childrenArray = _react.default.Children.toArray(children);
var childrenWithTableEdgeProps = (0, _expValEquals.expValEquals)('platform_editor_table_q4_loveability', 'isEnabled', true) ? addTableCellEdgeProps(childrenArray, tableNode) : childrenArray;
var orderedChildren = (0, _utils.compose)(this.addNumberColumnIndexes, this.addSortableColumn
// @ts-expect-error TS2345: Argument of type '(ReactChild | ReactFragment | ReactPortal)[]' is not assignable to parameter of type 'ReactElement<any, string | JSXElementConstructor<any>>[]'
)(childrenWithTableEdgeProps);
return /*#__PURE__*/_react.default.createElement(TableContainer, {
allowColumnSorting: allowColumnSorting,
allowFixedColumnWidthOption: allowFixedColumnWidthOption,
allowTableAlignment: allowTableAlignment,
allowTableResizing: allowTableResizing,
columnWidths: columnWidths,
disableTableOverflowShadow: disableTableOverflowShadow,
handleRef: handleRef,
isinsideMultiBodiedExtension: isinsideMultiBodiedExtension,
isInsideOfBlockNode: isInsideOfBlockNode,
isInsideOfTable: isInsideOfTable,
isNumberColumnEnabled: isNumberColumnEnabled,
isPresentational: isPresentational,
layout: layout,
rendererAppearance: rendererAppearance,
renderWidth: renderWidth,
shadowClassNames: shadowClassNames,
smartCardStorage: smartCardStorage,
stickyHeaders: stickyHeaders,
tabIndex: tabIndex,
tableNode: tableNode
}, orderedChildren);
}
}]);
}(_react.default.Component);