@atlaskit/editor-plugin-table 5.4.11 → 5.4.13

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/plugins/table/event-handlers.js +10 -4
  3. package/dist/cjs/plugins/table/toolbar.js +3 -6
  4. package/dist/cjs/plugins/table/ui/ColumnResizeWidget/index.js +3 -6
  5. package/dist/cjs/plugins/table/ui/DragHandle/index.js +12 -6
  6. package/dist/cjs/plugins/table/ui/DragPreview/index.js +2 -0
  7. package/dist/cjs/plugins/table/ui/FloatingContextualButton/index.js +7 -10
  8. package/dist/cjs/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +10 -11
  9. package/dist/cjs/plugins/table/ui/FloatingContextualMenu/index.js +3 -6
  10. package/dist/cjs/plugins/table/ui/FloatingDragMenu/DragMenu.js +4 -7
  11. package/dist/cjs/plugins/table/ui/TableFloatingColumnControls/index.js +1 -4
  12. package/dist/cjs/plugins/table/utils/column-controls.js +63 -1
  13. package/dist/cjs/plugins/table/utils/index.js +12 -0
  14. package/dist/es2019/plugins/table/event-handlers.js +11 -5
  15. package/dist/es2019/plugins/table/toolbar.js +3 -5
  16. package/dist/es2019/plugins/table/ui/ColumnResizeWidget/index.js +3 -5
  17. package/dist/es2019/plugins/table/ui/DragHandle/index.js +11 -4
  18. package/dist/es2019/plugins/table/ui/DragPreview/index.js +2 -0
  19. package/dist/es2019/plugins/table/ui/FloatingContextualButton/index.js +7 -9
  20. package/dist/es2019/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +9 -11
  21. package/dist/es2019/plugins/table/ui/FloatingContextualMenu/index.js +3 -5
  22. package/dist/es2019/plugins/table/ui/FloatingDragMenu/DragMenu.js +4 -6
  23. package/dist/es2019/plugins/table/ui/TableFloatingColumnControls/index.js +1 -4
  24. package/dist/es2019/plugins/table/utils/column-controls.js +62 -0
  25. package/dist/es2019/plugins/table/utils/index.js +1 -1
  26. package/dist/esm/plugins/table/event-handlers.js +11 -5
  27. package/dist/esm/plugins/table/toolbar.js +3 -5
  28. package/dist/esm/plugins/table/ui/ColumnResizeWidget/index.js +3 -5
  29. package/dist/esm/plugins/table/ui/DragHandle/index.js +11 -5
  30. package/dist/esm/plugins/table/ui/DragPreview/index.js +2 -0
  31. package/dist/esm/plugins/table/ui/FloatingContextualButton/index.js +7 -9
  32. package/dist/esm/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +9 -11
  33. package/dist/esm/plugins/table/ui/FloatingContextualMenu/index.js +3 -5
  34. package/dist/esm/plugins/table/ui/FloatingDragMenu/DragMenu.js +4 -6
  35. package/dist/esm/plugins/table/ui/TableFloatingColumnControls/index.js +1 -4
  36. package/dist/esm/plugins/table/utils/column-controls.js +62 -0
  37. package/dist/esm/plugins/table/utils/index.js +1 -1
  38. package/dist/types/plugins/table/utils/column-controls.d.ts +3 -0
  39. package/dist/types/plugins/table/utils/index.d.ts +1 -1
  40. package/dist/types-ts4.5/plugins/table/utils/column-controls.d.ts +3 -0
  41. package/dist/types-ts4.5/plugins/table/utils/index.d.ts +1 -1
  42. package/package.json +4 -4
  43. package/src/__tests__/unit/utils/column-controls.ts +145 -1
  44. package/src/plugins/table/event-handlers.ts +22 -4
  45. package/src/plugins/table/ui/DragHandle/index.tsx +30 -21
  46. package/src/plugins/table/ui/TableFloatingColumnControls/index.tsx +1 -5
  47. package/src/plugins/table/utils/column-controls.ts +81 -0
  48. package/src/plugins/table/utils/index.ts +2 -0
  49. package/src/__tests__/integration/block-node-selection.ts +0 -165
@@ -1,6 +1,4 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
- /** @jsx jsx */
3
- import { jsx } from '@emotion/react';
4
2
  import { ArrowKeyNavigationType, DropdownMenu } from '@atlaskit/editor-common/ui-menu';
5
3
  import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
