@atlaskit/editor-plugin-table 7.3.0 → 7.3.1

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 (98) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/dist/cjs/commands/misc.js +4 -2
  4. package/dist/cjs/commands/selection.js +4 -2
  5. package/dist/cjs/pm-plugins/drag-and-drop/commands.js +3 -1
  6. package/dist/cjs/pm-plugins/drag-and-drop/plugin.js +33 -1
  7. package/dist/cjs/pm-plugins/drag-and-drop/reducer.js +2 -1
  8. package/dist/cjs/pm-plugins/table-selection-keymap.js +2 -2
  9. package/dist/cjs/ui/DragHandle/HandleIconComponent.js +1 -3
  10. package/dist/cjs/ui/DragHandle/index.js +22 -10
  11. package/dist/cjs/ui/FloatingDragMenu/DragMenu.js +75 -33
  12. package/dist/cjs/ui/FloatingDragMenu/DropdownMenu.js +123 -0
  13. package/dist/cjs/ui/FloatingDragMenu/index.js +2 -2
  14. package/dist/cjs/ui/TableFloatingColumnControls/ColumnControls/index.js +24 -35
  15. package/dist/cjs/ui/TableFloatingColumnControls/index.js +1 -2
  16. package/dist/cjs/ui/TableFloatingControls/CornerControls/DragCornerControls.js +4 -0
  17. package/dist/cjs/ui/TableFloatingControls/RowControls/DragControls.js +26 -33
  18. package/dist/cjs/ui/common-styles.js +1 -1
  19. package/dist/cjs/ui/consts.js +3 -2
  20. package/dist/es2019/commands/misc.js +4 -4
  21. package/dist/es2019/commands/selection.js +4 -4
  22. package/dist/es2019/pm-plugins/drag-and-drop/commands.js +3 -3
  23. package/dist/es2019/pm-plugins/drag-and-drop/plugin.js +34 -1
  24. package/dist/es2019/pm-plugins/drag-and-drop/reducer.js +2 -1
  25. package/dist/es2019/pm-plugins/table-selection-keymap.js +2 -2
  26. package/dist/es2019/ui/DragHandle/HandleIconComponent.js +1 -3
  27. package/dist/es2019/ui/DragHandle/index.js +25 -10
  28. package/dist/es2019/ui/FloatingDragMenu/DragMenu.js +72 -32
  29. package/dist/es2019/ui/FloatingDragMenu/DropdownMenu.js +109 -0
  30. package/dist/es2019/ui/FloatingDragMenu/index.js +2 -2
  31. package/dist/es2019/ui/TableFloatingColumnControls/ColumnControls/index.js +24 -35
  32. package/dist/es2019/ui/TableFloatingColumnControls/index.js +1 -2
  33. package/dist/es2019/ui/TableFloatingControls/CornerControls/DragCornerControls.js +4 -0
  34. package/dist/es2019/ui/TableFloatingControls/RowControls/DragControls.js +26 -33
  35. package/dist/es2019/ui/common-styles.js +11 -1
  36. package/dist/es2019/ui/consts.js +2 -1
  37. package/dist/esm/commands/misc.js +4 -2
  38. package/dist/esm/commands/selection.js +4 -2
  39. package/dist/esm/pm-plugins/drag-and-drop/commands.js +3 -2
  40. package/dist/esm/pm-plugins/drag-and-drop/plugin.js +33 -1
  41. package/dist/esm/pm-plugins/drag-and-drop/reducer.js +2 -1
  42. package/dist/esm/pm-plugins/table-selection-keymap.js +2 -2
  43. package/dist/esm/ui/DragHandle/HandleIconComponent.js +1 -3
  44. package/dist/esm/ui/DragHandle/index.js +21 -9
  45. package/dist/esm/ui/FloatingDragMenu/DragMenu.js +74 -35
  46. package/dist/esm/ui/FloatingDragMenu/DropdownMenu.js +116 -0
  47. package/dist/esm/ui/FloatingDragMenu/index.js +2 -2
  48. package/dist/esm/ui/TableFloatingColumnControls/ColumnControls/index.js +24 -35
  49. package/dist/esm/ui/TableFloatingColumnControls/index.js +1 -2
  50. package/dist/esm/ui/TableFloatingControls/CornerControls/DragCornerControls.js +4 -0
  51. package/dist/esm/ui/TableFloatingControls/RowControls/DragControls.js +26 -33
  52. package/dist/esm/ui/common-styles.js +1 -1
  53. package/dist/esm/ui/consts.js +2 -1
  54. package/dist/types/commands/misc.d.ts +2 -2
  55. package/dist/types/commands/selection.d.ts +2 -2
  56. package/dist/types/pm-plugins/drag-and-drop/actions.d.ts +1 -0
  57. package/dist/types/pm-plugins/drag-and-drop/commands.d.ts +2 -1
  58. package/dist/types/pm-plugins/drag-and-drop/types.d.ts +2 -0
  59. package/dist/types/ui/DragHandle/HandleIconComponent.d.ts +1 -2
  60. package/dist/types/ui/DragHandle/index.d.ts +3 -2
  61. package/dist/types/ui/FloatingDragMenu/DragMenu.d.ts +7 -8
  62. package/dist/types/ui/FloatingDragMenu/DropdownMenu.d.ts +30 -0
  63. package/dist/types/ui/TableFloatingColumnControls/ColumnControls/index.d.ts +1 -2
  64. package/dist/types/ui/TableFloatingControls/RowControls/DragControls.d.ts +1 -1
  65. package/dist/types/ui/consts.d.ts +1 -0
  66. package/dist/types-ts4.5/commands/misc.d.ts +2 -2
  67. package/dist/types-ts4.5/commands/selection.d.ts +2 -2
  68. package/dist/types-ts4.5/pm-plugins/drag-and-drop/actions.d.ts +1 -0
  69. package/dist/types-ts4.5/pm-plugins/drag-and-drop/commands.d.ts +2 -1
  70. package/dist/types-ts4.5/pm-plugins/drag-and-drop/types.d.ts +2 -0
  71. package/dist/types-ts4.5/ui/DragHandle/HandleIconComponent.d.ts +1 -2
  72. package/dist/types-ts4.5/ui/DragHandle/index.d.ts +3 -2
  73. package/dist/types-ts4.5/ui/FloatingDragMenu/DragMenu.d.ts +7 -8
  74. package/dist/types-ts4.5/ui/FloatingDragMenu/DropdownMenu.d.ts +30 -0
  75. package/dist/types-ts4.5/ui/TableFloatingColumnControls/ColumnControls/index.d.ts +1 -2
  76. package/dist/types-ts4.5/ui/TableFloatingControls/RowControls/DragControls.d.ts +1 -1
  77. package/dist/types-ts4.5/ui/consts.d.ts +1 -0
  78. package/package.json +3 -2
  79. package/src/commands/misc.ts +17 -4
  80. package/src/commands/selection.ts +12 -4
  81. package/src/pm-plugins/drag-and-drop/actions.ts +1 -0
  82. package/src/pm-plugins/drag-and-drop/commands.ts +3 -0
  83. package/src/pm-plugins/drag-and-drop/plugin.ts +47 -0
  84. package/src/pm-plugins/drag-and-drop/reducer.ts +1 -0
  85. package/src/pm-plugins/drag-and-drop/types.ts +3 -0
  86. package/src/pm-plugins/table-selection-keymap.ts +2 -2
  87. package/src/ui/DragHandle/HandleIconComponent.tsx +2 -9
  88. package/src/ui/DragHandle/index.tsx +37 -16
  89. package/src/ui/FloatingDragMenu/DragMenu.tsx +362 -310
  90. package/src/ui/FloatingDragMenu/DropdownMenu.tsx +150 -0
  91. package/src/ui/FloatingDragMenu/index.tsx +3 -3
  92. package/src/ui/TableFloatingColumnControls/ColumnControls/index.tsx +72 -91
  93. package/src/ui/TableFloatingColumnControls/index.tsx +1 -2
  94. package/src/ui/TableFloatingControls/CornerControls/DragCornerControls.tsx +5 -0
  95. package/src/ui/TableFloatingControls/RowControls/DragControls.tsx +89 -104
  96. package/src/ui/common-styles.ts +11 -1
  97. package/src/ui/consts.ts +1 -0
  98. package/tsconfig.app.json +3 -0
