@atlaskit/renderer 137.1.9 → 137.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/cjs/react/nodes/table/colgroup.js +1 -2
- package/dist/cjs/react/nodes/table/sticky.js +2 -4
- package/dist/cjs/react/nodes/table/table.js +1 -12
- package/dist/cjs/react/nodes/table.js +471 -203
- package/dist/cjs/ui/Renderer/breakout-ssr.js +1 -2
- package/dist/cjs/ui/Renderer/index.js +3 -3
- package/dist/es2019/react/nodes/table/colgroup.js +1 -2
- package/dist/es2019/react/nodes/table/sticky.js +2 -3
- package/dist/es2019/react/nodes/table/table.js +1 -12
- package/dist/es2019/react/nodes/table.js +457 -190
- package/dist/es2019/ui/Renderer/breakout-ssr.js +1 -2
- package/dist/es2019/ui/Renderer/index.js +3 -3
- package/dist/esm/react/nodes/table/colgroup.js +1 -2
- package/dist/esm/react/nodes/table/sticky.js +2 -4
- package/dist/esm/react/nodes/table/table.js +1 -12
- package/dist/esm/react/nodes/table.js +471 -202
- package/dist/esm/ui/Renderer/breakout-ssr.js +1 -2
- package/dist/esm/ui/Renderer/index.js +3 -3
- package/dist/types/react/nodes/table/sticky.d.ts +1 -2
- package/dist/types/react/nodes/table.d.ts +78 -8
- package/package.json +4 -7
- package/dist/cjs/react/nodes/tableNew.js +0 -1020
- package/dist/es2019/react/nodes/tableNew.js +0 -956
- package/dist/esm/react/nodes/tableNew.js +0 -1014
- package/dist/types/react/nodes/tableNew.d.ts +0 -189
|
@@ -1,956 +0,0 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
/* eslint-disable @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/ui-styling-standard/enforce-style-prop, @repo/internal/react/no-class-components */
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { TableSharedCssClassName, tableMarginTop } from '@atlaskit/editor-common/styles';
|
|
5
|
-
import { getTableContainerWidth } from '@atlaskit/editor-common/node-width';
|
|
6
|
-
import { FullPagePadding } from '../../ui/Renderer/style';
|
|
7
|
-
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
8
|
-
import { RendererCssClassName } from '../../consts';
|
|
9
|
-
import { createCompareNodes, convertProsemirrorTableNodeToArrayOfRows, hasMergedCell, compose } from '@atlaskit/editor-common/utils';
|
|
10
|
-
import { SortOrder } from '@atlaskit/editor-common/types';
|
|
11
|
-
import { akEditorDefaultLayoutWidth, akEditorFullWidthLayoutWidth, akEditorMaxWidthLayoutWidth } from '@atlaskit/editor-shared-styles';
|
|
12
|
-
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
13
|
-
import { TableCell, TableHeader } from './tableCell';
|
|
14
|
-
import { StickyTable, tableStickyPadding, OverflowParent } from './table/sticky';
|
|
15
|
-
import { Table } from './table/table';
|
|
16
|
-
import { isCommentAppearance, isFullWidthOrFullPageAppearance, isFullWidthAppearance, isMaxWidthAppearance, isFullPageAppearance } from '../utils/appearance';
|
|
17
|
-
import { TableStickyScrollbar } from './TableStickyScrollbar';
|
|
18
|
-
import { useRendererContext } from '../../renderer-context';
|
|
19
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
20
|
-
import { isTableInContentMode } from '@atlaskit/editor-common/table';
|
|
21
|
-
import { isContentModeSupported } from './table/content-mode';
|
|
22
|
-
const stickyContainerBaseStyles = {
|
|
23
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
|
|
24
|
-
height: "var(--ds-space-250, 20px)",
|
|
25
|
-
// MAX_BROWSER_SCROLLBAR_HEIGHT
|
|
26
|
-
// Follow editor to hide by default so it does not show empty gap in SSR
|
|
27
|
-
// https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/editor/editor-plugin-table/src/nodeviews/TableComponent.tsx#957
|
|
28
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
|
|
29
|
-
display: 'block',
|
|
30
|
-
width: '100%'
|
|
31
|
-
};
|
|
32
|
-
const stickyContainerAdditionalStyles = {
|
|
33
|
-
visibility: 'hidden',
|
|
34
|
-
overflowX: 'auto',
|
|
35
|
-
position: 'sticky',
|
|
36
|
-
bottom: "var(--ds-space-0, 0px)",
|
|
37
|
-
zIndex: 1
|
|
38
|
-
};
|
|
39
|
-
export const isTableResizingEnabled = appearance => isFullWidthOrFullPageAppearance(appearance) || isCommentAppearance(appearance);
|
|
40
|
-
export const isStickyScrollbarEnabled = appearance => isFullWidthOrFullPageAppearance(appearance) && editorExperiment('platform_renderer_table_sticky_scrollbar', true, {
|
|
41
|
-
exposure: true
|
|
42
|
-
});
|
|
43
|
-
export const orderChildren = (children, tableNode, smartCardStorage, tableOrderStatus) => {
|
|
44
|
-
if (!tableOrderStatus || tableOrderStatus.order === SortOrder.NO_ORDER) {
|
|
45
|
-
return children;
|
|
46
|
-
}
|
|
47
|
-
const {
|
|
48
|
-
order,
|
|
49
|
-
columnIndex
|
|
50
|
-
} = tableOrderStatus;
|
|
51
|
-
const compareNodesInOrder = createCompareNodes({
|
|
52
|
-
getInlineCardTextFromStore(attrs) {
|
|
53
|
-
const {
|
|
54
|
-
url
|
|
55
|
-
} = attrs;
|
|
56
|
-
if (!url) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
return smartCardStorage.get(url) || null;
|
|
60
|
-
}
|
|
61
|
-
}, order);
|
|
62
|
-
const tableArray = convertProsemirrorTableNodeToArrayOfRows(tableNode);
|
|
63
|
-
const tableArrayWithChildren = tableArray.map((rowNodes, index) => ({
|
|
64
|
-
rowNodes,
|
|
65
|
-
rowReact: children[index]
|
|
66
|
-
}));
|
|
67
|
-
const headerRow = tableArrayWithChildren.shift();
|
|
68
|
-
const sortedTable = tableArrayWithChildren.sort((rowA, rowB) => compareNodesInOrder(rowA.rowNodes[columnIndex], rowB.rowNodes[columnIndex]));
|
|
69
|
-
if (headerRow) {
|
|
70
|
-
sortedTable.unshift(headerRow);
|
|
71
|
-
}
|
|
72
|
-
return sortedTable.map(elem => elem.rowReact);
|
|
73
|
-
};
|
|
74
|
-
export const hasRowspan = row => {
|
|
75
|
-
let hasRowspan = false;
|
|
76
|
-
row.forEach(cell => hasRowspan = hasRowspan || cell.attrs.rowspan > 1);
|
|
77
|
-
return hasRowspan;
|
|
78
|
-
};
|
|
79
|
-
export const getRefTop = refElement => {
|
|
80
|
-
return Math.round(refElement.getBoundingClientRect().top);
|
|
81
|
-
};
|
|
82
|
-
export const shouldHeaderStick = (scrollTop, tableTop, tableBottom, rowHeight) => tableTop <= scrollTop && !(tableBottom - rowHeight <= scrollTop);
|
|
83
|
-
export const shouldHeaderPinBottom = (scrollTop, tableBottom, rowHeight) => tableBottom - rowHeight <= scrollTop && !(tableBottom < scrollTop);
|
|
84
|
-
export const addSortableColumn = (rows, tableOrderStatus, onSorting
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
-
) => {
|
|
87
|
-
return React.Children.map(rows, (row, index) => {
|
|
88
|
-
if (index === 0) {
|
|
89
|
-
return /*#__PURE__*/React.cloneElement(React.Children.only(row), {
|
|
90
|
-
tableOrderStatus,
|
|
91
|
-
onSorting
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
return row;
|
|
95
|
-
});
|
|
96
|
-
};
|
|
97
|
-
export const isHeaderRowEnabled = (rows
|
|
98
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
-
) => {
|
|
100
|
-
if (!rows.length) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
// Ignored via go/ees005
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
-
const {
|
|
106
|
-
children
|
|
107
|
-
} = rows[0].props;
|
|
108
|
-
if (!children.length) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
if (children.length === 1) {
|
|
112
|
-
return children[0].type === TableHeader;
|
|
113
|
-
}
|
|
114
|
-
return children.every(node => node.type === TableHeader);
|
|
115
|
-
};
|
|
116
|
-
export const tableCanBeSticky = (node, children
|
|
117
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
118
|
-
) => {
|
|
119
|
-
return isHeaderRowEnabled(children) && node && node.firstChild && !hasRowspan(node.firstChild);
|
|
120
|
-
};
|
|
121
|
-
/**
|
|
122
|
-
* Fake left/right borders rendered as direct children of TABLE_CONTAINER
|
|
123
|
-
* (the non-scrolling parent of the horizontally scrolling TABLE_NODE_WRAPPER).
|
|
124
|
-
*
|
|
125
|
-
* The visible styling for these divs lives in `tableFakeBorderStyles`
|
|
126
|
-
* (`renderer/src/ui/Renderer/RendererStyleContainer.tsx`), which is applied
|
|
127
|
-
* when `isInsideSyncBlock` is true.
|
|
128
|
-
*
|
|
129
|
-
* Shared between `renderer/src/react/nodes/table.tsx` and
|
|
130
|
-
* `renderer/src/react/nodes/tableNew.tsx` so the two stay in sync.
|
|
131
|
-
*/
|
|
132
|
-
const TableFakeBorders = ({
|
|
133
|
-
isNumberColumnEnabled
|
|
134
|
-
}) => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
135
|
-
className: TableSharedCssClassName.TABLE_LEFT_BORDER,
|
|
136
|
-
"data-with-numbered-table": isNumberColumnEnabled ? 'true' : undefined,
|
|
137
|
-
"data-testid": "table-left-border"
|
|
138
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
139
|
-
className: TableSharedCssClassName.TABLE_RIGHT_BORDER,
|
|
140
|
-
"data-with-numbered-table": isNumberColumnEnabled ? 'true' : undefined,
|
|
141
|
-
"data-testid": "table-right-border"
|
|
142
|
-
}));
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Reads `nestedRendererType` from RendererContext and renders the fake left/right
|
|
146
|
-
* borders only when the current renderer is the nested renderer for a reference
|
|
147
|
-
* synced block.
|
|
148
|
-
*/
|
|
149
|
-
export const RefSyncBlockFakeBorders = ({
|
|
150
|
-
isNumberColumnEnabled
|
|
151
|
-
}) => {
|
|
152
|
-
const {
|
|
153
|
-
nestedRendererType
|
|
154
|
-
} = useRendererContext();
|
|
155
|
-
const isInsideOfRefSyncBlock = nestedRendererType === 'syncedBlock';
|
|
156
|
-
if (!isInsideOfRefSyncBlock) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
return /*#__PURE__*/React.createElement(TableFakeBorders, {
|
|
160
|
-
isNumberColumnEnabled: isNumberColumnEnabled
|
|
161
|
-
});
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* TableContainer renders tables using only CSS-based rules
|
|
166
|
-
*/
|
|
167
|
-
// Ignored via go/ees005
|
|
168
|
-
// eslint-disable-next-line @repo/internal/react/no-class-components
|
|
169
|
-
/**
|
|
170
|
-
*
|
|
171
|
-
*/
|
|
172
|
-
export class TableContainer extends React.Component {
|
|
173
|
-
constructor(...args) {
|
|
174
|
-
super(...args);
|
|
175
|
-
_defineProperty(this, "state", {
|
|
176
|
-
stickyMode: 'none',
|
|
177
|
-
wrapperWidth: 0,
|
|
178
|
-
headerRowHeight: 0
|
|
179
|
-
});
|
|
180
|
-
_defineProperty(this, "tableRef", /*#__PURE__*/React.createRef());
|
|
181
|
-
_defineProperty(this, "stickyHeaderRef", /*#__PURE__*/React.createRef());
|
|
182
|
-
_defineProperty(this, "stickyScrollbarRef", /*#__PURE__*/React.createRef());
|
|
183
|
-
// used for sync scroll + copying wrapper width to sticky header
|
|
184
|
-
_defineProperty(this, "stickyWrapperRef", /*#__PURE__*/React.createRef());
|
|
185
|
-
_defineProperty(this, "wrapperRef", /*#__PURE__*/React.createRef());
|
|
186
|
-
_defineProperty(this, "overflowParent", null);
|
|
187
|
-
_defineProperty(this, "updatedLayout", 'custom');
|
|
188
|
-
_defineProperty(this, "containerRef", null);
|
|
189
|
-
_defineProperty(this, "_isInsideNestedRenderer", null);
|
|
190
|
-
_defineProperty(this, "_isInsideTableCell", null);
|
|
191
|
-
// Stores the last computed style values from render() for use by applyNestedRendererTableFix().
|
|
192
|
-
// This avoids reading from the DOM which can be stale when React removes properties between renders.
|
|
193
|
-
_defineProperty(this, "lastComputedStyle", {});
|
|
194
|
-
_defineProperty(this, "resizeObserver", null);
|
|
195
|
-
_defineProperty(this, "applyResizerChange", entries => {
|
|
196
|
-
let wrapperWidth = this.state.wrapperWidth;
|
|
197
|
-
let headerRowHeight = this.state.headerRowHeight;
|
|
198
|
-
for (const entry of entries) {
|
|
199
|
-
if (entry.target === this.wrapperRef.current) {
|
|
200
|
-
wrapperWidth = entry.contentRect.width;
|
|
201
|
-
} else if (entry.target === this.stickyHeaderRef.current) {
|
|
202
|
-
headerRowHeight = Math.round(entry.contentRect.height);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (headerRowHeight !== this.state.headerRowHeight || wrapperWidth !== this.state.wrapperWidth) {
|
|
206
|
-
this.setState({
|
|
207
|
-
wrapperWidth,
|
|
208
|
-
headerRowHeight
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
/**
|
|
213
|
-
* Callback ref that captures the container DOM element and also forwards
|
|
214
|
-
* to the handleRef prop from the overflow shadow HOC.
|
|
215
|
-
*/
|
|
216
|
-
_defineProperty(this, "setContainerRef", el => {
|
|
217
|
-
if (this.containerRef !== el) {
|
|
218
|
-
this._isInsideNestedRenderer = null;
|
|
219
|
-
this._isInsideTableCell = null;
|
|
220
|
-
}
|
|
221
|
-
this.containerRef = el;
|
|
222
|
-
const {
|
|
223
|
-
handleRef
|
|
224
|
-
} = this.props;
|
|
225
|
-
if (typeof handleRef === 'function') {
|
|
226
|
-
handleRef(el);
|
|
227
|
-
} else if (handleRef && typeof handleRef === 'object') {
|
|
228
|
-
// Ignored via go/ees005
|
|
229
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
230
|
-
handleRef.current = el;
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
_defineProperty(this, "componentWillUnmount", () => {
|
|
234
|
-
if (this.overflowParent) {
|
|
235
|
-
// Ignored via go/ees005
|
|
236
|
-
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
237
|
-
this.overflowParent.removeEventListener('scroll', this.onScroll);
|
|
238
|
-
}
|
|
239
|
-
if (this.nextFrame) {
|
|
240
|
-
cancelAnimationFrame(this.nextFrame);
|
|
241
|
-
}
|
|
242
|
-
if (this.resizeObserver) {
|
|
243
|
-
this.resizeObserver.disconnect();
|
|
244
|
-
}
|
|
245
|
-
if (this.stickyScrollbar) {
|
|
246
|
-
this.stickyScrollbar.dispose();
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
_defineProperty(this, "getScrollTop", () => {
|
|
250
|
-
const {
|
|
251
|
-
stickyHeaders
|
|
252
|
-
} = this.props;
|
|
253
|
-
const offsetTop = stickyHeaders && stickyHeaders.offsetTop || 0;
|
|
254
|
-
return (this.overflowParent ? this.overflowParent.top : 0) + offsetTop;
|
|
255
|
-
});
|
|
256
|
-
_defineProperty(this, "updateSticky", () => {
|
|
257
|
-
const tableElem = this.tableRef.current;
|
|
258
|
-
const refElem = this.stickyHeaderRef.current;
|
|
259
|
-
if (!tableElem || !refElem) {
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const scrollTop = this.getScrollTop() + tableStickyPadding;
|
|
263
|
-
const tableTop = getRefTop(tableElem);
|
|
264
|
-
const tableBottom = tableTop + tableElem.clientHeight;
|
|
265
|
-
const shouldSticky = shouldHeaderStick(scrollTop, tableTop, tableBottom, refElem.clientHeight);
|
|
266
|
-
const shouldPin = shouldHeaderPinBottom(scrollTop, tableBottom, refElem.clientHeight);
|
|
267
|
-
let stickyMode = 'none';
|
|
268
|
-
if (shouldPin) {
|
|
269
|
-
stickyMode = 'pin-bottom';
|
|
270
|
-
} else if (shouldSticky) {
|
|
271
|
-
stickyMode = 'stick';
|
|
272
|
-
}
|
|
273
|
-
if (this.state.stickyMode !== stickyMode) {
|
|
274
|
-
this.setState({
|
|
275
|
-
stickyMode
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
this.nextFrame = undefined;
|
|
279
|
-
});
|
|
280
|
-
_defineProperty(this, "onScroll", () => {
|
|
281
|
-
if (!this.nextFrame) {
|
|
282
|
-
this.nextFrame = requestAnimationFrame(this.updateSticky);
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
_defineProperty(this, "onWrapperScrolled", () => {
|
|
286
|
-
if (!this.wrapperRef.current || !this.stickyWrapperRef.current) {
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
this.stickyWrapperRef.current.scrollLeft = this.wrapperRef.current.scrollLeft;
|
|
290
|
-
if (this.stickyScrollbarRef.current) {
|
|
291
|
-
this.stickyScrollbarRef.current.scrollLeft = this.wrapperRef.current.scrollLeft;
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
_defineProperty(this, "grabFirstRowRef", children => {
|
|
295
|
-
// Ignored via go/ees005
|
|
296
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
297
|
-
return React.Children.map(children || false, (child, idx) => {
|
|
298
|
-
if (idx === 0 && /*#__PURE__*/React.isValidElement(child)) {
|
|
299
|
-
return /*#__PURE__*/React.cloneElement(child, {
|
|
300
|
-
innerRef: this.stickyHeaderRef
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
return child;
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Checks if this table is inside a nested renderer (e.g. Include Page macro)
|
|
309
|
-
* by looking for multiple .ak-renderer-document ancestors in the DOM.
|
|
310
|
-
* The result is cached since a table's position in the DOM tree is stable after mount.
|
|
311
|
-
*/
|
|
312
|
-
isInsideNestedRenderer() {
|
|
313
|
-
if (this._isInsideNestedRenderer !== null) {
|
|
314
|
-
return this._isInsideNestedRenderer;
|
|
315
|
-
}
|
|
316
|
-
if (!this.containerRef) {
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
let docAncestorCount = 0;
|
|
320
|
-
let el = this.containerRef.parentElement;
|
|
321
|
-
while (el) {
|
|
322
|
-
if (el.classList.contains(RendererCssClassName.DOCUMENT)) {
|
|
323
|
-
docAncestorCount++;
|
|
324
|
-
if (docAncestorCount >= 2) {
|
|
325
|
-
this._isInsideNestedRenderer = true;
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
el = el.parentElement;
|
|
330
|
-
}
|
|
331
|
-
this._isInsideNestedRenderer = false;
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Checks if this table is inside a table cell in the DOM. The result is cached
|
|
337
|
-
* since a table's position in the DOM tree is stable after mount.
|
|
338
|
-
*/
|
|
339
|
-
isInsideTableCell() {
|
|
340
|
-
if (this._isInsideTableCell !== null) {
|
|
341
|
-
return this._isInsideTableCell;
|
|
342
|
-
}
|
|
343
|
-
if (!this.containerRef) {
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
this._isInsideTableCell = Boolean(this.containerRef.closest('td, th'));
|
|
347
|
-
return this._isInsideTableCell;
|
|
348
|
-
}
|
|
349
|
-
isInsideTableContext() {
|
|
350
|
-
return Boolean(this.props.isInsideOfTable || this.isInsideTableCell());
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* For tables inside nested renderers (e.g. Include Page macro), the parent
|
|
355
|
-
* renderer's CSS override forces width:100%!important and left:0!important
|
|
356
|
-
* which overrides the inline styles set by this component. Using
|
|
357
|
-
* style.setProperty with 'important' priority on inline styles beats
|
|
358
|
-
* stylesheet !important rules per the CSS cascade.
|
|
359
|
-
*
|
|
360
|
-
* Tables that are nested inside another table need to remain constrained by
|
|
361
|
-
* their table-cell context. Do not promote their width to inline !important,
|
|
362
|
-
* otherwise nested tables rendered through macros such as Excerpt Include can
|
|
363
|
-
* overflow and overlap adjacent cells.
|
|
364
|
-
*
|
|
365
|
-
* Top-level tables in nested renderers still preserve their saved width, but
|
|
366
|
-
* are capped to their containing block so wide tables do not escape constrained
|
|
367
|
-
* macro bodies such as Excerpt Include panels.
|
|
368
|
-
*
|
|
369
|
-
* Uses lastComputedStyle (populated during render) rather than reading from
|
|
370
|
-
* element.style, because React may remove properties from the DOM when their
|
|
371
|
-
* values transition to undefined between renders.
|
|
372
|
-
*
|
|
373
|
-
* `platform_nested_table_style_override_2` fixes a follow-up regression where a
|
|
374
|
-
* NON-resized table inside an Excerpt macro (not Excerpt Include) on a
|
|
375
|
-
* wide/full-width page shrinks to only show its first columns in the renderer.
|
|
376
|
-
* Inside a nested renderer (a macro body) a non-resized table's computed width
|
|
377
|
-
* is a default layout cap (760px/1800px, or a `cqw` length for full-page
|
|
378
|
-
* appearances) taken from the outer renderer's width context. That value does
|
|
379
|
-
* not match the macro's own box, so promoting it to inline `!important` makes
|
|
380
|
-
* the table collapse. Such tables have no author-chosen size to preserve, so
|
|
381
|
-
* under this gate we re-assert the parent stylesheet's intent (fill the box:
|
|
382
|
-
* `width: 100%; left: 0`) with inline `!important`.
|
|
383
|
-
*
|
|
384
|
-
* Explicitly resized tables (`tableNode.attrs.width` set) are excluded and keep
|
|
385
|
-
* the original PGXT-10226 behavior: their author-chosen width/left is promoted
|
|
386
|
-
* verbatim so the Include Page macro does not stretch or indent them. The
|
|
387
|
-
* table-cell short-circuit (PGXT-10294) above also still applies.
|
|
388
|
-
*/
|
|
389
|
-
applyNestedRendererTableFix() {
|
|
390
|
-
var _this$props$tableNode;
|
|
391
|
-
if (!this.containerRef || !fg('platform_nested_table_style_override')) {
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
if (!this.isInsideNestedRenderer() || this.isInsideTableContext()) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
const {
|
|
398
|
-
width,
|
|
399
|
-
left,
|
|
400
|
-
marginLeft
|
|
401
|
-
} = this.lastComputedStyle;
|
|
402
|
-
const style = this.containerRef.style;
|
|
403
|
-
|
|
404
|
-
// A table is "explicitly resized" when the author set a width on the node.
|
|
405
|
-
// Non-resized tables instead take a default layout cap (e.g. 760px/1800px)
|
|
406
|
-
// derived from the outer renderer's appearance.
|
|
407
|
-
const isExplicitlyResized = Boolean((_this$props$tableNode = this.props.tableNode) === null || _this$props$tableNode === void 0 ? void 0 : _this$props$tableNode.attrs.width);
|
|
408
|
-
if (!isExplicitlyResized && fg('platform_nested_table_style_override_2')) {
|
|
409
|
-
// PGXT-10421: For a NON-resized table inside a nested renderer (e.g. an
|
|
410
|
-
// Excerpt macro body), the computed width is a default layout cap
|
|
411
|
-
// (760px/1800px, or a `cqw` length for full-page appearances) taken from
|
|
412
|
-
// the *outer* renderer's width context. That value does not match the
|
|
413
|
-
// macro's own box, so promoting it to inline `!important` makes the table
|
|
414
|
-
// ignore the available space and collapse to its first columns.
|
|
415
|
-
//
|
|
416
|
-
// These tables have no author-chosen size to preserve, so re-assert the
|
|
417
|
-
// parent stylesheet's intent — fill the available box (`width: 100%`) —
|
|
418
|
-
// with inline `!important` priority so it survives React re-renders and
|
|
419
|
-
// wins over the stylesheet rule that leaks into nested renderers.
|
|
420
|
-
style.setProperty('width', '100%', 'important');
|
|
421
|
-
style.setProperty('max-width', '100%', 'important');
|
|
422
|
-
style.setProperty('left', '0', 'important');
|
|
423
|
-
style.setProperty('margin-left', '0', 'important');
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// PGXT-10226: An explicitly resized table inside a nested renderer (e.g.
|
|
428
|
-
// Include Page macro) must keep its author-chosen width and position. The
|
|
429
|
-
// parent stylesheet forces `width: 100% !important; left: 0 !important`,
|
|
430
|
-
// which would stretch and indent it, so we promote the component's own
|
|
431
|
-
// computed width/left with inline `!important` to win the cascade.
|
|
432
|
-
style.setProperty('width', width || 'auto', 'important');
|
|
433
|
-
style.setProperty('max-width', '100%', 'important');
|
|
434
|
-
style.setProperty('left', left || 'auto', 'important');
|
|
435
|
-
style.setProperty('margin-left', marginLeft || '0', 'important');
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* Starts observing table dimensions and wires sticky header/scrollbar behavior after mount.
|
|
439
|
-
*
|
|
440
|
-
* @example
|
|
441
|
-
*/
|
|
442
|
-
componentDidMount() {
|
|
443
|
-
this.resizeObserver = new ResizeObserver(this.applyResizerChange);
|
|
444
|
-
if (this.wrapperRef.current) {
|
|
445
|
-
this.resizeObserver.observe(this.wrapperRef.current);
|
|
446
|
-
}
|
|
447
|
-
if (this.stickyHeaderRef.current) {
|
|
448
|
-
this.resizeObserver.observe(this.stickyHeaderRef.current);
|
|
449
|
-
}
|
|
450
|
-
if (this.props.stickyHeaders) {
|
|
451
|
-
var _this$props$stickyHea;
|
|
452
|
-
this.overflowParent = 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);
|
|
453
|
-
// Ignored via go/ees005
|
|
454
|
-
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
455
|
-
this.overflowParent.addEventListener('scroll', this.onScroll);
|
|
456
|
-
}
|
|
457
|
-
if (this.wrapperRef.current && isStickyScrollbarEnabled(this.props.rendererAppearance)) {
|
|
458
|
-
this.stickyScrollbar = new TableStickyScrollbar(this.wrapperRef.current);
|
|
459
|
-
}
|
|
460
|
-
this.applyNestedRendererTableFix();
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Updates sticky header wiring and scroll synchronization after prop or state changes.
|
|
465
|
-
*
|
|
466
|
-
* @param prevProps
|
|
467
|
-
* @param prevState
|
|
468
|
-
* @example
|
|
469
|
-
*/
|
|
470
|
-
componentDidUpdate(prevProps, prevState) {
|
|
471
|
-
// toggling sticky headers visiblity
|
|
472
|
-
if (this.props.stickyHeaders && !this.overflowParent) {
|
|
473
|
-
var _this$props$stickyHea2;
|
|
474
|
-
this.overflowParent = 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);
|
|
475
|
-
} else if (!this.props.stickyHeaders && this.overflowParent) {
|
|
476
|
-
// Ignored via go/ees005
|
|
477
|
-
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
478
|
-
this.overflowParent.removeEventListener('scroll', this.onScroll);
|
|
479
|
-
this.overflowParent = null;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// offsetTop might have changed, re-position sticky header
|
|
483
|
-
if (this.props.stickyHeaders !== prevProps.stickyHeaders) {
|
|
484
|
-
this.updateSticky();
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// sync horizontal scroll in floating div when toggling modes
|
|
488
|
-
if (prevState.stickyMode !== this.state.stickyMode) {
|
|
489
|
-
this.onWrapperScrolled();
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// React re-applies the style prop on every render, which overwrites the
|
|
493
|
-
// !important priorities set at mount time. Re-apply after each update.
|
|
494
|
-
this.applyNestedRendererTableFix();
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Calculates the top offset used when the sticky header is pinned to the table bottom.
|
|
498
|
-
*/
|
|
499
|
-
get pinTop() {
|
|
500
|
-
if (!this.tableRef.current || !this.stickyHeaderRef.current) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
return this.tableRef.current.offsetHeight - this.stickyHeaderRef.current.offsetHeight + tableMarginTop - tableStickyPadding;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Determines whether sticky header positioning should include the default scroll root offset.
|
|
508
|
-
*/
|
|
509
|
-
get shouldAddOverflowParentOffsetTop_DO_NOT_USE() {
|
|
510
|
-
// IF the StickyHeaderConfig specifies that the default scroll root offsetTop should be added
|
|
511
|
-
// AND the StickyHeaderConfig specifies a default scroll root id
|
|
512
|
-
// AND the OverflowParent is the corresponding element
|
|
513
|
-
// THEN we should add the OverflowParent offset top (RETURN TRUE)
|
|
514
|
-
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;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* Resolves the top position for the sticky header based on the current sticky mode.
|
|
519
|
-
*/
|
|
520
|
-
get stickyTop() {
|
|
521
|
-
switch (this.state.stickyMode) {
|
|
522
|
-
case 'pin-bottom':
|
|
523
|
-
return this.pinTop;
|
|
524
|
-
case 'stick':
|
|
525
|
-
const offsetTop = this.props.stickyHeaders && this.props.stickyHeaders.offsetTop;
|
|
526
|
-
if (typeof offsetTop === 'number' && this.shouldAddOverflowParentOffsetTop_DO_NOT_USE) {
|
|
527
|
-
const overflowParentOffsetTop = this.overflowParent ? this.overflowParent.top : 0;
|
|
528
|
-
return offsetTop + overflowParentOffsetTop;
|
|
529
|
-
} else {
|
|
530
|
-
return offsetTop;
|
|
531
|
-
}
|
|
532
|
-
default:
|
|
533
|
-
return undefined;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Renders the table container, sticky header, table content, sticky scrollbar, and synced block borders.
|
|
539
|
-
*
|
|
540
|
-
* @example
|
|
541
|
-
*/
|
|
542
|
-
render() {
|
|
543
|
-
var _this$tableRef$curren;
|
|
544
|
-
const {
|
|
545
|
-
isNumberColumnEnabled,
|
|
546
|
-
layout,
|
|
547
|
-
columnWidths,
|
|
548
|
-
stickyHeaders,
|
|
549
|
-
tableNode,
|
|
550
|
-
rendererAppearance,
|
|
551
|
-
isInsideOfBlockNode,
|
|
552
|
-
isInsideOfNestedRenderer,
|
|
553
|
-
isInsideOfTable,
|
|
554
|
-
isinsideMultiBodiedExtension,
|
|
555
|
-
allowTableAlignment,
|
|
556
|
-
allowTableResizing,
|
|
557
|
-
isPresentational,
|
|
558
|
-
allowFixedColumnWidthOption
|
|
559
|
-
} = this.props;
|
|
560
|
-
const {
|
|
561
|
-
stickyMode
|
|
562
|
-
} = this.state;
|
|
563
|
-
const lineLengthFixedWidth = akEditorDefaultLayoutWidth;
|
|
564
|
-
let updatedLayout;
|
|
565
|
-
const fullPageRendererWidthCSS = editorExperiment('platform_editor_preview_panel_responsiveness', true, {
|
|
566
|
-
exposure: true
|
|
567
|
-
}) ? 'calc(100cqw - var(--ak-renderer--full-page-gutter) * 2)' : `100cqw - ${FullPagePadding}px * 2`;
|
|
568
|
-
const renderWidthCSS = rendererAppearance === 'full-page' ? fullPageRendererWidthCSS : `100cqw`;
|
|
569
|
-
const calcDefaultLayoutWidthByAppearance = (rendererAppearance, tableNode) => {
|
|
570
|
-
if (rendererAppearance === 'max' && !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width) && (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true))) {
|
|
571
|
-
return `min(${akEditorMaxWidthLayoutWidth}px, ${renderWidthCSS})`;
|
|
572
|
-
} else if (rendererAppearance === 'full-width' && !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width)) {
|
|
573
|
-
return `min(${akEditorFullWidthLayoutWidth}px, ${renderWidthCSS})`;
|
|
574
|
-
} else if (rendererAppearance === 'comment' && allowTableResizing && !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width)) {
|
|
575
|
-
return renderWidthCSS;
|
|
576
|
-
} else {
|
|
577
|
-
// custom width, or width mapped to breakpoint
|
|
578
|
-
const tableContainerWidth = getTableContainerWidth(tableNode);
|
|
579
|
-
return `min(${tableContainerWidth}px, ${renderWidthCSS})`;
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
const tableWidthCSS = calcDefaultLayoutWidthByAppearance(rendererAppearance, tableNode);
|
|
583
|
-
|
|
584
|
-
// Logic for table alignment in renderer
|
|
585
|
-
const isTableAlignStart = tableNode && tableNode.attrs && tableNode.attrs.layout === 'align-start' && allowTableAlignment;
|
|
586
|
-
const fullWidthLineLengthCSS = `min(${akEditorFullWidthLayoutWidth}px, ${renderWidthCSS})`;
|
|
587
|
-
const maxWidthLineLengthCSS = `min(${akEditorMaxWidthLayoutWidth}px, ${renderWidthCSS})`;
|
|
588
|
-
const isCommentAppearanceAndTableAlignmentEnabled = isCommentAppearance(rendererAppearance) && allowTableAlignment;
|
|
589
|
-
const lineLengthCSS = isFullWidthAppearance(rendererAppearance) ? fullWidthLineLengthCSS : isMaxWidthAppearance(rendererAppearance) && (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true)) ? maxWidthLineLengthCSS : isCommentAppearanceAndTableAlignmentEnabled ? renderWidthCSS : `${lineLengthFixedWidth}px`;
|
|
590
|
-
const tableWidthNew = getTableContainerWidth(tableNode);
|
|
591
|
-
const shouldCalculateLeftForAlignment = !isInsideOfBlockNode && !isInsideOfTable && isTableAlignStart && (isFullPageAppearance(rendererAppearance) && tableWidthNew <= lineLengthFixedWidth || isFullWidthAppearance(rendererAppearance) || isMaxWidthAppearance(rendererAppearance) && (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true)) || isCommentAppearanceAndTableAlignmentEnabled);
|
|
592
|
-
let leftCSS;
|
|
593
|
-
if (shouldCalculateLeftForAlignment) {
|
|
594
|
-
leftCSS = `(${tableWidthCSS} - ${lineLengthCSS}) / 2`;
|
|
595
|
-
}
|
|
596
|
-
if (!shouldCalculateLeftForAlignment && isFullPageAppearance(rendererAppearance)) {
|
|
597
|
-
// Note tableWidthCSS here is the renderer width
|
|
598
|
-
// When the screen is super wide we want table to break out.
|
|
599
|
-
// However if screen is smaller than 760px. We want table align to left.
|
|
600
|
-
leftCSS = `min(0px, ${lineLengthCSS} - ${tableWidthCSS}) / 2`;
|
|
601
|
-
}
|
|
602
|
-
const children = React.Children.toArray(this.props.children);
|
|
603
|
-
|
|
604
|
-
// Historically, tables in the full-width renderer had their layout set to 'default' which is deceiving.
|
|
605
|
-
// This check caters for those tables and helps with SSR logic
|
|
606
|
-
const isFullWidth = !(tableNode !== null && tableNode !== void 0 && tableNode.attrs.width) && rendererAppearance === 'full-width' && layout !== 'full-width';
|
|
607
|
-
if (isFullWidth) {
|
|
608
|
-
updatedLayout = 'full-width';
|
|
609
|
-
// if table has width explicity set, ensure SSR is handled
|
|
610
|
-
} else if (tableNode !== null && tableNode !== void 0 && tableNode.attrs.width) {
|
|
611
|
-
updatedLayout = 'custom';
|
|
612
|
-
} else {
|
|
613
|
-
updatedLayout = layout;
|
|
614
|
-
}
|
|
615
|
-
let finalTableContainerWidth = allowTableResizing ? tableWidthNew : 'inherit';
|
|
616
|
-
|
|
617
|
-
// We can only use CSS to determine the width when we have a known width in container.
|
|
618
|
-
// When appearance is full-page, full-width or comment we use CSS based width calculation.
|
|
619
|
-
// Otherwise it's fixed table width (customized width) or inherit.
|
|
620
|
-
if (rendererAppearance === 'full-page' || rendererAppearance === 'full-width' || rendererAppearance === 'max' && (expValEquals('editor_tinymce_full_width_mode', 'isEnabled', true) || expValEquals('confluence_max_width_content_appearance', 'isEnabled', true))) {
|
|
621
|
-
finalTableContainerWidth = allowTableResizing ? `calc(${tableWidthCSS})` : 'inherit';
|
|
622
|
-
}
|
|
623
|
-
if (rendererAppearance === 'comment' && allowTableResizing && !allowTableAlignment) {
|
|
624
|
-
// If table alignment is disabled and table width is akEditorDefaultLayoutWidth = 760,
|
|
625
|
-
// it is most likely a table created before "Support Table in Comments" FF was enabled
|
|
626
|
-
// and we would see a bug ED-24795. A table created before "Support Table in Comments",
|
|
627
|
-
// should inhirit the width of the renderer container.
|
|
628
|
-
|
|
629
|
-
// !NOTE: it a table resized to 760 is copied from 'full-page' editor and pasted in comment editor
|
|
630
|
-
// where (allowTableResizing && !allowTableAlignment), the table will loose 760px width.
|
|
631
|
-
finalTableContainerWidth = tableNode !== null && tableNode !== void 0 && tableNode.attrs.width && (tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.width) !== akEditorDefaultLayoutWidth ? `calc(${tableWidthCSS})` : 'inherit';
|
|
632
|
-
}
|
|
633
|
-
if (rendererAppearance === 'comment' && allowTableResizing && allowTableAlignment) {
|
|
634
|
-
// If table alignment is enabled and layout is not 'align-start' or 'center', we are loading a table that was
|
|
635
|
-
// created before "Support Table in Comments" FF was enabled. So the table should have the same width as renderer container
|
|
636
|
-
// instead of 760 that was set on tableNode when the table had been published.
|
|
637
|
-
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(${tableWidthCSS})` : 'inherit';
|
|
638
|
-
}
|
|
639
|
-
const isContentModeTable = isTableInContentMode({
|
|
640
|
-
tableNode,
|
|
641
|
-
isSupported: isContentModeSupported({
|
|
642
|
-
allowTableResizing,
|
|
643
|
-
rendererAppearance
|
|
644
|
-
}),
|
|
645
|
-
isTableNested: isInsideOfBlockNode || isInsideOfNestedRenderer || isInsideOfTable
|
|
646
|
-
}) && expValEquals('platform_editor_table_fit_to_content_auto_convert', 'isEnabled', true);
|
|
647
|
-
const style = {
|
|
648
|
-
...(isContentModeTable && {
|
|
649
|
-
'--renderer-table-max-width': renderWidthCSS
|
|
650
|
-
}),
|
|
651
|
-
width: finalTableContainerWidth,
|
|
652
|
-
left: leftCSS ? `calc(${leftCSS})` : undefined,
|
|
653
|
-
marginLeft: shouldCalculateLeftForAlignment && leftCSS !== undefined ? `calc(-1 * (${leftCSS}))` : undefined
|
|
654
|
-
};
|
|
655
|
-
|
|
656
|
-
// Store computed style values for applyNestedRendererTableFix() to use.
|
|
657
|
-
// Reading from props rather than the DOM ensures correctness when React
|
|
658
|
-
// removes properties (transitions from set to undefined) between renders.
|
|
659
|
-
this.lastComputedStyle = {
|
|
660
|
-
width: typeof style.width === 'number' ? `${style.width}px` : style.width,
|
|
661
|
-
left: style.left,
|
|
662
|
-
marginLeft: style.marginLeft
|
|
663
|
-
};
|
|
664
|
-
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
665
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
|
|
666
|
-
className: `${TableSharedCssClassName.TABLE_CONTAINER} ${this.props.shadowClassNames || ''}`,
|
|
667
|
-
"data-layout": updatedLayout,
|
|
668
|
-
"data-testid": "table-container",
|
|
669
|
-
ref: this.setContainerRef,
|
|
670
|
-
style: style
|
|
671
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
|
|
672
|
-
}, isStickyScrollbarEnabled(this.props.rendererAppearance) && /*#__PURE__*/React.createElement("div", {
|
|
673
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
|
|
674
|
-
className: TableSharedCssClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_TOP,
|
|
675
|
-
"data-testid": "sticky-scrollbar-sentinel-top"
|
|
676
|
-
}), stickyHeaders && tableNode && !isContentModeTable && tableCanBeSticky(tableNode, children) && /*#__PURE__*/React.createElement(StickyTable, {
|
|
677
|
-
isNumberColumnEnabled: isNumberColumnEnabled,
|
|
678
|
-
tableWidth: "inherit",
|
|
679
|
-
renderWidth: 0,
|
|
680
|
-
layout: layout,
|
|
681
|
-
handleRef: this.props.handleRef,
|
|
682
|
-
shadowClassNames: this.props.shadowClassNames,
|
|
683
|
-
top: this.stickyTop,
|
|
684
|
-
mode: stickyMode,
|
|
685
|
-
innerRef: this.stickyWrapperRef,
|
|
686
|
-
wrapperWidth: this.state.wrapperWidth,
|
|
687
|
-
columnWidths: columnWidths,
|
|
688
|
-
rowHeight: this.state.headerRowHeight,
|
|
689
|
-
tableNode: tableNode,
|
|
690
|
-
rendererAppearance: rendererAppearance,
|
|
691
|
-
allowTableResizing: allowTableResizing,
|
|
692
|
-
fixTableSSRResizing: true,
|
|
693
|
-
allowFixedColumnWidthOption: allowFixedColumnWidthOption
|
|
694
|
-
}, [children && children[0]]), /*#__PURE__*/React.createElement("div", {
|
|
695
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
|
|
696
|
-
className: TableSharedCssClassName.TABLE_NODE_WRAPPER,
|
|
697
|
-
ref: this.wrapperRef,
|
|
698
|
-
"data-number-column": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.isNumberColumnEnabled,
|
|
699
|
-
"data-layout": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.layout,
|
|
700
|
-
"data-autosize": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.__autoSize,
|
|
701
|
-
"data-table-local-id": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.localId,
|
|
702
|
-
"data-table-width": tableNode === null || tableNode === void 0 ? void 0 : tableNode.attrs.width,
|
|
703
|
-
"data-vc": "table-node-wrapper",
|
|
704
|
-
onScroll: this.props.stickyHeaders && this.onWrapperScrolled
|
|
705
|
-
}, /*#__PURE__*/React.createElement(Table, {
|
|
706
|
-
innerRef: this.tableRef,
|
|
707
|
-
columnWidths: columnWidths,
|
|
708
|
-
layout: layout,
|
|
709
|
-
renderWidth: 0,
|
|
710
|
-
isNumberColumnEnabled: isNumberColumnEnabled,
|
|
711
|
-
tableNode: tableNode,
|
|
712
|
-
rendererAppearance: rendererAppearance,
|
|
713
|
-
isInsideOfBlockNode: isInsideOfBlockNode,
|
|
714
|
-
isInsideOfNestedRenderer: isInsideOfNestedRenderer,
|
|
715
|
-
isInsideOfTable: isInsideOfTable,
|
|
716
|
-
isinsideMultiBodiedExtension: isinsideMultiBodiedExtension,
|
|
717
|
-
allowTableResizing: allowTableResizing,
|
|
718
|
-
isPresentational: isPresentational,
|
|
719
|
-
allowFixedColumnWidthOption: allowFixedColumnWidthOption
|
|
720
|
-
}, this.grabFirstRowRef(children))), isStickyScrollbarEnabled(this.props.rendererAppearance) && /*#__PURE__*/React.createElement("div", {
|
|
721
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
|
|
722
|
-
className: `${TableSharedCssClassName.TABLE_STICKY_SCROLLBAR_CONTAINER}${fg('confluence_frontend_table_scrollbar_ttvc_fix') ? '-view-page' : ''}`,
|
|
723
|
-
ref: this.stickyScrollbarRef,
|
|
724
|
-
"data-vc": "table-sticky-scrollbar-container",
|
|
725
|
-
style: fg('confluence_frontend_table_scrollbar_ttvc_fix') ? {
|
|
726
|
-
...stickyContainerBaseStyles,
|
|
727
|
-
...stickyContainerAdditionalStyles
|
|
728
|
-
} : stickyContainerBaseStyles
|
|
729
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
730
|
-
style: {
|
|
731
|
-
width: 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,
|
|
732
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
|
|
733
|
-
height: '100%'
|
|
734
|
-
}
|
|
735
|
-
})), isStickyScrollbarEnabled(this.props.rendererAppearance) && /*#__PURE__*/React.createElement("div", {
|
|
736
|
-
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
|
|
737
|
-
className: TableSharedCssClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_BOTTOM,
|
|
738
|
-
"data-testid": "sticky-scrollbar-sentinel-bottom"
|
|
739
|
-
}), /*#__PURE__*/React.createElement(RefSyncBlockFakeBorders, {
|
|
740
|
-
isNumberColumnEnabled: isNumberColumnEnabled
|
|
741
|
-
})));
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
const getCellEdgePropsByCellOffset = (tableNode, isNumberColumnEnabled) => {
|
|
745
|
-
const cellEdgePropsByCellOffset = new Map();
|
|
746
|
-
const cellRightByCellOffset = new Map();
|
|
747
|
-
const occupiedGrid = [];
|
|
748
|
-
let tableWidth = 0;
|
|
749
|
-
let cellOffset = 0;
|
|
750
|
-
tableNode.forEach((rowNode, _rowOffset, rowIndex) => {
|
|
751
|
-
var _occupiedGrid$rowInde;
|
|
752
|
-
occupiedGrid[rowIndex] = (_occupiedGrid$rowInde = occupiedGrid[rowIndex]) !== null && _occupiedGrid$rowInde !== void 0 ? _occupiedGrid$rowInde : [];
|
|
753
|
-
let columnIndex = 0;
|
|
754
|
-
cellOffset += 1;
|
|
755
|
-
rowNode.forEach(cellNode => {
|
|
756
|
-
while (occupiedGrid[rowIndex][columnIndex]) {
|
|
757
|
-
columnIndex += 1;
|
|
758
|
-
}
|
|
759
|
-
const colspan = cellNode.attrs.colspan || 1;
|
|
760
|
-
const rowspan = cellNode.attrs.rowspan || 1;
|
|
761
|
-
const cellLeft = columnIndex;
|
|
762
|
-
const cellRight = cellLeft + colspan;
|
|
763
|
-
const cellTop = rowIndex;
|
|
764
|
-
const cellBottom = cellTop + rowspan;
|
|
765
|
-
for (let row = cellTop; row < cellBottom; row += 1) {
|
|
766
|
-
var _occupiedGrid$row;
|
|
767
|
-
occupiedGrid[row] = (_occupiedGrid$row = occupiedGrid[row]) !== null && _occupiedGrid$row !== void 0 ? _occupiedGrid$row : [];
|
|
768
|
-
for (let column = cellLeft; column < cellRight; column += 1) {
|
|
769
|
-
occupiedGrid[row][column] = true;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
tableWidth = Math.max(tableWidth, cellRight);
|
|
773
|
-
cellRightByCellOffset.set(cellOffset, cellRight);
|
|
774
|
-
cellEdgePropsByCellOffset.set(cellOffset, {
|
|
775
|
-
reachesBottom: cellBottom >= tableNode.childCount,
|
|
776
|
-
// With number column enabled, PM column 0 is not visually leftmost and must not get `data-reaches-left`.
|
|
777
|
-
reachesLeft: cellLeft === 0 && !isNumberColumnEnabled,
|
|
778
|
-
reachesRight: false,
|
|
779
|
-
reachesTop: cellTop === 0
|
|
780
|
-
});
|
|
781
|
-
columnIndex = cellRight;
|
|
782
|
-
cellOffset += cellNode.nodeSize;
|
|
783
|
-
});
|
|
784
|
-
cellOffset += 1;
|
|
785
|
-
});
|
|
786
|
-
cellRightByCellOffset.forEach((cellRight, currentCellOffset) => {
|
|
787
|
-
const edgeProps = cellEdgePropsByCellOffset.get(currentCellOffset);
|
|
788
|
-
if (edgeProps) {
|
|
789
|
-
edgeProps.reachesRight = cellRight >= tableWidth;
|
|
790
|
-
}
|
|
791
|
-
});
|
|
792
|
-
return cellEdgePropsByCellOffset;
|
|
793
|
-
};
|
|
794
|
-
const isRenderedTableCell = element => {
|
|
795
|
-
const {
|
|
796
|
-
nodeType
|
|
797
|
-
} = element.props;
|
|
798
|
-
return nodeType === 'tableCell' || nodeType === 'tableHeader' || element.type === TableCell || element.type === TableHeader;
|
|
799
|
-
};
|
|
800
|
-
const addTableCellEdgePropsThroughWrappers = (rows, tableNode, isNumberColumnEnabled) => {
|
|
801
|
-
try {
|
|
802
|
-
if (!tableNode) {
|
|
803
|
-
return rows;
|
|
804
|
-
}
|
|
805
|
-
const cellEdgePropsByCellOffset = getCellEdgePropsByCellOffset(tableNode, isNumberColumnEnabled);
|
|
806
|
-
let cellOffset = 0;
|
|
807
|
-
return React.Children.map(rows, (row, rowIndex) => {
|
|
808
|
-
const rowNode = tableNode.child(rowIndex);
|
|
809
|
-
cellOffset += 1;
|
|
810
|
-
let cellIndex = 0;
|
|
811
|
-
const addEdgePropsToRenderedCells = element => {
|
|
812
|
-
if (isRenderedTableCell(element)) {
|
|
813
|
-
const cellNode = rowNode.child(cellIndex);
|
|
814
|
-
const edgeProps = cellEdgePropsByCellOffset.get(cellOffset);
|
|
815
|
-
cellIndex += 1;
|
|
816
|
-
cellOffset += cellNode.nodeSize;
|
|
817
|
-
return edgeProps ? /*#__PURE__*/React.cloneElement(element, edgeProps) : element;
|
|
818
|
-
}
|
|
819
|
-
const {
|
|
820
|
-
children
|
|
821
|
-
} = element.props;
|
|
822
|
-
if (children === undefined) {
|
|
823
|
-
return element;
|
|
824
|
-
}
|
|
825
|
-
const childrenWithEdgeProps = React.Children.map(children, child => /*#__PURE__*/React.isValidElement(child) ? addEdgePropsToRenderedCells(child) : child);
|
|
826
|
-
return /*#__PURE__*/React.cloneElement(element, undefined, childrenWithEdgeProps);
|
|
827
|
-
};
|
|
828
|
-
const rowWithEdgeProps = addEdgePropsToRenderedCells(row);
|
|
829
|
-
if (cellIndex !== rowNode.childCount) {
|
|
830
|
-
throw new Error('Rendered table row does not match its document node');
|
|
831
|
-
}
|
|
832
|
-
cellOffset += 1;
|
|
833
|
-
return rowWithEdgeProps;
|
|
834
|
-
});
|
|
835
|
-
} catch {
|
|
836
|
-
// Renderer can receive malformed historical ADF. If the table shape cannot
|
|
837
|
-
// be described safely, keep rendering without rounded edge metadata.
|
|
838
|
-
return rows;
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Processes table children before passing them to the styled table container.
|
|
844
|
-
*/
|
|
845
|
-
// Ignored via go/ees005
|
|
846
|
-
// eslint-disable-next-line @repo/internal/react/no-class-components
|
|
847
|
-
export class TableProcessorWithContainerStyles extends React.Component {
|
|
848
|
-
constructor(...args) {
|
|
849
|
-
super(...args);
|
|
850
|
-
_defineProperty(this, "state", {
|
|
851
|
-
tableOrderStatus: undefined
|
|
852
|
-
});
|
|
853
|
-
// adds sortable + re-orders children
|
|
854
|
-
_defineProperty(this, "addSortableColumn", childrenArray => {
|
|
855
|
-
const {
|
|
856
|
-
tableNode,
|
|
857
|
-
allowColumnSorting,
|
|
858
|
-
smartCardStorage
|
|
859
|
-
} = this.props;
|
|
860
|
-
const {
|
|
861
|
-
tableOrderStatus
|
|
862
|
-
} = this.state;
|
|
863
|
-
if (allowColumnSorting && isHeaderRowEnabled(childrenArray) && tableNode && !hasMergedCell(tableNode)) {
|
|
864
|
-
return addSortableColumn(orderChildren(childrenArray, tableNode, smartCardStorage, tableOrderStatus), tableOrderStatus, this.changeSortOrder);
|
|
865
|
-
}
|
|
866
|
-
return childrenArray;
|
|
867
|
-
});
|
|
868
|
-
_defineProperty(this, "changeSortOrder", (columnIndex, sortOrder) => {
|
|
869
|
-
this.setState({
|
|
870
|
-
tableOrderStatus: {
|
|
871
|
-
columnIndex,
|
|
872
|
-
order: sortOrder
|
|
873
|
-
}
|
|
874
|
-
});
|
|
875
|
-
});
|
|
876
|
-
// Ignored via go/ees005
|
|
877
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
878
|
-
_defineProperty(this, "addNumberColumnIndexes", rows => {
|
|
879
|
-
const {
|
|
880
|
-
isNumberColumnEnabled
|
|
881
|
-
} = this.props;
|
|
882
|
-
const headerRowEnabled = isHeaderRowEnabled(rows);
|
|
883
|
-
const lastRowIndex = React.Children.count(rows) - 1;
|
|
884
|
-
return React.Children.map(rows, (row, index) => {
|
|
885
|
-
return /*#__PURE__*/React.cloneElement(React.Children.only(row), {
|
|
886
|
-
isNumberColumnEnabled,
|
|
887
|
-
index: headerRowEnabled ? index === 0 ? '' : index : index + 1,
|
|
888
|
-
isFirstRow: index === 0,
|
|
889
|
-
isLastRow: index === lastRowIndex
|
|
890
|
-
});
|
|
891
|
-
});
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
/**
|
|
895
|
-
* Renders processed table children inside the table container.
|
|
896
|
-
*
|
|
897
|
-
* @example
|
|
898
|
-
*/
|
|
899
|
-
render() {
|
|
900
|
-
const {
|
|
901
|
-
allowColumnSorting,
|
|
902
|
-
allowFixedColumnWidthOption,
|
|
903
|
-
allowTableAlignment,
|
|
904
|
-
allowTableResizing,
|
|
905
|
-
children,
|
|
906
|
-
columnWidths,
|
|
907
|
-
disableTableOverflowShadow,
|
|
908
|
-
handleRef,
|
|
909
|
-
isinsideMultiBodiedExtension,
|
|
910
|
-
isInsideOfBlockNode,
|
|
911
|
-
isInsideOfNestedRenderer,
|
|
912
|
-
isInsideOfTable,
|
|
913
|
-
isNumberColumnEnabled,
|
|
914
|
-
isPresentational,
|
|
915
|
-
layout,
|
|
916
|
-
rendererAppearance,
|
|
917
|
-
renderWidth,
|
|
918
|
-
shadowClassNames,
|
|
919
|
-
smartCardStorage,
|
|
920
|
-
stickyHeaders,
|
|
921
|
-
tabIndex,
|
|
922
|
-
tableNode
|
|
923
|
-
} = this.props;
|
|
924
|
-
if (!children) {
|
|
925
|
-
return null;
|
|
926
|
-
}
|
|
927
|
-
const childrenArray = React.Children.toArray(children);
|
|
928
|
-
const childrenWithTableEdgeProps = expValEquals('platform_editor_table_q4_loveability', 'isEnabled', true) ? addTableCellEdgePropsThroughWrappers(childrenArray, tableNode, isNumberColumnEnabled) : childrenArray;
|
|
929
|
-
const orderedChildren = compose(this.addNumberColumnIndexes, this.addSortableColumn
|
|
930
|
-
// @ts-expect-error TS2345: Argument of type '(ReactChild | ReactFragment | ReactPortal)[]' is not assignable to parameter of type 'ReactElement<any, string | JSXElementConstructor<any>>[]'
|
|
931
|
-
)(childrenWithTableEdgeProps);
|
|
932
|
-
return /*#__PURE__*/React.createElement(TableContainer, {
|
|
933
|
-
allowColumnSorting: allowColumnSorting,
|
|
934
|
-
allowFixedColumnWidthOption: allowFixedColumnWidthOption,
|
|
935
|
-
allowTableAlignment: allowTableAlignment,
|
|
936
|
-
allowTableResizing: allowTableResizing,
|
|
937
|
-
columnWidths: columnWidths,
|
|
938
|
-
disableTableOverflowShadow: disableTableOverflowShadow,
|
|
939
|
-
handleRef: handleRef,
|
|
940
|
-
isinsideMultiBodiedExtension: isinsideMultiBodiedExtension,
|
|
941
|
-
isInsideOfBlockNode: isInsideOfBlockNode,
|
|
942
|
-
isInsideOfNestedRenderer: isInsideOfNestedRenderer,
|
|
943
|
-
isInsideOfTable: isInsideOfTable,
|
|
944
|
-
isNumberColumnEnabled: isNumberColumnEnabled,
|
|
945
|
-
isPresentational: isPresentational,
|
|
946
|
-
layout: layout,
|
|
947
|
-
rendererAppearance: rendererAppearance,
|
|
948
|
-
renderWidth: renderWidth,
|
|
949
|
-
shadowClassNames: shadowClassNames,
|
|
950
|
-
smartCardStorage: smartCardStorage,
|
|
951
|
-
stickyHeaders: stickyHeaders,
|
|
952
|
-
tabIndex: tabIndex,
|
|
953
|
-
tableNode: tableNode
|
|
954
|
-
}, orderedChildren);
|
|
955
|
-
}
|
|
956
|
-
}
|