6
4
  import { TableMap } from '@atlaskit/editor-tables/table-map';
@@ -31,15 +29,15 @@ var convertToDropdownItems = function convertToDropdownItems(dragMenuConfig) {
31
29
  name: item.id
32
30
  },
33
31
  isDisabled: item.disabled,
34
- elemBefore: item.icon ? jsx("div", {
32
+ elemBefore: item.icon ? /*#__PURE__*/React.createElement("div", {
35
33
  style: {
36
34
  marginRight: "var(--ds-space-negative-075, -6px)",
37
35
  display: 'flex'
38
36
  }
39
- }, jsx(item.icon, {
37
+ }, /*#__PURE__*/React.createElement(item.icon, {
40
38
  label: item.title
41
39
  })) : undefined,
42
- elemAfter: item.keymap ? jsx("div", {
40
+ elemAfter: item.keymap ? /*#__PURE__*/React.createElement("div", {
43
41
  css: shortcutStyle
44
42
  }, item.keymap) : undefined
45
43
  });
@@ -131,7 +129,7 @@ export var DragMenu = function DragMenu(_ref) {
131
129
  if (!menuItems) {
132
130
  return null;
133
131
  }
134
- return jsx(DropdownMenu, {
132
+ return /*#__PURE__*/React.createElement(DropdownMenu, {
135
133
  mountTo: mountPoint
136
134
  //This needs be removed when the a11y is completely handled
137
135
  //Disabling key navigation now as it works only partially
@@ -89,14 +89,11 @@ export var TableFloatingColumnControls = function TableFloatingColumnControls(_r
89
89
  }
90
90
  return [0];
91
91
  }, [tableRef, tableRect.height]);
92
- if (!tableRef) {
92
+ if (!tableRef || !tableActive) {
93
93
  return null;
94
94
  }
95
95
  var colWidths = getColumnsWidths(editorView);
96
96
  var stickyTop = stickyHeader && stickyHeader.sticky && hasHeaderRow ? stickyHeader.top : undefined;
97
- if (!tableActive) {
98
- return null;
99
- }
100
97
  return /*#__PURE__*/React.createElement("div", {
101
98
  className: ClassName.DRAG_COLUMN_CONTROLS_WRAPPER,
102
99
  "data-testid": "table-floating-column-controls-wrapper"
@@ -182,4 +182,66 @@ export var colWidthsForRow = function colWidthsForRow(tr) {
182
182
  return pctWidths.map(function (pct) {
183
183
  return "".concat(pct, "%");
184
184
  }).join(' ');
185
+ };
186
+ export var convertHTMLCellIndexToColumnIndex = function convertHTMLCellIndexToColumnIndex(htmlColIndex, htmlRowIndex, tableMap) {
187
+ // Same numbers (positions) in tableMap.map array mean that there are merged cells.
188
+ // Cells can be merged across columns. So we need to check if the cell on the left and current cell have the same value.
189
+ // Cells can be merged acroll rows. So we need to check if the cell above has the same value as current cell.
190
+ // When cell has the same value as the cell above it or the cell to the left of it, html cellIndex won't count it a separete column.
191
+ var width = tableMap.width;
192
+ var map = tableMap.map;
193
+ var htmlColCount = 0;
194
+ if (htmlRowIndex === 0) {
195
+ for (var colIndex = 0; colIndex < width; colIndex++) {
196
+ if (colIndex === 0 || map[colIndex] !== map[colIndex - 1]) {
197
+ htmlColCount++;
198
+ }
199
+ if (htmlColCount - 1 === htmlColIndex) {
200
+ return colIndex;
201
+ }
202
+ }
203
+ } else {
204
+ for (var _colIndex = 0; _colIndex < width; _colIndex++) {
205
+ var currentCellMapIndex = htmlRowIndex * width + _colIndex;
206
+ var cellAboveMapIndex = (htmlRowIndex - 1) * width + _colIndex;
207
+ if (_colIndex > 0) {
208
+ if (map[currentCellMapIndex] !== map[currentCellMapIndex - 1] && map[currentCellMapIndex] !== map[cellAboveMapIndex]) {
209
+ htmlColCount++;
210
+ }
211
+ } else {
212
+ if (map[currentCellMapIndex] !== map[cellAboveMapIndex]) {
213
+ htmlColCount++;
214
+ }
215
+ }
216
+ if (htmlColCount - 1 === htmlColIndex) {
217
+ return _colIndex;
218
+ }
219
+ }
220
+ }
221
+ return 0;
222
+ };
223
+
224
+ // When first row has merged cells, our converted column index needs to be mapped.
225
+ export var getColumnIndexMappedToColumnIndexInFirstRow = function getColumnIndexMappedToColumnIndexInFirstRow(convertedColIndex, htmlRowIndex, tableMap) {
226
+ var width = tableMap.width;
227
+ var map = tableMap.map;
228
+ var mapColIndexToFistRowColIndex = [];
229
+ var htmlColCounFirstRow = 0;
230
+ var colSpan = 0;
231
+ if (htmlRowIndex === 0) {
232
+ return convertedColIndex;
233
+ }
234
+ for (var colIndex = 0; colIndex < width; colIndex++) {
235
+ if (colIndex === 0 || map[colIndex] !== map[colIndex - 1]) {
236
+ if (colSpan > 0) {
237
+ htmlColCounFirstRow += colSpan;
238
+ colSpan = 0;
239
+ }
240
+ htmlColCounFirstRow++;
241
+ } else if (map[colIndex] === map[colIndex - 1]) {
242
+ colSpan++;
243
+ }
244
+ mapColIndexToFistRowColIndex[colIndex] = htmlColCounFirstRow - 1;
245
+ }
246
+ return mapColIndexToFistRowColIndex[convertedColIndex];
185
247
  };
@@ -3,7 +3,7 @@ export { findControlsHoverDecoration, createControlsHoverDecoration, createColum
3
3
  export { isIsolating, containsHeaderColumn, containsHeaderRow, checkIfHeaderColumnEnabled, checkIfHeaderRowEnabled, checkIfNumberColumnEnabled, isLayoutSupported, getTableWidth, tablesHaveDifferentColumnWidths, tablesHaveDifferentNoOfColumns, isTableNested, anyChildCellMergedAcrossRow, supportedHeaderRow } from './nodes';
4
4
  export { unwrapContentFromTable, removeTableFromFirstChild, removeTableFromLastChild, transformSliceToRemoveOpenTable, transformSliceToCorrectEmptyTableCells, transformSliceToFixHardBreakProblemOnCopyFromCell } from './paste';
5
5
  export { isCell, isCornerButton, isInsertRowButton, isColumnControlsDecorations, isTableControlsButton, isTableContainerOrWrapper, isRowControlsButton, isDragRowControlsButton, isDragColumnFloatingInsertDot, isDragRowFloatingInsertDot, isDragCornerButton, getColumnOrRowIndex, getMousePositionHorizontalRelativeByElement, getMousePositionVerticalRelativeByElement, updateResizeHandles, isResizeHandleDecoration, hasResizeHandler } from './dom';
6
- export { getColumnsWidths, isColumnDeleteButtonVisible, getColumnDeleteButtonParams, getColumnClassNames } from './column-controls';
6
+ export { convertHTMLCellIndexToColumnIndex, getColumnsWidths, isColumnDeleteButtonVisible, getColumnDeleteButtonParams, getColumnClassNames, getColumnIndexMappedToColumnIndexInFirstRow } from './column-controls';
7
7
  export { getRowHeights, isRowDeleteButtonVisible, getRowDeleteButtonParams, getRowsParams, getRowClassNames, copyPreviousRow } from './row-controls';
8
8
  export { getSelectedTableInfo, getSelectedCellInfo } from './analytics';
9
9
  export { getMergedCellsPositions } from './table';
@@ -1,5 +1,6 @@
1
1
  import type { Selection } from '@atlaskit/editor-prosemirror/state';
2
2
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
+ import { TableMap } from '@atlaskit/editor-tables/table-map';
3
4
  export declare const getColumnsWidths: (view: EditorView) => Array<number | undefined>;
4
5
  export declare const isColumnDeleteButtonVisible: (selection: Selection) => boolean;
5
6
  export declare const getColumnDeleteButtonParams: (columnsWidths: Array<number | undefined>, selection: Selection) => {
@@ -8,3 +9,5 @@ export declare const getColumnDeleteButtonParams: (columnsWidths: Array<number |
8
9
  } | null;
9
10
  export declare const getColumnClassNames: (index: number, selection: Selection, hoveredColumns?: number[], isInDanger?: boolean, isResizing?: boolean) => string;
10
11
  export declare const colWidthsForRow: (tr: HTMLTableRowElement) => string;
12
+ export declare const convertHTMLCellIndexToColumnIndex: (htmlColIndex: number, htmlRowIndex: number, tableMap: TableMap) => number;
13
+ export declare const getColumnIndexMappedToColumnIndexInFirstRow: (convertedColIndex: number, htmlRowIndex: number, tableMap: TableMap) => number;
@@ -3,7 +3,7 @@ export { findControlsHoverDecoration, createControlsHoverDecoration, createColum
3
3
  export { isIsolating, containsHeaderColumn, containsHeaderRow, checkIfHeaderColumnEnabled, checkIfHeaderRowEnabled, checkIfNumberColumnEnabled, isLayoutSupported, getTableWidth, tablesHaveDifferentColumnWidths, tablesHaveDifferentNoOfColumns, isTableNested, anyChildCellMergedAcrossRow, supportedHeaderRow, } from './nodes';
4
4
  export { unwrapContentFromTable, removeTableFromFirstChild, removeTableFromLastChild, transformSliceToRemoveOpenTable, transformSliceToCorrectEmptyTableCells, transformSliceToFixHardBreakProblemOnCopyFromCell, } from './paste';
5
5
  export { isCell, isCornerButton, isInsertRowButton, isColumnControlsDecorations, isTableControlsButton, isTableContainerOrWrapper, isRowControlsButton, isDragRowControlsButton, isDragColumnFloatingInsertDot, isDragRowFloatingInsertDot, isDragCornerButton, getColumnOrRowIndex, getMousePositionHorizontalRelativeByElement, getMousePositionVerticalRelativeByElement, updateResizeHandles, isResizeHandleDecoration, hasResizeHandler, } from './dom';
6
- export { getColumnsWidths, isColumnDeleteButtonVisible, getColumnDeleteButtonParams, getColumnClassNames, } from './column-controls';
6
+ export { convertHTMLCellIndexToColumnIndex, getColumnsWidths, isColumnDeleteButtonVisible, getColumnDeleteButtonParams, getColumnClassNames, getColumnIndexMappedToColumnIndexInFirstRow, } from './column-controls';
7
7
  export { getRowHeights, isRowDeleteButtonVisible, getRowDeleteButtonParams, getRowsParams, getRowClassNames, copyPreviousRow, } from './row-controls';
8
8
  export type { RowParams } from './row-controls';
9
9
  export { getSelectedTableInfo, getSelectedCellInfo } from './analytics';
@@ -1,5 +1,6 @@
1
1
  import type { Selection } from '@atlaskit/editor-prosemirror/state';
2
2
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
3
+ import { TableMap } from '@atlaskit/editor-tables/table-map';
3
4
  export declare const getColumnsWidths: (view: EditorView) => Array<number | undefined>;
4
5
  export declare const isColumnDeleteButtonVisible: (selection: Selection) => boolean;
5
6
  export declare const getColumnDeleteButtonParams: (columnsWidths: Array<number | undefined>, selection: Selection) => {
@@ -8,3 +9,5 @@ export declare const getColumnDeleteButtonParams: (columnsWidths: Array<number |
8
9
  } | null;
9
10
  export declare const getColumnClassNames: (index: number, selection: Selection, hoveredColumns?: number[], isInDanger?: boolean, isResizing?: boolean) => string;
10
11
  export declare const colWidthsForRow: (tr: HTMLTableRowElement) => string;
12
+ export declare const convertHTMLCellIndexToColumnIndex: (htmlColIndex: number, htmlRowIndex: number, tableMap: TableMap) => number;
13
+ export declare const getColumnIndexMappedToColumnIndexInFirstRow: (convertedColIndex: number, htmlRowIndex: number, tableMap: TableMap) => number;
@@ -3,7 +3,7 @@ export { findControlsHoverDecoration, createControlsHoverDecoration, createColum
3
3
  export { isIsolating, containsHeaderColumn, containsHeaderRow, checkIfHeaderColumnEnabled, checkIfHeaderRowEnabled, checkIfNumberColumnEnabled, isLayoutSupported, getTableWidth, tablesHaveDifferentColumnWidths, tablesHaveDifferentNoOfColumns, isTableNested, anyChildCellMergedAcrossRow, supportedHeaderRow, } from './nodes';
4
4
  export { unwrapContentFromTable, removeTableFromFirstChild, removeTableFromLastChild, transformSliceToRemoveOpenTable, transformSliceToCorrectEmptyTableCells, transformSliceToFixHardBreakProblemOnCopyFromCell, } from './paste';
5
5
  export { isCell, isCornerButton, isInsertRowButton, isColumnControlsDecorations, isTableControlsButton, isTableContainerOrWrapper, isRowControlsButton, isDragRowControlsButton, isDragColumnFloatingInsertDot, isDragRowFloatingInsertDot, isDragCornerButton, getColumnOrRowIndex, getMousePositionHorizontalRelativeByElement, getMousePositionVerticalRelativeByElement, updateResizeHandles, isResizeHandleDecoration, hasResizeHandler, } from './dom';
6
- export { getColumnsWidths, isColumnDeleteButtonVisible, getColumnDeleteButtonParams, getColumnClassNames, } from './column-controls';
6
+ export { convertHTMLCellIndexToColumnIndex, getColumnsWidths, isColumnDeleteButtonVisible, getColumnDeleteButtonParams, getColumnClassNames, getColumnIndexMappedToColumnIndexInFirstRow, } from './column-controls';
7
7
  export { getRowHeights, isRowDeleteButtonVisible, getRowDeleteButtonParams, getRowsParams, getRowClassNames, copyPreviousRow, } from './row-controls';
8
8
  export type { RowParams } from './row-controls';
9
9
  export { getSelectedTableInfo, getSelectedCellInfo } from './analytics';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "5.4.11",
3
+ "version": "5.4.13",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -27,9 +27,9 @@
27
27
  "releaseModel": "continuous"
28
28
  },
29
29
  "dependencies": {
30
- "@atlaskit/adf-schema": "^34.0.1",
31
- "@atlaskit/custom-steps": "^0.0.5",
32
- "@atlaskit/editor-common": "^76.24.0",
30
+ "@atlaskit/adf-schema": "^35.0.0",
31
+ "@atlaskit/custom-steps": "^0.0.6",
32
+ "@atlaskit/editor-common": "^76.25.0",
33
33
  "@atlaskit/editor-palette": "1.5.2",
34
34
  "@atlaskit/editor-plugin-analytics": "^0.3.0",
35
35
  "@atlaskit/editor-plugin-content-insertion": "^0.1.0",
@@ -1,4 +1,10 @@
1
- import { colWidthsForRow } from '../../../plugins/table/utils/column-controls';
1
+ import type { TableMap } from '@atlaskit/editor-tables';
2
+
3
+ import {
4
+ colWidthsForRow,
5
+ convertHTMLCellIndexToColumnIndex,
6
+ getColumnIndexMappedToColumnIndexInFirstRow,
7
+ } from '../../../plugins/table/utils/column-controls';
2
8
 
3
9
  const createRow = (colCount: number, childType = 'td') => {
4
10
  const tr = document.createElement('tr');
@@ -59,3 +65,141 @@ describe('table utilities', () => {
59
65
  });
60
66
  });
61
67
  });
68
+
69
+ describe('table utilities - convertHTMLCellIndexToColumnIndex', () => {
70
+ /*
71
+ Table looks like this:
72
+ ___ _______ ___ ___ ___
73
+ |___| |_A_|___|___|
74
+ |___| |___| |___|
75
+ |___|_______|_B_| |___|
76
+ |___|___|___|___| |___|
77
+ |___________|___|___|___|
78
+ |___________|___|_C_|___|
79
+ |___________|___|___|___|
80
+ |___|___|___|_D_|___|___|
81
+ |___|___|_E_____________|
82
+ */
83
+ const tableMap = {
84
+ width: 6,
85
+ height: 9,
86
+ map: [
87
+ 1, 5, 5, 9, 13, 17, 23, 5, 5, 27, 31, 35, 41, 5, 5, 45, 31, 49, 55, 59,
88
+ 63, 67, 31, 71, 77, 77, 77, 81, 31, 85, 91, 91, 91, 95, 99, 103, 109, 109,
89
+ 109, 113, 117, 121, 127, 131, 135, 139, 143, 147, 153, 157, 161, 161, 161,
90
+ 161,
91
+ ],
92
+ } as TableMap;
93
+
94
+ const testCases = [
95
+ [
96
+ 'case A: when hovered cell is in the first row and first row has merged cells',
97
+ 2, // htmlColIndex
98
+ 0, // htmlRowIndex
99
+ 3, // expected
100
+ ],
101
+ [
102
+ 'case B: when hoverd cell is not in the first row, but the row has merged cells',
103
+ 1,
104
+ 2,
105
+ 3,
106
+ ],
107
+ [
108
+ 'case C: when a cell that is in the row above and to the left of current cell is merged',
109
+ 3,
110
+ 5,
111
+ 5,
112
+ ],
113
+ [
114
+ 'case D: when first row has merged cell and hovered cell is below merged area',
115
+ 2,
116
+ 7,
117
+ 2,
118
+ ],
119
+ [
120
+ 'case E: when first row has merged cell and hovered cell is below merged area and is part of merged area',
121
+ 2,
122
+ 8,
123
+ 2,
124
+ ],
125
+ ];
126
+
127
+ describe('should convert cellIndex into colIndex correctly', () => {
128
+ test.each(testCases)(
129
+ '%s',
130
+ (message, htmlColIndex, htmlRowIndex, expected) => {
131
+ expect(
132
+ convertHTMLCellIndexToColumnIndex(
133
+ htmlColIndex as number,
134
+ htmlRowIndex as number,
135
+ tableMap,
136
+ ),
137
+ ).toEqual(expected);
138
+ },
139
+ );
140
+ });
141
+ });
142
+
143
+ describe('tableUtilities - getColumnIndexMappedToColumnIndexInFirstRow', () => {
144
+ /*
145
+ Table used for this tests looks like this:
146
+ ___ _______ ___ ___ ___ ___ ___ ___________ ___
147
+ |___|_______|___|___|___|___|___|___________|_D_|
148
+ |___|_A_|_B_|___|___|___|___|___|___|_E_|___|___|
149
+ |___|___|_______|___|_____C_|___|___|___|___|___|
150
+ |___|___|___|___|___|___|___|___|___|___|___|___|
151
+ */
152
+
153
+ const tableMap = {
154
+ width: 12,
155
+ height: 4,
156
+ map: [
157
+ 1, 5, 5, 9, 13, 17, 21, 25, 29, 29, 29, 33, 39, 43, 47, 51, 55, 59, 63,
158
+ 67, 71, 75, 79, 83, 89, 93, 97, 97, 101, 105, 105, 109, 113, 117, 121,
159
+ 125, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175,
160
+ ],
161
+ } as TableMap;
162
+
163
+ const testCases = [
164
+ [
165
+ 'case A: mouse is on a cell located below merged area of the first row closer to its start',
166
+ 2, // convertedColIndex
167
+ 1, // htmlRowIndex
168
+ 1, // expected
169
+ ],
170
+ [
171
+ 'case B: mouse is on a cell located below merged area of the first row close to its end',
172
+ 1,
173
+ 1,
174
+ 1,
175
+ ],
176
+ [
177
+ 'case E: mouse is on a cell located below merged area of the first row in the middle of the merged area',
178
+ 9,
179
+ 1,
180
+ 8,
181
+ ],
182
+ ['case C: mouse is on a merged cell located below the first row', 5, 2, 5],
183
+ [
184
+ 'case D: mouse is on a cell in the first row that is next to a merged cell',
185
+ 11,
186
+ 1,
187
+ 11,
188
+ ],
189
+ ];
190
+
191
+ describe('should adapt converted colIndex when first row has merged cells', () => {
192
+ test.each(testCases)(
193
+ '%s',
194
+ (message, convertedColIndex, htmlRowIndex, expected) => {
195
+ expect(
196
+ getColumnIndexMappedToColumnIndexInFirstRow(
197
+ convertedColIndex as number,
198
+ htmlRowIndex as number,
199
+ tableMap,
200
+ ),
201
+ ).toEqual(expected);
202
+ },
203
+ );
204
+ });
205
+ });
@@ -48,6 +48,8 @@ import { deleteColumns, deleteRows } from './transforms';
48
48
  import type { ElementContentRects } from './types';
49
49
  import { RESIZE_HANDLE_AREA_DECORATION_GAP } from './types';
50
50
  import {
51
+ convertHTMLCellIndexToColumnIndex,
52
+ getColumnIndexMappedToColumnIndexInFirstRow,
51
53
  getColumnOrRowIndex,
52
54
  getMousePositionHorizontalRelativeByElement,
53
55
  getMousePositionVerticalRelativeByElement,
@@ -552,24 +554,40 @@ const trackCellLocation = (view: EditorView, mouseEvent: Event) => {
552
554
  return;
553
555
  }
554
556
 
555
- const colIndex = maybeTableCell.cellIndex;
557
+ const htmlColIndex = maybeTableCell.cellIndex;
556
558
  const rowElement = closestElement(
557
559
  target as HTMLElement,
558
560
  'tr',
559
561
  ) as HTMLTableRowElement;
560
- const rowIndex = rowElement && rowElement.rowIndex;
562
+ const htmlRowIndex = rowElement && rowElement.rowIndex;
561
563
 
562
564
  const colHeight = tableRef.offsetHeight;
563
565
  const colWidth = maybeTableCell.offsetWidth;
564
566
 
567
+ const tableMap = tableNode && TableMap.get(tableNode);
568
+ let colIndex = htmlColIndex;
569
+ if (tableMap) {
570
+ const convertedColIndex = convertHTMLCellIndexToColumnIndex(
571
+ htmlColIndex,
572
+ htmlRowIndex,
573
+ tableMap,
574
+ );
575
+
576
+ colIndex = getColumnIndexMappedToColumnIndexInFirstRow(
577
+ convertedColIndex,
578
+ htmlRowIndex,
579
+ tableMap,
580
+ );
581
+ }
582
+
565
583
  if (
566
584
  hoveredCell.colIndex !== colIndex ||
567
- hoveredCell.rowIndex !== rowIndex ||
585
+ hoveredCell.rowIndex !== htmlRowIndex ||
568
586
  hoveredCell.colWidth !== colWidth ||
569
587
  hoveredCell.colHeight !== colHeight
570
588
  ) {
571
589
  hoverCell(
572
- rowIndex,
590
+ htmlRowIndex,
573
591
  colIndex,
574
592
  colWidth,
575
593
  colHeight,
@@ -134,26 +134,35 @@ export const DragHandle = ({
134
134
  ]);
135
135
 
136
136
  return (
137
- <button
138
- className={classnames(
139
- ClassName.DRAG_HANDLE_BUTTON_CONTAINER,
140
- appearance,
141
- isDragAndDropEnabled &&
142
- hasMergedCells &&
143
- ClassName.DRAG_HANDLE_DISABLED,
144
- )}
145
- ref={dragHandleDivRef}
146
- style={{
147
- transform: direction === 'column' ? 'none' : 'rotate(90deg)',
148
- pointerEvents: 'auto',
149
- }}
150
- data-testid="table-floating-column-controls-drag-handle"
151
- onMouseOver={onMouseOver}
152
- onMouseOut={onMouseOut}
153
- onMouseUp={onMouseUp}
154
- onClick={onClick}
155
- >
156
- <HandleIconComponent {...handleIconProps} />
137
+ <>
138
+ <button
139
+ className={classnames(
140
+ ClassName.DRAG_HANDLE_BUTTON_CONTAINER,
141
+ ClassName.CONTROLS_BUTTON,
142
+ appearance,
143
+ {
144
+ [ClassName.DRAG_HANDLE_DISABLED]:
145
+ isDragAndDropEnabled && hasMergedCells,
146
+ },
147
+ )}
148
+ ref={dragHandleDivRef}
149
+ style={{
150
+ transform: direction === 'column' ? 'none' : 'rotate(90deg)',
151
+ pointerEvents: 'auto',
152
+ }}
153
+ data-testid="table-floating-column-controls-drag-handle"
154
+ onMouseOver={onMouseOver}
155
+ onMouseOut={onMouseOut}
156
+ onMouseUp={(e) => {
157
+ // 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
158
+ // -> this is bad for a11y but is the current standard new copy/paste keyboard shortcuts should be introduced instead
159
+ editorView.focus();
160
+ onMouseUp && onMouseUp(e);
161
+ }}
162
+ onClick={onClick}
163
+ >
164
+ <HandleIconComponent {...handleIconProps} />
165
+ </button>
157
166
  {previewContainer &&
158
167
  previewWidth !== undefined &&
159
168
  previewHeight !== undefined &&
@@ -165,6 +174,6 @@ export const DragHandle = ({
165
174
  />,
166
175
  previewContainer,
167
176
  )}
168
- </button>
177
+ </>
169
178
  );
170
179
  };
@@ -108,7 +108,7 @@ export const TableFloatingColumnControls: React.FC<Props> = ({
108
108
  return [0];
109
109
  }, [tableRef, tableRect.height]);
110
110
 
111
- if (!tableRef) {
111
+ if (!tableRef || !tableActive) {
112
112
  return null;
113
113
  }
114
114
 
@@ -119,10 +119,6 @@ export const TableFloatingColumnControls: React.FC<Props> = ({
119
119
  ? stickyHeader.top
120
120
  : undefined;
121
121
 
122
- if (!tableActive) {
123
- return null;
124
- }
125
-
126
122
  return (
127
123
  <div
128
124
  className={ClassName.DRAG_COLUMN_CONTROLS_WRAPPER}
@@ -217,3 +217,84 @@ export const colWidthsForRow = (tr: HTMLTableRowElement) => {
217
217
 
218
218
  return pctWidths.map((pct) => `${pct}%`).join(' ');
219
219
  };
220
+
221
+ export const convertHTMLCellIndexToColumnIndex = (
222
+ htmlColIndex: number,
223
+ htmlRowIndex: number,
224
+ tableMap: TableMap,
225
+ ): number => {
226
+ // Same numbers (positions) in tableMap.map array mean that there are merged cells.
227
+ // Cells can be merged across columns. So we need to check if the cell on the left and current cell have the same value.
228
+ // Cells can be merged acroll rows. So we need to check if the cell above has the same value as current cell.
229
+ // When cell has the same value as the cell above it or the cell to the left of it, html cellIndex won't count it a separete column.
230
+ const width = tableMap.width;
231
+ const map = tableMap.map;
232
+ let htmlColCount = 0;
233
+
234
+ if (htmlRowIndex === 0) {
235
+ for (let colIndex = 0; colIndex < width; colIndex++) {
236
+ if (colIndex === 0 || map[colIndex] !== map[colIndex - 1]) {
237
+ htmlColCount++;
238
+ }
239
+
240
+ if (htmlColCount - 1 === htmlColIndex) {
241
+ return colIndex;
242
+ }
243
+ }
244
+ } else {
245
+ for (let colIndex = 0; colIndex < width; colIndex++) {
246
+ const currentCellMapIndex = htmlRowIndex * width + colIndex;
247
+ const cellAboveMapIndex = (htmlRowIndex - 1) * width + colIndex;
248
+ if (colIndex > 0) {
249
+ if (
250
+ map[currentCellMapIndex] !== map[currentCellMapIndex - 1] &&
251
+ map[currentCellMapIndex] !== map[cellAboveMapIndex]
252
+ ) {
253
+ htmlColCount++;
254
+ }
255
+ } else {
256
+ if (map[currentCellMapIndex] !== map[cellAboveMapIndex]) {
257
+ htmlColCount++;
258
+ }
259
+ }
260
+
261
+ if (htmlColCount - 1 === htmlColIndex) {
262
+ return colIndex;
263
+ }
264
+ }
265
+ }
266
+
267
+ return 0;
268
+ };
269
+
270
+ // When first row has merged cells, our converted column index needs to be mapped.
271
+ export const getColumnIndexMappedToColumnIndexInFirstRow = (
272
+ convertedColIndex: number,
273
+ htmlRowIndex: number,
274
+ tableMap: TableMap,
275
+ ): number => {
276
+ const width = tableMap.width;
277
+ const map = tableMap.map;
278
+ const mapColIndexToFistRowColIndex = [];
279
+ let htmlColCounFirstRow = 0;
280
+ let colSpan = 0;
281
+
282
+ if (htmlRowIndex === 0) {
283
+ return convertedColIndex;
284
+ }
285
+
286
+ for (let colIndex = 0; colIndex < width; colIndex++) {
287
+ if (colIndex === 0 || map[colIndex] !== map[colIndex - 1]) {
288
+ if (colSpan > 0) {
289
+ htmlColCounFirstRow += colSpan;
290
+ colSpan = 0;
291
+ }
292
+ htmlColCounFirstRow++;
293
+ } else if (map[colIndex] === map[colIndex - 1]) {
294
+ colSpan++;
295
+ }
296
+ mapColIndexToFistRowColIndex[colIndex] = htmlColCounFirstRow - 1;
297
+ }
298
+
299
+ return mapColIndexToFistRowColIndex[convertedColIndex];
300
+ };
@@ -59,10 +59,12 @@ export {
59
59
  hasResizeHandler,
60
60
  } from './dom';
61
61
  export {
62
+ convertHTMLCellIndexToColumnIndex,
62
63
  getColumnsWidths,
63
64
  isColumnDeleteButtonVisible,
64
65
  getColumnDeleteButtonParams,
65
66
  getColumnClassNames,
67
+ getColumnIndexMappedToColumnIndexInFirstRow,
66
68
  } from './column-controls';
67
69
  export {
68
70
  getRowHeights,