@atlaskit/editor-plugin-table 5.4.14 → 5.4.16

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 (123) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/afm-cc/tsconfig.json +70 -0
  3. package/dist/cjs/plugins/table/commands/hover.js +26 -6
  4. package/dist/cjs/plugins/table/commands/index.js +6 -0
  5. package/dist/cjs/plugins/table/commands/misc.js +8 -3
  6. package/dist/cjs/plugins/table/event-handlers.js +56 -34
  7. package/dist/cjs/plugins/table/nodeviews/TableComponent.js +4 -1
  8. package/dist/cjs/plugins/table/pm-plugins/drag-and-drop/commands.js +7 -6
  9. package/dist/cjs/plugins/table/pm-plugins/drag-and-drop/plugin.js +39 -7
  10. package/dist/cjs/plugins/table/pm-plugins/main.js +5 -3
  11. package/dist/cjs/plugins/table/reducer.js +1 -0
  12. package/dist/cjs/plugins/table/toolbar.js +6 -3
  13. package/dist/cjs/plugins/table/ui/ColumnResizeWidget/index.js +6 -3
  14. package/dist/cjs/plugins/table/ui/DragHandle/HandleIconComponent.js +3 -5
  15. package/dist/cjs/plugins/table/ui/DragHandle/index.js +3 -0
  16. package/dist/cjs/plugins/table/ui/DragPreview/index.js +0 -2
  17. package/dist/cjs/plugins/table/ui/FloatingContextualButton/index.js +10 -7
  18. package/dist/cjs/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +11 -10
  19. package/dist/cjs/plugins/table/ui/FloatingContextualMenu/index.js +6 -3
  20. package/dist/cjs/plugins/table/ui/FloatingDragMenu/DragMenu.js +7 -4
  21. package/dist/cjs/plugins/table/ui/TableFloatingColumnControls/ColumnControls/index.js +74 -37
  22. package/dist/cjs/plugins/table/ui/TableFloatingColumnControls/index.js +3 -1
  23. package/dist/cjs/plugins/table/ui/TableFloatingControls/RowControls/DragControls.js +63 -31
  24. package/dist/cjs/plugins/table/ui/TableFloatingControls/index.js +12 -8
  25. package/dist/cjs/plugins/table/utils/dom.js +16 -1
  26. package/dist/cjs/plugins/table/utils/index.js +6 -0
  27. package/dist/es2019/plugins/table/commands/hover.js +22 -5
  28. package/dist/es2019/plugins/table/commands/index.js +1 -1
  29. package/dist/es2019/plugins/table/commands/misc.js +8 -3
  30. package/dist/es2019/plugins/table/event-handlers.js +45 -20
  31. package/dist/es2019/plugins/table/nodeviews/TableComponent.js +4 -1
  32. package/dist/es2019/plugins/table/pm-plugins/drag-and-drop/commands.js +7 -6
  33. package/dist/es2019/plugins/table/pm-plugins/drag-and-drop/plugin.js +36 -3
  34. package/dist/es2019/plugins/table/pm-plugins/main.js +6 -4
  35. package/dist/es2019/plugins/table/reducer.js +1 -0
  36. package/dist/es2019/plugins/table/toolbar.js +5 -3
  37. package/dist/es2019/plugins/table/ui/ColumnResizeWidget/index.js +5 -3
  38. package/dist/es2019/plugins/table/ui/DragHandle/HandleIconComponent.js +3 -5
  39. package/dist/es2019/plugins/table/ui/DragHandle/index.js +2 -0
  40. package/dist/es2019/plugins/table/ui/DragPreview/index.js +0 -2
  41. package/dist/es2019/plugins/table/ui/FloatingContextualButton/index.js +9 -7
  42. package/dist/es2019/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +11 -9
  43. package/dist/es2019/plugins/table/ui/FloatingContextualMenu/index.js +5 -3
  44. package/dist/es2019/plugins/table/ui/FloatingDragMenu/DragMenu.js +6 -4
  45. package/dist/es2019/plugins/table/ui/TableFloatingColumnControls/ColumnControls/index.js +73 -37
  46. package/dist/es2019/plugins/table/ui/TableFloatingColumnControls/index.js +3 -1
  47. package/dist/es2019/plugins/table/ui/TableFloatingControls/RowControls/DragControls.js +62 -29
  48. package/dist/es2019/plugins/table/ui/TableFloatingControls/index.js +12 -9
  49. package/dist/es2019/plugins/table/utils/dom.js +13 -0
  50. package/dist/es2019/plugins/table/utils/index.js +1 -1
  51. package/dist/esm/plugins/table/commands/hover.js +25 -5
  52. package/dist/esm/plugins/table/commands/index.js +1 -1
  53. package/dist/esm/plugins/table/commands/misc.js +8 -3
  54. package/dist/esm/plugins/table/event-handlers.js +55 -33
  55. package/dist/esm/plugins/table/nodeviews/TableComponent.js +4 -1
  56. package/dist/esm/plugins/table/pm-plugins/drag-and-drop/commands.js +7 -6
  57. package/dist/esm/plugins/table/pm-plugins/drag-and-drop/plugin.js +35 -3
  58. package/dist/esm/plugins/table/pm-plugins/main.js +6 -4
  59. package/dist/esm/plugins/table/reducer.js +1 -0
  60. package/dist/esm/plugins/table/toolbar.js +5 -3
  61. package/dist/esm/plugins/table/ui/ColumnResizeWidget/index.js +5 -3
  62. package/dist/esm/plugins/table/ui/DragHandle/HandleIconComponent.js +3 -5
  63. package/dist/esm/plugins/table/ui/DragHandle/index.js +3 -0
  64. package/dist/esm/plugins/table/ui/DragPreview/index.js +0 -2
  65. package/dist/esm/plugins/table/ui/FloatingContextualButton/index.js +9 -7
  66. package/dist/esm/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +11 -9
  67. package/dist/esm/plugins/table/ui/FloatingContextualMenu/index.js +5 -3
  68. package/dist/esm/plugins/table/ui/FloatingDragMenu/DragMenu.js +6 -4
  69. package/dist/esm/plugins/table/ui/TableFloatingColumnControls/ColumnControls/index.js +74 -37
  70. package/dist/esm/plugins/table/ui/TableFloatingColumnControls/index.js +3 -1
  71. package/dist/esm/plugins/table/ui/TableFloatingControls/RowControls/DragControls.js +61 -29
  72. package/dist/esm/plugins/table/ui/TableFloatingControls/index.js +12 -8
  73. package/dist/esm/plugins/table/utils/dom.js +15 -0
  74. package/dist/esm/plugins/table/utils/index.js +1 -1
  75. package/dist/types/plugins/table/commands/hover.d.ts +2 -1
  76. package/dist/types/plugins/table/commands/index.d.ts +1 -1
  77. package/dist/types/plugins/table/commands/misc.d.ts +1 -1
  78. package/dist/types/plugins/table/event-handlers.d.ts +2 -0
  79. package/dist/types/plugins/table/pm-plugins/drag-and-drop/commands.d.ts +1 -1
  80. package/dist/types/plugins/table/types.d.ts +7 -2
  81. package/dist/types/plugins/table/ui/DragHandle/HandleIconComponent.d.ts +1 -0
  82. package/dist/types/plugins/table/ui/DragHandle/index.d.ts +2 -1
  83. package/dist/types/plugins/table/ui/TableFloatingColumnControls/ColumnControls/index.d.ts +2 -1
  84. package/dist/types/plugins/table/ui/TableFloatingColumnControls/index.d.ts +1 -0
  85. package/dist/types/plugins/table/ui/TableFloatingControls/RowControls/DragControls.d.ts +1 -0
  86. package/dist/types/plugins/table/ui/TableFloatingControls/index.d.ts +1 -0
  87. package/dist/types/plugins/table/utils/dom.d.ts +4 -0
  88. package/dist/types/plugins/table/utils/index.d.ts +1 -1
  89. package/dist/types-ts4.5/plugins/table/commands/hover.d.ts +2 -1
  90. package/dist/types-ts4.5/plugins/table/commands/index.d.ts +1 -1
  91. package/dist/types-ts4.5/plugins/table/commands/misc.d.ts +1 -1
  92. package/dist/types-ts4.5/plugins/table/event-handlers.d.ts +2 -0
  93. package/dist/types-ts4.5/plugins/table/pm-plugins/drag-and-drop/commands.d.ts +1 -1
  94. package/dist/types-ts4.5/plugins/table/types.d.ts +7 -2
  95. package/dist/types-ts4.5/plugins/table/ui/DragHandle/HandleIconComponent.d.ts +1 -0
  96. package/dist/types-ts4.5/plugins/table/ui/DragHandle/index.d.ts +2 -1
  97. package/dist/types-ts4.5/plugins/table/ui/TableFloatingColumnControls/ColumnControls/index.d.ts +2 -1
  98. package/dist/types-ts4.5/plugins/table/ui/TableFloatingColumnControls/index.d.ts +1 -0
  99. package/dist/types-ts4.5/plugins/table/ui/TableFloatingControls/RowControls/DragControls.d.ts +1 -0
  100. package/dist/types-ts4.5/plugins/table/ui/TableFloatingControls/index.d.ts +1 -0
  101. package/dist/types-ts4.5/plugins/table/utils/dom.d.ts +4 -0
  102. package/dist/types-ts4.5/plugins/table/utils/index.d.ts +1 -1
  103. package/package.json +2 -2
  104. package/src/__tests__/unit/event-handlers.ts +2 -2
  105. package/src/__tests__/unit/ui/RowDragControls.tsx +1 -0
  106. package/src/plugins/table/commands/hover.ts +37 -7
  107. package/src/plugins/table/commands/index.ts +1 -0
  108. package/src/plugins/table/commands/misc.ts +9 -3
  109. package/src/plugins/table/event-handlers.ts +47 -29
  110. package/src/plugins/table/nodeviews/TableComponent.tsx +4 -1
  111. package/src/plugins/table/pm-plugins/drag-and-drop/commands.ts +7 -5
  112. package/src/plugins/table/pm-plugins/drag-and-drop/plugin.ts +37 -2
  113. package/src/plugins/table/pm-plugins/main.ts +6 -3
  114. package/src/plugins/table/reducer.ts +1 -0
  115. package/src/plugins/table/types.ts +9 -2
  116. package/src/plugins/table/ui/DragHandle/HandleIconComponent.tsx +10 -9
  117. package/src/plugins/table/ui/DragHandle/index.tsx +4 -0
  118. package/src/plugins/table/ui/TableFloatingColumnControls/ColumnControls/index.tsx +129 -50
  119. package/src/plugins/table/ui/TableFloatingColumnControls/index.tsx +3 -0
  120. package/src/plugins/table/ui/TableFloatingControls/RowControls/DragControls.tsx +128 -41
  121. package/src/plugins/table/ui/TableFloatingControls/index.tsx +12 -5
  122. package/src/plugins/table/utils/dom.ts +22 -0
  123. package/src/plugins/table/utils/index.ts +1 -0
