@atlaskit/editor-plugin-table 5.4.25 → 5.5.0

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,11 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 5.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#56823](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/56823) [`d2fbdf3b6822`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/d2fbdf3b6822) - [ux] ECA11Y-111: Keyboard accessibility of table resizer
8
+
3
9
  ## 5.4.25
4
10
 
5
11
  ### Patch Changes
@@ -9,17 +9,20 @@ exports.TableResizer = void 0;
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
11
  var _react = _interopRequireWildcard(require("react"));
12
+ var _debounce = _interopRequireDefault(require("lodash/debounce"));
12
13
  var _rafSchd = _interopRequireDefault(require("raf-schd"));
13
14
  var _reactIntlNext = require("react-intl-next");
14
15
  var _analytics = require("@atlaskit/editor-common/analytics");
15
16
  var _guideline = require("@atlaskit/editor-common/guideline");
17
+ var _keymaps = require("@atlaskit/editor-common/keymaps");
16
18
  var _messages = require("@atlaskit/editor-common/messages");
17
19
  var _resizer = require("@atlaskit/editor-common/resizer");
18
- var _utils = require("@atlaskit/editor-tables/utils");
20
+ var _utils = require("@atlaskit/editor-common/utils");
21
+ var _utils2 = require("@atlaskit/editor-tables/utils");
19
22
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
20
23
  var _pluginFactory = require("../pm-plugins/plugin-factory");
21
24
  var _tableAnalytics = require("../pm-plugins/table-analytics");
22
- var _utils2 = require("../pm-plugins/table-resizing/utils");
25
+ var _utils3 = require("../pm-plugins/table-resizing/utils");
23
26
  var _tableWidth = require("../pm-plugins/table-width");
24
27
  var _consts = require("../ui/consts");
25
28
  var _analytics2 = require("../utils/analytics");
@@ -29,6 +32,8 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio
29
32
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
30
33
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
31
34
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
35
+ var DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER = 1000;
36
+ var RESIZE_STEP_VALUE = 10;
32
37
  var handles = {
33
38
  right: true
34
39
  };
@@ -59,8 +64,8 @@ var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
59
64
  return 'small';
60
65
  };
