UNPKG

@atlaskit/renderer

Version:
913 lines (896 loc) • 49 kB
"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);