@@ -177,7 +177,8 @@ export const createPlugin = (dispatch, eventDispatcher, editorAnalyticsAPI) => {
177
177
  dropTargetIndex: 0,
178
178
  isDragMenuOpen: false,
179
179
  dragMenuIndex: 0,
180
- isDragging: false
180
+ isDragging: false,
181
+ isKeyboardModeActive: false
181
182
  })),
182
183
  key: pluginKey,
183
184
  appendTransaction: (transactions, oldState, newState) => {
@@ -191,6 +192,20 @@ export const createPlugin = (dispatch, eventDispatcher, editorAnalyticsAPI) => {
191
192
  isDragMenuOpen,
192
193
  dragMenuIndex
193
194
  } = getPluginState(newState);
195
+ transactions.forEach(transaction => {
196
+ if (transaction.getMeta('selectedRowViaKeyboard')) {
197
+ const button = document.querySelector('#drag-handle-button-row');
198
+ if (button) {
199
+ button.focus();
200
+ }
201
+ }
202
+ if (transaction.getMeta('selectedColumnViaKeyboard')) {
203
+ const button = document.querySelector('#drag-handle-button-column');
204
+ if (button) {
205
+ button.focus();
206
+ }
207
+ }
208
+ });
194
209
 
195
210
  // What's happening here? you asked... In a nutshell;
196
211
  // If the target cell position changes while the drag menu is open then we want to close the drag menu if it has been opened.
@@ -232,6 +247,24 @@ export const createPlugin = (dispatch, eventDispatcher, editorAnalyticsAPI) => {
232
247
  decorationSet
233
248
  } = getPluginState(state);
234
249
  return decorationSet;
250
+ },
251
+ handleKeyDown: (view, event) => {
252
+ var _ref;
253
+ const isDragHandleFocused = ['drag-handle-button-row', 'drag-handle-button-column'].includes((_ref = event.target || null) === null || _ref === void 0 ? void 0 : _ref.id);
254
+ const keysToTrap = ['Enter', ' '];
255
+ const keysToTrapWhen = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
256
+ const {
257
+ isDragMenuOpen
258
+ } = getPluginState(view.state);
259
+
260
+ // drag handle is focused, and user presses any key return them back to editing
261
+ if (isDragHandleFocused && !isDragMenuOpen && !keysToTrap.includes(event.key)) {
262
+ view.dom.focus();
263
+ return true;
264
+ }
265
+ if (isDragHandleFocused && keysToTrap.includes(event.key) || isDragMenuOpen && keysToTrapWhen.includes(event.key)) {
266
+ return true;
267
+ }
235
268
  }
236
269
  }
237
270
  });
