@atlaskit/editor-plugin-table 42.1.0 → 42.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 42.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 42.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`d0007ab586b5f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d0007ab586b5f) -
14
+ Behind the platform_editor_sticky_header_insert_column_exp experiment, keep the column insert
15
+ button visible when the table header row is sticky
16
+
17
+ ### Patch Changes
18
+
19
+ - [`0e3b42ec20124`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0e3b42ec20124) -
20
+ Reduce redundant table work caused by editor focus changes behind the
21
+ `platform_editor_improve_inp` experiment.
22
+
3
23
  ## 42.1.0
4
24
 
5
25
  ### Minor Changes
@@ -27,6 +27,7 @@ var _dom2 = require("../pm-plugins/utils/dom");
27
27
  var _nodes = require("../pm-plugins/utils/nodes");
28
28
  var _types = require("../types");
29
29
  var _consts = require("../ui/consts");
30
+ var _isFocusOnlyChange = require("./is-focus-only-change");
30
31
  var _TableNodeViewBase = _interopRequireDefault(require("./TableNodeViewBase"));
31
32
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
32
33
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
@@ -57,6 +58,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
57
58
  });
58
59
  (0, _defineProperty2.default)(_this, "colControlsOffset", 0);
59
60
  (0, _defineProperty2.default)(_this, "focused", false);
61
+ (0, _defineProperty2.default)(_this, "focusOnlyUpdateGuard", new _isFocusOnlyChange.FocusOnlyUpdateGuard());
60
62
  (0, _defineProperty2.default)(_this, "topPosEditorElement", 0);
61
63
  (0, _defineProperty2.default)(_this, "sentinels", {});
62
64
  (0, _defineProperty2.default)(_this, "sentinelData", {
@@ -278,6 +280,8 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
278
280
  }, {
279
281
  key: "subscribe",
280
282
  value: function subscribe() {
283
+ this.focusOnlyUpdateGuard.reset();
284
+
281
285
  // Ignored via go/ees005
282
286
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
283
287
  this.editorScrollableElement = (0, _utils.findOverflowScrollParent)(this.view.dom) || window;
@@ -313,6 +317,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
313
317
  }, {
314
318
  key: "unsubscribe",
315
319
  value: function unsubscribe() {
320
+ this.focusOnlyUpdateGuard.reset();
316
321
  if (!this.listening) {
317
322
  return;
318
323
  }
@@ -624,11 +629,15 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
624
629
  }, {
625
630
  key: "onTablePluginState",
626
631
  value: function onTablePluginState(state) {
632
+ if (this.focusOnlyUpdateGuard.skipIfFocusOnly(state)) {
633
+ return;
634
+ }
627
635
  var tableRef = state.tableRef;
628
636
  var tree = (0, _dom2.getTree)(this.dom);
629
637
  if (!tree) {
630
638
  return;
631
639
  }
640
+ this.focusOnlyUpdateGuard.markProcessed(state);
632
641
 
633
642
  // when header rows are toggled off - mark sentinels as unobserved
634
643
  if (!state.isHeaderRowEnabled) {
@@ -31,6 +31,7 @@ var _dom2 = require("../pm-plugins/utils/dom");
31
31
  var _nodes = require("../pm-plugins/utils/nodes");
32
32
  var _types = require("../types");
33
33
  var _consts = require("../ui/consts");
34
+ var _isFocusOnlyChange = require("./is-focus-only-change");
34
35
  var _TableNodeViewBase = _interopRequireDefault(require("./TableNodeViewBase"));
35
36
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
36
37
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
@@ -63,6 +64,7 @@ var TableRowNativeStickyWithFallback = exports.default = /*#__PURE__*/function (
63
64
  });
64
65
  (0, _defineProperty2.default)(_this, "colControlsOffset", 0);
65
66
  (0, _defineProperty2.default)(_this, "focused", false);
67
+ (0, _defineProperty2.default)(_this, "focusOnlyUpdateGuard", new _isFocusOnlyChange.FocusOnlyUpdateGuard());
66
68
  (0, _defineProperty2.default)(_this, "topPosEditorElement", 0);
67
69
  (0, _defineProperty2.default)(_this, "sentinels", {});
68
70
  (0, _defineProperty2.default)(_this, "sentinelData", {
@@ -327,6 +329,8 @@ var TableRowNativeStickyWithFallback = exports.default = /*#__PURE__*/function (
327
329
  key: "subscribe",
328
330
  value: function subscribe() {
329
331
  var _this4 = this;
332
+ this.focusOnlyUpdateGuard.reset();
333
+
330
334
  // Ignored via go/ees005
331
335
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
332
336
  this.editorScrollableElement = (0, _utils.findOverflowScrollParent)(this.view.dom) || window;
@@ -404,6 +408,7 @@ var TableRowNativeStickyWithFallback = exports.default = /*#__PURE__*/function (
404
408
  }, {
405
409
  key: "unsubscribe",
406
410
  value: function unsubscribe() {
411
+ this.focusOnlyUpdateGuard.reset();
407
412
  if (!this.listening) {
408
413
  return;
409
414
  }
@@ -864,11 +869,15 @@ var TableRowNativeStickyWithFallback = exports.default = /*#__PURE__*/function (
864
869
  }, {
865
870
  key: "onTablePluginState",
866
871
  value: function onTablePluginState(state) {
872
+ if (this.focusOnlyUpdateGuard.skipIfFocusOnly(state)) {
873
+ return;
874
+ }
867
875
  var tableRef = state.tableRef;
868
876
  var tree = (0, _dom2.getTree)(this.dom);
869
877
  if (!tree) {
870
878
  return;
871
879
  }
880
+ this.focusOnlyUpdateGuard.markProcessed(state);
872
881
 
873
882
  // when header rows are toggled off - mark sentinels as unobserved
874
883
  if (!state.isHeaderRowEnabled) {
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.shallowEqualExcept = exports.isFocusOnlyChange = exports.FocusOnlyUpdateGuard = void 0;
8
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
11
+ var shallowEqualExcept = exports.shallowEqualExcept = function shallowEqualExcept(previousValue, currentValue, ignoredKey) {
12
+ var previousKeys = Object.keys(previousValue);
13
+ var currentKeys = Object.keys(currentValue);
14
+ return previousKeys.length === currentKeys.length && currentKeys.every(function (key) {
15
+ return key === ignoredKey || Object.prototype.hasOwnProperty.call(previousValue, key) && Object.is(previousValue[key], currentValue[key]);
16
+ });
17
+ };
18
+
19
+ /**
20
+ * Returns true when editor focus is the only table plugin state value that changed.
21
+ *
22
+ * Table reducers preserve references for unchanged values. If a reducer rebuilds a value, this
23
+ * returns false and callers take the original full-work path rather than skipping incorrectly.
24
+ */
25
+ var isFocusOnlyChange = exports.isFocusOnlyChange = function isFocusOnlyChange(previousState, currentState) {
26
+ return previousState.editorHasFocus !== currentState.editorHasFocus && shallowEqualExcept(previousState, currentState, 'editorHasFocus');
27
+ };
28
+ var FocusOnlyUpdateGuard = exports.FocusOnlyUpdateGuard = /*#__PURE__*/function () {
29
+ function FocusOnlyUpdateGuard() {
30
+ (0, _classCallCheck2.default)(this, FocusOnlyUpdateGuard);
31
+ }
32
+ return (0, _createClass2.default)(FocusOnlyUpdateGuard, [{
33
+ key: "skipIfFocusOnly",
34
+ value: /** Returns true and records the state when this is a skippable focus-only update. */
35
+ function skipIfFocusOnly(state) {
36
+ var lastProcessedState = this.lastProcessedState;
37
+ if (lastProcessedState !== undefined && isFocusOnlyChange(lastProcessedState, state) && (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_improve_inp')) {
38
+ this.lastProcessedState = state;
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+ }, {
44
+ key: "markProcessed",
45
+ value: function markProcessed(state) {
46
+ this.lastProcessedState = state;
47
+ }
48
+ }, {
49
+ key: "reset",
50
+ value: function reset() {
51
+ this.lastProcessedState = undefined;
52
+ }
53
+ }]);
54
+ }();
@@ -25,6 +25,7 @@ var _removeTable = require("@atlaskit/editor-tables/utils/remove-table");
25
25
  var _selectNodes = require("@atlaskit/editor-tables/utils/select-nodes");
26
26
  var _selectionCell = require("@atlaskit/editor-tables/utils/selection-cell");
27
27
  var _setCellAttrs = require("@atlaskit/editor-tables/utils/set-cell-attrs");
28
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
28
29
  var _types = require("../../types");
29
30
  var _plugin = require("../decorations/plugin");
30
31
  var _columnResizing = require("../decorations/utils/column-resizing");
@@ -55,11 +56,17 @@ var selectColumnWithMergedEdgeRows = function selectColumnWithMergedEdgeRows(tr,
55
56
  return transformedTr.setSelection(new _cellSelection.CellSelection(transformedTr.doc.resolve(lastCell.pos), transformedTr.doc.resolve(firstCell.pos)));
56
57
  };
57
58
  var setEditorFocus = exports.setEditorFocus = function setEditorFocus(editorHasFocus) {
58
- return (0, _pluginFactory.createCommand)({
59
- type: 'SET_EDITOR_FOCUS',
60
- data: {
61
- editorHasFocus: editorHasFocus
59
+ return (0, _pluginFactory.createCommand)(function (state) {
60
+ var pluginState = (0, _pluginFactory.getPluginState)(state);
61
+ if (pluginState.editorHasFocus === editorHasFocus && (0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_improve_inp')) {
62
+ return false;
62
63
  }
64
+ return {
65
+ type: 'SET_EDITOR_FOCUS',
66
+ data: {
67
+ editorHasFocus: editorHasFocus
68
+ }
69
+ };
63
70
  });
64
71
  };
65
72
  var setTableRef = exports.setTableRef = function setTableRef(ref) {
@@ -32,6 +32,37 @@ var _getPopupOptions = _interopRequireDefault(require("./getPopupOptions"));
32
32
  var _InsertButton = require("./InsertButton");
33
33
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
34
34
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
35
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
36
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
37
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
38
+ /**
39
+ * Distance from the table top to the top of the row containing `cell`, summed from row heights.
40
+ * Viewport rects cannot be used: a stuck header row is fixed to the viewport while the table scrolls.
41
+ */
42
+ var getRowOffsetFromTableTop = function getRowOffsetFromTableTop(cell, table) {
43
+ var anchorRow = (0, _dom.closestElement)(cell, 'tr');
44
+ if (!(table instanceof HTMLTableElement) || !anchorRow) {
45
+ return 0;
46
+ }
47
+ var offset = 0;
48
+ var _iterator = _createForOfIteratorHelper(table.rows),
49
+ _step;
50
+ try {
51
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
52
+ var row = _step.value;
53
+ if (row === anchorRow) {
54
+ return offset;
55
+ }
56
+ offset += row.offsetHeight;
57
+ }
58
+ } catch (err) {
59
+ _iterator.e(err);
60
+ } finally {
61
+ _iterator.f();
62
+ }
63
+ return 0;
64
+ };
65
+
35
66
  // Ignored via go/ees005
36
67
  // eslint-disable-next-line @repo/internal/react/no-class-components, @typescript-eslint/no-explicit-any
37
68
  var FloatingInsertButton = exports.FloatingInsertButton = /*#__PURE__*/function (_React$Component) {
@@ -142,7 +173,11 @@ var FloatingInsertButton = exports.FloatingInsertButton = /*#__PURE__*/function
142
173
  // ↑ button should render at row 0/table top
143
174
  var verticalOffsetCorrection = 0;
144
175
  if (type === 'column' && (0, _expValEquals.expValEquals)('platform_editor_table_menu_updates', 'isEnabled', true)) {
145
- verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
176
+ if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_sticky_header_insert_column_exp')) {
177
+ verticalOffsetCorrection = getRowOffsetFromTableTop(targetCellRef, tableRef);
178
+ } else {
179
+ verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
180
+ }
146
181
  }
147
182
  // Preserve the original popup placement: overflow row inserts and column inserts
148
183
  // sit above the header; other row inserts sit below it.
@@ -15,6 +15,7 @@ import { getTop, getTree } from '../pm-plugins/utils/dom';
15
15
  import { getTableRowIndex, supportedHeaderRow } from '../pm-plugins/utils/nodes';
16
16
  import { TableCssClassName as ClassName, TableCssClassName } from '../types';
17
17
  import { stickyHeaderBorderBottomWidth, stickyRowOffsetTop, tableControlsSpacing, tableScrollbarOffset } from '../ui/consts';
18
+ import { FocusOnlyUpdateGuard } from './is-focus-only-change';
18
19
  import TableNodeView from './TableNodeViewBase';
19
20
  // limit scroll event calls
20
21
  const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
@@ -41,6 +42,7 @@ export default class TableRow extends TableNodeView {
41
42
  });
42
43
  _defineProperty(this, "colControlsOffset", 0);
43
44
  _defineProperty(this, "focused", false);
45
+ _defineProperty(this, "focusOnlyUpdateGuard", new FocusOnlyUpdateGuard());
44
46
  _defineProperty(this, "topPosEditorElement", 0);
45
47
  _defineProperty(this, "sentinels", {});
46
48
  _defineProperty(this, "sentinelData", {
@@ -247,6 +249,8 @@ export default class TableRow extends TableNodeView {
247
249
  return true;
248
250
  }
249
251
  subscribe() {
252
+ this.focusOnlyUpdateGuard.reset();
253
+
250
254
  // Ignored via go/ees005
251
255
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
252
256
  this.editorScrollableElement = findOverflowScrollParent(this.view.dom) || window;
@@ -280,6 +284,7 @@ export default class TableRow extends TableNodeView {
280
284
  });
281
285
  }
282
286
  unsubscribe() {
287
+ this.focusOnlyUpdateGuard.reset();
283
288
  if (!this.listening) {
284
289
  return;
285
290
  }
@@ -578,11 +583,15 @@ export default class TableRow extends TableNodeView {
578
583
  /* receive external events */
579
584
 
580
585
  onTablePluginState(state) {
586
+ if (this.focusOnlyUpdateGuard.skipIfFocusOnly(state)) {
587
+ return;
588
+ }
581
589
  const tableRef = state.tableRef;
582
590
  const tree = getTree(this.dom);
583
591
  if (!tree) {
584
592
  return;
585
593
  }
594
+ this.focusOnlyUpdateGuard.markProcessed(state);
586
595
 
587
596
  // when header rows are toggled off - mark sentinels as unobserved
588
597
  if (!state.isHeaderRowEnabled) {
@@ -19,6 +19,7 @@ import { areAllRectsZero, getTop, getTree } from '../pm-plugins/utils/dom';
19
19
  import { getTableRowIndex, supportedHeaderRow } from '../pm-plugins/utils/nodes';
20
20
  import { TableCssClassName as ClassName, TableCssClassName } from '../types';
21
21
  import { stickyHeaderBorderBottomWidth, stickyRowOffsetTop, tableControlsSpacing, tableScrollbarOffset } from '../ui/consts';
22
+ import { FocusOnlyUpdateGuard } from './is-focus-only-change';
22
23
  import TableNodeView from './TableNodeViewBase';
23
24
  // limit scroll event calls
24
25
  const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
@@ -47,6 +48,7 @@ export default class TableRowNativeStickyWithFallback extends TableNodeView {
47
48
  });
48
49
  _defineProperty(this, "colControlsOffset", 0);
49
50
  _defineProperty(this, "focused", false);
51
+ _defineProperty(this, "focusOnlyUpdateGuard", new FocusOnlyUpdateGuard());
50
52
  _defineProperty(this, "topPosEditorElement", 0);
51
53
  _defineProperty(this, "sentinels", {});
52
54
  _defineProperty(this, "sentinelData", {
@@ -296,6 +298,8 @@ export default class TableRowNativeStickyWithFallback extends TableNodeView {
296
298
  return true;
297
299
  }
298
300
  subscribe() {
301
+ this.focusOnlyUpdateGuard.reset();
302
+
299
303
  // Ignored via go/ees005
300
304
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
301
305
  this.editorScrollableElement = findOverflowScrollParent(this.view.dom) || window;
@@ -371,6 +375,7 @@ export default class TableRowNativeStickyWithFallback extends TableNodeView {
371
375
  });
372
376
  }
373
377
  unsubscribe() {
378
+ this.focusOnlyUpdateGuard.reset();
374
379
  if (!this.listening) {
375
380
  return;
376
381
  }
@@ -809,11 +814,15 @@ export default class TableRowNativeStickyWithFallback extends TableNodeView {
809
814
  /* receive external events */
810
815
 
811
816
  onTablePluginState(state) {
817
+ if (this.focusOnlyUpdateGuard.skipIfFocusOnly(state)) {
818
+ return;
819
+ }
812
820
  const tableRef = state.tableRef;
813
821
  const tree = getTree(this.dom);
814
822
  if (!tree) {
815
823
  return;
816
824
  }
825
+ this.focusOnlyUpdateGuard.markProcessed(state);
817
826
 
818
827
  // when header rows are toggled off - mark sentinels as unobserved
819
828
  if (!state.isHeaderRowEnabled) {
@@ -0,0 +1,31 @@
1
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
2
+ export const shallowEqualExcept = (previousValue, currentValue, ignoredKey) => {
3
+ const previousKeys = Object.keys(previousValue);
4
+ const currentKeys = Object.keys(currentValue);
5
+ return previousKeys.length === currentKeys.length && currentKeys.every(key => key === ignoredKey || Object.prototype.hasOwnProperty.call(previousValue, key) && Object.is(previousValue[key], currentValue[key]));
6
+ };
7
+
8
+ /**
9
+ * Returns true when editor focus is the only table plugin state value that changed.
10
+ *
11
+ * Table reducers preserve references for unchanged values. If a reducer rebuilds a value, this
12
+ * returns false and callers take the original full-work path rather than skipping incorrectly.
13
+ */
14
+ export const isFocusOnlyChange = (previousState, currentState) => previousState.editorHasFocus !== currentState.editorHasFocus && shallowEqualExcept(previousState, currentState, 'editorHasFocus');
15
+ export class FocusOnlyUpdateGuard {
16
+ /** Returns true and records the state when this is a skippable focus-only update. */
17
+ skipIfFocusOnly(state) {
18
+ const lastProcessedState = this.lastProcessedState;
19
+ if (lastProcessedState !== undefined && isFocusOnlyChange(lastProcessedState, state) && isExperimentEnabled('platform_editor_improve_inp')) {
20
+ this.lastProcessedState = state;
21
+ return true;
22
+ }
23
+ return false;
24
+ }
25
+ markProcessed(state) {
26
+ this.lastProcessedState = state;
27
+ }
28
+ reset() {
29
+ this.lastProcessedState = undefined;
30
+ }
31
+ }
@@ -16,6 +16,7 @@ import { removeTable } from '@atlaskit/editor-tables/utils/remove-table';
16
16
  import { selectColumns as selectColumnsTransform, selectColumn as selectColumnTransform, selectRows as selectRowsTransform, selectRow as selectRowTransform } from '@atlaskit/editor-tables/utils/select-nodes';
17
17
  import { selectionCell } from '@atlaskit/editor-tables/utils/selection-cell';
18
18
  import { setCellAttrs } from '@atlaskit/editor-tables/utils/set-cell-attrs';
19
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
19
20
  import { TableCssClassName as ClassName, TableDecorations } from '../../types';
20
21
  import { getDecorations } from '../decorations/plugin';
21
22
  import { buildColumnResizingDecorations, clearColumnResizingDecorations } from '../decorations/utils/column-resizing';
@@ -42,11 +43,17 @@ const selectColumnWithMergedEdgeRows = (tr, column, expand) => {
42
43
  }
43
44
  return transformedTr.setSelection(new CellSelection(transformedTr.doc.resolve(lastCell.pos), transformedTr.doc.resolve(firstCell.pos)));
44
45
  };
45
- export const setEditorFocus = editorHasFocus => createCommand({
46
- type: 'SET_EDITOR_FOCUS',
47
- data: {
48
- editorHasFocus
46
+ export const setEditorFocus = editorHasFocus => createCommand(state => {
47
+ const pluginState = getPluginState(state);
48
+ if (pluginState.editorHasFocus === editorHasFocus && isExperimentEnabled('platform_editor_improve_inp')) {
49
+ return false;
49
50
  }
51
+ return {
52
+ type: 'SET_EDITOR_FOCUS',
53
+ data: {
54
+ editorHasFocus
55
+ }
56
+ };
50
57
  });
51
58
  export const setTableRef = ref => createCommand(state => {
52
59
  const tableRef = ref;
@@ -18,6 +18,25 @@ import { TableCssClassName as ClassName } from '../../types';
18
18
  import { getTableZIndexes } from '../sticky-header-z-index';
19
19
  import getPopupOptions from './getPopupOptions';
20
20
  import { DragAndDropInsertButton } from './InsertButton';
21
+ /**
22
+ * Distance from the table top to the top of the row containing `cell`, summed from row heights.
23
+ * Viewport rects cannot be used: a stuck header row is fixed to the viewport while the table scrolls.
24
+ */
25
+ const getRowOffsetFromTableTop = (cell, table) => {
26
+ const anchorRow = closestElement(cell, 'tr');
27
+ if (!(table instanceof HTMLTableElement) || !anchorRow) {
28
+ return 0;
29
+ }
30
+ let offset = 0;
31
+ for (const row of table.rows) {
32
+ if (row === anchorRow) {
33
+ return offset;
34
+ }
35
+ offset += row.offsetHeight;
36
+ }
37
+ return 0;
38
+ };
39
+
21
40
  // Ignored via go/ees005
22
41
  // eslint-disable-next-line @repo/internal/react/no-class-components, @typescript-eslint/no-explicit-any
23
42
  export class FloatingInsertButton extends React.Component {
@@ -127,7 +146,11 @@ export class FloatingInsertButton extends React.Component {
127
146
  // ↑ button should render at row 0/table top
128
147
  let verticalOffsetCorrection = 0;
129
148
  if (type === 'column' && expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
130
- verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
149
+ if (isExperimentEnabled('platform_editor_sticky_header_insert_column_exp')) {
150
+ verticalOffsetCorrection = getRowOffsetFromTableTop(targetCellRef, tableRef);
151
+ } else {
152
+ verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
153
+ }
131
154
  }
132
155
  // Preserve the original popup placement: overflow row inserts and column inserts
133
156
  // sit above the header; other row inserts sit below it.
@@ -22,6 +22,7 @@ import { getTop, getTree } from '../pm-plugins/utils/dom';
22
22
  import { getTableRowIndex, supportedHeaderRow } from '../pm-plugins/utils/nodes';
23
23
  import { TableCssClassName as ClassName, TableCssClassName } from '../types';
24
24
  import { stickyHeaderBorderBottomWidth, stickyRowOffsetTop, tableControlsSpacing, tableScrollbarOffset } from '../ui/consts';
25
+ import { FocusOnlyUpdateGuard } from './is-focus-only-change';
25
26
  import TableNodeView from './TableNodeViewBase';
26
27
  // limit scroll event calls
27
28
  var HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
@@ -50,6 +51,7 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
50
51
  });
51
52
  _defineProperty(_this, "colControlsOffset", 0);
52
53
  _defineProperty(_this, "focused", false);
54
+ _defineProperty(_this, "focusOnlyUpdateGuard", new FocusOnlyUpdateGuard());
53
55
  _defineProperty(_this, "topPosEditorElement", 0);
54
56
  _defineProperty(_this, "sentinels", {});
55
57
  _defineProperty(_this, "sentinelData", {
@@ -271,6 +273,8 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
271
273
  }, {
272
274
  key: "subscribe",
273
275
  value: function subscribe() {
276
+ this.focusOnlyUpdateGuard.reset();
277
+
274
278
  // Ignored via go/ees005
275
279
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
276
280
  this.editorScrollableElement = findOverflowScrollParent(this.view.dom) || window;
@@ -306,6 +310,7 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
306
310
  }, {
307
311
  key: "unsubscribe",
308
312
  value: function unsubscribe() {
313
+ this.focusOnlyUpdateGuard.reset();
309
314
  if (!this.listening) {
310
315
  return;
311
316
  }
@@ -617,11 +622,15 @@ var TableRow = /*#__PURE__*/function (_TableNodeView) {
617
622
  }, {
618
623
  key: "onTablePluginState",
619
624
  value: function onTablePluginState(state) {
625
+ if (this.focusOnlyUpdateGuard.skipIfFocusOnly(state)) {
626
+ return;
627
+ }
620
628
  var tableRef = state.tableRef;
621
629
  var tree = getTree(this.dom);
622
630
  if (!tree) {
623
631
  return;
624
632
  }
633
+ this.focusOnlyUpdateGuard.markProcessed(state);
625
634
 
626
635
  // when header rows are toggled off - mark sentinels as unobserved
627
636
  if (!state.isHeaderRowEnabled) {
@@ -26,6 +26,7 @@ import { areAllRectsZero, getTop, getTree } from '../pm-plugins/utils/dom';
26
26
  import { getTableRowIndex, supportedHeaderRow } from '../pm-plugins/utils/nodes';
27
27
  import { TableCssClassName as ClassName, TableCssClassName } from '../types';
28
28
  import { stickyHeaderBorderBottomWidth, stickyRowOffsetTop, tableControlsSpacing, tableScrollbarOffset } from '../ui/consts';
29
+ import { FocusOnlyUpdateGuard } from './is-focus-only-change';
29
30
  import TableNodeView from './TableNodeViewBase';
30
31
  // limit scroll event calls
31
32
  var HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
@@ -56,6 +57,7 @@ var TableRowNativeStickyWithFallback = /*#__PURE__*/function (_TableNodeView) {
56
57
  });
57
58
  _defineProperty(_this, "colControlsOffset", 0);
58
59
  _defineProperty(_this, "focused", false);
60
+ _defineProperty(_this, "focusOnlyUpdateGuard", new FocusOnlyUpdateGuard());
59
61
  _defineProperty(_this, "topPosEditorElement", 0);
60
62
  _defineProperty(_this, "sentinels", {});
61
63
  _defineProperty(_this, "sentinelData", {
@@ -320,6 +322,8 @@ var TableRowNativeStickyWithFallback = /*#__PURE__*/function (_TableNodeView) {
320
322
  key: "subscribe",
321
323
  value: function subscribe() {
322
324
  var _this4 = this;
325
+ this.focusOnlyUpdateGuard.reset();
326
+
323
327
  // Ignored via go/ees005
324
328
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
325
329
  this.editorScrollableElement = findOverflowScrollParent(this.view.dom) || window;
@@ -397,6 +401,7 @@ var TableRowNativeStickyWithFallback = /*#__PURE__*/function (_TableNodeView) {
397
401
  }, {
398
402
  key: "unsubscribe",
399
403
  value: function unsubscribe() {
404
+ this.focusOnlyUpdateGuard.reset();
400
405
  if (!this.listening) {
401
406
  return;
402
407
  }
@@ -857,11 +862,15 @@ var TableRowNativeStickyWithFallback = /*#__PURE__*/function (_TableNodeView) {
857
862
  }, {
858
863
  key: "onTablePluginState",
859
864
  value: function onTablePluginState(state) {
865
+ if (this.focusOnlyUpdateGuard.skipIfFocusOnly(state)) {
866
+ return;
867
+ }
860
868
  var tableRef = state.tableRef;
861
869
  var tree = getTree(this.dom);
862
870
  if (!tree) {
863
871
  return;
864
872
  }
873
+ this.focusOnlyUpdateGuard.markProcessed(state);
865
874
 
866
875
  // when header rows are toggled off - mark sentinels as unobserved
867
876
  if (!state.isHeaderRowEnabled) {
@@ -0,0 +1,47 @@
1
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
+ import _createClass from "@babel/runtime/helpers/createClass";
3
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
4
+ export var shallowEqualExcept = function shallowEqualExcept(previousValue, currentValue, ignoredKey) {
5
+ var previousKeys = Object.keys(previousValue);
6
+ var currentKeys = Object.keys(currentValue);
7
+ return previousKeys.length === currentKeys.length && currentKeys.every(function (key) {
8
+ return key === ignoredKey || Object.prototype.hasOwnProperty.call(previousValue, key) && Object.is(previousValue[key], currentValue[key]);
9
+ });
10
+ };
11
+
12
+ /**
13
+ * Returns true when editor focus is the only table plugin state value that changed.
14
+ *
15
+ * Table reducers preserve references for unchanged values. If a reducer rebuilds a value, this
16
+ * returns false and callers take the original full-work path rather than skipping incorrectly.
17
+ */
18
+ export var isFocusOnlyChange = function isFocusOnlyChange(previousState, currentState) {
19
+ return previousState.editorHasFocus !== currentState.editorHasFocus && shallowEqualExcept(previousState, currentState, 'editorHasFocus');
20
+ };
21
+ export var FocusOnlyUpdateGuard = /*#__PURE__*/function () {
22
+ function FocusOnlyUpdateGuard() {
23
+ _classCallCheck(this, FocusOnlyUpdateGuard);
24
+ }
25
+ return _createClass(FocusOnlyUpdateGuard, [{
26
+ key: "skipIfFocusOnly",
27
+ value: /** Returns true and records the state when this is a skippable focus-only update. */
28
+ function skipIfFocusOnly(state) {
29
+ var lastProcessedState = this.lastProcessedState;
30
+ if (lastProcessedState !== undefined && isFocusOnlyChange(lastProcessedState, state) && isExperimentEnabled('platform_editor_improve_inp')) {
31
+ this.lastProcessedState = state;
32
+ return true;
33
+ }
34
+ return false;
35
+ }
36
+ }, {
37
+ key: "markProcessed",
38
+ value: function markProcessed(state) {
39
+ this.lastProcessedState = state;
40
+ }
41
+ }, {
42
+ key: "reset",
43
+ value: function reset() {
44
+ this.lastProcessedState = undefined;
45
+ }
46
+ }]);
47
+ }();
@@ -20,6 +20,7 @@ import { removeTable } from '@atlaskit/editor-tables/utils/remove-table';
20
20
  import { selectColumns as selectColumnsTransform, selectColumn as selectColumnTransform, selectRows as selectRowsTransform, selectRow as selectRowTransform } from '@atlaskit/editor-tables/utils/select-nodes';
21
21
  import { selectionCell } from '@atlaskit/editor-tables/utils/selection-cell';
22
22
  import { setCellAttrs } from '@atlaskit/editor-tables/utils/set-cell-attrs';
23
+ import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
23
24
  import { TableCssClassName as ClassName, TableDecorations } from '../../types';
24
25
  import { getDecorations } from '../decorations/plugin';
25
26
  import { buildColumnResizingDecorations, clearColumnResizingDecorations } from '../decorations/utils/column-resizing';
@@ -48,11 +49,17 @@ var selectColumnWithMergedEdgeRows = function selectColumnWithMergedEdgeRows(tr,
48
49
  return transformedTr.setSelection(new CellSelection(transformedTr.doc.resolve(lastCell.pos), transformedTr.doc.resolve(firstCell.pos)));
49
50
  };
50
51
  export var setEditorFocus = function setEditorFocus(editorHasFocus) {
51
- return createCommand({
52
- type: 'SET_EDITOR_FOCUS',
53
- data: {
54
- editorHasFocus: editorHasFocus
52
+ return createCommand(function (state) {
53
+ var pluginState = getPluginState(state);
54
+ if (pluginState.editorHasFocus === editorHasFocus && isExperimentEnabled('platform_editor_improve_inp')) {
55
+ return false;
55
56
  }
57
+ return {
58
+ type: 'SET_EDITOR_FOCUS',
59
+ data: {
60
+ editorHasFocus: editorHasFocus
61
+ }
62
+ };
56
63
  });
57
64
  };
58
65
  export var setTableRef = function setTableRef(ref) {
@@ -7,6 +7,9 @@ import _inherits from "@babel/runtime/helpers/inherits";
7
7
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
8
8
  function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
9
9
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
10
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
11
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
12
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
10
13
  import React from 'react';
11
14
  import { injectIntl } from 'react-intl';
12
15
  import { ACTION, ACTION_SUBJECT, CONTENT_COMPONENT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics/types/enums';
@@ -25,6 +28,34 @@ import { TableCssClassName as ClassName } from '../../types';
25
28
  import { getTableZIndexes } from '../sticky-header-z-index';
26
29
  import getPopupOptions from './getPopupOptions';
27
30
  import { DragAndDropInsertButton } from './InsertButton';
31
+ /**
32
+ * Distance from the table top to the top of the row containing `cell`, summed from row heights.
33
+ * Viewport rects cannot be used: a stuck header row is fixed to the viewport while the table scrolls.
34
+ */
35
+ var getRowOffsetFromTableTop = function getRowOffsetFromTableTop(cell, table) {
36
+ var anchorRow = closestElement(cell, 'tr');
37
+ if (!(table instanceof HTMLTableElement) || !anchorRow) {
38
+ return 0;
39
+ }
40
+ var offset = 0;
41
+ var _iterator = _createForOfIteratorHelper(table.rows),
42
+ _step;
43
+ try {
44
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
45
+ var row = _step.value;
46
+ if (row === anchorRow) {
47
+ return offset;
48
+ }
49
+ offset += row.offsetHeight;
50
+ }
51
+ } catch (err) {
52
+ _iterator.e(err);
53
+ } finally {
54
+ _iterator.f();
55
+ }
56
+ return 0;
57
+ };
58
+
28
59
  // Ignored via go/ees005
29
60
  // eslint-disable-next-line @repo/internal/react/no-class-components, @typescript-eslint/no-explicit-any
30
61
  export var FloatingInsertButton = /*#__PURE__*/function (_React$Component) {
@@ -135,7 +166,11 @@ export var FloatingInsertButton = /*#__PURE__*/function (_React$Component) {
135
166
  // ↑ button should render at row 0/table top
136
167
  var verticalOffsetCorrection = 0;
137
168
  if (type === 'column' && expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) {
138
- verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
169
+ if (isExperimentEnabled('platform_editor_sticky_header_insert_column_exp')) {
170
+ verticalOffsetCorrection = getRowOffsetFromTableTop(targetCellRef, tableRef);
171
+ } else {
172
+ verticalOffsetCorrection = Math.max(0, targetCellRef.getBoundingClientRect().top - tableRef.getBoundingClientRect().top);
173
+ }
139
174
  }
140
175
  // Preserve the original popup placement: overflow row inserts and column inserts
141
176
  // sit above the header; other row inserts sit below it.
@@ -18,6 +18,7 @@ export default class TableRow extends TableNodeView<HTMLTableRowElement> impleme
18
18
  private editorScrollableElement?;
19
19
  private colControlsOffset;
20
20
  private focused;
21
+ private readonly focusOnlyUpdateGuard;
21
22
  private topPosEditorElement;
22
23
  private isSticky;
23
24
  private intersectionObserver?;
@@ -18,6 +18,7 @@ export default class TableRowNativeStickyWithFallback extends TableNodeView<HTML
18
18
  private editorScrollableElement?;
19
19
  private colControlsOffset;
20
20
  private focused;
21
+ private readonly focusOnlyUpdateGuard;
21
22
  private topPosEditorElement;
22
23
  private isLegacySticky;
23
24
  private intersectionObserver?;
@@ -0,0 +1,16 @@
1
+ import type { TablePluginState } from '../types';
2
+ export declare const shallowEqualExcept: <T extends object>(previousValue: T, currentValue: T, ignoredKey: keyof T) => boolean;
3
+ /**
4
+ * Returns true when editor focus is the only table plugin state value that changed.
5
+ *
6
+ * Table reducers preserve references for unchanged values. If a reducer rebuilds a value, this
7
+ * returns false and callers take the original full-work path rather than skipping incorrectly.
8
+ */
9
+ export declare const isFocusOnlyChange: (previousState: TablePluginState, currentState: TablePluginState) => boolean;
10
+ export declare class FocusOnlyUpdateGuard {
11
+ private lastProcessedState;
12
+ /** Returns true and records the state when this is a skippable focus-only update. */
13
+ skipIfFocusOnly(state: TablePluginState): boolean;
14
+ markProcessed(state: TablePluginState): void;
15
+ reset(): void;
16
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "42.1.0",
3
+ "version": "42.2.1",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -29,7 +29,7 @@
29
29
  "@atlaskit/editor-plugin-batch-attribute-updates": "^30.0.0",
30
30
  "@atlaskit/editor-plugin-content-insertion": "^30.0.0",
31
31
  "@atlaskit/editor-plugin-editor-viewmode": "^32.0.0",
32
- "@atlaskit/editor-plugin-extension": "33.0.2",
32
+ "@atlaskit/editor-plugin-extension": "33.0.3",
33
33
  "@atlaskit/editor-plugin-guideline": "^30.0.0",
34
34
  "@atlaskit/editor-plugin-interaction": "^44.0.0",
35
35
  "@atlaskit/editor-plugin-limited-mode": "^27.0.0",
@@ -53,7 +53,7 @@
53
53
  "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^3.2.0",
54
54
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^3.0.0",
55
55
  "@atlaskit/primitives": "^22.6.0",
56
- "@atlaskit/tmp-editor-statsig": "^248.1.0",
56
+ "@atlaskit/tmp-editor-statsig": "^249.0.0",
57
57
  "@atlaskit/toggle": "^17.3.0",
58
58
  "@atlaskit/tokens": "^20.8.0",
59
59
  "@atlaskit/tooltip": "^24.4.0",