@atlaskit/editor-plugin-table 10.4.5 → 10.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 10.4.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 10.4.6
10
+
11
+ ### Patch Changes
12
+
13
+ - [#124950](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/124950)
14
+ [`45961c4f4a9a3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/45961c4f4a9a3) -
15
+ [ux] ED-25485 Fixes issue where up/down arrow keys would move to the wrong cell in a table if
16
+ there was a node selection inside the table.
17
+
3
18
  ## 10.4.5
4
19
 
5
20
  ### Patch Changes
@@ -3,16 +3,16 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.goToNextCell = void 0;
6
+ exports.goToNextCellVertical = exports.goToNextCell = void 0;
7
7
  var _analytics = require("@atlaskit/editor-common/analytics");
8
+ var _nesting = require("@atlaskit/editor-common/nesting");
9
+ var _state = require("@atlaskit/editor-prosemirror/state");
8
10
  var _utils = require("@atlaskit/editor-prosemirror/utils");
9
11
  var _tableMap = require("@atlaskit/editor-tables/table-map");
10
12
  var _utils2 = require("@atlaskit/editor-tables/utils");
11
13
  var _pluginFactory = require("../plugin-factory");
12
14
  var _columnResize = require("./column-resize");
13
15
  var _commandsWithAnalytics = require("./commands-with-analytics");
14
- // #region Constants
15
-
16
16
  var TAB_FORWARD_DIRECTION = 1;
17
17
  var TAB_BACKWARD_DIRECTION = -1;
18
18
  var goToNextCell = exports.goToNextCell = function goToNextCell(editorAnalyticsAPI, ariaNotify, getIntl) {
@@ -41,7 +41,7 @@ var goToNextCell = exports.goToNextCell = function goToNextCell(editorAnalyticsA
41
41
  var firstCellPos = map.positionAt(0, 0, table.node) + table.start;
42
42
  var lastCellPos = map.positionAt(map.height - 1, map.width - 1, table.node) + table.start;
43
43
 
44
- // when tabbing backwards at first cell (top left), insert row at the start of table
44
+ // When tabbing backwards at first cell (top left), insert row at the start of table
45
45
  if (firstCellPos === cell.pos && direction === TAB_BACKWARD_DIRECTION) {
46
46
  (0, _commandsWithAnalytics.insertRowWithAnalytics)(editorAnalyticsAPI)(_analytics.INPUT_METHOD.KEYBOARD, {
47
47
  index: 0,
@@ -50,7 +50,7 @@ var goToNextCell = exports.goToNextCell = function goToNextCell(editorAnalyticsA
50
50
  return true;
51
51
  }
52
52
 
53
- // when tabbing forwards at last cell (bottom right), insert row at the end of table
53
+ // When tabbing forwards at last cell (bottom right), insert row at the end of table
54
54
  if (lastCellPos === cell.pos && direction === TAB_FORWARD_DIRECTION) {
55
55
  (0, _commandsWithAnalytics.insertRowWithAnalytics)(editorAnalyticsAPI)(_analytics.INPUT_METHOD.KEYBOARD, {
56
56
  index: map.height,
@@ -64,4 +64,94 @@ var goToNextCell = exports.goToNextCell = function goToNextCell(editorAnalyticsA
64
64
  return true;
65
65
  };
66
66
  };
67
- };
67
+ };
68
+
69
+ /**
70
+ * Moves the cursor vertically from a NodeSelection within a table cell.
71
+ * - If content exists above/below within the cell, lets ProseMirror handle it.
72
+ * - Otherwise, moves to the next cell (down to start, up to last line).
73
+ */
74
+ var goToNextCellVertical = exports.goToNextCellVertical = function goToNextCellVertical(direction) {
75
+ return function (state, dispatch) {
76
+ var selection = state.selection;
77
+ var nestedTableSelection = (0, _nesting.isSelectionTableNestedInTable)(state);
78
+ if (!(selection instanceof _state.NodeSelection) && !nestedTableSelection) {
79
+ return false; // Let ProseMirror handle other selection types
80
+ }
81
+ var table = (0, _utils2.findTable)(selection);
82
+ var cell = (0, _utils2.findCellClosestToPos)(state.doc.resolve(selection.from));
83
+ var selectionPos = selection.from;
84
+
85
+ // Handle when we have nested table fully selected
86
+ if (table && nestedTableSelection && (0, _utils2.isTableSelected)(selection)) {
87
+ var parentTablePos = table.pos;
88
+ table = (0, _utils2.findTableClosestToPos)(state.doc.resolve(parentTablePos));
89
+ cell = (0, _utils2.findCellClosestToPos)(state.doc.resolve(parentTablePos));
90
+ selectionPos = parentTablePos;
91
+ }
92
+ if (!table || !cell || !cell.pos) {
93
+ return false;
94
+ }
95
+ var _state$schema$nodes2 = state.schema.nodes,
96
+ tableCell = _state$schema$nodes2.tableCell,
97
+ tableHeader = _state$schema$nodes2.tableHeader;
98
+
99
+ // Let ProseMirror handle movement within the cell if content exists above/below
100
+ if (cellHasContentInDirection(cell.node, cell.pos, selectionPos, direction)) {
101
+ return false;
102
+ }
103
+
104
+ // Move to the next cell vertically
105
+ var map = _tableMap.TableMap.get(table.node);
106
+ var nextCellPos = map.nextCell(cell.pos - table.start, 'vert', direction);
107
+ if (dispatch && nextCellPos) {
108
+ var _$nextCell$nodeAfter, _$nextCell$nodeAfter2;
109
+ var nextCellStart = table.start + nextCellPos;
110
+ var $nextCell = state.doc.resolve(nextCellStart);
111
+ if ((_$nextCell$nodeAfter = $nextCell.nodeAfter) !== null && _$nextCell$nodeAfter !== void 0 && _$nextCell$nodeAfter.type && [tableCell, tableHeader].includes((_$nextCell$nodeAfter2 = $nextCell.nodeAfter) === null || _$nextCell$nodeAfter2 === void 0 ? void 0 : _$nextCell$nodeAfter2.type)) {
112
+ var contentPos = getTargetPositionInNextCell($nextCell.nodeAfter, nextCellStart, direction);
113
+ dispatch(state.tr.setSelection(_state.TextSelection.create(state.doc, contentPos)));
114
+ return true;
115
+ }
116
+ return false;
117
+ }
118
+ return false; // No next cell found
119
+ };
120
+ };
121
+ function cellHasContentInDirection(cellNode, cellPos, selectionPos, direction) {
122
+ var hasContent = false;
123
+ cellNode.content.forEach(function (node, offset) {
124
+ var nodeStart = cellPos + 1 + offset;
125
+ var nodeEnd = nodeStart + node.nodeSize;
126
+ if (direction === 1 && nodeStart > selectionPos && node.isBlock) {
127
+ hasContent = true; // Content below
128
+ } else if (direction === -1 && nodeEnd <= selectionPos && node.isBlock) {
129
+ hasContent = true; // Content above
130
+ }
131
+ });
132
+ return hasContent;
133
+ }
134
+ function getTargetPositionInNextCell(cellNode, nextCellStart, direction) {
135
+ var contentPos = nextCellStart + 1; // Default: just inside the cell
136
+ if (cellNode.content.size > 0) {
137
+ if (direction === 1) {
138
+ var firstChild = cellNode.firstChild;
139
+ if (firstChild && firstChild.isBlock) {
140
+ contentPos = nextCellStart + 1 + (firstChild.isLeaf ? 0 : 1); // Down: Start of first block
141
+ }
142
+ } else if (direction === -1) {
143
+ var lastBlock;
144
+ var lastOffset = 0;
145
+ cellNode.content.forEach(function (node, offset) {
146
+ if (node.isBlock) {
147
+ lastBlock = node;
148
+ lastOffset = offset;
149
+ }
150
+ });
151
+ if (lastBlock) {
152
+ contentPos = nextCellStart + 1 + lastOffset + (lastBlock.isLeaf ? 0 : lastBlock.nodeSize - 1);
153
+ }
154
+ }
155
+ }
156
+ return contentPos;
157
+ }
@@ -15,6 +15,7 @@ var _commandsWithAnalytics = require("../pm-plugins/drag-and-drop/commands-with-
15
15
  var _commands2 = require("./commands");
16
16
  var _columnResize = require("./commands/column-resize");
17
17
  var _commandsWithAnalytics2 = require("./commands/commands-with-analytics");
18
+ var _goToNextCell = require("./commands/go-to-next-cell");
18
19
  var _insert = require("./commands/insert");
19
20
  function keymapPlugin(getEditorContainerWidth, api, nodeViewPortalProviderAPI, editorAnalyticsAPI, dragAndDropEnabled) {
20
21
  var _pluginInjectionApi$a;
@@ -163,6 +164,14 @@ function keymapPlugin(getEditorContainerWidth, api, nodeViewPortalProviderAPI, e
163
164
  (0, _keymaps.bindKeymapWithCommand)(
164
165
  // Ignored via go/ees005
165
166
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
167
+ _keymaps.moveDown.common, (0, _goToNextCell.goToNextCellVertical)(1), list);
168
+ (0, _keymaps.bindKeymapWithCommand)(
169
+ // Ignored via go/ees005
170
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
171
+ _keymaps.moveUp.common, (0, _goToNextCell.goToNextCellVertical)(-1), list);
172
+ (0, _keymaps.bindKeymapWithCommand)(
173
+ // Ignored via go/ees005
174
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
166
175
  _keymaps.decreaseMediaSize.common, (0, _commandsWithAnalytics2.changeColumnWidthByStepWithAnalytics)(editorAnalyticsAPI, api)(-10, getEditorContainerWidth, isTableScalingEnabled, isTableFixedColumnWidthsOptionEnabled, !!isCommentEditor, _analytics.INPUT_METHOD.SHORTCUT, ariaNotifyPlugin, getIntl), list);
167
176
  (0, _keymaps.bindKeymapWithCommand)(
168
177
  // Ignored via go/ees005
@@ -1,9 +1,9 @@
1
- // #region Constants
2
-
3
1
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { isSelectionTableNestedInTable } from '@atlaskit/editor-common/nesting';
3
+ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
4
4
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
5
  import { TableMap } from '@atlaskit/editor-tables/table-map';
6
- import { goToNextCell as baseGotoNextCell, findTable } from '@atlaskit/editor-tables/utils';
6
+ import { goToNextCell as baseGotoNextCell, findCellClosestToPos, findTable, findTableClosestToPos, isTableSelected } from '@atlaskit/editor-tables/utils';
7
7
  import { getPluginState } from '../plugin-factory';
8
8
  import { stopKeyboardColumnResizing } from './column-resize';
9
9
  import { insertRowWithAnalytics } from './commands-with-analytics';
@@ -34,7 +34,7 @@ export const goToNextCell = (editorAnalyticsAPI, ariaNotify, getIntl) => directi
34
34
  const firstCellPos = map.positionAt(0, 0, table.node) + table.start;
35
35
  const lastCellPos = map.positionAt(map.height - 1, map.width - 1, table.node) + table.start;
36
36
 
37
- // when tabbing backwards at first cell (top left), insert row at the start of table
37
+ // When tabbing backwards at first cell (top left), insert row at the start of table
38
38
  if (firstCellPos === cell.pos && direction === TAB_BACKWARD_DIRECTION) {
39
39
  insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.KEYBOARD, {
40
40
  index: 0,
@@ -43,7 +43,7 @@ export const goToNextCell = (editorAnalyticsAPI, ariaNotify, getIntl) => directi
43
43
  return true;
44
44
  }
45
45
 
46
- // when tabbing forwards at last cell (bottom right), insert row at the end of table
46
+ // When tabbing forwards at last cell (bottom right), insert row at the end of table
47
47
  if (lastCellPos === cell.pos && direction === TAB_FORWARD_DIRECTION) {
48
48
  insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.KEYBOARD, {
49
49
  index: map.height,
@@ -55,4 +55,95 @@ export const goToNextCell = (editorAnalyticsAPI, ariaNotify, getIntl) => directi
55
55
  return baseGotoNextCell(direction)(state, dispatch);
56
56
  }
57
57
  return true;
58
- };
58
+ };
59
+
60
+ /**
61
+ * Moves the cursor vertically from a NodeSelection within a table cell.
62
+ * - If content exists above/below within the cell, lets ProseMirror handle it.
63
+ * - Otherwise, moves to the next cell (down to start, up to last line).
64
+ */
65
+ export const goToNextCellVertical = direction => (state, dispatch) => {
66
+ const {
67
+ selection
68
+ } = state;
69
+ const nestedTableSelection = isSelectionTableNestedInTable(state);
70
+ if (!(selection instanceof NodeSelection) && !nestedTableSelection) {
71
+ return false; // Let ProseMirror handle other selection types
72
+ }
73
+ let table = findTable(selection);
74
+ let cell = findCellClosestToPos(state.doc.resolve(selection.from));
75
+ let selectionPos = selection.from;
76
+
77
+ // Handle when we have nested table fully selected
78
+ if (table && nestedTableSelection && isTableSelected(selection)) {
79
+ const parentTablePos = table.pos;
80
+ table = findTableClosestToPos(state.doc.resolve(parentTablePos));
81
+ cell = findCellClosestToPos(state.doc.resolve(parentTablePos));
82
+ selectionPos = parentTablePos;
83
+ }
84
+ if (!table || !cell || !cell.pos) {
85
+ return false;
86
+ }
87
+ const {
88
+ tableCell,
89
+ tableHeader
90
+ } = state.schema.nodes;
91
+
92
+ // Let ProseMirror handle movement within the cell if content exists above/below
93
+ if (cellHasContentInDirection(cell.node, cell.pos, selectionPos, direction)) {
94
+ return false;
95
+ }
96
+
97
+ // Move to the next cell vertically
98
+ const map = TableMap.get(table.node);
99
+ const nextCellPos = map.nextCell(cell.pos - table.start, 'vert', direction);
100
+ if (dispatch && nextCellPos) {
101
+ var _$nextCell$nodeAfter, _$nextCell$nodeAfter2;
102
+ const nextCellStart = table.start + nextCellPos;
103
+ const $nextCell = state.doc.resolve(nextCellStart);
104
+ if ((_$nextCell$nodeAfter = $nextCell.nodeAfter) !== null && _$nextCell$nodeAfter !== void 0 && _$nextCell$nodeAfter.type && [tableCell, tableHeader].includes((_$nextCell$nodeAfter2 = $nextCell.nodeAfter) === null || _$nextCell$nodeAfter2 === void 0 ? void 0 : _$nextCell$nodeAfter2.type)) {
105
+ const contentPos = getTargetPositionInNextCell($nextCell.nodeAfter, nextCellStart, direction);
106
+ dispatch(state.tr.setSelection(TextSelection.create(state.doc, contentPos)));
107
+ return true;
108
+ }
109
+ return false;
110
+ }
111
+ return false; // No next cell found
112
+ };
113
+ function cellHasContentInDirection(cellNode, cellPos, selectionPos, direction) {
114
+ let hasContent = false;
115
+ cellNode.content.forEach((node, offset) => {
116
+ const nodeStart = cellPos + 1 + offset;
117
+ const nodeEnd = nodeStart + node.nodeSize;
118
+ if (direction === 1 && nodeStart > selectionPos && node.isBlock) {
119
+ hasContent = true; // Content below
120
+ } else if (direction === -1 && nodeEnd <= selectionPos && node.isBlock) {
121
+ hasContent = true; // Content above
122
+ }
123
+ });
124
+ return hasContent;
125
+ }
126
+ function getTargetPositionInNextCell(cellNode, nextCellStart, direction) {
127
+ let contentPos = nextCellStart + 1; // Default: just inside the cell
128
+ if (cellNode.content.size > 0) {
129
+ if (direction === 1) {
130
+ const firstChild = cellNode.firstChild;
131
+ if (firstChild && firstChild.isBlock) {
132
+ contentPos = nextCellStart + 1 + (firstChild.isLeaf ? 0 : 1); // Down: Start of first block
133
+ }
134
+ } else if (direction === -1) {
135
+ let lastBlock;
136
+ let lastOffset = 0;
137
+ cellNode.content.forEach((node, offset) => {
138
+ if (node.isBlock) {
139
+ lastBlock = node;
140
+ lastOffset = offset;
141
+ }
142
+ });
143
+ if (lastBlock) {
144
+ contentPos = nextCellStart + 1 + lastOffset + (lastBlock.isLeaf ? 0 : lastBlock.nodeSize - 1);
145
+ }
146
+ }
147
+ }
148
+ return contentPos;
149
+ }
@@ -1,5 +1,5 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
- import { addColumnAfter, addColumnAfterVO, addColumnBefore, addColumnBeforeVO, addRowAfter, addRowAfterVO, addRowBefore, addRowBeforeVO, backspace, bindKeymapWithCommand, decreaseMediaSize, deleteColumn, deleteRow, escape, focusToContextMenuTrigger, increaseMediaSize, moveColumnLeft, moveColumnRight, moveLeft, moveRight, moveRowDown, moveRowUp, nextCell, previousCell, startColumnResizing, toggleTable } from '@atlaskit/editor-common/keymaps';
2
+ import { addColumnAfter, addColumnAfterVO, addColumnBefore, addColumnBeforeVO, addRowAfter, addRowAfterVO, addRowBefore, addRowBeforeVO, backspace, bindKeymapWithCommand, decreaseMediaSize, deleteColumn, deleteRow, escape, focusToContextMenuTrigger, increaseMediaSize, moveColumnLeft, moveColumnRight, moveDown, moveLeft, moveRight, moveRowDown, moveRowUp, moveUp, nextCell, previousCell, startColumnResizing, toggleTable } from '@atlaskit/editor-common/keymaps';
3
3
  import { editorCommandToPMCommand } from '@atlaskit/editor-common/preset';
4
4
  import { chainCommands } from '@atlaskit/editor-prosemirror/commands';
5
5
  import { keymap } from '@atlaskit/editor-prosemirror/keymap';
@@ -8,6 +8,7 @@ import { moveSourceWithAnalyticsViaShortcut } from '../pm-plugins/drag-and-drop/
8
8
  import { goToNextCell, moveCursorBackward, setFocusToCellMenu } from './commands';
9
9
  import { activateNextResizeArea, initiateKeyboardColumnResizing, stopKeyboardColumnResizing } from './commands/column-resize';
10
10
  import { addRowAroundSelection, changeColumnWidthByStepWithAnalytics, deleteSelectedRowsOrColumnsWithAnalyticsViaShortcut, deleteTableIfSelectedWithAnalytics, emptyMultipleCellsWithAnalytics } from './commands/commands-with-analytics';
11
+ import { goToNextCellVertical } from './commands/go-to-next-cell';
11
12
  import { addColumnAfter as addColumnAfterCommand, addColumnBefore as addColumnBeforeCommand, createTable, insertTableWithNestingSupport } from './commands/insert';
12
13
  export function keymapPlugin(getEditorContainerWidth, api, nodeViewPortalProviderAPI, editorAnalyticsAPI, dragAndDropEnabled, isTableScalingEnabled = false, isTableAlignmentEnabled = false, isFullWidthEnabled, pluginInjectionApi, getIntl, isTableFixedColumnWidthsOptionEnabled = false, shouldUseIncreasedScalingPercent, isCommentEditor, isChromelessEditor, isTableResizingEnabled) {
13
14
  var _pluginInjectionApi$a;
@@ -146,6 +147,14 @@ export function keymapPlugin(getEditorContainerWidth, api, nodeViewPortalProvide
146
147
  bindKeymapWithCommand(
147
148
  // Ignored via go/ees005
148
149
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
150
+ moveDown.common, goToNextCellVertical(1), list);
151
+ bindKeymapWithCommand(
152
+ // Ignored via go/ees005
153
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
154
+ moveUp.common, goToNextCellVertical(-1), list);
155
+ bindKeymapWithCommand(
156
+ // Ignored via go/ees005
157
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
149
158
  decreaseMediaSize.common, changeColumnWidthByStepWithAnalytics(editorAnalyticsAPI, api)(-10, getEditorContainerWidth, isTableScalingEnabled, isTableFixedColumnWidthsOptionEnabled, !!isCommentEditor, INPUT_METHOD.SHORTCUT, ariaNotifyPlugin, getIntl), list);
150
159
  bindKeymapWithCommand(
151
160
  // Ignored via go/ees005
@@ -1,9 +1,9 @@
1
- // #region Constants
2
-
3
1
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
+ import { isSelectionTableNestedInTable } from '@atlaskit/editor-common/nesting';
3
+ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
4
4
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
5
5
  import { TableMap } from '@atlaskit/editor-tables/table-map';
6
- import { goToNextCell as baseGotoNextCell, findTable } from '@atlaskit/editor-tables/utils';
6
+ import { goToNextCell as baseGotoNextCell, findCellClosestToPos, findTable, findTableClosestToPos, isTableSelected } from '@atlaskit/editor-tables/utils';
7
7
  import { getPluginState } from '../plugin-factory';
8
8
  import { stopKeyboardColumnResizing } from './column-resize';
9
9
  import { insertRowWithAnalytics } from './commands-with-analytics';
@@ -35,7 +35,7 @@ export var goToNextCell = function goToNextCell(editorAnalyticsAPI, ariaNotify,
35
35
  var firstCellPos = map.positionAt(0, 0, table.node) + table.start;
36
36
  var lastCellPos = map.positionAt(map.height - 1, map.width - 1, table.node) + table.start;
37
37
 
38
- // when tabbing backwards at first cell (top left), insert row at the start of table
38
+ // When tabbing backwards at first cell (top left), insert row at the start of table
39
39
  if (firstCellPos === cell.pos && direction === TAB_BACKWARD_DIRECTION) {
40
40
  insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.KEYBOARD, {
41
41
  index: 0,
@@ -44,7 +44,7 @@ export var goToNextCell = function goToNextCell(editorAnalyticsAPI, ariaNotify,
44
44
  return true;
45
45
  }
46
46
 
47
- // when tabbing forwards at last cell (bottom right), insert row at the end of table
47
+ // When tabbing forwards at last cell (bottom right), insert row at the end of table
48
48
  if (lastCellPos === cell.pos && direction === TAB_FORWARD_DIRECTION) {
49
49
  insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.KEYBOARD, {
50
50
  index: map.height,
@@ -58,4 +58,94 @@ export var goToNextCell = function goToNextCell(editorAnalyticsAPI, ariaNotify,
58
58
  return true;
59
59
  };
60
60
  };
61
- };
61
+ };
62
+
63
+ /**
64
+ * Moves the cursor vertically from a NodeSelection within a table cell.
65
+ * - If content exists above/below within the cell, lets ProseMirror handle it.
66
+ * - Otherwise, moves to the next cell (down to start, up to last line).
67
+ */
68
+ export var goToNextCellVertical = function goToNextCellVertical(direction) {
69
+ return function (state, dispatch) {
70
+ var selection = state.selection;
71
+ var nestedTableSelection = isSelectionTableNestedInTable(state);
72
+ if (!(selection instanceof NodeSelection) && !nestedTableSelection) {
73
+ return false; // Let ProseMirror handle other selection types
74
+ }
75
+ var table = findTable(selection);
76
+ var cell = findCellClosestToPos(state.doc.resolve(selection.from));
77
+ var selectionPos = selection.from;
78
+
79
+ // Handle when we have nested table fully selected
80
+ if (table && nestedTableSelection && isTableSelected(selection)) {
81
+ var parentTablePos = table.pos;
82
+ table = findTableClosestToPos(state.doc.resolve(parentTablePos));
83
+ cell = findCellClosestToPos(state.doc.resolve(parentTablePos));
84
+ selectionPos = parentTablePos;
85
+ }
86
+ if (!table || !cell || !cell.pos) {
87
+ return false;
88
+ }
89
+ var _state$schema$nodes2 = state.schema.nodes,
90
+ tableCell = _state$schema$nodes2.tableCell,
91
+ tableHeader = _state$schema$nodes2.tableHeader;
92
+
93
+ // Let ProseMirror handle movement within the cell if content exists above/below
94
+ if (cellHasContentInDirection(cell.node, cell.pos, selectionPos, direction)) {
95
+ return false;
96
+ }
97
+
98
+ // Move to the next cell vertically
99
+ var map = TableMap.get(table.node);
100
+ var nextCellPos = map.nextCell(cell.pos - table.start, 'vert', direction);
101
+ if (dispatch && nextCellPos) {
102
+ var _$nextCell$nodeAfter, _$nextCell$nodeAfter2;
103
+ var nextCellStart = table.start + nextCellPos;
104
+ var $nextCell = state.doc.resolve(nextCellStart);
105
+ if ((_$nextCell$nodeAfter = $nextCell.nodeAfter) !== null && _$nextCell$nodeAfter !== void 0 && _$nextCell$nodeAfter.type && [tableCell, tableHeader].includes((_$nextCell$nodeAfter2 = $nextCell.nodeAfter) === null || _$nextCell$nodeAfter2 === void 0 ? void 0 : _$nextCell$nodeAfter2.type)) {
106
+ var contentPos = getTargetPositionInNextCell($nextCell.nodeAfter, nextCellStart, direction);
107
+ dispatch(state.tr.setSelection(TextSelection.create(state.doc, contentPos)));
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ return false; // No next cell found
113
+ };
114
+ };
115
+ function cellHasContentInDirection(cellNode, cellPos, selectionPos, direction) {
116
+ var hasContent = false;
117
+ cellNode.content.forEach(function (node, offset) {
118
+ var nodeStart = cellPos + 1 + offset;
119
+ var nodeEnd = nodeStart + node.nodeSize;
120
+ if (direction === 1 && nodeStart > selectionPos && node.isBlock) {
121
+ hasContent = true; // Content below
122
+ } else if (direction === -1 && nodeEnd <= selectionPos && node.isBlock) {
123
+ hasContent = true; // Content above
124
+ }
125
+ });
126
+ return hasContent;
127
+ }
128
+ function getTargetPositionInNextCell(cellNode, nextCellStart, direction) {
129
+ var contentPos = nextCellStart + 1; // Default: just inside the cell
130
+ if (cellNode.content.size > 0) {
131
+ if (direction === 1) {
132
+ var firstChild = cellNode.firstChild;
133
+ if (firstChild && firstChild.isBlock) {
134
+ contentPos = nextCellStart + 1 + (firstChild.isLeaf ? 0 : 1); // Down: Start of first block
135
+ }
136
+ } else if (direction === -1) {
137
+ var lastBlock;
138
+ var lastOffset = 0;
139
+ cellNode.content.forEach(function (node, offset) {
140
+ if (node.isBlock) {
141
+ lastBlock = node;
142
+ lastOffset = offset;
143
+ }
144
+ });
145
+ if (lastBlock) {
146
+ contentPos = nextCellStart + 1 + lastOffset + (lastBlock.isLeaf ? 0 : lastBlock.nodeSize - 1);
147
+ }
148
+ }
149
+ }
150
+ return contentPos;
151
+ }
@@ -1,5 +1,5 @@
1
1
  import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
2
- import { addColumnAfter, addColumnAfterVO, addColumnBefore, addColumnBeforeVO, addRowAfter, addRowAfterVO, addRowBefore, addRowBeforeVO, backspace, bindKeymapWithCommand, decreaseMediaSize, deleteColumn, deleteRow, escape, focusToContextMenuTrigger, increaseMediaSize, moveColumnLeft, moveColumnRight, moveLeft, moveRight, moveRowDown, moveRowUp, nextCell, previousCell, startColumnResizing, toggleTable } from '@atlaskit/editor-common/keymaps';
2
+ import { addColumnAfter, addColumnAfterVO, addColumnBefore, addColumnBeforeVO, addRowAfter, addRowAfterVO, addRowBefore, addRowBeforeVO, backspace, bindKeymapWithCommand, decreaseMediaSize, deleteColumn, deleteRow, escape, focusToContextMenuTrigger, increaseMediaSize, moveColumnLeft, moveColumnRight, moveDown, moveLeft, moveRight, moveRowDown, moveRowUp, moveUp, nextCell, previousCell, startColumnResizing, toggleTable } from '@atlaskit/editor-common/keymaps';
3
3
  import { editorCommandToPMCommand } from '@atlaskit/editor-common/preset';
4
4
  import { chainCommands } from '@atlaskit/editor-prosemirror/commands';
5
5
  import { keymap } from '@atlaskit/editor-prosemirror/keymap';
@@ -8,6 +8,7 @@ import { moveSourceWithAnalyticsViaShortcut } from '../pm-plugins/drag-and-drop/
8
8
  import { goToNextCell, moveCursorBackward, setFocusToCellMenu } from './commands';
9
9
  import { activateNextResizeArea, initiateKeyboardColumnResizing, stopKeyboardColumnResizing } from './commands/column-resize';
10
10
  import { addRowAroundSelection, changeColumnWidthByStepWithAnalytics, deleteSelectedRowsOrColumnsWithAnalyticsViaShortcut, deleteTableIfSelectedWithAnalytics, emptyMultipleCellsWithAnalytics } from './commands/commands-with-analytics';
11
+ import { goToNextCellVertical } from './commands/go-to-next-cell';
11
12
  import { addColumnAfter as addColumnAfterCommand, addColumnBefore as addColumnBeforeCommand, createTable, insertTableWithNestingSupport } from './commands/insert';
12
13
  export function keymapPlugin(getEditorContainerWidth, api, nodeViewPortalProviderAPI, editorAnalyticsAPI, dragAndDropEnabled) {
13
14
  var _pluginInjectionApi$a;
@@ -156,6 +157,14 @@ export function keymapPlugin(getEditorContainerWidth, api, nodeViewPortalProvide
156
157
  bindKeymapWithCommand(
157
158
  // Ignored via go/ees005
158
159
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
160
+ moveDown.common, goToNextCellVertical(1), list);
161
+ bindKeymapWithCommand(
162
+ // Ignored via go/ees005
163
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
164
+ moveUp.common, goToNextCellVertical(-1), list);
165
+ bindKeymapWithCommand(
166
+ // Ignored via go/ees005
167
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
159
168
  decreaseMediaSize.common, changeColumnWidthByStepWithAnalytics(editorAnalyticsAPI, api)(-10, getEditorContainerWidth, isTableScalingEnabled, isTableFixedColumnWidthsOptionEnabled, !!isCommentEditor, INPUT_METHOD.SHORTCUT, ariaNotifyPlugin, getIntl), list);
160
169
  bindKeymapWithCommand(
161
170
  // Ignored via go/ees005
@@ -3,3 +3,9 @@ import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
3
3
  import type { Command } from '@atlaskit/editor-common/types';
4
4
  import type { Direction } from '@atlaskit/editor-tables/types';
5
5
  export declare const goToNextCell: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined | null, ariaNotify?: ((message: string) => void) | undefined, getIntl?: () => IntlShape) => (direction: Direction) => Command;
6
+ /**
7
+ * Moves the cursor vertically from a NodeSelection within a table cell.
8
+ * - If content exists above/below within the cell, lets ProseMirror handle it.
9
+ * - Otherwise, moves to the next cell (down to start, up to last line).
10
+ */
11
+ export declare const goToNextCellVertical: (direction: Direction) => Command;
@@ -3,3 +3,9 @@ import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
3
3
  import type { Command } from '@atlaskit/editor-common/types';
4
4
  import type { Direction } from '@atlaskit/editor-tables/types';
5
5
  export declare const goToNextCell: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined | null, ariaNotify?: ((message: string) => void) | undefined, getIntl?: () => IntlShape) => (direction: Direction) => Command;
6
+ /**
7
+ * Moves the cursor vertically from a NodeSelection within a table cell.
8
+ * - If content exists above/below within the cell, lets ProseMirror handle it.
9
+ * - Otherwise, moves to the next cell (down to start, up to last line).
10
+ */
11
+ export declare const goToNextCellVertical: (direction: Direction) => Command;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "10.4.5",
3
+ "version": "10.4.7",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -39,7 +39,7 @@
39
39
  "@atlaskit/editor-plugin-batch-attribute-updates": "^2.1.0",
40
40
  "@atlaskit/editor-plugin-content-insertion": "^2.1.0",
41
41
  "@atlaskit/editor-plugin-editor-viewmode": "^3.0.0",
42
- "@atlaskit/editor-plugin-extension": "^5.0.0",
42
+ "@atlaskit/editor-plugin-extension": "5.0.8",
43
43
  "@atlaskit/editor-plugin-guideline": "^2.0.0",
44
44
  "@atlaskit/editor-plugin-selection": "^2.1.0",
45
45
  "@atlaskit/editor-plugin-width": "^3.0.0",
@@ -52,7 +52,7 @@
52
52
  "@atlaskit/pragmatic-drag-and-drop": "^1.5.0",
53
53
  "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.0",
54
54
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.0",
55
- "@atlaskit/primitives": "^14.1.0",
55
+ "@atlaskit/primitives": "^14.2.0",
56
56
  "@atlaskit/theme": "^18.0.0",
57
57
  "@atlaskit/tmp-editor-statsig": "^4.1.0",
58
58
  "@atlaskit/toggle": "^15.0.0",
@@ -1,13 +1,21 @@
1
- // #region Constants
2
1
  import type { IntlShape } from 'react-intl-next/src/types';
3
2
 
4
3
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
4
  import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
5
+ import { isSelectionTableNestedInTable } from '@atlaskit/editor-common/nesting';
6
6
  import type { Command } from '@atlaskit/editor-common/types';
7
+ import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
8
+ import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
7
9
  import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
8
10
  import { TableMap } from '@atlaskit/editor-tables/table-map';
9
11
  import type { Direction } from '@atlaskit/editor-tables/types';
10
- import { goToNextCell as baseGotoNextCell, findTable } from '@atlaskit/editor-tables/utils';
12
+ import {
13
+ goToNextCell as baseGotoNextCell,
14
+ findCellClosestToPos,
15
+ findTable,
16
+ findTableClosestToPos,
17
+ isTableSelected,
18
+ } from '@atlaskit/editor-tables/utils';
11
19
 
12
20
  import { getPluginState } from '../plugin-factory';
13
21
 
@@ -47,7 +55,7 @@ export const goToNextCell =
47
55
  const firstCellPos = map.positionAt(0, 0, table.node) + table.start;
48
56
  const lastCellPos = map.positionAt(map.height - 1, map.width - 1, table.node) + table.start;
49
57
 
50
- // when tabbing backwards at first cell (top left), insert row at the start of table
58
+ // When tabbing backwards at first cell (top left), insert row at the start of table
51
59
  if (firstCellPos === cell.pos && direction === TAB_BACKWARD_DIRECTION) {
52
60
  insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.KEYBOARD, {
53
61
  index: 0,
@@ -56,7 +64,7 @@ export const goToNextCell =
56
64
  return true;
57
65
  }
58
66
 
59
- // when tabbing forwards at last cell (bottom right), insert row at the end of table
67
+ // When tabbing forwards at last cell (bottom right), insert row at the end of table
60
68
  if (lastCellPos === cell.pos && direction === TAB_FORWARD_DIRECTION) {
61
69
  insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.KEYBOARD, {
62
70
  index: map.height,
@@ -71,3 +79,120 @@ export const goToNextCell =
71
79
 
72
80
  return true;
73
81
  };
82
+
83
+ /**
84
+ * Moves the cursor vertically from a NodeSelection within a table cell.
85
+ * - If content exists above/below within the cell, lets ProseMirror handle it.
86
+ * - Otherwise, moves to the next cell (down to start, up to last line).
87
+ */
88
+ export const goToNextCellVertical =
89
+ (direction: Direction): Command =>
90
+ (state, dispatch) => {
91
+ const { selection } = state;
92
+ const nestedTableSelection = isSelectionTableNestedInTable(state);
93
+
94
+ if (!(selection instanceof NodeSelection) && !nestedTableSelection) {
95
+ return false; // Let ProseMirror handle other selection types
96
+ }
97
+
98
+ let table = findTable(selection);
99
+ let cell = findCellClosestToPos(state.doc.resolve(selection.from));
100
+ let selectionPos = selection.from;
101
+
102
+ // Handle when we have nested table fully selected
103
+ if (table && nestedTableSelection && isTableSelected(selection)) {
104
+ const parentTablePos = table.pos;
105
+ table = findTableClosestToPos(state.doc.resolve(parentTablePos));
106
+ cell = findCellClosestToPos(state.doc.resolve(parentTablePos));
107
+ selectionPos = parentTablePos;
108
+ }
109
+
110
+ if (!table || !cell || !cell.pos) {
111
+ return false;
112
+ }
113
+
114
+ const { tableCell, tableHeader } = state.schema.nodes;
115
+
116
+ // Let ProseMirror handle movement within the cell if content exists above/below
117
+ if (cellHasContentInDirection(cell.node, cell.pos, selectionPos, direction)) {
118
+ return false;
119
+ }
120
+
121
+ // Move to the next cell vertically
122
+ const map = TableMap.get(table.node);
123
+ const nextCellPos = map.nextCell(cell.pos - table.start, 'vert', direction);
124
+ if (dispatch && nextCellPos) {
125
+ const nextCellStart = table.start + nextCellPos;
126
+ const $nextCell = state.doc.resolve(nextCellStart);
127
+
128
+ if (
129
+ $nextCell.nodeAfter?.type &&
130
+ [tableCell, tableHeader].includes($nextCell.nodeAfter?.type)
131
+ ) {
132
+ const contentPos = getTargetPositionInNextCell(
133
+ $nextCell.nodeAfter,
134
+ nextCellStart,
135
+ direction,
136
+ );
137
+
138
+ dispatch(state.tr.setSelection(TextSelection.create(state.doc, contentPos)));
139
+ return true;
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ return false; // No next cell found
146
+ };
147
+
148
+ function cellHasContentInDirection(
149
+ cellNode: PMNode,
150
+ cellPos: number,
151
+ selectionPos: number,
152
+ direction: Direction,
153
+ ): boolean {
154
+ let hasContent = false;
155
+
156
+ cellNode.content.forEach((node: PMNode, offset: number) => {
157
+ const nodeStart = cellPos + 1 + offset;
158
+ const nodeEnd = nodeStart + node.nodeSize;
159
+
160
+ if (direction === 1 && nodeStart > selectionPos && node.isBlock) {
161
+ hasContent = true; // Content below
162
+ } else if (direction === -1 && nodeEnd <= selectionPos && node.isBlock) {
163
+ hasContent = true; // Content above
164
+ }
165
+ });
166
+
167
+ return hasContent;
168
+ }
169
+
170
+ function getTargetPositionInNextCell(
171
+ cellNode: PMNode,
172
+ nextCellStart: number,
173
+ direction: Direction,
174
+ ): number {
175
+ let contentPos = nextCellStart + 1; // Default: just inside the cell
176
+ if (cellNode.content.size > 0) {
177
+ if (direction === 1) {
178
+ const firstChild = cellNode.firstChild;
179
+ if (firstChild && firstChild.isBlock) {
180
+ contentPos = nextCellStart + 1 + (firstChild.isLeaf ? 0 : 1); // Down: Start of first block
181
+ }
182
+ } else if (direction === -1) {
183
+ let lastBlock: PMNode | undefined;
184
+ let lastOffset = 0;
185
+ cellNode.content.forEach((node: PMNode, offset: number) => {
186
+ if (node.isBlock) {
187
+ lastBlock = node;
188
+ lastOffset = offset;
189
+ }
190
+ });
191
+ if (lastBlock) {
192
+ contentPos =
193
+ nextCellStart + 1 + lastOffset + (lastBlock.isLeaf ? 0 : lastBlock.nodeSize - 1);
194
+ }
195
+ }
196
+ }
197
+ return contentPos;
198
+ }
@@ -27,10 +27,12 @@ import {
27
27
  increaseMediaSize,
28
28
  moveColumnLeft,
29
29
  moveColumnRight,
30
+ moveDown,
30
31
  moveLeft,
31
32
  moveRight,
32
33
  moveRowDown,
33
34
  moveRowUp,
35
+ moveUp,
34
36
  nextCell,
35
37
  previousCell,
36
38
  startColumnResizing,
@@ -60,6 +62,7 @@ import {
60
62
  deleteTableIfSelectedWithAnalytics,
61
63
  emptyMultipleCellsWithAnalytics,
62
64
  } from './commands/commands-with-analytics';
65
+ import { goToNextCellVertical } from './commands/go-to-next-cell';
63
66
  import {
64
67
  addColumnAfter as addColumnAfterCommand,
65
68
  addColumnBefore as addColumnBeforeCommand,
@@ -362,6 +365,22 @@ export function keymapPlugin(
362
365
  list,
363
366
  );
364
367
 
368
+ bindKeymapWithCommand(
369
+ // Ignored via go/ees005
370
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
371
+ moveDown.common!,
372
+ goToNextCellVertical(1),
373
+ list,
374
+ );
375
+
376
+ bindKeymapWithCommand(
377
+ // Ignored via go/ees005
378
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
379
+ moveUp.common!,
380
+ goToNextCellVertical(-1),
381
+ list,
382
+ );
383
+
365
384
  bindKeymapWithCommand(
366
385
  // Ignored via go/ees005
367
386
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion