@atlaskit/editor-plugin-table 24.2.2 → 24.2.3
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 +13 -0
- package/dist/cjs/pm-plugins/utils/column-controls.js +119 -2
- package/dist/cjs/pm-plugins/utils/dom.js +20 -1
- package/dist/cjs/pm-plugins/utils/row-controls.js +43 -1
- package/dist/cjs/pm-plugins/utils/selection.js +61 -1
- package/dist/cjs/ui/DragHandle/index.js +10 -2
- package/dist/cjs/ui/FloatingInsertButton/getPopupOptions.js +19 -5
- package/dist/cjs/ui/FloatingInsertButton/index.js +37 -3
- package/dist/cjs/ui/TableFloatingColumnControls/ColumnControls/index.js +25 -15
- package/dist/cjs/ui/TableFloatingColumnControls/index.js +1 -1
- package/dist/cjs/ui/TableFloatingControls/RowControls/DragControls.js +28 -12
- package/dist/cjs/ui/common-styles.js +6 -1
- package/dist/cjs/ui/event-handlers.js +47 -7
- package/dist/es2019/pm-plugins/utils/column-controls.js +114 -2
- package/dist/es2019/pm-plugins/utils/dom.js +19 -0
- package/dist/es2019/pm-plugins/utils/row-controls.js +42 -0
- package/dist/es2019/pm-plugins/utils/selection.js +60 -0
- package/dist/es2019/ui/DragHandle/index.js +10 -2
- package/dist/es2019/ui/FloatingInsertButton/getPopupOptions.js +24 -5
- package/dist/es2019/ui/FloatingInsertButton/index.js +35 -3
- package/dist/es2019/ui/TableFloatingColumnControls/ColumnControls/index.js +26 -16
- package/dist/es2019/ui/TableFloatingColumnControls/index.js +2 -2
- package/dist/es2019/ui/TableFloatingControls/RowControls/DragControls.js +29 -12
- package/dist/es2019/ui/common-styles.js +26 -1
- package/dist/es2019/ui/event-handlers.js +47 -7
- package/dist/esm/pm-plugins/utils/column-controls.js +118 -1
- package/dist/esm/pm-plugins/utils/dom.js +19 -0
- package/dist/esm/pm-plugins/utils/row-controls.js +42 -0
- package/dist/esm/pm-plugins/utils/selection.js +60 -0
- package/dist/esm/ui/DragHandle/index.js +10 -2
- package/dist/esm/ui/FloatingInsertButton/getPopupOptions.js +19 -5
- package/dist/esm/ui/FloatingInsertButton/index.js +37 -3
- package/dist/esm/ui/TableFloatingColumnControls/ColumnControls/index.js +27 -17
- package/dist/esm/ui/TableFloatingColumnControls/index.js +2 -2
- package/dist/esm/ui/TableFloatingControls/RowControls/DragControls.js +30 -14
- package/dist/esm/ui/common-styles.js +7 -2
- package/dist/esm/ui/event-handlers.js +49 -9
- package/dist/types/pm-plugins/utils/column-controls.d.ts +35 -1
- package/dist/types/pm-plugins/utils/dom.d.ts +10 -0
- package/dist/types/pm-plugins/utils/row-controls.d.ts +16 -0
- package/dist/types/pm-plugins/utils/selection.d.ts +13 -0
- package/dist/types/ui/DragHandle/index.d.ts +1 -1
- package/dist/types/ui/FloatingInsertButton/getPopupOptions.d.ts +1 -1
- package/dist/types/ui/FloatingInsertButton/index.d.ts +1 -0
- package/package.json +3 -3
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { TableMap } from '@atlaskit/editor-tables/table-map';
|
|
2
|
+
import { findTableClosestToPos, getSelectionRect } from '@atlaskit/editor-tables/utils';
|
|
1
3
|
export const getSelectedColumnIndexes = selectionRect => {
|
|
2
4
|
const columnIndexes = [];
|
|
3
5
|
for (let i = selectionRect.left; i < selectionRect.right; i++) {
|
|
@@ -11,4 +13,62 @@ export const getSelectedRowIndexes = selectionRect => {
|
|
|
11
13
|
rowIndexes.push(i);
|
|
12
14
|
}
|
|
13
15
|
return rowIndexes;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Treats a column as fully selected when its first-row area is covered by a horizontal merge and
|
|
20
|
+
* the explicit `CellSelection` starts below that merged cell. `CellSelection.isColSelection()`
|
|
21
|
+
* returns `false` in this case because the selection does not include the merged first-row cell.
|
|
22
|
+
*/
|
|
23
|
+
export const isColumnSelectionWithMergedFirstRow = selection => {
|
|
24
|
+
const rect = getSelectionRect(selection);
|
|
25
|
+
if (!rect) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const table = findTableClosestToPos(selection.$anchorCell);
|
|
29
|
+
if (!table) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const map = TableMap.get(table.node);
|
|
33
|
+
if (rect.bottom !== map.height || rect.top === 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
for (let col = rect.left; col < rect.right; col++) {
|
|
37
|
+
const topCellRect = map.findCell(map.map[col]);
|
|
38
|
+
const isHorizontallyMerged = topCellRect.right - topCellRect.left > 1;
|
|
39
|
+
const bridgesGapAboveSelection = topCellRect.bottom >= rect.top;
|
|
40
|
+
if (!isHorizontallyMerged || !bridgesGapAboveSelection) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Treats a row as fully selected when its first-column area is covered by a vertical merge and the
|
|
49
|
+
* explicit `CellSelection` starts to the right of that merged cell. `CellSelection.isRowSelection()`
|
|
50
|
+
* returns `false` in this case because the selection does not include the merged first-column cell.
|
|
51
|
+
*/
|
|
52
|
+
export const isRowSelectionWithMergedFirstColumn = selection => {
|
|
53
|
+
const rect = getSelectionRect(selection);
|
|
54
|
+
if (!rect) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const table = findTableClosestToPos(selection.$anchorCell);
|
|
58
|
+
if (!table) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const map = TableMap.get(table.node);
|
|
62
|
+
if (rect.right !== map.width || rect.left === 0) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
for (let row = rect.top; row < rect.bottom; row++) {
|
|
66
|
+
const leftCellRect = map.findCell(map.map[row * map.width]);
|
|
67
|
+
const isVerticallyMerged = leftCellRect.bottom - leftCellRect.top > 1;
|
|
68
|
+
const bridgesGapLeftOfSelection = leftCellRect.right >= rect.left;
|
|
69
|
+
if (!isVerticallyMerged || !bridgesGapLeftOfSelection) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
14
74
|
};
|
|
@@ -216,12 +216,20 @@ const DragHandleComponent = ({
|
|
|
216
216
|
// return focus to editor so copying table selections whilst still works, i cannot call e.preventDefault in a mousemove event as this stops dragstart events from firing
|
|
217
217
|
// -> this is bad for a11y but is the current standard new copy/paste keyboard shortcuts should be introduced instead
|
|
218
218
|
editorView.focus();
|
|
219
|
-
|
|
219
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
220
|
+
toggleDragMenu && toggleDragMenu('mouse', e, indexes[0]);
|
|
221
|
+
} else {
|
|
222
|
+
toggleDragMenu && toggleDragMenu('mouse', e);
|
|
223
|
+
}
|
|
220
224
|
},
|
|
221
225
|
onClick: onClick,
|
|
222
226
|
onKeyDown: e => {
|
|
223
227
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
224
|
-
|
|
228
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
229
|
+
toggleDragMenu && toggleDragMenu('keyboard', undefined, indexes[0]);
|
|
230
|
+
} else {
|
|
231
|
+
toggleDragMenu && toggleDragMenu('keyboard');
|
|
232
|
+
}
|
|
225
233
|
}
|
|
226
234
|
}
|
|
227
235
|
}, appearance !== 'placeholder' ?
|
|
@@ -30,7 +30,9 @@ function getRowOptions(index) {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
function getColumnOptions(index, tableContainer, hasNumberedColumns
|
|
33
|
+
function getColumnOptions(index, tableContainer, hasNumberedColumns,
|
|
34
|
+
// Distance between the target cell top and table top, used when anchoring to a lower-row cell.
|
|
35
|
+
verticalOffsetCorrection = 0) {
|
|
34
36
|
const options = {
|
|
35
37
|
alignX: 'end',
|
|
36
38
|
alignY: 'top',
|
|
@@ -41,16 +43,32 @@ function getColumnOptions(index, tableContainer, hasNumberedColumns) {
|
|
|
41
43
|
// we should always set the InsertButton on the start,
|
|
42
44
|
// considering the offset from the first column
|
|
43
45
|
onPositionCalculated(position) {
|
|
46
|
+
// Move the popup upward whether the offset parent provides `top` or `bottom`.
|
|
47
|
+
let verticalCorrection;
|
|
48
|
+
if (verticalOffsetCorrection) {
|
|
49
|
+
if (position.top !== undefined) {
|
|
50
|
+
verticalCorrection = {
|
|
51
|
+
top: position.top - verticalOffsetCorrection
|
|
52
|
+
};
|
|
53
|
+
} else if (position.bottom !== undefined) {
|
|
54
|
+
verticalCorrection = {
|
|
55
|
+
bottom: position.bottom + verticalOffsetCorrection
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
44
59
|
const {
|
|
45
60
|
left
|
|
46
61
|
} = position;
|
|
47
62
|
if (!left) {
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
return {
|
|
64
|
+
...position,
|
|
65
|
+
...verticalCorrection
|
|
66
|
+
};
|
|
50
67
|
}
|
|
51
68
|
if (index === 0) {
|
|
52
69
|
return {
|
|
53
70
|
...position,
|
|
71
|
+
...verticalCorrection,
|
|
54
72
|
left: hasNumberedColumns ? HORIZONTAL_ALIGN_NUMBERED_COLUMN_BUTTON : HORIZONTAL_ALIGN_COLUMN_BUTTON
|
|
55
73
|
};
|
|
56
74
|
}
|
|
@@ -59,6 +77,7 @@ function getColumnOptions(index, tableContainer, hasNumberedColumns) {
|
|
|
59
77
|
const rect = tableContainer ? tableContainer.getBoundingClientRect() : null;
|
|
60
78
|
return {
|
|
61
79
|
...position,
|
|
80
|
+
...verticalCorrection,
|
|
62
81
|
left: rect && left > rect.width ? rect.width : left
|
|
63
82
|
};
|
|
64
83
|
}
|
|
@@ -75,10 +94,10 @@ function getColumnOptions(index, tableContainer, hasNumberedColumns) {
|
|
|
75
94
|
}
|
|
76
95
|
return options;
|
|
77
96
|
}
|
|
78
|
-
function getPopupOptions(direction, index, hasNumberedColumns, tableContainer) {
|
|
97
|
+
function getPopupOptions(direction, index, hasNumberedColumns, tableContainer, verticalOffsetCorrection = 0) {
|
|
79
98
|
switch (direction) {
|
|
80
99
|
case 'column':
|
|
81
|
-
return getColumnOptions(index, tableContainer, hasNumberedColumns);
|
|
100
|
+
return getColumnOptions(index, tableContainer, hasNumberedColumns, verticalOffsetCorrection);
|
|
82
101
|
case 'row':
|
|
83
102
|
return getRowOptions(index);
|
|
84
103
|
default:
|
|
@@ -13,6 +13,7 @@ import { findTable } from '@atlaskit/editor-tables/utils';
|
|
|
13
13
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
14
14
|
import { insertColumnWithAnalytics, insertRowWithAnalytics } from '../../pm-plugins/commands/commands-with-analytics';
|
|
15
15
|
import { checkIfNumberColumnEnabled } from '../../pm-plugins/utils/nodes';
|
|
16
|
+
import { isColumnSelectionWithMergedFirstRow, isRowSelectionWithMergedFirstColumn } from '../../pm-plugins/utils/selection';
|
|
16
17
|
import { TableCssClassName as ClassName } from '../../types';
|
|
17
18
|
import getPopupOptions from './getPopupOptions';
|
|
18
19
|
import { DragAndDropInsertButton } from './InsertButton';
|
|
@@ -65,8 +66,14 @@ export class FloatingInsertButton extends React.Component {
|
|
|
65
66
|
tr
|
|
66
67
|
}
|
|
67
68
|
} = editorView;
|
|
68
|
-
if (
|
|
69
|
-
|
|
69
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
70
|
+
if (tr.selection instanceof CellSelection && (tr.selection.isColSelection() || tr.selection.isRowSelection() || isColumnSelectionWithMergedFirstRow(tr.selection) || isRowSelectionWithMergedFirstColumn(tr.selection))) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
if (tr.selection instanceof CellSelection && (tr.selection.isColSelection() || tr.selection.isRowSelection())) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
70
77
|
}
|
|
71
78
|
const tablePos = findTable(tr.selection);
|
|
72
79
|
if (!tablePos) {
|
|
@@ -120,6 +127,15 @@ export class FloatingInsertButton extends React.Component {
|
|
|
120
127
|
const index = type === 'column' ? insertColumnButtonIndex : insertRowButtonIndex;
|
|
121
128
|
const hasNumberedColumns = checkIfNumberColumnEnabled(editorView.state.selection);
|
|
122
129
|
|
|
130
|
+
// If row 0 has a colspan, anchor to a lower row for the real column boundary (X),
|
|
131
|
+
// then move back up to the table top (Y):
|
|
132
|
+
// row 0: [ colspan=2 ]
|
|
133
|
+
// row 1: [ col 1 ][ col 2 ] ← anchor here for X
|
|
134
|
+
// ↑ button should render at row 0/table top
|
|
135
|
+
let verticalOffsetCorrection = 0;
|
|
136
|
+
if (type === 'column' && expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
137
|
+
verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
|
|
138
|
+
}
|
|
123
139
|
// Fixed the 'add column button' not visible issue when sticky header is enabled
|
|
124
140
|
// By setting the Popup z-index higher than the sticky header z-index ( common-styles.ts tr.sticky)
|
|
125
141
|
// Only when inserting a column, otherwise set to undefined
|
|
@@ -137,7 +153,7 @@ export class FloatingInsertButton extends React.Component {
|
|
|
137
153
|
allowOutOfBounds: true
|
|
138
154
|
// Ignored via go/ees005
|
|
139
155
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
140
|
-
}, getPopupOptions(type, index, hasNumberedColumns, tableContainerWrapper), {
|
|
156
|
+
}, getPopupOptions(type, index, hasNumberedColumns, tableContainerWrapper, verticalOffsetCorrection), {
|
|
141
157
|
zIndex: zIndex
|
|
142
158
|
}), /*#__PURE__*/React.createElement(DragAndDropInsertButton, {
|
|
143
159
|
type: type,
|
|
@@ -147,6 +163,18 @@ export class FloatingInsertButton extends React.Component {
|
|
|
147
163
|
isChromelessEditor: isChromelessEditor
|
|
148
164
|
}));
|
|
149
165
|
}
|
|
166
|
+
|
|
167
|
+
// Finds a row where `columnIndex` has real left/right cell boundaries.
|
|
168
|
+
findRowWithUnmergedColumn(tableMap, columnIndex) {
|
|
169
|
+
for (let row = 0; row < tableMap.height; row++) {
|
|
170
|
+
const pos = tableMap.map[row * tableMap.width + columnIndex];
|
|
171
|
+
const rect = tableMap.findCell(pos);
|
|
172
|
+
if (rect.left === columnIndex && rect.right === columnIndex + 1) {
|
|
173
|
+
return row;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
150
178
|
getCellPosition(type, tableNode) {
|
|
151
179
|
const {
|
|
152
180
|
insertColumnButtonIndex,
|
|
@@ -163,6 +191,10 @@ export class FloatingInsertButton extends React.Component {
|
|
|
163
191
|
if (columnIndex > tableMap.width - 1) {
|
|
164
192
|
return null;
|
|
165
193
|
}
|
|
194
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
195
|
+
const rowWithRealColumn = this.findRowWithUnmergedColumn(tableMap, columnIndex);
|
|
196
|
+
return rowWithRealColumn === null ? null : tableMap.positionAt(rowWithRealColumn, columnIndex, tableNode);
|
|
197
|
+
}
|
|
166
198
|
return tableMap.positionAt(0, columnIndex, tableNode);
|
|
167
199
|
} else {
|
|
168
200
|
// This condition is to make typescript happy.
|
|
@@ -8,15 +8,29 @@ import { akEditorTableNumberColumnWidth } from '@atlaskit/editor-shared-styles';
|
|
|
8
8
|
import { CellSelection } from '@atlaskit/editor-tables';
|
|
9
9
|
import { getSelectionRect } from '@atlaskit/editor-tables/utils';
|
|
10
10
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
11
|
-
import { clearHoverSelection,
|
|
11
|
+
import { clearHoverSelection, hoverCell, hoverColumns, selectColumn, selectColumns, toggleActiveTableMenu } from '../../../pm-plugins/commands';
|
|
12
12
|
import { toggleDragMenuWithAnalytics } from '../../../pm-plugins/drag-and-drop/commands-with-analytics';
|
|
13
13
|
import { getPluginState as getTablePluginState } from '../../../pm-plugins/plugin-factory';
|
|
14
14
|
import { getRowsParams } from '../../../pm-plugins/utils/row-controls';
|
|
15
|
-
import { getSelectedColumnIndexes } from '../../../pm-plugins/utils/selection';
|
|
15
|
+
import { getSelectedColumnIndexes, isColumnSelectionWithMergedFirstRow } from '../../../pm-plugins/utils/selection';
|
|
16
16
|
import { TableCssClassName as ClassName } from '../../../types';
|
|
17
17
|
import { DragHandle } from '../../DragHandle';
|
|
18
18
|
const getSelectedColumns = selection => {
|
|
19
|
-
if (selection instanceof CellSelection
|
|
19
|
+
if (!(selection instanceof CellSelection)) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
23
|
+
// New behaviour: also treat a column selection that sits below a merged first-row cell as a
|
|
24
|
+
// full column selection.
|
|
25
|
+
if (!selection.isColSelection() && !isColumnSelectionWithMergedFirstRow(selection)) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
const rect = getSelectionRect(selection);
|
|
29
|
+
return rect ? getSelectedColumnIndexes(rect) : [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Old behaviour: only standard column selections are recognised.
|
|
33
|
+
if (selection.isColSelection()) {
|
|
20
34
|
const rect = getSelectionRect(selection);
|
|
21
35
|
if (!rect) {
|
|
22
36
|
return [];
|
|
@@ -125,27 +139,23 @@ export const ColumnControls = ({
|
|
|
125
139
|
clearHoverSelection()(state, dispatch);
|
|
126
140
|
}
|
|
127
141
|
}, [editorView, tableActive]);
|
|
128
|
-
const toggleDragMenuHandler = useCallback((trigger, event) => {
|
|
142
|
+
const toggleDragMenuHandler = useCallback((trigger, event, handleIndex) => {
|
|
129
143
|
var _api$analytics2;
|
|
144
|
+
if (event !== null && event !== void 0 && event.shiftKey) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
130
147
|
const {
|
|
131
148
|
state,
|
|
132
149
|
dispatch
|
|
133
150
|
} = editorView;
|
|
134
|
-
if (event !== null && event !== void 0 && event.shiftKey) {
|
|
135
|
-
// Shift-click extends the selection rather than toggling the menu, but the
|
|
136
|
-
// open drag menu would otherwise stay anchored to a stale column. Close it here
|
|
137
|
-
// for the updated menu (legacy menu closes via outside-click on its dropdown).
|
|
138
|
-
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
139
|
-
api === null || api === void 0 ? void 0 : api.core.actions.execute(closeActiveTableMenu(api));
|
|
140
|
-
}
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
151
|
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
144
|
-
|
|
152
|
+
// Use the clicked handle index because `hoveredCell` can point at a merged cell's first column.
|
|
153
|
+
const targetColIndex = handleIndex !== null && handleIndex !== void 0 ? handleIndex : colIndex;
|
|
154
|
+
if (targetColIndex !== undefined && api) {
|
|
145
155
|
const {
|
|
146
156
|
activeTableMenu: currentActiveTableMenu
|
|
147
157
|
} = getTablePluginState(state);
|
|
148
|
-
const isSameActiveMenu = (currentActiveTableMenu === null || currentActiveTableMenu === void 0 ? void 0 : currentActiveTableMenu.type) === 'column' && currentActiveTableMenu.index ===
|
|
158
|
+
const isSameActiveMenu = (currentActiveTableMenu === null || currentActiveTableMenu === void 0 ? void 0 : currentActiveTableMenu.type) === 'column' && currentActiveTableMenu.index === targetColIndex;
|
|
149
159
|
api.core.actions.execute(({
|
|
150
160
|
tr
|
|
151
161
|
}) => {
|
|
@@ -164,7 +174,7 @@ export const ColumnControls = ({
|
|
|
164
174
|
}
|
|
165
175
|
toggleActiveTableMenu({
|
|
166
176
|
type: 'column',
|
|
167
|
-
index:
|
|
177
|
+
index: targetColIndex,
|
|
168
178
|
openedBy: trigger
|
|
169
179
|
}, currentActiveTableMenu, api)({
|
|
170
180
|
tr
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
3
3
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
4
|
-
import { getColumnsWidths } from '../../pm-plugins/utils/column-controls';
|
|
4
|
+
import { getColumnsWidths, getColumnsWidthsWithMergedCells } from '../../pm-plugins/utils/column-controls';
|
|
5
5
|
import { containsHeaderColumn } from '../../pm-plugins/utils/nodes';
|
|
6
6
|
import { getRowHeights } from '../../pm-plugins/utils/row-controls';
|
|
7
7
|
import { isNativeStickySupported } from '../../pm-plugins/utils/sticky-header';
|
|
@@ -63,7 +63,7 @@ const TableFloatingColumnControls = ({
|
|
|
63
63
|
if (!tableRef || !tableActive || isResizing) {
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
|
-
const colWidths = getColumnsWidths(editorView);
|
|
66
|
+
const colWidths = expValEquals('platform_editor_table_menu_updates', 'isEnabled', true) ? getColumnsWidthsWithMergedCells(editorView) : getColumnsWidths(editorView);
|
|
67
67
|
if (stickyTop) {
|
|
68
68
|
var _containerRef$current;
|
|
69
69
|
const columnControlTopOffsetFromParent = '-12px';
|
|
@@ -7,17 +7,31 @@ import { CellSelection } from '@atlaskit/editor-tables';
|
|
|
7
7
|
import { getSelectionRect } from '@atlaskit/editor-tables/utils';
|
|
8
8
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
9
9
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
10
|
-
import { clearHoverSelection,
|
|
10
|
+
import { clearHoverSelection, toggleActiveTableMenu } from '../../../pm-plugins/commands';
|
|
11
11
|
import { toggleDragMenuWithAnalytics } from '../../../pm-plugins/drag-and-drop/commands-with-analytics';
|
|
12
12
|
import { getPluginState as getTablePluginState } from '../../../pm-plugins/plugin-factory';
|
|
13
13
|
import { getRowHeights, getRowsParams } from '../../../pm-plugins/utils/row-controls';
|
|
14
|
-
import { getSelectedRowIndexes } from '../../../pm-plugins/utils/selection';
|
|
14
|
+
import { getSelectedRowIndexes, isRowSelectionWithMergedFirstColumn } from '../../../pm-plugins/utils/selection';
|
|
15
15
|
import { TableCssClassName as ClassName } from '../../../types';
|
|
16
16
|
import { dragRowControlsWidth, dropTargetExtendedWidth } from '../../consts';
|
|
17
17
|
import { DragHandle } from '../../DragHandle';
|
|
18
18
|
import RowDropTarget from '../RowDropTarget';
|
|
19
19
|
const getSelectedRows = selection => {
|
|
20
|
-
if (selection instanceof CellSelection
|
|
20
|
+
if (!(selection instanceof CellSelection)) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
24
|
+
// New behaviour: also treat a row selection that sits to the right of a merged first-column
|
|
25
|
+
// cell as a full row selection.
|
|
26
|
+
if (!selection.isRowSelection() && !isRowSelectionWithMergedFirstColumn(selection)) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const rect = getSelectionRect(selection);
|
|
30
|
+
return rect ? getSelectedRowIndexes(rect) : [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Old behaviour: only standard row selections are recognised.
|
|
34
|
+
if (selection.isRowSelection()) {
|
|
21
35
|
const rect = getSelectionRect(selection);
|
|
22
36
|
if (!rect) {
|
|
23
37
|
return [];
|
|
@@ -78,18 +92,14 @@ export const DragControls = ({
|
|
|
78
92
|
}
|
|
79
93
|
});
|
|
80
94
|
}, [editorView]);
|
|
81
|
-
const toggleDragMenuHandler = useCallback((trigger, event) => {
|
|
95
|
+
const toggleDragMenuHandler = useCallback((trigger, event, handleIndex) => {
|
|
82
96
|
var _api$analytics2;
|
|
83
97
|
if (event !== null && event !== void 0 && event.shiftKey) {
|
|
84
|
-
// Shift-click extends the selection rather than toggling the menu, but the
|
|
85
|
-
// open drag menu would otherwise stay anchored to a stale row. Close it here
|
|
86
|
-
// for the updated menu (legacy menu closes via outside-click on its dropdown).
|
|
87
|
-
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
88
|
-
api === null || api === void 0 ? void 0 : api.core.actions.execute(closeActiveTableMenu(api));
|
|
89
|
-
}
|
|
90
98
|
return;
|
|
91
99
|
}
|
|
92
|
-
|
|
100
|
+
|
|
101
|
+
// Use the clicked handle index because `hoveredCell` can point at a merged cell's first row.
|
|
102
|
+
const rowIndex = handleIndex !== null && handleIndex !== void 0 ? handleIndex : hoveredCell === null || hoveredCell === void 0 ? void 0 : hoveredCell.rowIndex;
|
|
93
103
|
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
94
104
|
if (rowIndex !== undefined && api) {
|
|
95
105
|
const {
|
|
@@ -230,10 +240,17 @@ export const DragControls = ({
|
|
|
230
240
|
if (!tableActive) {
|
|
231
241
|
return null;
|
|
232
242
|
}
|
|
243
|
+
const selectedAppearance = isRowSelected && isEntireTableSelected ? isInDanger ? 'danger' : 'selected' : 'placeholder';
|
|
233
244
|
|
|
234
245
|
// placeholder / selected need to always render at least one handle
|
|
235
246
|
// so it can be focused via keyboard shortcuts
|
|
236
|
-
|
|
247
|
+
const selectedGridRow = expValEquals('platform_editor_table_menu_updates', 'isEnabled', true) ?
|
|
248
|
+
// New behaviour: always position the placeholder in the first row to avoid an invalid
|
|
249
|
+
// `NaN / span 0` grid placement (which makes the handle disappear) when no rows are selected.
|
|
250
|
+
selectedAppearance === 'placeholder' ? '1 / span 1' : `${selectedRowIndexes[0] + 1} / span ${selectedRowIndexes.length}` :
|
|
251
|
+
// Old behaviour.
|
|
252
|
+
`${selectedRowIndexes[0] + 1} / span ${selectedRowIndexes.length}`;
|
|
253
|
+
handles.push(generateHandleByType('selected', selectedAppearance, selectedGridRow, selectedRowIndexes));
|
|
237
254
|
if (hoveredCell && isTableHovered && rowIndex !== undefined && !selectedRowIndexes.includes(rowIndex) && rowIndex < rowHeights.length) {
|
|
238
255
|
handles.push(generateHandleByType('hover', 'default', `${rowIndex + 1} / span 1`, rowIndexes));
|
|
239
256
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
7
7
|
import { css } from '@emotion/react';
|
|
8
8
|
import { getBrowserInfo } from '@atlaskit/editor-common/browser';
|
|
9
|
-
import { ANCHOR_VARIABLE_NAME, tableMarginTop, tableSharedStyle, TableSharedCssClassName } from '@atlaskit/editor-common/styles';
|
|
9
|
+
import { ANCHOR_VARIABLE_NAME, DRAG_HANDLE_WIDTH, tableMarginTop, tableSharedStyle, TableSharedCssClassName } from '@atlaskit/editor-common/styles';
|
|
10
10
|
import { SORTABLE_COLUMN_ICON_CLASSNAME } from '@atlaskit/editor-common/table';
|
|
11
11
|
import { akEditorSelectedNodeClassName, akEditorSmallZIndex, akEditorStickyHeaderZIndex, akEditorTableCellOnStickyHeaderZIndex, akEditorTableNumberColumnWidth, akEditorTableToolbarSize, akEditorUnitZIndex, getSelectionStyles, MAX_BROWSER_SCROLLBAR_HEIGHT, SelectionStyle, relativeSizeToBaseFontSize, relativeFontSizeToBase16, akEditorSelectedBorderColor } from '@atlaskit/editor-shared-styles';
|
|
12
12
|
import { akEditorTableContainerBg } from '@atlaskit/editor-shared-styles/consts';
|
|
@@ -425,6 +425,31 @@ const baseTableStylesWithoutSharedStyle = props => css`
|
|
|
425
425
|
width: ${insertColumnButtonOffset + 1}px;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
+
/* use :before element to hide table row insert dots when legacy table sticky header is activated */
|
|
429
|
+
${expValEquals('platform_editor_table_col_insert', 'isEnabled', true) ?
|
|
430
|
+
// Mask geometry mirrors the drag-handle wrapper geometry from
|
|
431
|
+
// editor-plugin-block-controls/src/ui/drag-handle.tsx
|
|
432
|
+
// (`buttonWrapperStyles`):
|
|
433
|
+
// height = paddingTop (=calc(space.400 - 1px)) + paddingBottom (=space.200) + DRAG_HANDLE_HEIGHT
|
|
434
|
+
// width = DRAG_HANDLE_WIDTH + paddingRight('space.150')
|
|
435
|
+
`.${ClassName.TABLE_CONTAINER}.${ClassName.TABLE_STICKY}:has(tr.sticky)::before {
|
|
436
|
+
content: ' ';
|
|
437
|
+
position: sticky;
|
|
438
|
+
pointer-events: none;
|
|
439
|
+
top: 0;
|
|
440
|
+
float: left;
|
|
441
|
+
transform: translateX(calc(-1 * (${DRAG_HANDLE_WIDTH}px + ${"var(--ds-space-150, 12px)"})));
|
|
442
|
+
margin-bottom: calc(-1 * (${"var(--ds-space-400, 32px)"} - 1px + ${"var(--ds-space-200, 16px)"} + ${"var(--ds-space-300, 24px)"}));
|
|
443
|
+
height: calc(${"var(--ds-space-400, 32px)"} - 1px + ${"var(--ds-space-200, 16px)"} + ${"var(--ds-space-300, 24px)"});
|
|
444
|
+
width: calc(${DRAG_HANDLE_WIDTH}px + ${"var(--ds-space-150, 12px)"});
|
|
445
|
+
background: linear-gradient(
|
|
446
|
+
to bottom,
|
|
447
|
+
${expValEquals('platform_editor_nest_table_in_panel', 'isEnabled', true) ? `var(${akEditorTableContainerBg}, ${"var(--ds-surface, #FFFFFF)"})` : "var(--ds-surface, #FFFFFF)"} 90%,
|
|
448
|
+
transparent
|
|
449
|
+
);
|
|
450
|
+
z-index: ${rowControlsZIndex + 5};
|
|
451
|
+
}` : ``}
|
|
452
|
+
|
|
428
453
|
/* To fix jumpiness caused in Chrome Browsers for sticky headers */
|
|
429
454
|
.${ClassName.TABLE_STICKY} .sticky + tr {
|
|
430
455
|
min-height: 0px;
|
|
@@ -16,9 +16,10 @@ import { getPluginState as getResizePluginState } from '../pm-plugins/table-resi
|
|
|
16
16
|
import { deleteColumns } from '../pm-plugins/transforms/delete-columns';
|
|
17
17
|
import { deleteRows } from '../pm-plugins/transforms/delete-rows';
|
|
18
18
|
import { getSelectedCellInfo } from '../pm-plugins/utils/analytics';
|
|
19
|
-
import { convertHTMLCellIndexToColumnIndex, getColumnIndexMappedToColumnIndexInFirstRow } from '../pm-plugins/utils/column-controls';
|
|
20
|
-
import { getColumnOrRowIndex, getMousePositionHorizontalRelativeByElement, getMousePositionVerticalRelativeByElement, hasResizeHandler, isCell, isColumnControlsDecorations, isCornerButton, isDragColumnFloatingInsertDot, isDragCornerButton, isDragRowFloatingInsertDot, isInsertRowButton, isResizeHandleDecoration, isRowControlsButton, isTableContainerOrWrapper, isTableControlsButton } from '../pm-plugins/utils/dom';
|
|
19
|
+
import { convertHTMLCellIndexToColumnIndex, getColumnIndexByMousePosition, getColumnIndexMappedToColumnIndexInFirstRow } from '../pm-plugins/utils/column-controls';
|
|
20
|
+
import { getColumnOrRowIndex, getIndexAttributeSourceElement, getMousePositionHorizontalRelativeByElement, getMousePositionVerticalRelativeByElement, hasResizeHandler, isCell, isColumnControlsDecorations, isCornerButton, isDragColumnFloatingInsertDot, isDragCornerButton, isDragRowFloatingInsertDot, isInsertRowButton, isResizeHandleDecoration, isRowControlsButton, isTableContainerOrWrapper, isTableControlsButton } from '../pm-plugins/utils/dom';
|
|
21
21
|
import { getAllowAddColumnCustomStep } from '../pm-plugins/utils/get-allow-add-column-custom-step';
|
|
22
|
+
import { getRowIndexByMousePosition } from '../pm-plugins/utils/row-controls';
|
|
22
23
|
import { TableCssClassName as ClassName, RESIZE_HANDLE_AREA_DECORATION_GAP } from '../types';
|
|
23
24
|
import { TABLE_MENU_SELECTOR } from './TableMenu/shared/consts';
|
|
24
25
|
const isFocusingCalendar = event => event instanceof FocusEvent && event.relatedTarget instanceof HTMLElement && event.relatedTarget.getAttribute('aria-label') === 'calendar';
|
|
@@ -292,6 +293,19 @@ const handleMouseMoveDebounce = nodeViewPortalProviderAPI => rafSchedule((view,
|
|
|
292
293
|
return false;
|
|
293
294
|
}
|
|
294
295
|
const element = event.target;
|
|
296
|
+
const isTableMenuUpdatesEnabled = expValEquals('platform_editor_table_menu_updates', 'isEnabled', true);
|
|
297
|
+
|
|
298
|
+
// Spanned cells need mouse-position-based hover indexes; normal cells already report them.
|
|
299
|
+
if (isTableMenuUpdatesEnabled) {
|
|
300
|
+
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
301
|
+
const tableCell = isElementInTableCell(element);
|
|
302
|
+
if (tableCell && (tableCell.rowSpan > 1 || tableCell.colSpan > 1)) {
|
|
303
|
+
const dragDropState = getDragDropPluginState(view.state);
|
|
304
|
+
if (dragDropState && !dragDropState.isDragging) {
|
|
305
|
+
trackCellLocation(view, event);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
295
309
|
if (isColumnControlsDecorations(element) || isDragColumnFloatingInsertDot(element)) {
|
|
296
310
|
const {
|
|
297
311
|
state,
|
|
@@ -300,7 +314,8 @@ const handleMouseMoveDebounce = nodeViewPortalProviderAPI => rafSchedule((view,
|
|
|
300
314
|
const {
|
|
301
315
|
insertColumnButtonIndex
|
|
302
316
|
} = getPluginState(state);
|
|
303
|
-
const
|
|
317
|
+
const indexSourceElement = isTableMenuUpdatesEnabled ? getIndexAttributeSourceElement(element) : element;
|
|
318
|
+
const [startIndex, endIndex] = getColumnOrRowIndex(indexSourceElement);
|
|
304
319
|
const positionColumn = getMousePositionHorizontalRelativeByElement(event, offsetX, undefined) === 'right' ? endIndex : startIndex;
|
|
305
320
|
if (positionColumn !== insertColumnButtonIndex) {
|
|
306
321
|
return showInsertColumnButton(positionColumn)(state, dispatch);
|
|
@@ -314,7 +329,8 @@ const handleMouseMoveDebounce = nodeViewPortalProviderAPI => rafSchedule((view,
|
|
|
314
329
|
const {
|
|
315
330
|
insertRowButtonIndex
|
|
316
331
|
} = getPluginState(state);
|
|
317
|
-
const
|
|
332
|
+
const indexSourceElement = isTableMenuUpdatesEnabled ? getIndexAttributeSourceElement(element) : element;
|
|
333
|
+
const [startIndex, endIndex] = getColumnOrRowIndex(indexSourceElement);
|
|
318
334
|
const positionRow = getMousePositionVerticalRelativeByElement(event) === 'bottom' ? endIndex : startIndex;
|
|
319
335
|
if (positionRow !== insertRowButtonIndex) {
|
|
320
336
|
return showInsertRowButton(positionRow)(state, dispatch);
|
|
@@ -462,7 +478,8 @@ export const whenTableInFocus = (eventHandler, pluginInjectionApi) => (view, mou
|
|
|
462
478
|
return eventHandler(view, mouseEvent);
|
|
463
479
|
};
|
|
464
480
|
const trackCellLocation = (view, mouseEvent) => {
|
|
465
|
-
var _tableElement$dataset;
|
|
481
|
+
var _tableElement$dataset, _getRowIndexByMousePo;
|
|
482
|
+
const isTableMenuUpdatesEnabled = expValEquals('platform_editor_table_menu_updates', 'isEnabled', true);
|
|
466
483
|
const target = mouseEvent.target;
|
|
467
484
|
// Ignored via go/ees005
|
|
468
485
|
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
@@ -488,13 +505,36 @@ const trackCellLocation = (view, mouseEvent) => {
|
|
|
488
505
|
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
489
506
|
const rowElement = closestElement(target, 'tr');
|
|
490
507
|
const htmlRowIndex = rowElement && rowElement.rowIndex;
|
|
508
|
+
const rowIndex = maybeTableCell.rowSpan > 1 && mouseEvent instanceof MouseEvent && isTableMenuUpdatesEnabled ? (_getRowIndexByMousePo = getRowIndexByMousePosition(tableRef, mouseEvent, {
|
|
509
|
+
startIndex: htmlRowIndex,
|
|
510
|
+
endIndex: htmlRowIndex + maybeTableCell.rowSpan
|
|
511
|
+
})) !== null && _getRowIndexByMousePo !== void 0 ? _getRowIndexByMousePo : htmlRowIndex : htmlRowIndex;
|
|
491
512
|
const tableMap = tableNode && TableMap.get(tableNode);
|
|
492
513
|
let colIndex = htmlColIndex;
|
|
493
514
|
if (tableMap) {
|
|
494
515
|
const convertedColIndex = convertHTMLCellIndexToColumnIndex(htmlColIndex, htmlRowIndex, tableMap);
|
|
495
|
-
|
|
516
|
+
if (isTableMenuUpdatesEnabled) {
|
|
517
|
+
// New behaviour: the column controls grid renders one track per visual column
|
|
518
|
+
// (`getColumnsWidthsWithMergedCells`), so the hover index must be the visual column
|
|
519
|
+
// index — not the first-row-cell index. Mapping to the first-row cell would collapse
|
|
520
|
+
// every column under a merged first-row cell to the same index, leaving the drag handle
|
|
521
|
+
// stuck when hovering cells in other rows. For a colspanned cell, refine the converted
|
|
522
|
+
// (left-most) visual column to the exact visual column under the pointer.
|
|
523
|
+
colIndex = convertedColIndex;
|
|
524
|
+
if (maybeTableCell.colSpan > 1 && mouseEvent instanceof MouseEvent) {
|
|
525
|
+
var _getColumnIndexByMous;
|
|
526
|
+
colIndex = (_getColumnIndexByMous = getColumnIndexByMousePosition(maybeTableCell, mouseEvent, {
|
|
527
|
+
startIndex: convertedColIndex,
|
|
528
|
+
endIndex: convertedColIndex + maybeTableCell.colSpan
|
|
529
|
+
})) !== null && _getColumnIndexByMous !== void 0 ? _getColumnIndexByMous : convertedColIndex;
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
// Old behaviour: the legacy grid renders one track per first-row cell, so snap to the
|
|
533
|
+
// first-row column index (a merged first-row cell collapses to a single column track).
|
|
534
|
+
colIndex = getColumnIndexMappedToColumnIndexInFirstRow(convertedColIndex, htmlRowIndex, tableMap);
|
|
535
|
+
}
|
|
496
536
|
}
|
|
497
|
-
hoverCell(
|
|
537
|
+
hoverCell(rowIndex, colIndex)(view.state, view.dispatch);
|
|
498
538
|
};
|
|
499
539
|
export const withCellTracking = eventHandler => (view, mouseEvent) => {
|
|
500
540
|
if (getDragDropPluginState(view.state) && !getDragDropPluginState(view.state).isDragging) {
|