61
66
  var getResizerMinWidth = function getResizerMinWidth(node) {
62
- var currentColumnCount = (0, _utils2.getColgroupChildrenLength)(node);
63
- var minColumnWidth = currentColumnCount <= 3 ? currentColumnCount * _utils2.COLUMN_MIN_WIDTH : 3 * _utils2.COLUMN_MIN_WIDTH;
67
+ var currentColumnCount = (0, _utils3.getColgroupChildrenLength)(node);
68
+ var minColumnWidth = currentColumnCount <= 3 ? currentColumnCount * _utils3.COLUMN_MIN_WIDTH : 3 * _utils3.COLUMN_MIN_WIDTH;
64
69
  // add an extra pixel as the scale table logic will scale columns to be tableContainerWidth - 1
65
70
  // the table can't scale past its min-width, so instead restrict table container min width to avoid that situation
66
71
  return minColumnWidth + 1;
@@ -96,12 +101,36 @@ var TableResizer = exports.TableResizer = function TableResizer(_ref) {
96
101
  var currentGap = (0, _react.useRef)(0);
97
102
  // track resizing state - use ref over state to avoid re-render
98
103
  var isResizing = (0, _react.useRef)(false);
104
+ var areResizeMetaKeysPressed = (0, _react.useRef)(false);
105
+ var resizerRef = (0, _react.useRef)(null);
106
+
107
+ // used to reposition tooltip when table is resizing via keyboard
108
+ var updateTooltip = _react.default.useRef();
99
109
  var _useState = (0, _react.useState)(false),
100
110
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
101
111
  snappingEnabled = _useState2[0],
102
112
  setSnappingEnabled = _useState2[1];
113
+
114
+ // we don't want to update aria-live region on each width change, it might provide bad experience for screen reader users
115
+ var _useState3 = (0, _react.useState)({
116
+ type: 'none',
117
+ width: width
118
+ }),
119
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
120
+ screenReaderResizeInformation = _useState4[0],
121
+ setScreenReaderResizeInformation = _useState4[1];
103
122
  var _useIntl = (0, _reactIntlNext.useIntl)(),
104
123
  formatMessage = _useIntl.formatMessage;
124
+ var screenReaderResizeAnnouncerMessages = {
125
+ increase: formatMessage(_messages.tableMessages.tableSizeIncreaseScreenReaderInformation, {
126
+ newWidth: screenReaderResizeInformation.width
127
+ }),
128
+ decrease: formatMessage(_messages.tableMessages.tableSizeDecreaseScreenReaderInformation, {
129
+ newWidth: screenReaderResizeInformation.width
130
+ }),
131
+ none: ''
132
+ };
133
+ var isTableSelected = ((_findTable = (0, _utils2.findTable)((_editorView$state = editorView.state) === null || _editorView$state === void 0 ? void 0 : _editorView$state.selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) === getPos();
105
134
  var resizerMinWidth = getResizerMinWidth(node);
106
135
  var handleSize = getResizerHandleHeight(tableRef);
107
136
  var _getPluginState = (0, _pluginFactory.getPluginState)(editorView.state),
@@ -171,7 +200,7 @@ var TableResizer = exports.TableResizer = function TableResizer(_ref) {
171
200
  if (typeof pos !== 'number') {
172
201
  return;
173
202
  }
174
- (0, _utils2.previewScaleTable)(tableRef, {
203
+ (0, _utils3.previewScaleTable)(tableRef, {
175
204
  node: node,
176
205
  prevNode: node,
177
206
  start: pos + 1,
@@ -211,7 +240,7 @@ var TableResizer = exports.TableResizer = function TableResizer(_ref) {
211
240
  width: newWidth
212
241
  }));
213
242
  var newNode = tr.doc.nodeAt(pos);
214
- tr = (0, _utils2.scaleTable)(tableRef, {
243
+ tr = (0, _utils3.scaleTable)(tableRef, {
215
244
  node: newNode,
216
245
  prevNode: node,
217
246
  start: pos + 1,
@@ -235,8 +264,109 @@ var TableResizer = exports.TableResizer = function TableResizer(_ref) {
235
264
  }
236
265
  return newWidth;
237
266
  }, [displayGapCursor, updateWidth, editorView, getPos, node, tableRef, scheduleResize, displayGuideline, attachAnalyticsEvent, endMeasure, onResizeStop]);
238
- var isTableSelected = ((_findTable = (0, _utils.findTable)((_editorView$state = editorView.state) === null || _editorView$state === void 0 ? void 0 : _editorView$state.selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) === getPos();
239
- return /*#__PURE__*/_react.default.createElement(_resizer.ResizerNext, {
267
+ var handleTableSizeChangeOnKeypress = (0, _react.useCallback)(function (step) {
268
+ var newWidth = width + step;
269
+ if (newWidth > maxWidth || newWidth < resizerMinWidth) {
270
+ return;
271
+ }
272
+ handleResizeStop({
273
+ width: width,
274
+ x: 0,
275
+ y: 0,
276
+ height: 0
277
+ }, {
278
+ width: step,
279
+ height: 0
280
+ });
281
+ }, [width, handleResizeStop, maxWidth, resizerMinWidth]);
282
+ var handleEscape = (0, _react.useCallback)(function () {
283
+ editorView === null || editorView === void 0 || editorView.focus();
284
+ }, [editorView]);
285
+ var handleKeyDown = (0, _react.useCallback)(function (event) {
286
+ var isBracketKey = event.code === 'BracketRight' || event.code === 'BracketLeft';
287
+ var metaKey = _utils.browser.mac ? event.metaKey : event.ctrlKey;
288
+ if (event.altKey || metaKey || event.shiftKey) {
289
+ areResizeMetaKeysPressed.current = true;
290
+ }
291
+ if (event.altKey && metaKey) {
292
+ if (isBracketKey) {
293
+ event.preventDefault();
294
+ handleTableSizeChangeOnKeypress(event.code === 'BracketRight' ? RESIZE_STEP_VALUE : -RESIZE_STEP_VALUE);
295
+ }
296
+ } else if (!areResizeMetaKeysPressed.current) {
297
+ handleEscape();
298
+ }
299
+ }, [handleEscape, handleTableSizeChangeOnKeypress]);
300
+ var handleKeyUp = (0, _react.useCallback)(function (event) {
301
+ if (event.altKey || event.metaKey) {
302
+ areResizeMetaKeysPressed.current = false;
303
+ }
304
+ return;
305
+ }, [areResizeMetaKeysPressed]);
306
+ (0, _react.useLayoutEffect)(function () {
307
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.a11y-table-resizing_uapcv')) {
308
+ if (!resizerRef.current) {
309
+ return;
310
+ }
311
+ var resizeHandleThumbEl = resizerRef.current.getResizerThumbEl();
312
+ var globalKeyDownHandler = function globalKeyDownHandler(event) {
313
+ var metaKey = _utils.browser.mac ? event.metaKey : event.ctrlKey;
314
+ if (!isTableSelected) {
315
+ return;
316
+ }
317
+ if (event.altKey && event.shiftKey && metaKey && event.code === 'KeyR') {
318
+ event.preventDefault();
319
+ if (!resizeHandleThumbEl) {
320
+ return;
321
+ }
322
+ resizeHandleThumbEl.focus();
323
+ resizeHandleThumbEl.scrollIntoView({
324
+ behavior: 'smooth',
325
+ block: 'center',
326
+ inline: 'nearest'
327
+ });
328
+ }
329
+ };
330
+ var editorViewDom = editorView === null || editorView === void 0 ? void 0 : editorView.dom;
331
+ editorViewDom === null || editorViewDom === void 0 || editorViewDom.addEventListener('keydown', globalKeyDownHandler);
332
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.addEventListener('keydown', handleKeyDown);
333
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.addEventListener('keyup', handleKeyUp);
334
+ return function () {
335
+ editorViewDom === null || editorViewDom === void 0 || editorViewDom.removeEventListener('keydown', globalKeyDownHandler);
336
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.removeEventListener('keydown', handleKeyDown);
337
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.removeEventListener('keyup', handleKeyUp);
338
+ };
339
+ }
340
+ }, [resizerRef, editorView, handleResizeStop, isTableSelected, handleKeyDown, handleKeyUp]);
341
+ (0, _react.useLayoutEffect)(function () {
342
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.a11y-table-resizing_uapcv')) {
343
+ var _updateTooltip$curren;
344
+ (_updateTooltip$curren = updateTooltip.current) === null || _updateTooltip$curren === void 0 || _updateTooltip$curren.call(updateTooltip);
345
+ }
346
+ }, [width]);
347
+ (0, _react.useEffect)(function () {
348
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.a11y-table-resizing_uapcv')) {
349
+ var debouncedSetWidth = (0, _debounce.default)(setScreenReaderResizeInformation, DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER);
350
+ debouncedSetWidth(function (prevState) {
351
+ var type = 'none';
352
+ if (prevState.width > width) {
353
+ type = 'decrease';
354
+ }
355
+ if (prevState.width < width) {
356
+ type = 'increase';
357
+ }
358
+ return {
359
+ type: type,
360
+ width: width
361
+ };
362
+ });
363
+ return function () {
364
+ debouncedSetWidth.cancel();
365
+ };
366
+ }
367
+ }, [width]);
368
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_resizer.ResizerNext, {
369
+ ref: resizerRef,
240
370
  enable: handles,
241
371
  width: width,
242
372
  handleAlignmentMethod: "sticky",
@@ -254,6 +384,16 @@ var TableResizer = exports.TableResizer = function TableResizer(_ref) {
254
384
  isHandleVisible: isTableSelected,
255
385
  appearance: isTableSelected && isWholeTableInDanger ? 'danger' : undefined,
256
386
  handleHighlight: "shadow",
257
- handleTooltipContent: formatMessage(_messages.tableMessages.resizeTable)
258
- }, children);
387
+ handleTooltipContent: (0, _platformFeatureFlags.getBooleanFF)('platform.editor.a11y-table-resizing_uapcv') ? function (_ref3) {
388
+ var update = _ref3.update;
389
+ updateTooltip.current = update;
390
+ return /*#__PURE__*/_react.default.createElement(_keymaps.ToolTipContent, {
391
+ description: formatMessage(_messages.tableMessages.resizeTable),
392
+ keymap: _keymaps.focusTableResizer
393
+ });
394
+ } : formatMessage(_messages.tableMessages.resizeTable)
395
+ }, children), (0, _platformFeatureFlags.getBooleanFF)('platform.editor.a11y-table-resizing_uapcv') && /*#__PURE__*/_react.default.createElement("div", {
396
+ className: "assistive",
397
+ role: "status"
398
+ }, screenReaderResizeAnnouncerMessages[screenReaderResizeInformation.type]));
259
399
  };
@@ -1,10 +1,13 @@
1
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
+ import debounce from 'lodash/debounce';
2
3
  import rafSchd from 'raf-schd';
3
4
  import { useIntl } from 'react-intl-next';
4
5
  import { TABLE_OVERFLOW_CHANGE_TRIGGER } from '@atlaskit/editor-common/analytics';
5
6
  import { getGuidelinesWithHighlights } from '@atlaskit/editor-common/guideline';
7
+ import { focusTableResizer, ToolTipContent } from '@atlaskit/editor-common/keymaps';
6
8
  import { tableMessages as messages } from '@atlaskit/editor-common/messages';
7
9
  import { ResizerNext } from '@atlaskit/editor-common/resizer';
10
+ import { browser } from '@atlaskit/editor-common/utils';
8
11
  import { findTable } from '@atlaskit/editor-tables/utils';
9
12
  import { getBooleanFF } from '@atlaskit/platform-feature-flags';
10
13
  import { getPluginState } from '../pm-plugins/plugin-factory';
@@ -15,6 +18,8 @@ import { TABLE_HIGHLIGHT_GAP, TABLE_HIGHLIGHT_TOLERANCE, TABLE_SNAP_GAP } from '
15
18
  import { generateResizedPayload, generateResizeFrameRatePayloads, useMeasureFramerate } from '../utils/analytics';
16
19
  import { defaultGuidelines } from '../utils/guidelines';
17
20
  import { defaultSnappingWidths, findClosestSnap } from '../utils/snapping';
21
+ const DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER = 1000;
22
+ const RESIZE_STEP_VALUE = 10;
18
23
  const handles = {
19
24
  right: true
20
25
  };
@@ -83,10 +88,31 @@ export const TableResizer = ({
83
88
  const currentGap = useRef(0);
84
89
  // track resizing state - use ref over state to avoid re-render
85
90
  const isResizing = useRef(false);
91
+ const areResizeMetaKeysPressed = useRef(false);
92
+ const resizerRef = useRef(null);
93
+
94
+ // used to reposition tooltip when table is resizing via keyboard
95
+ const updateTooltip = React.useRef();
86
96
  const [snappingEnabled, setSnappingEnabled] = useState(false);
97
+
98
+ // we don't want to update aria-live region on each width change, it might provide bad experience for screen reader users
99
+ const [screenReaderResizeInformation, setScreenReaderResizeInformation] = useState({
100
+ type: 'none',
101
+ width
102
+ });
87
103
  const {
88
104
  formatMessage
89
105
  } = useIntl();
106
+ const screenReaderResizeAnnouncerMessages = {
107
+ increase: formatMessage(messages.tableSizeIncreaseScreenReaderInformation, {
108
+ newWidth: screenReaderResizeInformation.width
109
+ }),
110
+ decrease: formatMessage(messages.tableSizeDecreaseScreenReaderInformation, {
111
+ newWidth: screenReaderResizeInformation.width
112
+ }),
113
+ none: ''
114
+ };
115
+ const isTableSelected = ((_findTable = findTable((_editorView$state = editorView.state) === null || _editorView$state === void 0 ? void 0 : _editorView$state.selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) === getPos();
90
116
  const resizerMinWidth = getResizerMinWidth(node);
91
117
  const handleSize = getResizerHandleHeight(tableRef);
92
118
  const {
@@ -230,8 +256,109 @@ export const TableResizer = ({
230
256
  }
231
257
  return newWidth;
232
258
  }, [displayGapCursor, updateWidth, editorView, getPos, node, tableRef, scheduleResize, displayGuideline, attachAnalyticsEvent, endMeasure, onResizeStop]);
233
- const isTableSelected = ((_findTable = findTable((_editorView$state = editorView.state) === null || _editorView$state === void 0 ? void 0 : _editorView$state.selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) === getPos();
234
- return /*#__PURE__*/React.createElement(ResizerNext, {
259
+ const handleTableSizeChangeOnKeypress = useCallback(step => {
260
+ const newWidth = width + step;
261
+ if (newWidth > maxWidth || newWidth < resizerMinWidth) {
262
+ return;
263
+ }
264
+ handleResizeStop({
265
+ width: width,
266
+ x: 0,
267
+ y: 0,
268
+ height: 0
269
+ }, {
270
+ width: step,
271
+ height: 0
272
+ });
273
+ }, [width, handleResizeStop, maxWidth, resizerMinWidth]);
274
+ const handleEscape = useCallback(() => {
275
+ editorView === null || editorView === void 0 ? void 0 : editorView.focus();
276
+ }, [editorView]);
277
+ const handleKeyDown = useCallback(event => {
278
+ const isBracketKey = event.code === 'BracketRight' || event.code === 'BracketLeft';
279
+ const metaKey = browser.mac ? event.metaKey : event.ctrlKey;
280
+ if (event.altKey || metaKey || event.shiftKey) {
281
+ areResizeMetaKeysPressed.current = true;
282
+ }
283
+ if (event.altKey && metaKey) {
284
+ if (isBracketKey) {
285
+ event.preventDefault();
286
+ handleTableSizeChangeOnKeypress(event.code === 'BracketRight' ? RESIZE_STEP_VALUE : -RESIZE_STEP_VALUE);
287
+ }
288
+ } else if (!areResizeMetaKeysPressed.current) {
289
+ handleEscape();
290
+ }
291
+ }, [handleEscape, handleTableSizeChangeOnKeypress]);
292
+ const handleKeyUp = useCallback(event => {
293
+ if (event.altKey || event.metaKey) {
294
+ areResizeMetaKeysPressed.current = false;
295
+ }
296
+ return;
297
+ }, [areResizeMetaKeysPressed]);
298
+ useLayoutEffect(() => {
299
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
300
+ if (!resizerRef.current) {
301
+ return;
302
+ }
303
+ const resizeHandleThumbEl = resizerRef.current.getResizerThumbEl();
304
+ const globalKeyDownHandler = event => {
305
+ const metaKey = browser.mac ? event.metaKey : event.ctrlKey;
306
+ if (!isTableSelected) {
307
+ return;
308
+ }
309
+ if (event.altKey && event.shiftKey && metaKey && event.code === 'KeyR') {
310
+ event.preventDefault();
311
+ if (!resizeHandleThumbEl) {
312
+ return;
313
+ }
314
+ resizeHandleThumbEl.focus();
315
+ resizeHandleThumbEl.scrollIntoView({
316
+ behavior: 'smooth',
317
+ block: 'center',
318
+ inline: 'nearest'
319
+ });
320
+ }
321
+ };
322
+ const editorViewDom = editorView === null || editorView === void 0 ? void 0 : editorView.dom;
323
+ editorViewDom === null || editorViewDom === void 0 ? void 0 : editorViewDom.addEventListener('keydown', globalKeyDownHandler);
324
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 ? void 0 : resizeHandleThumbEl.addEventListener('keydown', handleKeyDown);
325
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 ? void 0 : resizeHandleThumbEl.addEventListener('keyup', handleKeyUp);
326
+ return () => {
327
+ editorViewDom === null || editorViewDom === void 0 ? void 0 : editorViewDom.removeEventListener('keydown', globalKeyDownHandler);
328
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 ? void 0 : resizeHandleThumbEl.removeEventListener('keydown', handleKeyDown);
329
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 ? void 0 : resizeHandleThumbEl.removeEventListener('keyup', handleKeyUp);
330
+ };
331
+ }
332
+ }, [resizerRef, editorView, handleResizeStop, isTableSelected, handleKeyDown, handleKeyUp]);
333
+ useLayoutEffect(() => {
334
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
335
+ var _updateTooltip$curren;
336
+ (_updateTooltip$curren = updateTooltip.current) === null || _updateTooltip$curren === void 0 ? void 0 : _updateTooltip$curren.call(updateTooltip);
337
+ }
338
+ }, [width]);
339
+ useEffect(() => {
340
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
341
+ const debouncedSetWidth = debounce(setScreenReaderResizeInformation, DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER);
342
+ debouncedSetWidth(prevState => {
343
+ let type = 'none';
344
+ if (prevState.width > width) {
345
+ type = 'decrease';
346
+ }
347
+ if (prevState.width < width) {
348
+ type = 'increase';
349
+ }
350
+ return {
351
+ type,
352
+ width
353
+ };
354
+ });
355
+ return () => {
356
+ debouncedSetWidth.cancel();
357
+ };
358
+ }
359
+ }, [width]);
360
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ResizerNext, {
361
+ ref: resizerRef,
235
362
  enable: handles,
236
363
  width: width,
237
364
  handleAlignmentMethod: "sticky",
@@ -249,6 +376,17 @@ export const TableResizer = ({
249
376
  isHandleVisible: isTableSelected,
250
377
  appearance: isTableSelected && isWholeTableInDanger ? 'danger' : undefined,
251
378
  handleHighlight: "shadow",
252
- handleTooltipContent: formatMessage(messages.resizeTable)
253
- }, children);
379
+ handleTooltipContent: getBooleanFF('platform.editor.a11y-table-resizing_uapcv') ? ({
380
+ update
381
+ }) => {
382
+ updateTooltip.current = update;
383
+ return /*#__PURE__*/React.createElement(ToolTipContent, {
384
+ description: formatMessage(messages.resizeTable),
385
+ keymap: focusTableResizer
386
+ });
387
+ } : formatMessage(messages.resizeTable)
388
+ }, children), getBooleanFF('platform.editor.a11y-table-resizing_uapcv') && /*#__PURE__*/React.createElement("div", {
389
+ className: "assistive",
390
+ role: "status"
391
+ }, screenReaderResizeAnnouncerMessages[screenReaderResizeInformation.type]));
254
392
  };
@@ -2,13 +2,16 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
4
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
6
+ import debounce from 'lodash/debounce';
6
7
  import rafSchd from 'raf-schd';
7
8
  import { useIntl } from 'react-intl-next';
8
9
  import { TABLE_OVERFLOW_CHANGE_TRIGGER } from '@atlaskit/editor-common/analytics';
9
10
  import { getGuidelinesWithHighlights } from '@atlaskit/editor-common/guideline';
11
+ import { focusTableResizer, ToolTipContent } from '@atlaskit/editor-common/keymaps';
10
12
  import { tableMessages as messages } from '@atlaskit/editor-common/messages';
11
13
  import { ResizerNext } from '@atlaskit/editor-common/resizer';
14
+ import { browser } from '@atlaskit/editor-common/utils';
12
15
  import { findTable } from '@atlaskit/editor-tables/utils';
13
16
  import { getBooleanFF } from '@atlaskit/platform-feature-flags';
14
17
  import { getPluginState } from '../pm-plugins/plugin-factory';
@@ -19,6 +22,8 @@ import { TABLE_HIGHLIGHT_GAP, TABLE_HIGHLIGHT_TOLERANCE, TABLE_SNAP_GAP } from '
19
22
  import { generateResizedPayload, generateResizeFrameRatePayloads, useMeasureFramerate } from '../utils/analytics';
20
23
  import { defaultGuidelines } from '../utils/guidelines';
21
24
  import { defaultSnappingWidths, findClosestSnap } from '../utils/snapping';
25
+ var DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER = 1000;
26
+ var RESIZE_STEP_VALUE = 10;
22
27
  var handles = {
23
28
  right: true
24
29
  };
@@ -86,12 +91,36 @@ export var TableResizer = function TableResizer(_ref) {
86
91
  var currentGap = useRef(0);
87
92
  // track resizing state - use ref over state to avoid re-render
88
93
  var isResizing = useRef(false);
94
+ var areResizeMetaKeysPressed = useRef(false);
95
+ var resizerRef = useRef(null);
96
+
97
+ // used to reposition tooltip when table is resizing via keyboard
98
+ var updateTooltip = React.useRef();
89
99
  var _useState = useState(false),
90
100
  _useState2 = _slicedToArray(_useState, 2),
91
101
  snappingEnabled = _useState2[0],
92
102
  setSnappingEnabled = _useState2[1];
103
+
104
+ // we don't want to update aria-live region on each width change, it might provide bad experience for screen reader users
105
+ var _useState3 = useState({
106
+ type: 'none',
107
+ width: width
108
+ }),
109
+ _useState4 = _slicedToArray(_useState3, 2),
110
+ screenReaderResizeInformation = _useState4[0],
111
+ setScreenReaderResizeInformation = _useState4[1];
93
112
  var _useIntl = useIntl(),
94
113
  formatMessage = _useIntl.formatMessage;
114
+ var screenReaderResizeAnnouncerMessages = {
115
+ increase: formatMessage(messages.tableSizeIncreaseScreenReaderInformation, {
116
+ newWidth: screenReaderResizeInformation.width
117
+ }),
118
+ decrease: formatMessage(messages.tableSizeDecreaseScreenReaderInformation, {
119
+ newWidth: screenReaderResizeInformation.width
120
+ }),
121
+ none: ''
122
+ };
123
+ var isTableSelected = ((_findTable = findTable((_editorView$state = editorView.state) === null || _editorView$state === void 0 ? void 0 : _editorView$state.selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) === getPos();
95
124
  var resizerMinWidth = getResizerMinWidth(node);
96
125
  var handleSize = getResizerHandleHeight(tableRef);
97
126
  var _getPluginState = getPluginState(editorView.state),
@@ -225,8 +254,109 @@ export var TableResizer = function TableResizer(_ref) {
225
254
  }
226
255
  return newWidth;
227
256
  }, [displayGapCursor, updateWidth, editorView, getPos, node, tableRef, scheduleResize, displayGuideline, attachAnalyticsEvent, endMeasure, onResizeStop]);
228
- var isTableSelected = ((_findTable = findTable((_editorView$state = editorView.state) === null || _editorView$state === void 0 ? void 0 : _editorView$state.selection)) === null || _findTable === void 0 ? void 0 : _findTable.pos) === getPos();
229
- return /*#__PURE__*/React.createElement(ResizerNext, {
257
+ var handleTableSizeChangeOnKeypress = useCallback(function (step) {
258
+ var newWidth = width + step;
259
+ if (newWidth > maxWidth || newWidth < resizerMinWidth) {
260
+ return;
261
+ }
262
+ handleResizeStop({
263
+ width: width,
264
+ x: 0,
265
+ y: 0,
266
+ height: 0
267
+ }, {
268
+ width: step,
269
+ height: 0
270
+ });
271
+ }, [width, handleResizeStop, maxWidth, resizerMinWidth]);
272
+ var handleEscape = useCallback(function () {
273
+ editorView === null || editorView === void 0 || editorView.focus();
274
+ }, [editorView]);
275
+ var handleKeyDown = useCallback(function (event) {
276
+ var isBracketKey = event.code === 'BracketRight' || event.code === 'BracketLeft';
277
+ var metaKey = browser.mac ? event.metaKey : event.ctrlKey;
278
+ if (event.altKey || metaKey || event.shiftKey) {
279
+ areResizeMetaKeysPressed.current = true;
280
+ }
281
+ if (event.altKey && metaKey) {
282
+ if (isBracketKey) {
283
+ event.preventDefault();
284
+ handleTableSizeChangeOnKeypress(event.code === 'BracketRight' ? RESIZE_STEP_VALUE : -RESIZE_STEP_VALUE);
285
+ }
286
+ } else if (!areResizeMetaKeysPressed.current) {
287
+ handleEscape();
288
+ }
289
+ }, [handleEscape, handleTableSizeChangeOnKeypress]);
290
+ var handleKeyUp = useCallback(function (event) {
291
+ if (event.altKey || event.metaKey) {
292
+ areResizeMetaKeysPressed.current = false;
293
+ }
294
+ return;
295
+ }, [areResizeMetaKeysPressed]);
296
+ useLayoutEffect(function () {
297
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
298
+ if (!resizerRef.current) {
299
+ return;
300
+ }
301
+ var resizeHandleThumbEl = resizerRef.current.getResizerThumbEl();
302
+ var globalKeyDownHandler = function globalKeyDownHandler(event) {
303
+ var metaKey = browser.mac ? event.metaKey : event.ctrlKey;
304
+ if (!isTableSelected) {
305
+ return;
306
+ }
307
+ if (event.altKey && event.shiftKey && metaKey && event.code === 'KeyR') {
308
+ event.preventDefault();
309
+ if (!resizeHandleThumbEl) {
310
+ return;
311
+ }
312
+ resizeHandleThumbEl.focus();
313
+ resizeHandleThumbEl.scrollIntoView({
314
+ behavior: 'smooth',
315
+ block: 'center',
316
+ inline: 'nearest'
317
+ });
318
+ }
319
+ };
320
+ var editorViewDom = editorView === null || editorView === void 0 ? void 0 : editorView.dom;
321
+ editorViewDom === null || editorViewDom === void 0 || editorViewDom.addEventListener('keydown', globalKeyDownHandler);
322
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.addEventListener('keydown', handleKeyDown);
323
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.addEventListener('keyup', handleKeyUp);
324
+ return function () {
325
+ editorViewDom === null || editorViewDom === void 0 || editorViewDom.removeEventListener('keydown', globalKeyDownHandler);
326
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.removeEventListener('keydown', handleKeyDown);
327
+ resizeHandleThumbEl === null || resizeHandleThumbEl === void 0 || resizeHandleThumbEl.removeEventListener('keyup', handleKeyUp);
328
+ };
329
+ }
330
+ }, [resizerRef, editorView, handleResizeStop, isTableSelected, handleKeyDown, handleKeyUp]);
331
+ useLayoutEffect(function () {
332
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
333
+ var _updateTooltip$curren;
334
+ (_updateTooltip$curren = updateTooltip.current) === null || _updateTooltip$curren === void 0 || _updateTooltip$curren.call(updateTooltip);
335
+ }
336
+ }, [width]);
337
+ useEffect(function () {
338
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
339
+ var debouncedSetWidth = debounce(setScreenReaderResizeInformation, DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER);
340
+ debouncedSetWidth(function (prevState) {
341
+ var type = 'none';
342
+ if (prevState.width > width) {
343
+ type = 'decrease';
344
+ }
345
+ if (prevState.width < width) {
346
+ type = 'increase';
347
+ }
348
+ return {
349
+ type: type,
350
+ width: width
351
+ };
352
+ });
353
+ return function () {
354
+ debouncedSetWidth.cancel();
355
+ };
356
+ }
357
+ }, [width]);
358
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ResizerNext, {
359
+ ref: resizerRef,
230
360
  enable: handles,
231
361
  width: width,
232
362
  handleAlignmentMethod: "sticky",
@@ -244,6 +374,16 @@ export var TableResizer = function TableResizer(_ref) {
244
374
  isHandleVisible: isTableSelected,
245
375
  appearance: isTableSelected && isWholeTableInDanger ? 'danger' : undefined,
246
376
  handleHighlight: "shadow",
247
- handleTooltipContent: formatMessage(messages.resizeTable)
248
- }, children);
377
+ handleTooltipContent: getBooleanFF('platform.editor.a11y-table-resizing_uapcv') ? function (_ref3) {
378
+ var update = _ref3.update;
379
+ updateTooltip.current = update;
380
+ return /*#__PURE__*/React.createElement(ToolTipContent, {
381
+ description: formatMessage(messages.resizeTable),
382
+ keymap: focusTableResizer
383
+ });
384
+ } : formatMessage(messages.resizeTable)
385
+ }, children), getBooleanFF('platform.editor.a11y-table-resizing_uapcv') && /*#__PURE__*/React.createElement("div", {
386
+ className: "assistive",
387
+ role: "status"
388
+ }, screenReaderResizeAnnouncerMessages[screenReaderResizeInformation.type]));
249
389
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "5.4.25",
3
+ "version": "5.5.0",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -141,6 +141,9 @@
141
141
  },
142
142
  "platform.editor.table.analytics-plugin-moved-event": {
143
143
  "type": "boolean"
144
+ },
145
+ "platform.editor.a11y-table-resizing_uapcv": {
146
+ "type": "boolean"
144
147
  }
145
148
  }
146
149
  }
@@ -9,6 +9,7 @@ import {
9
9
  TABLE_ACTION,
10
10
  } from '@atlaskit/editor-common/analytics';
11
11
  import type { DocBuilder } from '@atlaskit/editor-common/types';
12
+ import { browser } from '@atlaskit/editor-common/utils';
12
13
  import { akEditorWideLayoutWidth } from '@atlaskit/editor-shared-styles';
13
14
  import { findTable } from '@atlaskit/editor-tables/utils';
14
15
  // eslint-disable-next-line import/no-extraneous-dependencies -- Removed import for fixing circular dependencies
@@ -22,6 +23,7 @@ import {
22
23
  tdEmpty,
23
24
  tr,
24
25
  } from '@atlaskit/editor-test-helpers/doc-builder';
26
+ import { ffTest } from '@atlassian/feature-flags-test-utils';
25
27
 
26
28
  import tablePlugin from '../../../plugins/table-plugin';
27
29
  import {
@@ -128,6 +130,89 @@ describe('table -> nodeviews -> TableContainer.tsx', () => {
128
130
  });
129
131
  });
130
132
 
133
+ describe('should focus resize handle thumb after Cmd + Opt + R key comibnation is pressed', () => {
134
+ let originalScrollBy: typeof window.scrollBy;
135
+ let originalScrollIntoView: typeof window.HTMLElement.prototype.scrollIntoView;
136
+
137
+ beforeEach(() => {
138
+ originalScrollBy = window.scrollBy;
139
+ originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView;
140
+
141
+ window.scrollBy = jest.fn();
142
+ window.HTMLElement.prototype.scrollIntoView = jest.fn();
143
+ });
144
+
145
+ afterEach(() => {
146
+ window.scrollBy = originalScrollBy;
147
+ window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView;
148
+ });
149
+
150
+ const buildContainer = (
151
+ isTableResizingEnabled: boolean,
152
+ isBreakoutEnabled: boolean = true,
153
+ ) => {
154
+ const { table, editorView } = createNode();
155
+
156
+ const { container } = render(
157
+ <TableContainer
158
+ containerWidth={{
159
+ width: 1800,
160
+ lineLength: 720,
161
+ }}
162
+ node={table}
163
+ isTableResizingEnabled={isTableResizingEnabled}
164
+ isBreakoutEnabled={isBreakoutEnabled}
165
+ className={''}
166
+ editorView={editorView}
167
+ getPos={() => 1}
168
+ tableRef={{} as any}
169
+ isNested={false}
170
+ />,
171
+ );
172
+
173
+ return container;
174
+ };
175
+ ffTest(
176
+ 'platform.editor.a11y-table-resizing_uapcv',
177
+ () => {
178
+ const { editorView } = createNode();
179
+ const container = buildContainer(true);
180
+ const resizerThumbHandle = container.querySelector(
181
+ '[data-testid="resizer-handle-right-thumb"',
182
+ );
183
+
184
+ fireEvent.keyDown(editorView.dom, {
185
+ key: 'R',
186
+ code: 'KeyR',
187
+ altKey: true,
188
+ metaKey: browser.mac,
189
+ shiftKey: true,
190
+ ctrlKey: !browser.mac,
191
+ });
192
+
193
+ expect(resizerThumbHandle).toStrictEqual(document.activeElement);
194
+ },
195
+ () => {
196
+ const { editorView } = createNode();
197
+ const container = buildContainer(true);
198
+ const resizerThumbHandle = container.querySelector(
199
+ '[data-testid="resizer-handle-right-thumb"',
200
+ );
201
+
202
+ fireEvent.keyDown(editorView.dom, {
203
+ key: 'R',
204
+ code: 'KeyR',
205
+ altKey: true,
206
+ metaKey: browser.mac,
207
+ shiftKey: true,
208
+ ctrlKey: !browser.mac,
209
+ });
210
+
211
+ expect(resizerThumbHandle).not.toStrictEqual(document.activeElement);
212
+ },
213
+ );
214
+ });
215
+
131
216
  describe('when table is nested', () => {
132
217
  const buildContainer = (
133
218
  isTableResizingEnabled: boolean,
@@ -2,11 +2,13 @@ import type { PropsWithChildren } from 'react';
2
2
  import React, {
3
3
  useCallback,
4
4
  useEffect,
5
+ useLayoutEffect,
5
6
  useMemo,
6
7
  useRef,
7
8
  useState,
8
9
  } from 'react';
9
10
 
11
+ import debounce from 'lodash/debounce';
10
12
  import rafSchd from 'raf-schd';
11
13
  import { useIntl } from 'react-intl-next';
12
14
 
@@ -14,9 +16,14 @@ import type { TableEventPayload } from '@atlaskit/editor-common/analytics';
14
16
  import { TABLE_OVERFLOW_CHANGE_TRIGGER } from '@atlaskit/editor-common/analytics';
15
17
  import { getGuidelinesWithHighlights } from '@atlaskit/editor-common/guideline';
16
18
  import type { GuidelineConfig } from '@atlaskit/editor-common/guideline';
19
+ import {
20
+ focusTableResizer,
21
+ ToolTipContent,
22
+ } from '@atlaskit/editor-common/keymaps';
17
23
  import { tableMessages as messages } from '@atlaskit/editor-common/messages';
18
24
  import type { HandleResize, HandleSize } from '@atlaskit/editor-common/resizer';
19
25
  import { ResizerNext } from '@atlaskit/editor-common/resizer';
26
+ import { browser } from '@atlaskit/editor-common/utils';
20
27
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
21
28
  import type { Transaction } from '@atlaskit/editor-prosemirror/state';
22
29
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
@@ -67,6 +74,13 @@ export interface TableResizerImprovementProps extends TableResizerProps {
67
74
  onResizeStart?: () => void;
68
75
  }
69
76
 
77
+ type ResizerNextHandler = React.ElementRef<typeof ResizerNext>;
78
+
79
+ type ResizeAction = 'increase' | 'decrease' | 'none';
80
+
81
+ const DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER = 1000;
82
+ const RESIZE_STEP_VALUE = 10;
83
+
70
84
  const handles = { right: true };
71
85
  const handleStyles = {
72
86
  right: {
@@ -150,8 +164,37 @@ export const TableResizer = ({
150
164
  const currentGap = useRef(0);
151
165
  // track resizing state - use ref over state to avoid re-render
152
166
  const isResizing = useRef(false);
167
+ const areResizeMetaKeysPressed = useRef(false);
168
+
169
+ const resizerRef = useRef<ResizerNextHandler>(null);
170
+
171
+ // used to reposition tooltip when table is resizing via keyboard
172
+ const updateTooltip = React.useRef<() => void>();
153
173
  const [snappingEnabled, setSnappingEnabled] = useState(false);
174
+
175
+ // we don't want to update aria-live region on each width change, it might provide bad experience for screen reader users
176
+ const [screenReaderResizeInformation, setScreenReaderResizeInformation] =
177
+ useState<{
178
+ type: ResizeAction;
179
+ width: number;
180
+ }>({
181
+ type: 'none',
182
+ width,
183
+ });
184
+
154
185
  const { formatMessage } = useIntl();
186
+ const screenReaderResizeAnnouncerMessages = {
187
+ increase: formatMessage(messages.tableSizeIncreaseScreenReaderInformation, {
188
+ newWidth: screenReaderResizeInformation.width,
189
+ }),
190
+ decrease: formatMessage(messages.tableSizeDecreaseScreenReaderInformation, {
191
+ newWidth: screenReaderResizeInformation.width,
192
+ }),
193
+ none: '',
194
+ };
195
+
196
+ const isTableSelected =
197
+ findTable(editorView.state?.selection)?.pos === getPos();
155
198
 
156
199
  const resizerMinWidth = getResizerMinWidth(node);
157
200
  const handleSize = getResizerHandleHeight(tableRef);
@@ -374,33 +417,196 @@ export const TableResizer = ({
374
417
  ],
375
418
  );
376
419
 
377
- const isTableSelected =
378
- findTable(editorView.state?.selection)?.pos === getPos();
420
+ const handleTableSizeChangeOnKeypress = useCallback(
421
+ (step: number) => {
422
+ const newWidth = width + step;
379
423
 
380
- return (
381
- <ResizerNext
382
- enable={handles}
383
- width={width}
384
- handleAlignmentMethod="sticky"
385
- handleSize={handleSize}
386
- handleStyles={handleStyles}
387
- handleResizeStart={handleResizeStart}
388
- handleResize={scheduleResize}
389
- handleResizeStop={handleResizeStop}
390
- resizeRatio={2}
391
- minWidth={resizerMinWidth}
392
- maxWidth={maxWidth}
393
- snapGap={TABLE_SNAP_GAP}
394
- snap={guidelineSnaps}
395
- handlePositioning="adjacent"
396
- isHandleVisible={isTableSelected}
397
- appearance={
398
- isTableSelected && isWholeTableInDanger ? 'danger' : undefined
424
+ if (newWidth > maxWidth || newWidth < resizerMinWidth) {
425
+ return;
426
+ }
427
+ handleResizeStop(
428
+ { width: width, x: 0, y: 0, height: 0 },
429
+ { width: step, height: 0 },
430
+ );
431
+ },
432
+ [width, handleResizeStop, maxWidth, resizerMinWidth],
433
+ );
434
+
435
+ const handleEscape = useCallback((): void => {
436
+ editorView?.focus();
437
+ }, [editorView]);
438
+
439
+ const handleKeyDown = useCallback(
440
+ (event: KeyboardEvent): void => {
441
+ const isBracketKey =
442
+ event.code === 'BracketRight' || event.code === 'BracketLeft';
443
+
444
+ const metaKey = browser.mac ? event.metaKey : event.ctrlKey;
445
+
446
+ if (event.altKey || metaKey || event.shiftKey) {
447
+ areResizeMetaKeysPressed.current = true;
448
+ }
449
+
450
+ if (event.altKey && metaKey) {
451
+ if (isBracketKey) {
452
+ event.preventDefault();
453
+ handleTableSizeChangeOnKeypress(
454
+ event.code === 'BracketRight'
455
+ ? RESIZE_STEP_VALUE
456
+ : -RESIZE_STEP_VALUE,
457
+ );
458
+ }
459
+ } else if (!areResizeMetaKeysPressed.current) {
460
+ handleEscape();
399
461
  }
400
- handleHighlight="shadow"
401
- handleTooltipContent={formatMessage(messages.resizeTable)}
402
- >
403
- {children}
404
- </ResizerNext>
462
+ },
463
+ [handleEscape, handleTableSizeChangeOnKeypress],
464
+ );
465
+
466
+ const handleKeyUp = useCallback(
467
+ (event: KeyboardEvent): void => {
468
+ if (event.altKey || event.metaKey) {
469
+ areResizeMetaKeysPressed.current = false;
470
+ }
471
+ return;
472
+ },
473
+ [areResizeMetaKeysPressed],
474
+ );
475
+
476
+ useLayoutEffect(() => {
477
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
478
+ if (!resizerRef.current) {
479
+ return;
480
+ }
481
+ const resizeHandleThumbEl = resizerRef.current.getResizerThumbEl();
482
+
483
+ const globalKeyDownHandler = (event: KeyboardEvent): void => {
484
+ const metaKey = browser.mac ? event.metaKey : event.ctrlKey;
485
+
486
+ if (!isTableSelected) {
487
+ return;
488
+ }
489
+ if (
490
+ event.altKey &&
491
+ event.shiftKey &&
492
+ metaKey &&
493
+ event.code === 'KeyR'
494
+ ) {
495
+ event.preventDefault();
496
+
497
+ if (!resizeHandleThumbEl) {
498
+ return;
499
+ }
500
+ resizeHandleThumbEl.focus();
501
+ resizeHandleThumbEl.scrollIntoView({
502
+ behavior: 'smooth',
503
+ block: 'center',
504
+ inline: 'nearest',
505
+ });
506
+ }
507
+ };
508
+
509
+ const editorViewDom = editorView?.dom as HTMLElement | undefined;
510
+ editorViewDom?.addEventListener('keydown', globalKeyDownHandler);
511
+ resizeHandleThumbEl?.addEventListener('keydown', handleKeyDown);
512
+ resizeHandleThumbEl?.addEventListener('keyup', handleKeyUp);
513
+ return () => {
514
+ editorViewDom?.removeEventListener('keydown', globalKeyDownHandler);
515
+ resizeHandleThumbEl?.removeEventListener('keydown', handleKeyDown);
516
+ resizeHandleThumbEl?.removeEventListener('keyup', handleKeyUp);
517
+ };
518
+ }
519
+ }, [
520
+ resizerRef,
521
+ editorView,
522
+ handleResizeStop,
523
+ isTableSelected,
524
+ handleKeyDown,
525
+ handleKeyUp,
526
+ ]);
527
+
528
+ useLayoutEffect(() => {
529
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
530
+ updateTooltip.current?.();
531
+ }
532
+ }, [width]);
533
+
534
+ useEffect(() => {
535
+ if (getBooleanFF('platform.editor.a11y-table-resizing_uapcv')) {
536
+ const debouncedSetWidth = debounce(
537
+ setScreenReaderResizeInformation,
538
+ DEBOUNCE_TIME_FOR_SCREEN_READER_ANNOUNCER,
539
+ );
540
+ debouncedSetWidth((prevState) => {
541
+ let type: ResizeAction = 'none';
542
+ if (prevState.width > width) {
543
+ type = 'decrease';
544
+ }
545
+
546
+ if (prevState.width < width) {
547
+ type = 'increase';
548
+ }
549
+
550
+ return {
551
+ type,
552
+ width,
553
+ };
554
+ });
555
+
556
+ return () => {
557
+ debouncedSetWidth.cancel();
558
+ };
559
+ }
560
+ }, [width]);
561
+
562
+ return (
563
+ <>
564
+ <ResizerNext
565
+ ref={resizerRef}
566
+ enable={handles}
567
+ width={width}
568
+ handleAlignmentMethod="sticky"
569
+ handleSize={handleSize}
570
+ handleStyles={handleStyles}
571
+ handleResizeStart={handleResizeStart}
572
+ handleResize={scheduleResize}
573
+ handleResizeStop={handleResizeStop}
574
+ resizeRatio={2}
575
+ minWidth={resizerMinWidth}
576
+ maxWidth={maxWidth}
577
+ snapGap={TABLE_SNAP_GAP}
578
+ snap={guidelineSnaps}
579
+ handlePositioning="adjacent"
580
+ isHandleVisible={isTableSelected}
581
+ appearance={
582
+ isTableSelected && isWholeTableInDanger ? 'danger' : undefined
583
+ }
584
+ handleHighlight="shadow"
585
+ handleTooltipContent={
586
+ getBooleanFF('platform.editor.a11y-table-resizing_uapcv')
587
+ ? ({ update }) => {
588
+ updateTooltip.current = update;
589
+ return (
590
+ <ToolTipContent
591
+ description={formatMessage(messages.resizeTable)}
592
+ keymap={focusTableResizer}
593
+ />
594
+ );
595
+ }
596
+ : formatMessage(messages.resizeTable)
597
+ }
598
+ >
599
+ {children}
600
+ </ResizerNext>
601
+ {getBooleanFF('platform.editor.a11y-table-resizing_uapcv') && (
602
+ <div className="assistive" role="status">
603
+ {
604
+ screenReaderResizeAnnouncerMessages[
605
+ screenReaderResizeInformation.type
606
+ ]
607
+ }
608
+ </div>
609
+ )}
610
+ </>
405
611
  );
406
612
  };