@@ -9,6 +9,7 @@ import {
9
9
  type HandleIconProps = {
10
10
  hasMergedCells: boolean;
11
11
  direction: 'row' | 'column';
12
+ forceDefaultHandle: boolean;
12
13
  isDragMenuOpen: boolean | undefined;
13
14
  isRowHandleHovered: boolean;
14
15
  isColumnHandleHovered: boolean;
@@ -20,6 +21,7 @@ type HandleIconProps = {
20
21
  export const HandleIconComponent = (props: HandleIconProps) => {
21
22
  const {
22
23
  direction,
24
+ forceDefaultHandle,
23
25
  isDragMenuOpen,
24
26
  isRowHandleHovered,
25
27
  isColumnHandleHovered,
@@ -29,22 +31,21 @@ export const HandleIconComponent = (props: HandleIconProps) => {
29
31
  dragMenuDirection,
30
32
  } = props;
31
33
  const isHandleHovered = isRowHandleHovered || isColumnHandleHovered;
34
+
32
35
  const isCurrentRowOrColumnSelected =
33
36
  isCurrentRowSelected || isCurrentColumnSelected;
37
+
34
38
  const isDragMenuOpenOnCurrentRowOrColumn =
35
39
  isDragMenuOpen &&
36
40
  dragMenuDirection === direction &&
37
41
  isCurrentRowOrColumnSelected;
38
42
 
39
- const showNormalHandle = hasMergedCells ? (
40
- <DragHandleDisabledIcon />
41
- ) : (
42
- <DragHandleIcon />
43
- );
44
-
45
- // hoverred handle or open drag menu
46
- if (isHandleHovered || isDragMenuOpenOnCurrentRowOrColumn) {
47
- return showNormalHandle;
43
+ if (
44
+ forceDefaultHandle ||
45
+ isHandleHovered ||
46
+ isDragMenuOpenOnCurrentRowOrColumn
47
+ ) {
48
+ return hasMergedCells ? <DragHandleDisabledIcon /> : <DragHandleIcon />;
48
49
  }
49
50
 
50
51
  return <MinimisedHandleIcon />;
@@ -22,6 +22,7 @@ type DragHandleAppearance = 'default' | 'selected' | 'disabled' | 'danger';
22
22
  type DragHandleProps = {
23
23
  tableLocalId: string;
24
24
  indexes: number[];
25
+ forceDefaultHandle?: boolean;
25
26
  previewWidth?: number;
26
27
  previewHeight?: number;
27
28
  direction?: TableDirection;
@@ -38,6 +39,7 @@ export const DragHandle = ({
38
39
  direction = 'row',
39
40
  appearance = 'default',
40
41
  indexes,
42
+ forceDefaultHandle = false,
41
43
  previewWidth,
42
44
  previewHeight,
43
45
  onMouseOver,
@@ -63,6 +65,7 @@ export const DragHandle = ({
63
65
  isDragMenuOpen &&
64
66
  direction === 'row' &&
65
67
  hoveredCell.rowIndex === dragMenuIndex;
68
+
66
69
  const isCurrentColumnSelected =
67
70
  isDragMenuOpen &&
68
71
  direction === 'column' &&
@@ -83,6 +86,7 @@ export const DragHandle = ({
83
86
  const handleIconProps = {
84
87
  hasMergedCells,
85
88
  direction,
89
+ forceDefaultHandle,
86
90
  isDragMenuOpen,
87
91
  isRowHandleHovered,
88
92
  isColumnHandleHovered,
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @atlaskit/design-system/prefer-primitives */
1
2
  import type { MouseEvent } from 'react';
2
3
  import React, { useCallback, useMemo } from 'react';
3
4
 
@@ -13,7 +14,8 @@ import {
13
14
  selectColumn,
14
15
  } from '../../../commands';
15
16
  import { toggleDragMenu } from '../../../pm-plugins/drag-and-drop/commands';
16
- import type { CellHoverMeta } from '../../../types';
17
+ import { getPluginState as getDragDropPluginState } from '../../../pm-plugins/drag-and-drop/plugin-factory';
18
+ import type { CellHoverMeta, HandleTypes } from '../../../types';
17
19
  import { TableCssClassName as ClassName } from '../../../types';
18
20
  import { getRowsParams, getSelectedColumnIndexes } from '../../../utils';
19
21
  import { DragHandle } from '../../DragHandle';
@@ -30,6 +32,7 @@ export interface ColumnControlsProps {
30
32
  rowHeights?: number[];
31
33
  colWidths?: (number | undefined)[];
32
34
  hasHeaderColumn?: boolean;
35
+ isTableHovered?: boolean;
33
36
  }
34
37
 
35
38
  const getSelectedColumns = (selection: Selection) => {
@@ -55,6 +58,7 @@ export const ColumnControls = ({
55
58
  rowHeights,
56
59
  colWidths,
57
60
  hasHeaderColumn,
61
+ isTableHovered,
58
62
  }: ColumnControlsProps) => {
59
63
  const widths =
60
64
  colWidths?.map((width) => (width ? `${width - 1}px` : '0px')).join(' ') ??
@@ -64,14 +68,6 @@ export const ColumnControls = ({
64
68
  const colIndex = hoveredCell?.colIndex;
65
69
  const selectedColIndexes = getSelectedColumns(editorView.state.selection);
66
70
 
67
- const gridColumnPosition = useMemo(() => {
68
- // if more than one row is selected, ensure the handle spans over the selected range
69
- if (selectedColIndexes.includes(colIndex!)) {
70
- return `${selectedColIndexes[0] + 1} / span ${selectedColIndexes.length}`;
71
- }
72
- return `${colIndex! + 1} / span 1`;
73
- }, [colIndex, selectedColIndexes]);
74
-
75
71
  const firstRow = tableRef.querySelector('tr');
76
72
  const hasHeaderRow = firstRow
77
73
  ? firstRow.getAttribute('data-header-row')
@@ -109,11 +105,15 @@ export const ColumnControls = ({
109
105
 
110
106
  // update hovered cell location
111
107
  const { state, dispatch } = editorView;
112
- if (tableActive && hoveredCell?.colIndex !== Number(colIndex)) {
113
- hoverCell(hoveredCell?.rowIndex, Number(colIndex))(state, dispatch);
108
+ if (tableActive) {
109
+ // For context: Whenever we mouse over a column or row drag handle, we will ALWAYS be hovering over the 0 index
110
+ // of the opposite dimension. For example; here when we mouse over the column drag handle then we're technically
111
+ // also hovering over row 0 index. And vice-versa with rows. This means we don't need to worry about knowing the
112
+ // current row index. We can just force it to 0.
113
+ hoverCell(0, Number(colIndex))(state, dispatch);
114
114
  }
115
115
  },
116
- [editorView, hoveredCell?.colIndex, hoveredCell?.rowIndex, tableActive],
116
+ [editorView, tableActive],
117
117
  );
118
118
 
119
119
  const handleMouseOut = useCallback(() => {
@@ -132,6 +132,122 @@ export const ColumnControls = ({
132
132
  return [colIndex!];
133
133
  }, [colIndex]);
134
134
 
135
+ const previewHeight = rowHeights?.reduce((sum, cur) => sum + cur, 0) ?? 0;
136
+
137
+ const generateHandleByType = useCallback(
138
+ (type: HandleTypes): JSX.Element | null => {
139
+ if (!hoveredCell) {
140
+ return null;
141
+ }
142
+ const { isDragMenuOpen, dragMenuIndex, dragMenuDirection } =
143
+ getDragDropPluginState(editorView.state);
144
+ const isHover = type === 'hover';
145
+
146
+ const isHoveredOnSelectedColumn =
147
+ isDragMenuOpen &&
148
+ dragMenuDirection === 'column' &&
149
+ dragMenuIndex === colIndex;
150
+
151
+ const showCondition = isHover
152
+ ? !isHoveredOnSelectedColumn && Number.isFinite(hoveredCell?.colIndex)
153
+ : isDragMenuOpen && dragMenuDirection === 'column';
154
+
155
+ if (!showCondition) {
156
+ return null;
157
+ }
158
+
159
+ const selectedColumnPosition = `${dragMenuIndex + 1} / span ${
160
+ selectedColIndexes.length
161
+ }`;
162
+
163
+ const gridColumnPosition = selectedColIndexes.includes(colIndex!)
164
+ ? `${selectedColIndexes[0] + 1} / span ${selectedColIndexes.length}`
165
+ : `${colIndex! + 1} / span 1`;
166
+
167
+ const selectedApprearance = isInDanger ? 'danger' : 'selected';
168
+ const hoveredAppearance = selectedColIndexes.includes(colIndex!)
169
+ ? isInDanger
170
+ ? 'danger'
171
+ : 'selected'
172
+ : 'default';
173
+
174
+ return (
175
+ showCondition && (
176
+ <div
177
+ key={type}
178
+ style={{
179
+ gridColumn: isHover ? gridColumnPosition : selectedColumnPosition,
180
+ display: 'flex',
181
+ justifyContent: 'center',
182
+ alignItems: 'center',
183
+ height: 'fit-content',
184
+ placeSelf: 'center',
185
+ zIndex: 99,
186
+ }}
187
+ data-column-control-index={hoveredCell.colIndex}
188
+ data-testid={
189
+ isHover
190
+ ? 'table-floating-column-control'
191
+ : 'table-floating-column-control-selected'
192
+ }
193
+ >
194
+ <DragHandle
195
+ direction="column"
196
+ tableLocalId={localId || ''}
197
+ indexes={isHover ? colIndexes : selectedColIndexes}
198
+ forceDefaultHandle={!isHover}
199
+ previewWidth={colWidths?.[colIndex!] ?? 48}
200
+ previewHeight={previewHeight}
201
+ appearance={isHover ? hoveredAppearance : selectedApprearance}
202
+ onClick={handleClick}
203
+ onMouseOver={handleMouseOver}
204
+ onMouseOut={handleMouseOut}
205
+ onMouseUp={handleMouseUp}
206
+ editorView={editorView}
207
+ />
208
+ </div>
209
+ )
210
+ );
211
+ },
212
+ [
213
+ colIndex,
214
+ previewHeight,
215
+ colWidths,
216
+ colIndexes,
217
+ editorView,
218
+ handleClick,
219
+ handleMouseOut,
220
+ handleMouseOver,
221
+ handleMouseUp,
222
+ hoveredCell,
223
+ isInDanger,
224
+ localId,
225
+ selectedColIndexes,
226
+ ],
227
+ );
228
+
229
+ const columnHandles = useCallback(
230
+ (hoveredCell: CellHoverMeta | undefined) => {
231
+ if (!hoveredCell) {
232
+ return null;
233
+ }
234
+
235
+ if (hoveredCell.colIndex === undefined) {
236
+ return generateHandleByType('selected');
237
+ }
238
+
239
+ const sortedHandles = [
240
+ generateHandleByType('hover'),
241
+ generateHandleByType('selected'),
242
+ ];
243
+
244
+ return hoveredCell.colIndex < selectedColIndexes[0]
245
+ ? sortedHandles
246
+ : sortedHandles.reverse();
247
+ },
248
+ [generateHandleByType, selectedColIndexes],
249
+ );
250
+
135
251
  return (
136
252
  <div
137
253
  className={ClassName.DRAG_COLUMN_CONTROLS}
@@ -173,44 +289,7 @@ export const ColumnControls = ({
173
289
  />
174
290
  </div>
175
291
  ))}
176
- {tableActive &&
177
- !isResizing &&
178
- !!hoveredCell &&
179
- Number.isFinite(hoveredCell.colIndex) && (
180
- <div
181
- style={{
182
- gridColumn: gridColumnPosition,
183
- display: 'flex',
184
- justifyContent: 'center',
185
- alignItems: 'center',
186
- height: 'fit-content',
187
- placeSelf: 'center',
188
- zIndex: 99,
189
- }}
190
- data-column-control-index={hoveredCell.colIndex}
191
- data-testid="table-floating-column-control"
192
- >
193
- <DragHandle
194
- direction="column"
195
- tableLocalId={localId || ''}
196
- indexes={colIndexes}
197
- previewWidth={hoveredCell.colWidth}
198
- previewHeight={hoveredCell.colHeight}
199
- appearance={
200
- selectedColIndexes.includes(hoveredCell.colIndex!)
201
- ? isInDanger
202
- ? 'danger'
203
- : 'selected'
204
- : 'default'
205
- }
206
- onClick={handleClick}
207
- onMouseOver={handleMouseOver}
208
- onMouseOut={handleMouseOut}
209
- onMouseUp={handleMouseUp}
210
- editorView={editorView}
211
- />
212
- </div>
213
- )}
292
+ {tableActive && !isResizing && columnHandles(hoveredCell)}
214
293
  </div>
215
294
  </div>
216
295
  );
@@ -34,6 +34,7 @@ export interface Props {
34
34
  isResizing?: boolean;
35
35
  ordering?: TableColumnOrdering;
36
36
  stickyHeader?: RowStickyState;
37
+ isTableHovered?: boolean;
37
38
  }
38
39
 
39
40
  export const TableFloatingColumnControls: React.FC<Props> = ({
@@ -47,6 +48,7 @@ export const TableFloatingColumnControls: React.FC<Props> = ({
47
48
  stickyHeader,
48
49
  selection,
49
50
  isInDanger,
51
+ isTableHovered,
50
52
  }) => {
51
53
  const [tableRect, setTableRect] = useState<{ width: number; height: number }>(
52
54
  { width: 0, height: 0 },
@@ -130,6 +132,7 @@ export const TableFloatingColumnControls: React.FC<Props> = ({
130
132
  tableRef={tableRef}
131
133
  isResizing={isResizing}
132
134
  tableActive={tableActive}
135
+ isTableHovered={isTableHovered}
133
136
  stickyTop={tableActive ? stickyTop : undefined}
134
137
  localId={currentNodeLocalId}
135
138
  isInDanger={isInDanger}
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @atlaskit/design-system/prefer-primitives */
1
2
  import type { MouseEvent } from 'react';
2
3
  import React, {
3
4
  Fragment,
@@ -20,9 +21,14 @@ import { token } from '@atlaskit/tokens';
20
21
 
21
22
  import { clearHoverSelection } from '../../../commands';
22
23
  import { toggleDragMenu } from '../../../pm-plugins/drag-and-drop/commands';
24
+ import { getPluginState as getDragDropPluginState } from '../../../pm-plugins/drag-and-drop/plugin-factory';
23
25
  import { getPluginState as getTablePluginState } from '../../../pm-plugins/plugin-factory';
24
26
  import { TableCssClassName as ClassName } from '../../../types';
25
- import type { CellHoverMeta, DraggableSourceData } from '../../../types';
27
+ import type {
28
+ CellHoverMeta,
29
+ DraggableSourceData,
30
+ HandleTypes,
31
+ } from '../../../types';
26
32
  import {
27
33
  getRowHeights,
28
34
  getRowsParams,
@@ -41,6 +47,7 @@ type DragControlsProps = {
41
47
  hoveredCell?: CellHoverMeta;
42
48
  isInDanger?: boolean;
43
49
  isResizing?: boolean;
50
+ isTableHovered?: boolean;
44
51
  hoverRows: (rows: number[], danger?: boolean) => void;
45
52
  selectRow: (row: number, expand: boolean) => void;
46
53
  updateCellHoverLocation: (rowIndex: number) => void;
@@ -66,6 +73,7 @@ const DragControlsComponent = ({
66
73
  editorView,
67
74
  isInDanger,
68
75
  isResizing,
76
+ isTableHovered,
69
77
  hoverRows,
70
78
  selectRow,
71
79
  updateCellHoverLocation,
@@ -112,14 +120,6 @@ const DragControlsComponent = ({
112
120
 
113
121
  const rowIndex = hoveredCell?.rowIndex;
114
122
 
115
- const gridRowPosition = useMemo(() => {
116
- // if more than one row is selected, ensure the handle spans over the selected range
117
- if (selectedRowIndexes.includes(rowIndex!)) {
118
- return `${selectedRowIndexes[0] + 1} / span ${selectedRowIndexes.length}`;
119
- }
120
- return `${rowIndex! + 1} / span 1`;
121
- }, [rowIndex, selectedRowIndexes]);
122
-
123
123
  const handleMouseOut = useCallback(() => {
124
124
  if (tableActive) {
125
125
  const { state, dispatch } = editorView;
@@ -161,6 +161,124 @@ const DragControlsComponent = ({
161
161
  [rowIndex, selectRow],
162
162
  );
163
163
 
164
+ const generateHandleByType = useCallback(
165
+ (type: HandleTypes): JSX.Element | null => {
166
+ if (!hoveredCell) {
167
+ return null;
168
+ }
169
+ const { isDragMenuOpen, dragMenuIndex, dragMenuDirection } =
170
+ getDragDropPluginState(editorView.state);
171
+ const isHover = type === 'hover';
172
+
173
+ const isHoveredOnSelectedRow =
174
+ isDragMenuOpen &&
175
+ dragMenuDirection === 'row' &&
176
+ dragMenuIndex === rowIndex;
177
+
178
+ const showCondition = isHover
179
+ ? !isHoveredOnSelectedRow &&
180
+ !selectedRowIndexes.includes(rowIndex!) &&
181
+ Number.isFinite(hoveredCell?.colIndex)
182
+ : isDragMenuOpen &&
183
+ dragMenuDirection === 'row' &&
184
+ Number.isFinite(hoveredCell?.colIndex);
185
+
186
+ if (!showCondition) {
187
+ return null;
188
+ }
189
+
190
+ const gridRowPosition =
191
+ // if more than one row is selected, ensure the handle spans over the selected range
192
+ selectedRowIndexes.includes(rowIndex!)
193
+ ? `${selectedRowIndexes[0] + 1} / span ${selectedRowIndexes.length}`
194
+ : `${rowIndex! + 1} / span 1`;
195
+
196
+ const selectedRowPosition = `${dragMenuIndex + 1} / span ${
197
+ selectedRowIndexes.length
198
+ }`;
199
+
200
+ const selectedApprearance = isInDanger ? 'danger' : 'selected';
201
+ const hoveredAppearance = selectedRowIndexes.includes(rowIndex!)
202
+ ? isInDanger
203
+ ? 'danger'
204
+ : 'selected'
205
+ : 'default';
206
+
207
+ return (
208
+ showCondition && (
209
+ <div
210
+ key={type}
211
+ style={{
212
+ gridRow: isHover ? gridRowPosition : selectedRowPosition,
213
+ gridColumn: '2',
214
+ display: 'flex',
215
+ height: '100%',
216
+ alignItems: 'center',
217
+ justifyContent: 'center',
218
+ }}
219
+ data-testid={
220
+ isHover
221
+ ? 'table-floating-row-drag-handle'
222
+ : 'table-floating-row-control-selected'
223
+ }
224
+ >
225
+ <DragHandle
226
+ direction="row"
227
+ tableLocalId={currentNodeLocalId}
228
+ indexes={isHover ? rowIndexes : selectedRowIndexes}
229
+ forceDefaultHandle={!isHover}
230
+ previewWidth={tableWidth}
231
+ previewHeight={rowHeights[rowIndex!]}
232
+ appearance={isHover ? hoveredAppearance : selectedApprearance}
233
+ onClick={handleClick}
234
+ onMouseOver={handleMouseOver}
235
+ onMouseOut={handleMouseOut}
236
+ onMouseUp={onMouseUp}
237
+ editorView={editorView}
238
+ />
239
+ </div>
240
+ )
241
+ );
242
+ },
243
+ [
244
+ currentNodeLocalId,
245
+ editorView,
246
+ handleClick,
247
+ handleMouseOut,
248
+ handleMouseOver,
249
+ hoveredCell,
250
+ isInDanger,
251
+ onMouseUp,
252
+ rowHeights,
253
+ rowIndex,
254
+ rowIndexes,
255
+ selectedRowIndexes,
256
+ tableWidth,
257
+ ],
258
+ );
259
+
260
+ const rowHandles = useCallback(
261
+ (hoveredCell: CellHoverMeta | undefined) => {
262
+ if (!hoveredCell) {
263
+ return null;
264
+ }
265
+
266
+ if (hoveredCell.rowIndex === undefined) {
267
+ return generateHandleByType('selected');
268
+ }
269
+
270
+ const sortedHandles = [
271
+ generateHandleByType('hover'),
272
+ generateHandleByType('selected'),
273
+ ];
274
+
275
+ return hoveredCell.rowIndex < selectedRowIndexes[0]
276
+ ? sortedHandles
277
+ : sortedHandles.reverse();
278
+ },
279
+ [generateHandleByType, selectedRowIndexes],
280
+ );
281
+
164
282
  return (
165
283
  <div
166
284
  className={ClassName.DRAG_ROW_CONTROLS}
@@ -217,38 +335,7 @@ const DragControlsComponent = ({
217
335
  )}
218
336
  </Fragment>
219
337
  ))}
220
- {!isResizing && Number.isFinite(rowIndex) && (
221
- <div
222
- style={{
223
- gridRow: gridRowPosition,
224
- gridColumn: '2',
225
- display: 'flex',
226
- height: '100%',
227
- alignItems: 'center',
228
- justifyContent: 'center',
229
- }}
230
- data-testid="table-floating-row-drag-handle"
231
- >
232
- <DragHandle
233
- tableLocalId={currentNodeLocalId}
234
- indexes={rowIndexes}
235
- previewWidth={tableWidth}
236
- previewHeight={rowHeights[rowIndex!]}
237
- appearance={
238
- selectedRowIndexes.includes(rowIndex!)
239
- ? isInDanger
240
- ? 'danger'
241
- : 'selected'
242
- : 'default'
243
- }
244
- onClick={handleClick}
245
- onMouseOver={handleMouseOver}
246
- onMouseOut={handleMouseOut}
247
- onMouseUp={onMouseUp}
248
- editorView={editorView}
249
- />
250
- </div>
251
- )}
338
+ {!isResizing && Number.isFinite(rowIndex) && rowHandles(hoveredCell)}
252
339
  </div>
253
340
  );
254
341
  };
@@ -7,7 +7,6 @@ import type { Selection } from '@atlaskit/editor-prosemirror/state';
7
7
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
8
8
 
9
9
  import { hoverCell, hoverRows, selectRow } from '../../commands';
10
- import { getPluginState } from '../../pm-plugins/plugin-factory';
11
10
  import type { RowStickyState } from '../../pm-plugins/sticky-headers';
12
11
  import { TableCssClassName as ClassName } from '../../types';
13
12
  import type { CellHoverMeta } from '../../types';
@@ -24,6 +23,7 @@ export interface Props {
24
23
  tableNode?: PmNode;
25
24
  tableActive?: boolean;
26
25
  isInDanger?: boolean;
26
+ isTableHovered?: boolean;
27
27
  isResizing?: boolean;
28
28
  isHeaderRowEnabled?: boolean;
29
29
  isHeaderColumnEnabled?: boolean;
@@ -105,6 +105,7 @@ export default class TableFloatingControls extends Component<Props, State> {
105
105
  headerRowHeight,
106
106
  stickyHeader,
107
107
  hoveredCell,
108
+ isTableHovered,
108
109
  } = this.props;
109
110
  return (
110
111
  this.state.tableWrapperWidth !== nextState.tableWrapperWidth ||
@@ -121,7 +122,8 @@ export default class TableFloatingControls extends Component<Props, State> {
121
122
  isSelectionUpdated(selection!, nextProps.selection) ||
122
123
  headerRowHeight !== nextProps.headerRowHeight ||
123
124
  stickyHeader !== nextProps.stickyHeader ||
124
- hoveredCell !== nextProps.hoveredCell
125
+ hoveredCell !== nextProps.hoveredCell ||
126
+ isTableHovered !== nextProps.isTableHovered
125
127
  );
126
128
  }
127
129
 
@@ -147,6 +149,7 @@ export default class TableFloatingControls extends Component<Props, State> {
147
149
  stickyHeader,
148
150
  isDragAndDropEnabled,
149
151
  hoveredCell,
152
+ isTableHovered,
150
153
  } = this.props;
151
154
 
152
155
  if (!tableRef) {
@@ -196,6 +199,7 @@ export default class TableFloatingControls extends Component<Props, State> {
196
199
  tableRef={tableRef}
197
200
  tableNode={tableNode}
198
201
  hoveredCell={hoveredCell}
202
+ isTableHovered={isTableHovered}
199
203
  editorView={editorView}
200
204
  tableActive={tableActive}
201
205
  isInDanger={isInDanger}
@@ -256,10 +260,13 @@ export default class TableFloatingControls extends Component<Props, State> {
256
260
  private updateCellHoverLocation = (rowIndex: number) => {
257
261
  const { editorView, tableActive } = this.props;
258
262
  const { state, dispatch } = editorView;
259
- const { hoveredCell } = getPluginState(state);
260
263
 
261
- if (tableActive && hoveredCell.rowIndex !== rowIndex) {
262
- hoverCell(rowIndex, hoveredCell.colIndex)(state, dispatch);
264
+ if (tableActive) {
265
+ // For context: Whenever we mouse over a column or row drag handle, we will ALWAYS be hovering over the 0 index
266
+ // of the opposite dimension. For example; here when we mouse over the row drag handle then we're technically
267
+ // also hovering over column 0 index. And vice-versa with columns. This means we don't need to worry about knowing the
268
+ // current column index. We can just force it to 0.
269
+ hoverCell(rowIndex, 0)(state, dispatch);
263
270
  }
264
271
  };
265
272
  }
@@ -264,3 +264,25 @@ export const getTop = (element: HTMLElement | Window | undefined): number => {
264
264
 
265
265
  return element?.getBoundingClientRect?.()?.top ?? 0;
266
266
  };
267
+
268
+ export const findNearestCellIndexToPoint = (
269
+ x: number,
270
+ y: number,
271
+ ): { row: number; col: number } | undefined => {
272
+ const elements = document.elementsFromPoint(x, y);
273
+ const cell = elements.find(
274
+ (el) =>
275
+ el.nodeName.toUpperCase() === 'TD' || el.nodeName.toUpperCase() === 'TH',
276
+ ) as HTMLTableCellElement | undefined;
277
+ const row = (cell?.parentElement ?? undefined) as
278
+ | HTMLTableRowElement
279
+ | undefined;
280
+
281
+ if (!Number.isFinite(row?.rowIndex) || !Number.isFinite(cell?.cellIndex)) {
282
+ return undefined;
283
+ }
284
+ return {
285
+ row: row!.rowIndex,
286
+ col: cell!.cellIndex,
287
+ };
288
+ };
@@ -57,6 +57,7 @@ export {
57
57
  updateResizeHandles,
58
58
  isResizeHandleDecoration,
59
59
  hasResizeHandler,
60
+ findNearestCellIndexToPoint,
60
61
  } from './dom';
61
62
  export {
62
63
  convertHTMLCellIndexToColumnIndex,