@atlaskit/editor-plugin-table 24.2.2 → 24.2.4
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 +23 -0
- package/dist/cjs/pm-plugins/commands/active-table-menu.js +14 -5
- package/dist/cjs/pm-plugins/drag-and-drop/plugin.js +3 -1
- package/dist/cjs/pm-plugins/main.js +3 -1
- package/dist/cjs/pm-plugins/utils/column-controls.js +119 -2
- package/dist/cjs/pm-plugins/utils/dom.js +23 -1
- package/dist/cjs/pm-plugins/utils/row-controls.js +43 -1
- package/dist/cjs/pm-plugins/utils/selection.js +74 -1
- package/dist/cjs/ui/DragHandle/index.js +46 -4
- package/dist/cjs/ui/FloatingInsertButton/getPopupOptions.js +19 -5
- package/dist/cjs/ui/FloatingInsertButton/index.js +37 -3
- package/dist/cjs/ui/FloatingTableMenu/index.js +4 -7
- package/dist/cjs/ui/TableFloatingColumnControls/ColumnControls/index.js +26 -15
- package/dist/cjs/ui/TableFloatingColumnControls/index.js +1 -1
- package/dist/cjs/ui/TableFloatingControls/RowControls/DragControls.js +29 -12
- package/dist/cjs/ui/common-styles.js +6 -1
- package/dist/cjs/ui/event-handlers.js +61 -8
- package/dist/es2019/pm-plugins/commands/active-table-menu.js +14 -5
- package/dist/es2019/pm-plugins/drag-and-drop/plugin.js +3 -1
- package/dist/es2019/pm-plugins/main.js +1 -1
- package/dist/es2019/pm-plugins/utils/column-controls.js +114 -2
- package/dist/es2019/pm-plugins/utils/dom.js +20 -0
- package/dist/es2019/pm-plugins/utils/row-controls.js +42 -0
- package/dist/es2019/pm-plugins/utils/selection.js +73 -0
- package/dist/es2019/ui/DragHandle/index.js +47 -4
- package/dist/es2019/ui/FloatingInsertButton/getPopupOptions.js +24 -5
- package/dist/es2019/ui/FloatingInsertButton/index.js +35 -3
- package/dist/es2019/ui/FloatingTableMenu/index.js +4 -7
- package/dist/es2019/ui/TableFloatingColumnControls/ColumnControls/index.js +27 -16
- package/dist/es2019/ui/TableFloatingColumnControls/index.js +2 -2
- package/dist/es2019/ui/TableFloatingControls/RowControls/DragControls.js +30 -12
- package/dist/es2019/ui/common-styles.js +26 -1
- package/dist/es2019/ui/event-handlers.js +61 -8
- package/dist/esm/pm-plugins/commands/active-table-menu.js +14 -5
- package/dist/esm/pm-plugins/drag-and-drop/plugin.js +3 -1
- package/dist/esm/pm-plugins/main.js +3 -1
- package/dist/esm/pm-plugins/utils/column-controls.js +118 -1
- package/dist/esm/pm-plugins/utils/dom.js +22 -0
- package/dist/esm/pm-plugins/utils/row-controls.js +42 -0
- package/dist/esm/pm-plugins/utils/selection.js +73 -0
- package/dist/esm/ui/DragHandle/index.js +47 -5
- package/dist/esm/ui/FloatingInsertButton/getPopupOptions.js +19 -5
- package/dist/esm/ui/FloatingInsertButton/index.js +37 -3
- package/dist/esm/ui/FloatingTableMenu/index.js +4 -7
- package/dist/esm/ui/TableFloatingColumnControls/ColumnControls/index.js +28 -17
- package/dist/esm/ui/TableFloatingColumnControls/index.js +2 -2
- package/dist/esm/ui/TableFloatingControls/RowControls/DragControls.js +31 -14
- package/dist/esm/ui/common-styles.js +7 -2
- package/dist/esm/ui/event-handlers.js +63 -10
- package/dist/types/pm-plugins/commands/active-table-menu.d.ts +3 -1
- package/dist/types/pm-plugins/utils/column-controls.d.ts +35 -1
- package/dist/types/pm-plugins/utils/dom.d.ts +11 -0
- package/dist/types/pm-plugins/utils/row-controls.d.ts +16 -0
- package/dist/types/pm-plugins/utils/selection.d.ts +20 -0
- package/dist/types/ui/DragHandle/index.d.ts +3 -2
- package/dist/types/ui/FloatingInsertButton/getPopupOptions.d.ts +1 -1
- package/dist/types/ui/FloatingInsertButton/index.d.ts +1 -0
- package/dist/types/ui/event-handlers.d.ts +1 -1
- package/package.json +4 -4
|
@@ -29,6 +29,7 @@ var _analytics2 = require("../pm-plugins/utils/analytics");
|
|
|
29
29
|
var _columnControls = require("../pm-plugins/utils/column-controls");
|
|
30
30
|
var _dom = require("../pm-plugins/utils/dom");
|
|
31
31
|
var _getAllowAddColumnCustomStep = require("../pm-plugins/utils/get-allow-add-column-custom-step");
|
|
32
|
+
var _rowControls = require("../pm-plugins/utils/row-controls");
|
|
32
33
|
var _types = require("../types");
|
|
33
34
|
var _consts = require("./TableMenu/shared/consts");
|
|
34
35
|
var isFocusingCalendar = function isFocusingCalendar(event) {
|
|
@@ -206,14 +207,27 @@ var handleMouseUp = exports.handleMouseUp = function handleMouseUp(view, mouseEv
|
|
|
206
207
|
}
|
|
207
208
|
return false;
|
|
208
209
|
};
|
|
210
|
+
var resetDragHandleSelectedIntentOnMouseDown = function resetDragHandleSelectedIntentOnMouseDown(event, api) {
|
|
211
|
+
var _api$userIntent;
|
|
212
|
+
if ((0, _dom.isTableDragHandleButton)(event.target)) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if ((api === null || api === void 0 || (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 || (_api$userIntent = _api$userIntent.sharedState.currentState()) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.currentUserIntent) === 'dragHandleSelected') {
|
|
216
|
+
var _api$core, _api$userIntent2;
|
|
217
|
+
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent('default'));
|
|
218
|
+
}
|
|
219
|
+
};
|
|
209
220
|
|
|
210
221
|
// Ignore any `mousedown` `event` from control and numbered column buttons
|
|
211
222
|
// PM end up changing selection during shift selection if not prevented
|
|
212
|
-
var handleMouseDown = exports.handleMouseDown = function handleMouseDown(
|
|
223
|
+
var handleMouseDown = exports.handleMouseDown = function handleMouseDown(_view, event, api) {
|
|
213
224
|
var isControl = !!(event.target && event.target instanceof HTMLElement && ((0, _dom.isTableContainerOrWrapper)(event.target) || (0, _dom.isColumnControlsDecorations)(event.target) || (0, _dom.isRowControlsButton)(event.target) || (0, _dom.isDragCornerButton)(event.target)));
|
|
214
225
|
if (isControl) {
|
|
215
226
|
event.preventDefault();
|
|
216
227
|
}
|
|
228
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
229
|
+
resetDragHandleSelectedIntentOnMouseDown(event, api);
|
|
230
|
+
}
|
|
217
231
|
return isControl;
|
|
218
232
|
};
|
|
219
233
|
var handleMouseOut = exports.handleMouseOut = function handleMouseOut(view, mouseEvent) {
|
|
@@ -292,12 +306,26 @@ var handleMouseMoveDebounce = function handleMouseMoveDebounce(nodeViewPortalPro
|
|
|
292
306
|
return false;
|
|
293
307
|
}
|
|
294
308
|
var element = event.target;
|
|
309
|
+
var isTableMenuUpdatesEnabled = (0, _expValEquals.expValEquals)('platform_editor_table_menu_updates', 'isEnabled', true);
|
|
310
|
+
|
|
311
|
+
// Spanned cells need mouse-position-based hover indexes; normal cells already report them.
|
|
312
|
+
if (isTableMenuUpdatesEnabled) {
|
|
313
|
+
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
314
|
+
var tableCell = (0, _utils.isElementInTableCell)(element);
|
|
315
|
+
if (tableCell && (tableCell.rowSpan > 1 || tableCell.colSpan > 1)) {
|
|
316
|
+
var dragDropState = (0, _pluginFactory.getPluginState)(view.state);
|
|
317
|
+
if (dragDropState && !dragDropState.isDragging) {
|
|
318
|
+
trackCellLocation(view, event);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
295
322
|
if ((0, _dom.isColumnControlsDecorations)(element) || (0, _dom.isDragColumnFloatingInsertDot)(element)) {
|
|
296
323
|
var state = view.state,
|
|
297
324
|
dispatch = view.dispatch;
|
|
298
325
|
var _getPluginState6 = (0, _pluginFactory2.getPluginState)(state),
|
|
299
326
|
insertColumnButtonIndex = _getPluginState6.insertColumnButtonIndex;
|
|
300
|
-
var
|
|
327
|
+
var indexSourceElement = isTableMenuUpdatesEnabled ? (0, _dom.getIndexAttributeSourceElement)(element) : element;
|
|
328
|
+
var _getColumnOrRowIndex9 = (0, _dom.getColumnOrRowIndex)(indexSourceElement),
|
|
301
329
|
_getColumnOrRowIndex0 = (0, _slicedToArray2.default)(_getColumnOrRowIndex9, 2),
|
|
302
330
|
startIndex = _getColumnOrRowIndex0[0],
|
|
303
331
|
endIndex = _getColumnOrRowIndex0[1];
|
|
@@ -311,7 +339,8 @@ var handleMouseMoveDebounce = function handleMouseMoveDebounce(nodeViewPortalPro
|
|
|
311
339
|
_dispatch4 = view.dispatch;
|
|
312
340
|
var _getPluginState7 = (0, _pluginFactory2.getPluginState)(_state3),
|
|
313
341
|
insertRowButtonIndex = _getPluginState7.insertRowButtonIndex;
|
|
314
|
-
var
|
|
342
|
+
var _indexSourceElement = isTableMenuUpdatesEnabled ? (0, _dom.getIndexAttributeSourceElement)(element) : element;
|
|
343
|
+
var _getColumnOrRowIndex1 = (0, _dom.getColumnOrRowIndex)(_indexSourceElement),
|
|
315
344
|
_getColumnOrRowIndex10 = (0, _slicedToArray2.default)(_getColumnOrRowIndex1, 2),
|
|
316
345
|
_startIndex3 = _getColumnOrRowIndex10[0],
|
|
317
346
|
_endIndex2 = _getColumnOrRowIndex10[1];
|
|
@@ -329,8 +358,8 @@ var handleMouseMoveDebounce = function handleMouseMoveDebounce(nodeViewPortalPro
|
|
|
329
358
|
resizeHandleColumnIndex = _getPluginState8.resizeHandleColumnIndex,
|
|
330
359
|
resizeHandleRowIndex = _getPluginState8.resizeHandleRowIndex;
|
|
331
360
|
var isKeyboardResize = (0, _pluginFactory2.getPluginState)(_state4).isKeyboardResize;
|
|
332
|
-
var
|
|
333
|
-
var cellStartPosition = view.posAtDOM(
|
|
361
|
+
var _tableCell = (0, _utils.closestElement)(element, 'td, th');
|
|
362
|
+
var cellStartPosition = view.posAtDOM(_tableCell, 0);
|
|
334
363
|
var rect = (0, _utils3.findCellRectClosestToPos)(_state4.doc.resolve(cellStartPosition));
|
|
335
364
|
if (rect) {
|
|
336
365
|
var columnEndIndexTarget = _positionColumn === 'left' ? rect.left : rect.right;
|
|
@@ -459,7 +488,8 @@ var whenTableInFocus = exports.whenTableInFocus = function whenTableInFocus(even
|
|
|
459
488
|
};
|
|
460
489
|
};
|
|
461
490
|
var trackCellLocation = function trackCellLocation(view, mouseEvent) {
|
|
462
|
-
var _tableElement$dataset;
|
|
491
|
+
var _tableElement$dataset, _getRowIndexByMousePo;
|
|
492
|
+
var isTableMenuUpdatesEnabled = (0, _expValEquals.expValEquals)('platform_editor_table_menu_updates', 'isEnabled', true);
|
|
463
493
|
var target = mouseEvent.target;
|
|
464
494
|
// Ignored via go/ees005
|
|
465
495
|
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
@@ -484,13 +514,36 @@ var trackCellLocation = function trackCellLocation(view, mouseEvent) {
|
|
|
484
514
|
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
485
515
|
var rowElement = (0, _utils.closestElement)(target, 'tr');
|
|
486
516
|
var htmlRowIndex = rowElement && rowElement.rowIndex;
|
|
517
|
+
var rowIndex = maybeTableCell.rowSpan > 1 && mouseEvent instanceof MouseEvent && isTableMenuUpdatesEnabled ? (_getRowIndexByMousePo = (0, _rowControls.getRowIndexByMousePosition)(tableRef, mouseEvent, {
|
|
518
|
+
startIndex: htmlRowIndex,
|
|
519
|
+
endIndex: htmlRowIndex + maybeTableCell.rowSpan
|
|
520
|
+
})) !== null && _getRowIndexByMousePo !== void 0 ? _getRowIndexByMousePo : htmlRowIndex : htmlRowIndex;
|
|
487
521
|
var tableMap = tableNode && _tableMap.TableMap.get(tableNode);
|
|
488
522
|
var colIndex = htmlColIndex;
|
|
489
523
|
if (tableMap) {
|
|
490
524
|
var convertedColIndex = (0, _columnControls.convertHTMLCellIndexToColumnIndex)(htmlColIndex, htmlRowIndex, tableMap);
|
|
491
|
-
|
|
525
|
+
if (isTableMenuUpdatesEnabled) {
|
|
526
|
+
// New behaviour: the column controls grid renders one track per visual column
|
|
527
|
+
// (`getColumnsWidthsWithMergedCells`), so the hover index must be the visual column
|
|
528
|
+
// index — not the first-row-cell index. Mapping to the first-row cell would collapse
|
|
529
|
+
// every column under a merged first-row cell to the same index, leaving the drag handle
|
|
530
|
+
// stuck when hovering cells in other rows. For a colspanned cell, refine the converted
|
|
531
|
+
// (left-most) visual column to the exact visual column under the pointer.
|
|
532
|
+
colIndex = convertedColIndex;
|
|
533
|
+
if (maybeTableCell.colSpan > 1 && mouseEvent instanceof MouseEvent) {
|
|
534
|
+
var _getColumnIndexByMous;
|
|
535
|
+
colIndex = (_getColumnIndexByMous = (0, _columnControls.getColumnIndexByMousePosition)(maybeTableCell, mouseEvent, {
|
|
536
|
+
startIndex: convertedColIndex,
|
|
537
|
+
endIndex: convertedColIndex + maybeTableCell.colSpan
|
|
538
|
+
})) !== null && _getColumnIndexByMous !== void 0 ? _getColumnIndexByMous : convertedColIndex;
|
|
539
|
+
}
|
|
540
|
+
} else {
|
|
541
|
+
// Old behaviour: the legacy grid renders one track per first-row cell, so snap to the
|
|
542
|
+
// first-row column index (a merged first-row cell collapses to a single column track).
|
|
543
|
+
colIndex = (0, _columnControls.getColumnIndexMappedToColumnIndexInFirstRow)(convertedColIndex, htmlRowIndex, tableMap);
|
|
544
|
+
}
|
|
492
545
|
}
|
|
493
|
-
(0, _commands.hoverCell)(
|
|
546
|
+
(0, _commands.hoverCell)(rowIndex, colIndex)(view.state, view.dispatch);
|
|
494
547
|
};
|
|
495
548
|
var withCellTracking = exports.withCellTracking = function withCellTracking(eventHandler) {
|
|
496
549
|
return function (view, mouseEvent) {
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { pluginKey } from '../plugin-key';
|
|
2
|
+
import { isFullRowOrColumnSelected } from '../utils/selection';
|
|
2
3
|
const applyMenuUserIntent = (tr, api, nextActiveTableMenu) => {
|
|
3
|
-
var _api$
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
var _api$userIntent2;
|
|
5
|
+
if (nextActiveTableMenu.type === 'row' || nextActiveTableMenu.type === 'column') {
|
|
6
|
+
var _api$userIntent;
|
|
7
|
+
api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('tableDragMenuPopupOpen')({
|
|
8
|
+
tr
|
|
9
|
+
});
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
api === null || api === void 0 ? void 0 : (_api$userIntent2 = api.userIntent) === null || _api$userIntent2 === void 0 ? void 0 : _api$userIntent2.commands.setCurrentUserIntent(isFullRowOrColumnSelected(tr.selection) ? 'dragHandleSelected' : 'default')({
|
|
6
13
|
tr
|
|
7
14
|
});
|
|
8
15
|
};
|
|
9
|
-
export const closeActiveTableMenu = api => ({
|
|
16
|
+
export const closeActiveTableMenu = (api, options) => ({
|
|
10
17
|
tr
|
|
11
18
|
}) => {
|
|
12
19
|
const nextActiveTableMenu = {
|
|
@@ -18,7 +25,9 @@ export const closeActiveTableMenu = api => ({
|
|
|
18
25
|
activeTableMenu: nextActiveTableMenu
|
|
19
26
|
}
|
|
20
27
|
});
|
|
21
|
-
|
|
28
|
+
if (!(options !== null && options !== void 0 && options.skipUserIntent)) {
|
|
29
|
+
applyMenuUserIntent(tr, api, nextActiveTableMenu);
|
|
30
|
+
}
|
|
22
31
|
if (!tr.docChanged) {
|
|
23
32
|
tr.setMeta('addToHistory', false);
|
|
24
33
|
}
|
|
@@ -98,7 +98,9 @@ const destroyFn = (editorView, editorAnalyticsAPI, isTableScalingEnabled, isTabl
|
|
|
98
98
|
tr
|
|
99
99
|
}) => {
|
|
100
100
|
var _api$userIntent;
|
|
101
|
-
closeActiveTableMenu(api
|
|
101
|
+
closeActiveTableMenu(api, {
|
|
102
|
+
skipUserIntent: true
|
|
103
|
+
})({
|
|
102
104
|
tr
|
|
103
105
|
});
|
|
104
106
|
api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('dragging')({
|
|
@@ -346,7 +346,7 @@ export const createPlugin = (dispatchAnalyticsEvent, dispatch, portalProviderAPI
|
|
|
346
346
|
handleDOMEvents: {
|
|
347
347
|
focus: handleFocus,
|
|
348
348
|
blur: handleBlur,
|
|
349
|
-
mousedown: withCellTracking(handleMouseDown),
|
|
349
|
+
mousedown: withCellTracking((view, event) => handleMouseDown(view, event, pluginInjectionApi)),
|
|
350
350
|
mouseleave: handleMouseLeave,
|
|
351
351
|
mousemove: whenTableInFocus(handleMouseMove(nodeViewPortalProviderAPI), pluginInjectionApi),
|
|
352
352
|
mouseenter: handleMouseEnter,
|
|
@@ -34,6 +34,69 @@ export const getColumnsWidths = view => {
|
|
|
34
34
|
}
|
|
35
35
|
return widths;
|
|
36
36
|
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Splits a merged cell's rendered width by `colwidth` ratios, falling back to an even split when
|
|
40
|
+
* usable ratios are unavailable.
|
|
41
|
+
*/
|
|
42
|
+
export const getProportionalColumnWidths = (totalWidth, columnCount, ratios) => {
|
|
43
|
+
const evenSplit = () => new Array(columnCount).fill(totalWidth / columnCount);
|
|
44
|
+
if (!ratios || ratios.length !== columnCount) {
|
|
45
|
+
return evenSplit();
|
|
46
|
+
}
|
|
47
|
+
const total = ratios.reduce((sum, ratio) => sum + (ratio > 0 ? ratio : 0), 0);
|
|
48
|
+
if (total <= 0) {
|
|
49
|
+
return evenSplit();
|
|
50
|
+
}
|
|
51
|
+
return ratios.map(ratio => totalWidth * ((ratio > 0 ? ratio : 0) / total));
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Like `getColumnsWidths`, but fills every visual column under a first-row `colspan` so column
|
|
56
|
+
* controls can render one grid track per column.
|
|
57
|
+
*/
|
|
58
|
+
export const getColumnsWidthsWithMergedCells = view => {
|
|
59
|
+
const {
|
|
60
|
+
selection
|
|
61
|
+
} = view.state;
|
|
62
|
+
const table = findTable(selection);
|
|
63
|
+
if (!table) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const map = TableMap.get(table.node);
|
|
67
|
+
const domAtPos = view.domAtPos.bind(view);
|
|
68
|
+
const widths = Array.from({
|
|
69
|
+
length: map.width
|
|
70
|
+
});
|
|
71
|
+
for (let i = 0; i < map.width; i++) {
|
|
72
|
+
var _node$attrs$colspan;
|
|
73
|
+
if (map.isCellMergedTopLeft(0, i)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Ignored via go/ees005
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
79
|
+
const node = table.node.nodeAt(map.map[i]);
|
|
80
|
+
const pos = map.map[i] + table.start;
|
|
81
|
+
// Ignored via go/ees005
|
|
82
|
+
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
83
|
+
const cellRef = findDomRefAtPos(pos, domAtPos);
|
|
84
|
+
const rect = cellRef.getBoundingClientRect();
|
|
85
|
+
const measuredWidth = (rect ? rect.width : cellRef.offsetWidth) + 1;
|
|
86
|
+
const colspan = (_node$attrs$colspan = node.attrs.colspan) !== null && _node$attrs$colspan !== void 0 ? _node$attrs$colspan : 1;
|
|
87
|
+
if (colspan <= 1) {
|
|
88
|
+
widths[i] = measuredWidth;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const colwidth = Array.isArray(node.attrs.colwidth) ? node.attrs.colwidth : undefined;
|
|
92
|
+
const perColumnWidths = getProportionalColumnWidths(measuredWidth, colspan, colwidth);
|
|
93
|
+
for (let span = 0; span < colspan && i + span < map.width; span++) {
|
|
94
|
+
widths[i + span] = perColumnWidths[span];
|
|
95
|
+
}
|
|
96
|
+
i += colspan - 1;
|
|
97
|
+
}
|
|
98
|
+
return widths;
|
|
99
|
+
};
|
|
37
100
|
export const getColumnDeleteButtonParams = (columnsWidths, selection) => {
|
|
38
101
|
const rect = getSelectionRect(selection);
|
|
39
102
|
if (!rect) {
|
|
@@ -108,7 +171,9 @@ const getRelativeDomCellWidths = ({
|
|
|
108
171
|
const totalCalculatedCellWidth = cellColWidths.reduce((acc, cellColWidth) => acc + cellColWidth, 0);
|
|
109
172
|
return cellColWidths.map(cellColWidth => width * (cellColWidth / totalCalculatedCellWidth));
|
|
110
173
|
};
|
|
111
|
-
export const colWidthsForRow = tr
|
|
174
|
+
export const colWidthsForRow = (tr, {
|
|
175
|
+
useColwidthRatios = false
|
|
176
|
+
} = {}) => {
|
|
112
177
|
// get the colspans
|
|
113
178
|
const rowColSpans = maphElem(tr, cell => Number(cell.getAttribute('colspan') || 1 /* default to span of 1 */));
|
|
114
179
|
|
|
@@ -132,7 +197,7 @@ export const colWidthsForRow = tr => {
|
|
|
132
197
|
// reverse engineer cell widths from table widths
|
|
133
198
|
const domBasedCellWidths = [];
|
|
134
199
|
cellInfos.map(cell => {
|
|
135
|
-
domBasedCellWidths.push(...getRelativeDomCellWidths(cell));
|
|
200
|
+
domBasedCellWidths.push(...(useColwidthRatios ? getRelativeDomCellWidths(cell) : new Array(cell.colspan).fill(cell.width / cell.colspan)));
|
|
136
201
|
});
|
|
137
202
|
if (cellInfos.reduce((acc, cell) => acc + cell.width, 0) !== 0) {
|
|
138
203
|
const newWidths = mapTableColwidthsToRow(rowColSpans, domBasedCellWidths);
|
|
@@ -146,6 +211,53 @@ export const colWidthsForRow = tr => {
|
|
|
146
211
|
const pctWidths = rowColSpans.map(cellColSpan => cellColSpan / visualColCount * 100);
|
|
147
212
|
return pctWidths.map(pct => `${pct}%`).join(' ');
|
|
148
213
|
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Returns the visual column index that the mouse pointer is over within a column-spanned
|
|
217
|
+
* cell, by walking the per-column boundaries of the spanned cell and finding the column whose
|
|
218
|
+
* horizontal range contains `mouseEvent.clientX`.
|
|
219
|
+
*
|
|
220
|
+
* `<td>.cellIndex` for a cell with `colspan > 1` only resolves to the first column the cell
|
|
221
|
+
* occupies, so hovering anywhere inside a colspanned first-row cell pins the column drag handle
|
|
222
|
+
* to that first column. This resolver disambiguates which visual column the pointer is actually
|
|
223
|
+
* over so the handle/menu can target a single column.
|
|
224
|
+
*
|
|
225
|
+
* The search is restricted to columns in `[startIndex, endIndex)` (the cell's own span). The
|
|
226
|
+
* spanned cell's bounding rect is read once and split into per-column boundaries using the cell's
|
|
227
|
+
* `data-colwidth` ratios (falling back to an even split for unresized columns) — this keeps the
|
|
228
|
+
* work bounded by the colspan size and avoids measuring the whole table.
|
|
229
|
+
*
|
|
230
|
+
* Returns `undefined` when the mouse is outside the spanned range (so callers can fall back to
|
|
231
|
+
* the converted HTML column index).
|
|
232
|
+
*/
|
|
233
|
+
export const getColumnIndexByMousePosition = (cellElement, mouseEvent, columnIndexRange) => {
|
|
234
|
+
var _cellElement$dataset$;
|
|
235
|
+
const {
|
|
236
|
+
startIndex,
|
|
237
|
+
endIndex
|
|
238
|
+
} = columnIndexRange;
|
|
239
|
+
const columnCount = endIndex - startIndex;
|
|
240
|
+
if (columnCount <= 0) {
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
const cellRect = cellElement.getBoundingClientRect();
|
|
244
|
+
if (mouseEvent.clientX < cellRect.left || mouseEvent.clientX >= cellRect.right) {
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Read DOM ratios here to avoid resolving the ProseMirror node on this mouse-move path.
|
|
249
|
+
const ratios = (_cellElement$dataset$ = cellElement.dataset.colwidth) === null || _cellElement$dataset$ === void 0 ? void 0 : _cellElement$dataset$.split(',').map(Number).filter(value => !Number.isNaN(value));
|
|
250
|
+
const perColumnWidths = getProportionalColumnWidths(cellRect.width, columnCount, ratios);
|
|
251
|
+
const offsetFromLeft = mouseEvent.clientX - cellRect.left;
|
|
252
|
+
let cumulativeWidth = 0;
|
|
253
|
+
for (let column = 0; column < columnCount; column++) {
|
|
254
|
+
cumulativeWidth += perColumnWidths[column];
|
|
255
|
+
if (offsetFromLeft < cumulativeWidth) {
|
|
256
|
+
return startIndex + column;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return endIndex - 1;
|
|
260
|
+
};
|
|
149
261
|
export const convertHTMLCellIndexToColumnIndex = (htmlColIndex, htmlRowIndex, tableMap) => {
|
|
150
262
|
// Same numbers (positions) in tableMap.map array mean that there are merged cells.
|
|
151
263
|
// Cells can be merged across columns. So we need to check if the cell on the left and current cell have the same value.
|
|
@@ -6,6 +6,25 @@ export const isCell = node => {
|
|
|
6
6
|
export const isCornerButton = node => containsClassName(node, ClassName.CONTROLS_CORNER_BUTTON);
|
|
7
7
|
export const isInsertRowButton = node => containsClassName(node, ClassName.CONTROLS_INSERT_ROW) || closestElement(node, `.${ClassName.CONTROLS_INSERT_ROW}`) || containsClassName(node, ClassName.CONTROLS_BUTTON_OVERLAY) && closestElement(node, `.${ClassName.ROW_CONTROLS}`);
|
|
8
8
|
export const getColumnOrRowIndex = target => [parseInt(target.getAttribute('data-start-index') || '-1', 10), parseInt(target.getAttribute('data-end-index') || '-1', 10)];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the element that carries the `data-start-index` / `data-end-index` attributes for a
|
|
12
|
+
* column/row control.
|
|
13
|
+
*
|
|
14
|
+
* The floating insert dot is a child of a wrapper that holds the index attributes, so when the
|
|
15
|
+
* pointer is directly over the dot the attributes are not on the event target. This walks up to
|
|
16
|
+
* the nearest ancestor that has `data-start-index`, falling back to the original element so the
|
|
17
|
+
* behaviour is unchanged when the target already carries the attributes.
|
|
18
|
+
*/
|
|
19
|
+
export const getIndexAttributeSourceElement = target => {
|
|
20
|
+
if (target.hasAttribute('data-start-index')) {
|
|
21
|
+
return target;
|
|
22
|
+
}
|
|
23
|
+
// Ignored via go/ees005
|
|
24
|
+
// eslint-disable-next-line @atlaskit/editor/no-as-casting
|
|
25
|
+
const closestWithIndex = target.closest('[data-start-index]');
|
|
26
|
+
return closestWithIndex !== null && closestWithIndex !== void 0 ? closestWithIndex : target;
|
|
27
|
+
};
|
|
9
28
|
export const isColumnControlsDecorations = node => containsClassName(node, ClassName.COLUMN_CONTROLS_DECORATIONS);
|
|
10
29
|
export const isRowControlsButton = node => containsClassName(node, ClassName.ROW_CONTROLS_BUTTON) || containsClassName(node, ClassName.NUMBERED_COLUMN_BUTTON);
|
|
11
30
|
export const isResizeHandleDecoration = node => containsClassName(node, ClassName.RESIZE_HANDLE_DECORATION);
|
|
@@ -16,6 +35,7 @@ export const isTableContainerOrWrapper = node => containsClassName(node, ClassNa
|
|
|
16
35
|
export const isDragRowFloatingInsertDot = node => containsClassName(node, ClassName.DRAG_ROW_FLOATING_INSERT_DOT_WRAPPER);
|
|
17
36
|
export const isDragColumnFloatingInsertDot = node => containsClassName(node, ClassName.DRAG_COLUMN_FLOATING_INSERT_DOT_WRAPPER);
|
|
18
37
|
export const isDragCornerButton = node => containsClassName(node, ClassName.DRAG_CORNER_BUTTON) || containsClassName(node, ClassName.DRAG_CORNER_BUTTON_INNER);
|
|
38
|
+
export const isTableDragHandleButton = target => target instanceof HTMLElement && Boolean(target.closest(`.${ClassName.DRAG_HANDLE_BUTTON_CONTAINER}`));
|
|
19
39
|
|
|
20
40
|
/*
|
|
21
41
|
* This function returns which side of a given element the mouse cursor is,
|
|
@@ -74,6 +74,48 @@ export const getRowsParams = rowsHeights => {
|
|
|
74
74
|
}
|
|
75
75
|
return rows;
|
|
76
76
|
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns the visual row index that the mouse pointer is over, by walking the row heights
|
|
80
|
+
* inside `tbody` and finding the row whose vertical range contains `mouseEvent.clientY`.
|
|
81
|
+
*
|
|
82
|
+
* When `rowIndexRange` is provided, the search is restricted to rows in that range (the
|
|
83
|
+
* `endIndex` is exclusive). This is the hot path used on `mousemove` when hovering over a
|
|
84
|
+
* row-spanned cell — restricting the range to `[startIndex, endIndex)` keeps the number
|
|
85
|
+
* of forced layout reads bounded by the row-span size, not the table size.
|
|
86
|
+
*
|
|
87
|
+
* Returns `undefined` when the mouse is above the search range or below it (so callers can
|
|
88
|
+
* fall back to the HTML row index).
|
|
89
|
+
*/
|
|
90
|
+
export const getRowIndexByMousePosition = (tableRef, mouseEvent, rowIndexRange) => {
|
|
91
|
+
var _rowIndexRange$startI, _rowIndexRange$endInd;
|
|
92
|
+
const tableBody = tableRef.querySelector('tbody');
|
|
93
|
+
if (!tableBody) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
const rows = tableBody.children;
|
|
97
|
+
const startIndex = (_rowIndexRange$startI = rowIndexRange === null || rowIndexRange === void 0 ? void 0 : rowIndexRange.startIndex) !== null && _rowIndexRange$startI !== void 0 ? _rowIndexRange$startI : 0;
|
|
98
|
+
const endIndex = Math.min((_rowIndexRange$endInd = rowIndexRange === null || rowIndexRange === void 0 ? void 0 : rowIndexRange.endIndex) !== null && _rowIndexRange$endInd !== void 0 ? _rowIndexRange$endInd : rows.length, rows.length);
|
|
99
|
+
if (startIndex >= endIndex) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const firstRowRect = rows[startIndex].getBoundingClientRect();
|
|
103
|
+
if (mouseEvent.clientY < firstRowRect.top) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
let rowBottom = firstRowRect.bottom;
|
|
107
|
+
if (mouseEvent.clientY < rowBottom) {
|
|
108
|
+
return startIndex;
|
|
109
|
+
}
|
|
110
|
+
for (let rowIndex = startIndex + 1; rowIndex < endIndex; rowIndex++) {
|
|
111
|
+
const rowRect = rows[rowIndex].getBoundingClientRect();
|
|
112
|
+
rowBottom = rowRect.bottom;
|
|
113
|
+
if (mouseEvent.clientY < rowBottom) {
|
|
114
|
+
return rowIndex;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
};
|
|
77
119
|
export const getRowClassNames = (index, selection, hoveredRows = [], isInDanger, isResizing) => {
|
|
78
120
|
const classNames = [];
|
|
79
121
|
if (isRowSelected(index)(selection) || hoveredRows.indexOf(index) > -1 && !isResizing) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
|
|
2
|
+
import { TableMap } from '@atlaskit/editor-tables/table-map';
|
|
3
|
+
import { findTableClosestToPos, getSelectionRect } from '@atlaskit/editor-tables/utils';
|
|
1
4
|
export const getSelectedColumnIndexes = selectionRect => {
|
|
2
5
|
const columnIndexes = [];
|
|
3
6
|
for (let i = selectionRect.left; i < selectionRect.right; i++) {
|
|
@@ -11,4 +14,74 @@ export const getSelectedRowIndexes = selectionRect => {
|
|
|
11
14
|
rowIndexes.push(i);
|
|
12
15
|
}
|
|
13
16
|
return rowIndexes;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Treats a column as fully selected when its first-row area is covered by a horizontal merge and
|
|
21
|
+
* the explicit `CellSelection` starts below that merged cell. `CellSelection.isColSelection()`
|
|
22
|
+
* returns `false` in this case because the selection does not include the merged first-row cell.
|
|
23
|
+
*/
|
|
24
|
+
export const isColumnSelectionWithMergedFirstRow = selection => {
|
|
25
|
+
const rect = getSelectionRect(selection);
|
|
26
|
+
if (!rect) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const table = findTableClosestToPos(selection.$anchorCell);
|
|
30
|
+
if (!table) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const map = TableMap.get(table.node);
|
|
34
|
+
if (rect.bottom !== map.height || rect.top === 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
for (let col = rect.left; col < rect.right; col++) {
|
|
38
|
+
const topCellRect = map.findCell(map.map[col]);
|
|
39
|
+
const isHorizontallyMerged = topCellRect.right - topCellRect.left > 1;
|
|
40
|
+
const bridgesGapAboveSelection = topCellRect.bottom >= rect.top;
|
|
41
|
+
if (!isHorizontallyMerged || !bridgesGapAboveSelection) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Treats a row as fully selected when its first-column area is covered by a vertical merge and the
|
|
50
|
+
* explicit `CellSelection` starts to the right of that merged cell. `CellSelection.isRowSelection()`
|
|
51
|
+
* returns `false` in this case because the selection does not include the merged first-column cell.
|
|
52
|
+
*/
|
|
53
|
+
export const isRowSelectionWithMergedFirstColumn = selection => {
|
|
54
|
+
const rect = getSelectionRect(selection);
|
|
55
|
+
if (!rect) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const table = findTableClosestToPos(selection.$anchorCell);
|
|
59
|
+
if (!table) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const map = TableMap.get(table.node);
|
|
63
|
+
if (rect.right !== map.width || rect.left === 0) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
for (let row = rect.top; row < rect.bottom; row++) {
|
|
67
|
+
const leftCellRect = map.findCell(map.map[row * map.width]);
|
|
68
|
+
const isVerticallyMerged = leftCellRect.bottom - leftCellRect.top > 1;
|
|
69
|
+
const bridgesGapLeftOfSelection = leftCellRect.right >= rect.left;
|
|
70
|
+
if (!isVerticallyMerged || !bridgesGapLeftOfSelection) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns `true` when the selection covers one or more complete rows or columns (the kind of
|
|
79
|
+
* selection produced by clicking a row/column drag handle), including the merged-cell variants
|
|
80
|
+
* where `CellSelection.isRowSelection()` / `isColSelection()` return `false`.
|
|
81
|
+
*/
|
|
82
|
+
export const isFullRowOrColumnSelected = selection => {
|
|
83
|
+
if (!(selection instanceof CellSelection)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return selection.isRowSelection() || selection.isColSelection() || isRowSelectionWithMergedFirstColumn(selection) || isColumnSelectionWithMergedFirstRow(selection);
|
|
14
87
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @atlaskit/design-system/no-html-button */
|
|
2
2
|
|
|
3
|
-
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import classnames from 'classnames';
|
|
5
5
|
import ReactDOM from 'react-dom';
|
|
6
6
|
import { injectIntl } from 'react-intl';
|
|
@@ -11,6 +11,7 @@ import { findTable, TableMap } from '@atlaskit/editor-tables';
|
|
|
11
11
|
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
12
12
|
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
|
|
13
13
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
14
|
+
import { closeActiveTableMenu } from '../../pm-plugins/commands/active-table-menu';
|
|
14
15
|
import { getPluginState as getDnDPluginState } from '../../pm-plugins/drag-and-drop/plugin-factory';
|
|
15
16
|
import { findDuplicatePosition, hasMergedCellsInSelection } from '../../pm-plugins/utils/merged-cells';
|
|
16
17
|
import { TableCssClassName as ClassName } from '../../types';
|
|
@@ -18,6 +19,7 @@ import { dragTableInsertColumnButtonSize } from '../consts';
|
|
|
18
19
|
import { DragPreview } from '../DragPreview';
|
|
19
20
|
import { HandleIconComponent } from './HandleIconComponent';
|
|
20
21
|
const DragHandleComponent = ({
|
|
22
|
+
api,
|
|
21
23
|
isDragMenuTarget,
|
|
22
24
|
tableLocalId,
|
|
23
25
|
direction = 'row',
|
|
@@ -148,6 +150,36 @@ const DragHandleComponent = ({
|
|
|
148
150
|
}, [tableLocalId, direction, indexes, isRow, editorView.state.selection, hasMergedCells]);
|
|
149
151
|
const showDragMenuAnchorId = isRow ? 'drag-handle-button-row' : 'drag-handle-button-column';
|
|
150
152
|
const browser = getBrowserInfo();
|
|
153
|
+
|
|
154
|
+
// Clicking the drag handle's clickable zone is a plain row/column selection (not the menu
|
|
155
|
+
// trigger), so the selection toolbar should appear. The floating menu defers closing for clicks
|
|
156
|
+
// inside the drag controls, so we close it here and reset the user intent to 'default' (in the
|
|
157
|
+
// same transaction) so the toolbar is shown.
|
|
158
|
+
const closeActiveDragMenuAndShowToolbar = useCallback(() => {
|
|
159
|
+
var _api$table, _api$table$sharedStat, _api$core;
|
|
160
|
+
if (!api) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const activeTableMenu = (_api$table = api.table) === null || _api$table === void 0 ? void 0 : (_api$table$sharedStat = _api$table.sharedState.currentState()) === null || _api$table$sharedStat === void 0 ? void 0 : _api$table$sharedStat.activeTableMenu;
|
|
164
|
+
const isActiveTableMenuOpen = (activeTableMenu === null || activeTableMenu === void 0 ? void 0 : activeTableMenu.type) === 'row' || (activeTableMenu === null || activeTableMenu === void 0 ? void 0 : activeTableMenu.type) === 'column';
|
|
165
|
+
if (!isActiveTableMenuOpen) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
(_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(({
|
|
169
|
+
tr
|
|
170
|
+
}) => {
|
|
171
|
+
var _api$userIntent;
|
|
172
|
+
closeActiveTableMenu(api, {
|
|
173
|
+
skipUserIntent: true
|
|
174
|
+
})({
|
|
175
|
+
tr
|
|
176
|
+
});
|
|
177
|
+
(_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.commands.setCurrentUserIntent('default')({
|
|
178
|
+
tr
|
|
179
|
+
});
|
|
180
|
+
return tr;
|
|
181
|
+
});
|
|
182
|
+
}, [api]);
|
|
151
183
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("button", {
|
|
152
184
|
type: "button"
|
|
153
185
|
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
|
|
@@ -175,7 +207,10 @@ const DragHandleComponent = ({
|
|
|
175
207
|
// 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
|
|
176
208
|
// -> this is bad for a11y but is the current standard new copy/paste keyboard shortcuts should be introduced instead
|
|
177
209
|
editorView.focus();
|
|
178
|
-
if (
|
|
210
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
211
|
+
// New menu: close the open row/column menu (and show the selection toolbar).
|
|
212
|
+
closeActiveDragMenuAndShowToolbar();
|
|
213
|
+
} else if (isDragMenuOpen) {
|
|
179
214
|
toggleDragMenu && toggleDragMenu('mouse', e);
|
|
180
215
|
}
|
|
181
216
|
},
|
|
@@ -216,12 +251,20 @@ const DragHandleComponent = ({
|
|
|
216
251
|
// 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
252
|
// -> this is bad for a11y but is the current standard new copy/paste keyboard shortcuts should be introduced instead
|
|
218
253
|
editorView.focus();
|
|
219
|
-
|
|
254
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
255
|
+
toggleDragMenu && toggleDragMenu('mouse', e, indexes[0]);
|
|
256
|
+
} else {
|
|
257
|
+
toggleDragMenu && toggleDragMenu('mouse', e);
|
|
258
|
+
}
|
|
220
259
|
},
|
|
221
260
|
onClick: onClick,
|
|
222
261
|
onKeyDown: e => {
|
|
223
262
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
224
|
-
|
|
263
|
+
if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
|
|
264
|
+
toggleDragMenu && toggleDragMenu('keyboard', undefined, indexes[0]);
|
|
265
|
+
} else {
|
|
266
|
+
toggleDragMenu && toggleDragMenu('keyboard');
|
|
267
|
+
}
|
|
225
268
|
}
|
|
226
269
|
}
|
|
227
270
|
}, 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:
|