@@ -23,7 +23,8 @@ export default ((pluginState, action) => {
23
23
  ...pluginState,
24
24
  isDragMenuOpen: action.data.isDragMenuOpen,
25
25
  dragMenuDirection: action.data.direction,
26
- dragMenuIndex: action.data.index
26
+ dragMenuIndex: action.data.index,
27
+ isKeyboardModeActive: action.data.isKeyboardModeActive
27
28
  };
28
29
  default:
29
30
  return pluginState;
@@ -7,8 +7,8 @@ export function tableSelectionKeymapPlugin(editorSelectionAPI) {
7
7
  bindKeymapWithCommand(moveRight.common, arrowRightFromTable(editorSelectionAPI)(), list);
8
8
  bindKeymapWithCommand(moveLeft.common, arrowLeftFromTable(editorSelectionAPI)(), list);
9
9
  if (getBooleanFF('platform.editor.a11y.table-selection_9uv33')) {
10
- bindKeymapArrayWithCommand(selectColumn, selectColumns(editorSelectionAPI)(), list);
11
- bindKeymapArrayWithCommand(selectRow, selectRows(editorSelectionAPI)(), list);
10
+ bindKeymapArrayWithCommand(selectColumn, selectColumns(editorSelectionAPI)(true), list);
11
+ bindKeymapArrayWithCommand(selectRow, selectRows(editorSelectionAPI)(true), list);
12
12
  }
13
13
  if (getBooleanFF('platform.editor.table.shift-arrowup-fix')) {
14
14
  bindKeymapWithCommand(shiftArrowUp.common, shiftArrowUpFromTable(editorSelectionAPI)(), list);
@@ -3,11 +3,9 @@ import { DragHandleDisabledIcon, DragHandleIcon, MinimisedHandleIcon } from '../
3
3
  export const HandleIconComponent = props => {
4
4
  const {
5
5
  forceDefaultHandle,
6
- isRowHandleHovered,
7
- isColumnHandleHovered,
6
+ isHandleHovered,
8
7
  hasMergedCells
9
8
  } = props;
10
- const isHandleHovered = isRowHandleHovered || isColumnHandleHovered;
11
9
  if (isHandleHovered || forceDefaultHandle) {
12
10
  return hasMergedCells ? /*#__PURE__*/React.createElement(DragHandleDisabledIcon, null) : /*#__PURE__*/React.createElement(DragHandleIcon, null);
13
11
  }
@@ -6,6 +6,7 @@ import { tableMessages as messages } from '@atlaskit/editor-common/messages';
6
6
  import { browser } from '@atlaskit/editor-common/utils';
7
7
  import { draggable } from '@atlaskit/pragmatic-drag-and-drop/adapter/element';
8
8
  import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/util/set-custom-native-drag-preview';
9
+ import { getPluginState as getDnDPluginState } from '../../pm-plugins/drag-and-drop/plugin-factory';
9
10
  import { getPluginState } from '../../pm-plugins/plugin-factory';
10
11
  import { TableCssClassName as ClassName } from '../../types';
11
12
  import { hasMergedCellsInColumn, hasMergedCellsInRow } from '../../utils';
@@ -23,7 +24,7 @@ const DragHandleComponent = ({
23
24
  previewHeight,
24
25
  onMouseOver,
25
26
  onMouseOut,
26
- onMouseUp,
27
+ toggleDragMenu,
27
28
  onClick,
28
29
  editorView,
29
30
  intl: {
@@ -32,13 +33,19 @@ const DragHandleComponent = ({
32
33
  }) => {
33
34
  const dragHandleDivRef = useRef(null);
34
35
  const [previewContainer, setPreviewContainer] = useState(null);
36
+ const {
37
+ state,
38
+ state: {
39
+ selection
40
+ }
41
+ } = editorView;
35
42
  const {
36
43
  hoveredColumns,
37
44
  hoveredRows
38
- } = getPluginState(editorView.state);
45
+ } = getPluginState(state);
39
46
  const {
40
- selection
41
- } = editorView.state;
47
+ isDragMenuOpen
48
+ } = getDnDPluginState(state);
42
49
  const isRow = direction === 'row';
43
50
  const isColumn = direction === 'column';
44
51
  const isRowHandleHovered = isRow && hoveredRows.length > 0;
@@ -46,8 +53,7 @@ const DragHandleComponent = ({
46
53
  const hasMergedCells = useMemo(() => isRow ? hasMergedCellsInRow(indexes[0])(selection) : hasMergedCellsInColumn(indexes[0])(selection), [indexes, isRow, selection]);
47
54
  const handleIconProps = {
48
55
  forceDefaultHandle,
49
- isColumnHandleHovered,
50
- isRowHandleHovered,
56
+ isHandleHovered: isColumnHandleHovered || isRowHandleHovered,
51
57
  hasMergedCells
52
58
  };
53
59
  useEffect(() => {
@@ -133,20 +139,29 @@ const DragHandleComponent = ({
133
139
  },
134
140
  "data-testid": "table-drag-handle-button",
135
141
  "aria-label": formatMessage(isRow ? messages.rowDragHandle : messages.columnDragHandle),
142
+ "aria-expanded": isDragMenuOpen && isDragMenuTarget ? 'true' : 'false',
143
+ "aria-haspopup": "menu",
136
144
  onMouseOver: onMouseOver,
137
145
  onMouseOut: onMouseOut,
138
146
  onMouseUp: e => {
139
147
  // 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
140
148
  // -> this is bad for a11y but is the current standard new copy/paste keyboard shortcuts should be introduced instead
141
149
  editorView.focus();
142
- onMouseUp && onMouseUp(e);
150
+ toggleDragMenu && toggleDragMenu('mouse', e);
143
151
  },
144
- onClick: onClick
145
- }, browser.gecko ? /*#__PURE__*/React.createElement(HandleIconComponent, handleIconProps) : /*#__PURE__*/React.createElement("span", {
152
+ onClick: onClick,
153
+ onKeyDown: e => {
154
+ if (e.key === 'Enter' || e.key === ' ') {
155
+ toggleDragMenu && toggleDragMenu('keyboard');
156
+ }
157
+ }
158
+ }, appearance !== 'placeholder' ?
159
+ // cannot block pointer events in Firefox as it breaks Dragging functionality
160
+ browser.gecko ? /*#__PURE__*/React.createElement(HandleIconComponent, handleIconProps) : /*#__PURE__*/React.createElement("span", {
146
161
  style: {
147
162
  pointerEvents: 'none'
148
163
  }
149
- }, /*#__PURE__*/React.createElement(HandleIconComponent, handleIconProps))), previewContainer && previewWidth !== undefined && previewHeight !== undefined && /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(DragPreview, {
164
+ }, /*#__PURE__*/React.createElement(HandleIconComponent, handleIconProps)) : null), previewContainer && previewWidth !== undefined && previewHeight !== undefined && /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(DragPreview, {
150
165
  direction: direction,
151
166
  width: previewWidth,
152
167
  height: previewHeight
@@ -1,13 +1,15 @@
1
1
  /* eslint-disable @atlaskit/design-system/prefer-primitives */
2
2
  /** @jsx jsx */
3
- import { useState } from 'react';
3
+ /** @jsxFrag */
4
+ import React, { useEffect, useState } from 'react';
4
5
  import { jsx } from '@emotion/react';
5
6
  import { injectIntl } from 'react-intl-next';
6
7
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
7
8
  import { tableMessages as messages } from '@atlaskit/editor-common/messages';
8
9
  import { DropdownMenuSharedCssClassName } from '@atlaskit/editor-common/styles';
9
- import { backgroundPaletteTooltipMessages, cellBackgroundColorPalette, ColorPalette } from '@atlaskit/editor-common/ui-color';
10
- import { ArrowKeyNavigationType, DropdownMenu } from '@atlaskit/editor-common/ui-menu';
10
+ import { withOuterListeners } from '@atlaskit/editor-common/ui';
11
+ import { backgroundPaletteTooltipMessages, cellBackgroundColorPalette, ColorPalette, getSelectedRowAndColumnFromPalette } from '@atlaskit/editor-common/ui-color';
12
+ import { ArrowKeyNavigationProvider, ArrowKeyNavigationType } from '@atlaskit/editor-common/ui-menu';
11
13
  import { closestElement } from '@atlaskit/editor-common/utils';
12
14
  import { hexToEditorBackgroundPaletteColor } from '@atlaskit/editor-palette';
13
15
  import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
@@ -24,7 +26,8 @@ import { getPluginState as getTablePluginState } from '../../pm-plugins/plugin-f
24
26
  import { TableCssClassName as ClassName } from '../../types';
25
27
  import { checkIfHeaderColumnEnabled, checkIfHeaderRowEnabled, checkIfNumberColumnEnabled, getMergedCellsPositions, getSelectedColumnIndexes, getSelectedRowIndexes, hasMergedCellsInColumn, hasMergedCellsInRow } from '../../utils';
26
28
  import { getDragMenuConfig } from '../../utils/drag-menu';
27
- import { dragMenuDropdownWidth } from '../consts';
29
+ import { colorPalletteColumns } from '../consts';
30
+ import { DropdownMenu } from './DropdownMenu';
28
31
  import { cellColourPreviewStyles, dragMenuBackgroundColorStyles, elementBeforeIconStyles, toggleStyles } from './styles';
29
32
  const MapDragMenuOptionIdToMessage = {
30
33
  add_row_above: {
@@ -153,15 +156,14 @@ const convertToDropdownItems = (dragMenuConfig, formatMessage, selectionRect) =>
153
156
  menuCallback
154
157
  };
155
158
  };
156
- export const DragMenu = ({
159
+ const ColorPaletteWithListeners = withOuterListeners(ColorPalette);
160
+ export const DragMenu = /*#__PURE__*/React.memo(({
157
161
  direction = 'row',
158
162
  index,
163
+ target,
159
164
  isOpen,
160
165
  editorView,
161
166
  tableNode,
162
- mountPoint,
163
- boundariesElement,
164
- scrollableElement,
165
167
  targetCellPosition,
166
168
  getEditorContainerWidth,
167
169
  editorAnalyticsAPI,
@@ -180,6 +182,9 @@ export const DragMenu = ({
180
182
  } = state;
181
183
  const tableMap = tableNode ? TableMap.get(tableNode) : undefined;
182
184
  const [isSubmenuOpen, setIsSubmenuOpen] = useState(false);
185
+ const {
186
+ isKeyboardModeActive
187
+ } = getPluginState(state);
183
188
  const selectionRect = isSelectionType(selection, 'cell') ? getSelectionRect(selection) : findCellRectClosestToPos(selection.$from);
184
189
  const hasMergedCells = direction === 'row' ? hasMergedCellsInRow : hasMergedCellsInColumn;
185
190
  const shouldMoveDisabled = index !== undefined && hasMergedCells(index)(selection);
@@ -220,7 +225,12 @@ export const DragMenu = ({
220
225
  } = getTablePluginState(editorView.state);
221
226
  const node = targetCellPosition ? state.doc.nodeAt(targetCellPosition) : null;
222
227
  const background = hexToEditorBackgroundPaletteColor((node === null || node === void 0 ? void 0 : (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.background) || '#ffffff');
228
+ const {
229
+ selectedRowIndex,
230
+ selectedColumnIndex
231
+ } = getSelectedRowAndColumnFromPalette(cellBackgroundColorPalette, background, colorPalletteColumns);
223
232
  return {
233
+ key: 'background',
224
234
  content: formatMessage(messages.backgroundColor),
225
235
  value: {
226
236
  name: 'background'
@@ -240,16 +250,33 @@ export const DragMenu = ({
240
250
  }), isSubmenuOpen && jsx("div", {
241
251
  className: ClassName.DRAG_SUBMENU,
242
252
  ref: handleSubMenuRef
243
- }, jsx(ColorPalette, {
244
- cols: 7,
245
- onClick: setColor,
253
+ }, jsx(ArrowKeyNavigationProvider, {
254
+ type: ArrowKeyNavigationType.COLOR,
255
+ selectedRowIndex: selectedRowIndex,
256
+ selectedColumnIndex: selectedColumnIndex,
257
+ handleClose: () => {
258
+ const keyboardEvent = new KeyboardEvent('keydown', {
259
+ key: 'ArrowDown',
260
+ bubbles: true
261
+ });
262
+ setIsSubmenuOpen(false);
263
+ target === null || target === void 0 ? void 0 : target.focus();
264
+ target === null || target === void 0 ? void 0 : target.dispatchEvent(keyboardEvent);
265
+ },
266
+ isPopupPositioned: true,
267
+ isOpenedByKeyboard: isKeyboardModeActive
268
+ }, jsx(ColorPaletteWithListeners, {
269
+ cols: colorPalletteColumns,
270
+ onClick: color => {
271
+ setColor(color);
272
+ },
246
273
  selectedColor: background,
247
274
  paletteOptions: {
248
275
  palette: cellBackgroundColorPalette,
249
276
  paletteColorTooltipMessages: backgroundPaletteTooltipMessages,
250
277
  hexToPaletteColor: hexToEditorBackgroundPaletteColor
251
278
  }
252
- })))
279
+ }))))
253
280
  };
254
281
  };
255
282
  const toggleHeaderColumn = () => {
@@ -261,8 +288,9 @@ export const DragMenu = ({
261
288
  const toggleRowNumbers = () => {
262
289
  toggleNumberColumnWithAnalytics(editorAnalyticsAPI)(state, dispatch);
263
290
  };
264
- const createhHeaderRowColumnMenuItem = direction => {
291
+ const createHeaderRowColumnMenuItem = direction => {
265
292
  return direction === 'column' ? {
293
+ key: 'header_column',
266
294
  content: formatMessage(messages.headerColumn),
267
295
  value: {
268
296
  name: 'header_column'
@@ -275,6 +303,7 @@ export const DragMenu = ({
275
303
  isChecked: checkIfHeaderColumnEnabled(selection)
276
304
  }))
277
305
  } : {
306
+ key: 'header_row',
278
307
  content: formatMessage(messages.headerRow),
279
308
  value: {
280
309
  name: 'header_row'
@@ -290,6 +319,7 @@ export const DragMenu = ({
290
319
  };
291
320
  const createRowNumbersMenuItem = () => {
292
321
  return {
322
+ key: 'row_numbers',
293
323
  content: formatMessage(messages.rowNumbers),
294
324
  value: {
295
325
  name: 'row_numbers'
@@ -326,12 +356,17 @@ export const DragMenu = ({
326
356
  return true;
327
357
  }
328
358
  };
329
- const closeMenu = () => {
359
+ const closeMenu = (focusTarget = 'handle') => {
330
360
  const {
331
361
  state,
332
362
  dispatch
333
363
  } = editorView;
334
364
  if (shouldCloseMenu(state)) {
365
+ if (target && focusTarget === 'handle') {
366
+ target === null || target === void 0 ? void 0 : target.focus();
367
+ } else {
368
+ editorView.dom.focus();
369
+ }
335
370
  toggleDragMenu(false, direction, index)(state, dispatch);
336
371
  }
337
372
  };
@@ -341,6 +376,9 @@ export const DragMenu = ({
341
376
  var _menuCallback$item$va;
342
377
  (_menuCallback$item$va = menuCallback[item.value.name]) === null || _menuCallback$item$va === void 0 ? void 0 : _menuCallback$item$va.call(menuCallback, state, dispatch);
343
378
  switch (item.value.name) {
379
+ case 'background':
380
+ setIsSubmenuOpen(!isSubmenuOpen);
381
+ break;
344
382
  case 'header_column':
345
383
  toggleHeaderColumn();
346
384
  break;
@@ -354,7 +392,7 @@ export const DragMenu = ({
354
392
  break;
355
393
  }
356
394
  if (['header_column', 'header_row', 'row_numbers', 'background'].indexOf(item.value.name) <= -1) {
357
- closeMenu();
395
+ closeMenu('editor');
358
396
  }
359
397
  };
360
398
  const handleItemMouseEnter = ({
@@ -382,6 +420,17 @@ export const DragMenu = ({
382
420
  clearHoverSelection()(state, dispatch);
383
421
  }
384
422
  };
423
+ useEffect(() => {
424
+ // focus on first menu item automatically when menu renders
425
+ // and user is using keyboard
426
+ if (isOpen && target && isKeyboardModeActive) {
427
+ const keyboardEvent = new KeyboardEvent('keydown', {
428
+ key: 'ArrowDown',
429
+ bubbles: true
430
+ });
431
+ target.dispatchEvent(keyboardEvent);
432
+ }
433
+ }, [isOpen, target, isKeyboardModeActive]);
385
434
  if (!menuItems) {
386
435
  return null;
387
436
  }
@@ -389,11 +438,11 @@ export const DragMenu = ({
389
438
  menuItems[0].items.unshift(createBackgroundColorMenuItem());
390
439
  }
391
440
 
392
- // If first row, add toggle for Hearder row, default is true
441
+ // If first row, add toggle for Header row, default is true
393
442
  // If first column, add toggle for Header column, default is false
394
443
  if (getBooleanFF('platform.editor.table.new-cell-context-menu-styling') && index === 0) {
395
444
  menuItems.push({
396
- items: [createhHeaderRowColumnMenuItem(direction)]
445
+ items: [createHeaderRowColumnMenuItem(direction)]
397
446
  });
398
447
  }
399
448
 
@@ -404,25 +453,16 @@ export const DragMenu = ({
404
453
  });
405
454
  }
406
455
  return jsx(DropdownMenu, {
407
- mountTo: mountPoint
408
- //This needs be removed when the a11y is completely handled
409
- //Disabling key navigation now as it works only partially
410
- ,
411
- arrowKeyNavigationProviderOptions: {
412
- type: ArrowKeyNavigationType.MENU,
413
- disableArrowKeyNavigation: true
456
+ disableKeyboardHandling: isSubmenuOpen,
457
+ section: {
458
+ hasSeparator: true
414
459
  },
460
+ target: target,
415
461
  items: menuItems,
416
- isOpen: isOpen,
417
- onOpenChange: closeMenu,
418
462
  onItemActivated: handleMenuItemActivated,
419
463
  onMouseEnter: handleItemMouseEnter,
420
464
  onMouseLeave: handleItemMouseLeave,
421
- fitWidth: dragMenuDropdownWidth,
422
- boundariesElement: boundariesElement,
423
- section: {
424
- hasSeparator: true
425
- }
465
+ handleClose: closeMenu
426
466
  });
427
- };
467
+ });
428
468
  export default injectIntl(DragMenu);
@@ -0,0 +1,109 @@
1
+ import React from 'react';
2
+ import { DropList } from '@atlaskit/editor-common/ui';
3
+ import { ArrowKeyNavigationProvider, ArrowKeyNavigationType, DropdownMenuItem } from '@atlaskit/editor-common/ui-menu';
4
+ import { withReactEditorViewOuterListeners } from '@atlaskit/editor-common/ui-react';
5
+ import { MenuGroup, Section } from '@atlaskit/menu';
6
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
7
+ import { dragMenuDropdownWidth } from '../consts';
8
+ const DropListWithOutsideListeners = withReactEditorViewOuterListeners(DropList);
9
+ export const DropdownMenu = ({
10
+ target,
11
+ items,
12
+ section,
13
+ disableKeyboardHandling,
14
+ onItemActivated,
15
+ handleClose,
16
+ onMouseEnter,
17
+ onMouseLeave
18
+ }) => {
19
+ const innerMenu = () => {
20
+ return /*#__PURE__*/React.createElement(DropListWithOutsideListeners, {
21
+ isOpen: true,
22
+ shouldFitContainer: true,
23
+ position: ['bottom', 'left'].join(' '),
24
+ handleClickOutside: () => handleClose('editor'),
25
+ handleEscapeKeydown: () => {
26
+ if (!disableKeyboardHandling) {
27
+ handleClose('handle');
28
+ }
29
+ },
30
+ handleEnterKeydown: e => {
31
+ if (!disableKeyboardHandling) {
32
+ e.preventDefault();
33
+ e.stopPropagation();
34
+ }
35
+ },
36
+ targetRef: target
37
+ }, /*#__PURE__*/React.createElement("div", {
38
+ style: {
39
+ height: 0,
40
+ minWidth: dragMenuDropdownWidth
41
+ }
42
+ }), /*#__PURE__*/React.createElement(MenuGroup, {
43
+ role: "menu"
44
+ }, items.map((group, index) => /*#__PURE__*/React.createElement(Section, {
45
+ hasSeparator: (section === null || section === void 0 ? void 0 : section.hasSeparator) && index > 0,
46
+ title: section === null || section === void 0 ? void 0 : section.title,
47
+ key: index
48
+ }, group.items.map(item => {
49
+ var _item$key;
50
+ return /*#__PURE__*/React.createElement(DropdownMenuItem, {
51
+ shouldUseDefaultRole: false,
52
+ key: (_item$key = item.key) !== null && _item$key !== void 0 ? _item$key : String(item.content),
53
+ item: item,
54
+ onItemActivated: onItemActivated,
55
+ onMouseEnter: onMouseEnter,
56
+ onMouseLeave: onMouseLeave
57
+ });
58
+ })))));
59
+ };
60
+ if (disableKeyboardHandling) {
61
+ return innerMenu();
62
+ }
63
+ return /*#__PURE__*/React.createElement(ArrowKeyNavigationProvider, {
64
+ closeOnTab: true,
65
+ type: ArrowKeyNavigationType.MENU,
66
+ onSelection: index => {
67
+ const results = items.flatMap(item => 'items' in item ? item.items : item);
68
+
69
+ // onSelection is called when any focusable element is 'activated'
70
+ // this is an issue as some menu items have toggles, which cause the index value
71
+ // in the callback to be outside of array length.
72
+ // The logic below normalises the index value based on the number
73
+ // of menu items with 2 focusable elements, and adjusts the index to ensure
74
+ // the correct menu item is sent in onItemActivated callback
75
+ if (getBooleanFF('platform.editor.table.new-cell-context-menu-styling')) {
76
+ const keys = ['row_numbers', 'header_row', 'header_column'];
77
+ let doubleItemCount = 0;
78
+ const firstIndex = results.findIndex(value => keys.includes(value.key));
79
+ if (firstIndex === -1 || index <= firstIndex) {
80
+ onItemActivated && onItemActivated({
81
+ item: results[index]
82
+ });
83
+ return;
84
+ }
85
+ for (let i = firstIndex; i < results.length; i += 1) {
86
+ if (keys.includes(results[i].key)) {
87
+ doubleItemCount += 1;
88
+ }
89
+ if (firstIndex % 2 === 0 && index - doubleItemCount === i) {
90
+ onItemActivated && onItemActivated({
91
+ item: results[i]
92
+ });
93
+ return;
94
+ }
95
+ if (firstIndex % 2 === 1 && index - doubleItemCount === i) {
96
+ onItemActivated && onItemActivated({
97
+ item: results[i]
98
+ });
99
+ return;
100
+ }
101
+ }
102
+ } else {
103
+ onItemActivated && onItemActivated({
104
+ item: results[index]
105
+ });
106
+ }
107
+ }
108
+ }, innerMenu());
109
+ };
@@ -24,10 +24,10 @@ const FloatingDragMenu = ({
24
24
  }
25
25
  const inStickyMode = stickyHeaders === null || stickyHeaders === void 0 ? void 0 : stickyHeaders.sticky;
26
26
  const targetHandleRef = direction === 'row' ? document.querySelector('#drag-handle-button-row') : document.querySelector('#drag-handle-button-column');
27
+ const offset = direction === 'row' ? [-9, 6] : [0, -7];
27
28
  if (!targetHandleRef || !(editorView.state.selection instanceof CellSelection)) {
28
29
  return null;
29
30
  }
30
- const offset = direction === 'row' ? [-9, 6] : [0, -7];
31
31
 
32
32
  // TODO: we will need to adjust the alignment and offset values depending on whether this is a row or column menu.
33
33
  return /*#__PURE__*/React.createElement(Popup, {
@@ -49,10 +49,10 @@ const FloatingDragMenu = ({
49
49
  }, /*#__PURE__*/React.createElement(DragMenu, {
50
50
  editorView: editorView,
51
51
  isOpen: isOpen,
52
- boundariesElement: boundariesElement,
53
52
  tableNode: tableNode,
54
53
  direction: direction,
55
54
  index: index,
55
+ target: targetHandleRef || undefined,
56
56
  targetCellPosition: targetCellPosition,
57
57
  getEditorContainerWidth: getEditorContainerWidth,
58
58
  editorAnalyticsAPI: editorAnalyticsAPI,
@@ -25,7 +25,6 @@ export const ColumnControls = ({
25
25
  tableActive,
26
26
  tableRef,
27
27
  hoveredCell,
28
- isResizing,
29
28
  stickyTop,
30
29
  localId,
31
30
  isInDanger,
@@ -102,15 +101,15 @@ export const ColumnControls = ({
102
101
  clearHoverSelection()(state, dispatch);
103
102
  }
104
103
  }, [editorView, tableActive]);
105
- const handleMouseUp = useCallback(event => {
104
+ const toggleDragMenuHandler = useCallback((trigger, event) => {
106
105
  const {
107
106
  state,
108
107
  dispatch
109
108
  } = editorView;
110
- if (event.shiftKey) {
109
+ if (event !== null && event !== void 0 && event.shiftKey) {
111
110
  return;
112
111
  }
113
- toggleDragMenu(undefined, 'column', colIndex)(state, dispatch);
112
+ toggleDragMenu(undefined, 'column', colIndex, trigger)(state, dispatch);
114
113
  }, [editorView, colIndex]);
115
114
  const colIndexes = useMemo(() => {
116
115
  return [colIndex];
@@ -119,31 +118,16 @@ export const ColumnControls = ({
119
118
  var _getScrollOffset;
120
119
  columnControlsRef.current.scrollLeft = (_getScrollOffset = getScrollOffset === null || getScrollOffset === void 0 ? void 0 : getScrollOffset()) !== null && _getScrollOffset !== void 0 ? _getScrollOffset : 0;
121
120
  }
122
- const generateHandleByType = type => {
121
+ const generateHandleByType = (type, appearance, gridColumn, indexes) => {
123
122
  var _rowHeights$reduce, _colWidths$reduce;
124
- if (!hoveredCell || !(colWidths !== null && colWidths !== void 0 && colWidths.length)) {
125
- return null;
126
- }
127
123
  const isHover = type === 'hover';
128
- const isColumnsSelected = selectedColIndexes.length > 0;
129
124
  const previewHeight = (_rowHeights$reduce = rowHeights === null || rowHeights === void 0 ? void 0 : rowHeights.reduce((sum, cur) => sum + cur, 0)) !== null && _rowHeights$reduce !== void 0 ? _rowHeights$reduce : 0;
130
- const showCondition = isHover ? isColumnsSelected && !selectedColIndexes.includes(colIndex) && Number.isFinite(hoveredCell === null || hoveredCell === void 0 ? void 0 : hoveredCell.colIndex) : selectedColIndexes.length < (colWidths === null || colWidths === void 0 ? void 0 : colWidths.length) && Number.isFinite(hoveredCell === null || hoveredCell === void 0 ? void 0 : hoveredCell.colIndex);
131
- if (!showCondition) {
132
- return null;
133
- }
134
- const gridColumnPosition = `${colIndex + 1} / span 1`;
135
- const selectedColumnPosition = `${selectedColIndexes[0] + 1} / span ${selectedColIndexes.length}`;
136
- const hoveredAppearance = selectedColIndexes.includes(colIndex) ? isInDanger ? 'danger' : 'selected' : 'default';
137
- const currentSelectionAppearance = isColumnsSelected ? isInDanger ? 'danger' : 'selected' : hoveredAppearance;
138
- const isSelecting = isColumnsSelected && !isHover;
139
-
140
- // this indexes are used to calculate the drag and drop source
141
- const indexes = isColumnsSelected ? isHover ? colIndexes : selectedColIndexes : colIndexes;
142
125
  const previewWidth = (_colWidths$reduce = colWidths === null || colWidths === void 0 ? void 0 : colWidths.reduce((sum, v, i) => sum + (v !== null && v !== void 0 ? v : tableCellMinWidth) * (indexes.includes(i) ? 1 : 0), 0)) !== null && _colWidths$reduce !== void 0 ? _colWidths$reduce : tableCellMinWidth;
143
126
  return /*#__PURE__*/React.createElement("div", {
144
127
  key: type,
145
128
  style: {
146
- gridColumn: isSelecting ? selectedColumnPosition : gridColumnPosition,
129
+ gridColumn,
130
+ gridRow: '1',
147
131
  display: 'flex',
148
132
  justifyContent: 'center',
149
133
  alignItems: 'center',
@@ -153,33 +137,38 @@ export const ColumnControls = ({
153
137
  width: '100%',
154
138
  position: 'relative'
155
139
  },
156
- "data-column-control-index": hoveredCell.colIndex,
157
- "data-testid": `table-floating-column-${isSelecting ? selectedColIndexes[0] : colIndex}-drag-handle`
140
+ "data-testid": `table-floating-column-${isHover ? colIndex : selectedColIndexes[0]}-drag-handle`
158
141
  }, /*#__PURE__*/React.createElement(DragHandle, {
159
142
  isDragMenuTarget: !isHover,
160
143
  direction: "column",
161
144
  tableLocalId: localId || '',
162
145
  indexes: indexes,
163
- forceDefaultHandle: isHover ? false : isColumnsSelected,
164
146
  previewWidth: previewWidth,
147
+ forceDefaultHandle: !isHover,
165
148
  previewHeight: previewHeight,
166
- appearance: isSelecting ? currentSelectionAppearance : hoveredAppearance,
149
+ appearance: appearance,
167
150
  onClick: handleClick,
168
151
  onMouseOver: handleMouseOver,
169
152
  onMouseOut: handleMouseOut,
170
- onMouseUp: handleMouseUp,
153
+ toggleDragMenu: toggleDragMenuHandler,
171
154
  editorView: editorView
172
155
  }));
173
156
  };
174
- const columnHandles = hoveredCell => {
175
- if (!hoveredCell) {
157
+ const columnHandles = () => {
158
+ const handles = [];
159
+ const isColumnSelected = selectedColIndexes.length > 0;
160
+ const isEntireTableSelected = (colWidths || []).length > selectedColIndexes.length;
161
+ if (!tableActive) {
176
162
  return null;
177
163
  }
178
- if (hoveredCell.colIndex === undefined) {
179
- return generateHandleByType('selected');
164
+
165
+ // placeholder / selected need to always render at least one handle
166
+ // so it can be focused via keyboard shortcuts
167
+ handles.push(generateHandleByType('selected', isColumnSelected && isEntireTableSelected ? isInDanger ? 'danger' : 'selected' : 'placeholder', `${selectedColIndexes[0] + 1} / span ${selectedColIndexes.length}`, selectedColIndexes));
168
+ if (hoveredCell && isTableHovered && colIndex !== undefined && !selectedColIndexes.includes(colIndex)) {
169
+ handles.push(generateHandleByType('hover', 'default', `${colIndex + 1} / span 1`, colIndexes));
180
170
  }
181
- const sortedHandles = [generateHandleByType('hover'), generateHandleByType('selected')];
182
- return hoveredCell.colIndex < selectedColIndexes[0] ? sortedHandles : sortedHandles.reverse();
171
+ return handles;
183
172
  };
184
173
  const containerWidth = isNumberColumnEnabled && tableContainerWidth ? tableContainerWidth - akEditorTableNumberColumnWidth : tableContainerWidth;
185
174
  return /*#__PURE__*/React.createElement("div", {
@@ -196,7 +185,7 @@ export const ColumnControls = ({
196
185
  overflowX: stickyTop ? 'hidden' : 'visible',
197
186
  pointerEvents: isDragging ? 'none' : undefined
198
187
  }
199
- }, !isResizing && columnParams.map(({
188
+ }, columnParams.map(({
200
189
  startIndex,
201
190
  endIndex
202
191
  }, index) => /*#__PURE__*/React.createElement("div", {
@@ -213,6 +202,6 @@ export const ColumnControls = ({
213
202
  style: columnParams.length - 1 === index ? {
214
203
  right: '0'
215
204
  } : {}
216
- }))), tableActive && isTableHovered && !isResizing && columnHandles(hoveredCell)));
205
+ }))), columnHandles()));
217
206
  };
218
207
  export default ColumnControls;