@atlaskit/editor-plugin-table 42.0.3 → 42.2.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,28 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 42.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`d0007ab586b5f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d0007ab586b5f) -
8
+ Behind the platform_editor_sticky_header_insert_column_exp experiment, keep the column insert
9
+ button visible when the table header row is sticky
10
+
11
+ ### Patch Changes
12
+
13
+ - [`0e3b42ec20124`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0e3b42ec20124) -
14
+ Reduce redundant table work caused by editor focus changes behind the
15
+ `platform_editor_improve_inp` experiment.
16
+
17
+ ## 42.1.0
18
+
19
+ ### Minor Changes
20
+
21
+ - [`5eff440523b45`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5eff440523b45) -
22
+ Rescale full-page tables with measured column widths when the editor container width changes
23
+ without a window resize (for example, a side panel opening), behind
24
+ platform_editor_table_scale_on_container_resize
25
+
3
26
  ## 42.0.3
4
27
 
5
28
  ### Patch Changes
@@ -61,6 +61,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
61
61
  // Prevent unnecessary parentWidth updates when table is nested inside of a node that is nested itself.
62
62
  var NESTED_TABLE_IN_NESTED_PARENT_WIDTH_DIFF_MIN_THRESHOLD = 2;
63
63
  var NESTED_TABLE_IN_NESTED_PARENT_WIDTH_DIFF_MAX_THRESHOLD = 20;
64
+ var MEASURED_COLUMN_WIDTH = /^[\.0-9]+px$/;
64
65
  // eslint-disable-next-line @repo/internal/react/no-class-components
65
66
  var TableComponent = /*#__PURE__*/function (_React$Component) {
66
67
  function TableComponent(props) {
@@ -336,6 +337,16 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
336
337
  (0, _defineProperty2.default)(_this, "tableNodeLayoutSize", function (node, containerWidth, options) {
337
338
  return (0, _misc.getLayoutSize)(node.attrs.layout, containerWidth || _this.props.containerWidth.width, options || _this.props.options || {});
338
339
  });
340
+ /**
341
+ * Full-page tables scale with their container through CSS `calc()` column widths. Once a
342
+ * colgroup has been rewritten with measured px widths (column resize, collab, column add or
343
+ * delete, window resize), CSS can no longer scale it and the colgroup must be updated in JS.
344
+ */
345
+ (0, _defineProperty2.default)(_this, "hasMeasuredColumnWidths", function () {
346
+ var _this$table;
347
+ var firstColumn = (_this$table = _this.table) === null || _this$table === void 0 ? void 0 : _this$table.querySelector(':scope > colgroup > col');
348
+ return !!firstColumn && MEASURED_COLUMN_WIDTH.test(firstColumn.style.width);
349
+ });
339
350
  (0, _defineProperty2.default)(_this, "shouldUpdateColgroup", function (params) {
340
351
  var _this$props$options, _this$props$options2;
341
352
  var isWindowResized = params.isWindowResized,
@@ -429,9 +440,9 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
429
440
  }, {
430
441
  key: "initialiseEventListenersAfterMount",
431
442
  value: function initialiseEventListenersAfterMount() {
432
- var _this$table,
433
- _this$table2,
443
+ var _this$table2,
434
444
  _this$table3,
445
+ _this$table4,
435
446
  _this3 = this;
436
447
  var _this$props6 = this.props,
437
448
  allowColumnResizing = _this$props6.allowColumnResizing,
@@ -444,15 +455,15 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
444
455
 
445
456
  // Ignored via go/ees005
446
457
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
447
- this === null || this === void 0 || (_this$table = this.table) === null || _this$table === void 0 || _this$table.addEventListener('mouseenter', this.handleMouseEnter);
458
+ this === null || this === void 0 || (_this$table2 = this.table) === null || _this$table2 === void 0 || _this$table2.addEventListener('mouseenter', this.handleMouseEnter);
448
459
 
449
460
  // Ignored via go/ees005
450
461
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
451
- this === null || this === void 0 || (_this$table2 = this.table) === null || _this$table2 === void 0 || _this$table2.addEventListener('mouseout', this.handleMouseOut);
462
+ this === null || this === void 0 || (_this$table3 = this.table) === null || _this$table3 === void 0 || _this$table3.addEventListener('mouseout', this.handleMouseOut);
452
463
 
453
464
  // Ignored via go/ees005
454
465
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
455
- this === null || this === void 0 || (_this$table3 = this.table) === null || _this$table3 === void 0 || _this$table3.addEventListener('mouseover', this.handleMouseOver);
466
+ this === null || this === void 0 || (_this$table4 = this.table) === null || _this$table4 === void 0 || _this$table4.addEventListener('mouseover', this.handleMouseOver);
456
467
  if (this.wrapper) {
457
468
  this.wrapperReisizeObserver = new ResizeObserver(function (entries) {
458
469
  var _iterator2 = _createForOfIteratorHelper(entries),
@@ -511,7 +522,7 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
511
522
  }, {
512
523
  key: "componentWillUnmount",
513
524
  value: function componentWillUnmount() {
514
- var _this$resizeObserver, _this$wrapperReisizeO, _this$table4, _this$table5, _this$table6;
525
+ var _this$resizeObserver, _this$wrapperReisizeO, _this$table5, _this$table6, _this$table7;
515
526
  var _this$props7 = this.props,
516
527
  allowColumnResizing = _this$props7.allowColumnResizing,
517
528
  allowTableResizing = _this$props7.allowTableResizing,
@@ -559,15 +570,15 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
559
570
 
560
571
  // Ignored via go/ees005
561
572
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
562
- this === null || this === void 0 || (_this$table4 = this.table) === null || _this$table4 === void 0 || _this$table4.removeEventListener('mouseenter', this.handleMouseEnter);
573
+ this === null || this === void 0 || (_this$table5 = this.table) === null || _this$table5 === void 0 || _this$table5.removeEventListener('mouseenter', this.handleMouseEnter);
563
574
 
564
575
  // Ignored via go/ees005
565
576
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
566
- this === null || this === void 0 || (_this$table5 = this.table) === null || _this$table5 === void 0 || _this$table5.removeEventListener('mouseout', this.handleMouseOut);
577
+ this === null || this === void 0 || (_this$table6 = this.table) === null || _this$table6 === void 0 || _this$table6.removeEventListener('mouseout', this.handleMouseOut);
567
578
 
568
579
  // Ignored via go/ees005
569
580
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
570
- this === null || this === void 0 || (_this$table6 = this.table) === null || _this$table6 === void 0 || _this$table6.removeEventListener('mouseover', this.handleMouseOver);
581
+ this === null || this === void 0 || (_this$table7 = this.table) === null || _this$table7 === void 0 || _this$table7.removeEventListener('mouseover', this.handleMouseOver);
571
582
 
572
583
  // Ignored via go/ees005
573
584
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -605,6 +616,7 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
605
616
  key: "handleColgroupUpdates",
606
617
  value: function handleColgroupUpdates() {
607
618
  var _this$containerWidth,
619
+ _this$containerWidth2,
608
620
  _this4 = this;
609
621
  var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
610
622
  var _this$props9 = this.props,
@@ -641,16 +653,20 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
641
653
  containerLineLength = containerWidth.lineLength;
642
654
  var isLineLengthChanged = ((_this$containerWidth = this.containerWidth) === null || _this$containerWidth === void 0 ? void 0 : _this$containerWidth.lineLength) !== containerLineLength;
643
655
  var isFullWidthModeAndLineLengthChanged = this.updateColGroupFromFullWidthChange && isLineLengthChanged;
644
- var maybeScale = isTableSquashed || isTableWidthChanged || isTableResizedFullWidth && !(options !== null && options !== void 0 && options.isCommentEditor) || isNumberColumnChanged || isNumberOfColumnsChanged || this.state.windowResized;
656
+ var previousContainerWidth = (_this$containerWidth2 = this.containerWidth) === null || _this$containerWidth2 === void 0 ? void 0 : _this$containerWidth2.width;
657
+ var isFullPageEditor = !(options !== null && options !== void 0 && options.isCommentEditor) && !(options !== null && options !== void 0 && options.isChromelessEditor);
658
+ var isContainerResized = (0, _fg.fg)('platform_editor_table_scale_on_container_resize') && isFullPageEditor && !!previousContainerWidth && previousContainerWidth !== containerWidthValue && this.hasMeasuredColumnWidths();
659
+ var isWindowOrContainerResized = this.state.windowResized || isContainerResized;
660
+ var maybeScale = isTableSquashed || isTableWidthChanged || isTableResizedFullWidth && !(options !== null && options !== void 0 && options.isCommentEditor) || isNumberColumnChanged || isNumberOfColumnsChanged || isWindowOrContainerResized;
645
661
  if (force || maybeScale || isFullWidthModeAndLineLengthChanged) {
646
- var _this$containerWidth2, _this$props0, _this$props$options3, _this$props$options4, _this$props$options5;
647
- var isWidthChanged = ((_this$containerWidth2 = this.containerWidth) === null || _this$containerWidth2 === void 0 ? void 0 : _this$containerWidth2.width) !== containerWidthValue;
662
+ var _this$containerWidth3, _this$props0, _this$props$options3, _this$props$options4, _this$props$options5;
663
+ var isWidthChanged = ((_this$containerWidth3 = this.containerWidth) === null || _this$containerWidth3 === void 0 ? void 0 : _this$containerWidth3.width) !== containerWidthValue;
648
664
  var wasTableResized = (0, _colgroup.hasTableBeenResized)(this.node);
649
665
  var isTableResized = (0, _colgroup.hasTableBeenResized)(tableNode);
650
666
  var isColumnsDistributed = wasTableResized && !isTableResized;
651
667
  var isTableDisplayModeChanged = this.node.attrs.displayMode !== tableNode.attrs.displayMode;
652
668
  var shouldUpdateColgroup = this.shouldUpdateColgroup({
653
- isWindowResized: this.state.windowResized,
669
+ isWindowResized: isWindowOrContainerResized,
654
670
  isWidthChanged: isWidthChanged,
655
671
  isTableWidthChanged: isTableWidthChanged,
656
672
  isColumnsDistributed: isColumnsDistributed,
@@ -779,14 +795,14 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
779
795
  var view = this.props.view;
780
796
  var shouldRecreateResizeCols = !allowTableResizing || !isResizing || isNoOfColumnsChanged && isResizing;
781
797
  if (shouldRecreateResizeCols) {
782
- var _this$table7, _this$props$options10, _this$props$options11;
798
+ var _this$table8, _this$props$options10, _this$props$options11;
783
799
  var start = getPos() || 0;
784
800
  var depth = view.state.doc.resolve(start).depth;
785
801
  shouldScale = depth === 0 && shouldScale;
786
802
  (0, _colgroup.insertColgroupFromNode)(this.table, currentTable, shouldScale, undefined, shouldUseIncreasedScalingPercent, options === null || options === void 0 ? void 0 : options.isCommentEditor);
787
803
 
788
804
  // Deferred from setDomAttrs — remove only once colgroup is updated and table has left content mode.
789
- if ((_this$table7 = this.table) !== null && _this$table7 !== void 0 && _this$table7.hasAttribute('data-initial-width-mode') && !(0, _contentMode.isTableInContentMode)({
805
+ if ((_this$table8 = this.table) !== null && _this$table8 !== void 0 && _this$table8.hasAttribute('data-initial-width-mode') && !(0, _contentMode.isTableInContentMode)({
790
806
  tableNode: currentTable,
791
807
  isSupported: (0, _isContentModeSupported.isContentModeSupported)({
792
808
  allowColumnResizing: !!allowColumnResizing,
@@ -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.
@@ -42,6 +42,7 @@ import { TableStickyScrollbar } from './TableStickyScrollbar';
42
42
  // Prevent unnecessary parentWidth updates when table is nested inside of a node that is nested itself.
43
43
  const NESTED_TABLE_IN_NESTED_PARENT_WIDTH_DIFF_MIN_THRESHOLD = 2;
44
44
  const NESTED_TABLE_IN_NESTED_PARENT_WIDTH_DIFF_MAX_THRESHOLD = 20;
45
+ const MEASURED_COLUMN_WIDTH = /^[\d.]+px$/u;
45
46
  // eslint-disable-next-line @repo/internal/react/no-class-components
46
47
  class TableComponent extends React.Component {
47
48
  constructor(props) {
@@ -325,6 +326,16 @@ class TableComponent extends React.Component {
325
326
  });
326
327
  });
327
328
  _defineProperty(this, "tableNodeLayoutSize", (node, containerWidth, options) => getLayoutSize(node.attrs.layout, containerWidth || this.props.containerWidth.width, options || this.props.options || {}));
329
+ /**
330
+ * Full-page tables scale with their container through CSS `calc()` column widths. Once a
331
+ * colgroup has been rewritten with measured px widths (column resize, collab, column add or
332
+ * delete, window resize), CSS can no longer scale it and the colgroup must be updated in JS.
333
+ */
334
+ _defineProperty(this, "hasMeasuredColumnWidths", () => {
335
+ var _this$table;
336
+ const firstColumn = (_this$table = this.table) === null || _this$table === void 0 ? void 0 : _this$table.querySelector(':scope > colgroup > col');
337
+ return !!firstColumn && MEASURED_COLUMN_WIDTH.test(firstColumn.style.width);
338
+ });
328
339
  _defineProperty(this, "shouldUpdateColgroup", params => {
329
340
  var _this$props$options, _this$props$options2;
330
341
  const {
@@ -405,7 +416,7 @@ class TableComponent extends React.Component {
405
416
  this.dispatchTableRefUpdate();
406
417
  }
407
418
  initialiseEventListenersAfterMount() {
408
- var _this$table, _this$table2, _this$table3;
419
+ var _this$table2, _this$table3, _this$table4;
409
420
  const {
410
421
  allowColumnResizing,
411
422
  allowTableResizing,
@@ -418,15 +429,15 @@ class TableComponent extends React.Component {
418
429
 
419
430
  // Ignored via go/ees005
420
431
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
421
- this === null || this === void 0 ? void 0 : (_this$table = this.table) === null || _this$table === void 0 ? void 0 : _this$table.addEventListener('mouseenter', this.handleMouseEnter);
432
+ this === null || this === void 0 ? void 0 : (_this$table2 = this.table) === null || _this$table2 === void 0 ? void 0 : _this$table2.addEventListener('mouseenter', this.handleMouseEnter);
422
433
 
423
434
  // Ignored via go/ees005
424
435
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
425
- this === null || this === void 0 ? void 0 : (_this$table2 = this.table) === null || _this$table2 === void 0 ? void 0 : _this$table2.addEventListener('mouseout', this.handleMouseOut);
436
+ this === null || this === void 0 ? void 0 : (_this$table3 = this.table) === null || _this$table3 === void 0 ? void 0 : _this$table3.addEventListener('mouseout', this.handleMouseOut);
426
437
 
427
438
  // Ignored via go/ees005
428
439
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
429
- this === null || this === void 0 ? void 0 : (_this$table3 = this.table) === null || _this$table3 === void 0 ? void 0 : _this$table3.addEventListener('mouseover', this.handleMouseOver);
440
+ this === null || this === void 0 ? void 0 : (_this$table4 = this.table) === null || _this$table4 === void 0 ? void 0 : _this$table4.addEventListener('mouseover', this.handleMouseOver);
430
441
  if (this.wrapper) {
431
442
  this.wrapperReisizeObserver = new ResizeObserver(entries => {
432
443
  for (const entry of entries) {
@@ -474,7 +485,7 @@ class TableComponent extends React.Component {
474
485
  eventDispatcher.on(stickyHeadersPluginKey.key, this.onStickyState);
475
486
  }
476
487
  componentWillUnmount() {
477
- var _this$resizeObserver, _this$wrapperReisizeO, _this$table4, _this$table5, _this$table6;
488
+ var _this$resizeObserver, _this$wrapperReisizeO, _this$table5, _this$table6, _this$table7;
478
489
  const {
479
490
  allowColumnResizing,
480
491
  allowTableResizing,
@@ -523,15 +534,15 @@ class TableComponent extends React.Component {
523
534
 
524
535
  // Ignored via go/ees005
525
536
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
526
- this === null || this === void 0 ? void 0 : (_this$table4 = this.table) === null || _this$table4 === void 0 ? void 0 : _this$table4.removeEventListener('mouseenter', this.handleMouseEnter);
537
+ this === null || this === void 0 ? void 0 : (_this$table5 = this.table) === null || _this$table5 === void 0 ? void 0 : _this$table5.removeEventListener('mouseenter', this.handleMouseEnter);
527
538
 
528
539
  // Ignored via go/ees005
529
540
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
530
- this === null || this === void 0 ? void 0 : (_this$table5 = this.table) === null || _this$table5 === void 0 ? void 0 : _this$table5.removeEventListener('mouseout', this.handleMouseOut);
541
+ this === null || this === void 0 ? void 0 : (_this$table6 = this.table) === null || _this$table6 === void 0 ? void 0 : _this$table6.removeEventListener('mouseout', this.handleMouseOut);
531
542
 
532
543
  // Ignored via go/ees005
533
544
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
534
- this === null || this === void 0 ? void 0 : (_this$table6 = this.table) === null || _this$table6 === void 0 ? void 0 : _this$table6.removeEventListener('mouseover', this.handleMouseOver);
545
+ this === null || this === void 0 ? void 0 : (_this$table7 = this.table) === null || _this$table7 === void 0 ? void 0 : _this$table7.removeEventListener('mouseover', this.handleMouseOver);
535
546
 
536
547
  // Ignored via go/ees005
537
548
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -564,7 +575,7 @@ class TableComponent extends React.Component {
564
575
  }
565
576
  }
566
577
  handleColgroupUpdates(force = false) {
567
- var _this$containerWidth;
578
+ var _this$containerWidth, _this$containerWidth2;
568
579
  const {
569
580
  getNode,
570
581
  containerWidth,
@@ -602,16 +613,20 @@ class TableComponent extends React.Component {
602
613
  } = containerWidth;
603
614
  const isLineLengthChanged = ((_this$containerWidth = this.containerWidth) === null || _this$containerWidth === void 0 ? void 0 : _this$containerWidth.lineLength) !== containerLineLength;
604
615
  const isFullWidthModeAndLineLengthChanged = this.updateColGroupFromFullWidthChange && isLineLengthChanged;
605
- const maybeScale = isTableSquashed || isTableWidthChanged || isTableResizedFullWidth && !(options !== null && options !== void 0 && options.isCommentEditor) || isNumberColumnChanged || isNumberOfColumnsChanged || this.state.windowResized;
616
+ const previousContainerWidth = (_this$containerWidth2 = this.containerWidth) === null || _this$containerWidth2 === void 0 ? void 0 : _this$containerWidth2.width;
617
+ const isFullPageEditor = !(options !== null && options !== void 0 && options.isCommentEditor) && !(options !== null && options !== void 0 && options.isChromelessEditor);
618
+ const isContainerResized = fg('platform_editor_table_scale_on_container_resize') && isFullPageEditor && !!previousContainerWidth && previousContainerWidth !== containerWidthValue && this.hasMeasuredColumnWidths();
619
+ const isWindowOrContainerResized = this.state.windowResized || isContainerResized;
620
+ const maybeScale = isTableSquashed || isTableWidthChanged || isTableResizedFullWidth && !(options !== null && options !== void 0 && options.isCommentEditor) || isNumberColumnChanged || isNumberOfColumnsChanged || isWindowOrContainerResized;
606
621
  if (force || maybeScale || isFullWidthModeAndLineLengthChanged) {
607
- var _this$containerWidth2, _this$props, _this$props$options3, _this$props$options4, _this$props$options5;
608
- const isWidthChanged = ((_this$containerWidth2 = this.containerWidth) === null || _this$containerWidth2 === void 0 ? void 0 : _this$containerWidth2.width) !== containerWidthValue;
622
+ var _this$containerWidth3, _this$props, _this$props$options3, _this$props$options4, _this$props$options5;
623
+ const isWidthChanged = ((_this$containerWidth3 = this.containerWidth) === null || _this$containerWidth3 === void 0 ? void 0 : _this$containerWidth3.width) !== containerWidthValue;
609
624
  const wasTableResized = hasTableBeenResized(this.node);
610
625
  const isTableResized = hasTableBeenResized(tableNode);
611
626
  const isColumnsDistributed = wasTableResized && !isTableResized;
612
627
  const isTableDisplayModeChanged = this.node.attrs.displayMode !== tableNode.attrs.displayMode;
613
628
  const shouldUpdateColgroup = this.shouldUpdateColgroup({
614
- isWindowResized: this.state.windowResized,
629
+ isWindowResized: isWindowOrContainerResized,
615
630
  isWidthChanged,
616
631
  isTableWidthChanged,
617
632
  isColumnsDistributed,
@@ -742,14 +757,14 @@ class TableComponent extends React.Component {
742
757
  } = this.props;
743
758
  const shouldRecreateResizeCols = !allowTableResizing || !isResizing || isNoOfColumnsChanged && isResizing;
744
759
  if (shouldRecreateResizeCols) {
745
- var _this$table7, _this$props$options12, _this$props$options13;
760
+ var _this$table8, _this$props$options12, _this$props$options13;
746
761
  const start = getPos() || 0;
747
762
  const depth = view.state.doc.resolve(start).depth;
748
763
  shouldScale = depth === 0 && shouldScale;
749
764
  insertColgroupFromNode(this.table, currentTable, shouldScale, undefined, shouldUseIncreasedScalingPercent, options === null || options === void 0 ? void 0 : options.isCommentEditor);
750
765
 
751
766
  // Deferred from setDomAttrs — remove only once colgroup is updated and table has left content mode.
752
- if ((_this$table7 = this.table) !== null && _this$table7 !== void 0 && _this$table7.hasAttribute('data-initial-width-mode') && !isTableInContentMode({
767
+ if ((_this$table8 = this.table) !== null && _this$table8 !== void 0 && _this$table8.hasAttribute('data-initial-width-mode') && !isTableInContentMode({
753
768
  tableNode: currentTable,
754
769
  isSupported: isContentModeSupported({
755
770
  allowColumnResizing: !!allowColumnResizing,
@@ -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.
@@ -55,6 +55,7 @@ import { TableStickyScrollbar } from './TableStickyScrollbar';
55
55
  // Prevent unnecessary parentWidth updates when table is nested inside of a node that is nested itself.
56
56
  var NESTED_TABLE_IN_NESTED_PARENT_WIDTH_DIFF_MIN_THRESHOLD = 2;
57
57
  var NESTED_TABLE_IN_NESTED_PARENT_WIDTH_DIFF_MAX_THRESHOLD = 20;
58
+ var MEASURED_COLUMN_WIDTH = /^[\.0-9]+px$/;
58
59
  // eslint-disable-next-line @repo/internal/react/no-class-components
59
60
  var TableComponent = /*#__PURE__*/function (_React$Component) {
60
61
  function TableComponent(props) {
@@ -330,6 +331,16 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
330
331
  _defineProperty(_this, "tableNodeLayoutSize", function (node, containerWidth, options) {
331
332
  return getLayoutSize(node.attrs.layout, containerWidth || _this.props.containerWidth.width, options || _this.props.options || {});
332
333
  });
334
+ /**
335
+ * Full-page tables scale with their container through CSS `calc()` column widths. Once a
336
+ * colgroup has been rewritten with measured px widths (column resize, collab, column add or
337
+ * delete, window resize), CSS can no longer scale it and the colgroup must be updated in JS.
338
+ */
339
+ _defineProperty(_this, "hasMeasuredColumnWidths", function () {
340
+ var _this$table;
341
+ var firstColumn = (_this$table = _this.table) === null || _this$table === void 0 ? void 0 : _this$table.querySelector(':scope > colgroup > col');
342
+ return !!firstColumn && MEASURED_COLUMN_WIDTH.test(firstColumn.style.width);
343
+ });
333
344
  _defineProperty(_this, "shouldUpdateColgroup", function (params) {
334
345
  var _this$props$options, _this$props$options2;
335
346
  var isWindowResized = params.isWindowResized,
@@ -423,9 +434,9 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
423
434
  }, {
424
435
  key: "initialiseEventListenersAfterMount",
425
436
  value: function initialiseEventListenersAfterMount() {
426
- var _this$table,
427
- _this$table2,
437
+ var _this$table2,
428
438
  _this$table3,
439
+ _this$table4,
429
440
  _this3 = this;
430
441
  var _this$props6 = this.props,
431
442
  allowColumnResizing = _this$props6.allowColumnResizing,
@@ -438,15 +449,15 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
438
449
 
439
450
  // Ignored via go/ees005
440
451
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
441
- this === null || this === void 0 || (_this$table = this.table) === null || _this$table === void 0 || _this$table.addEventListener('mouseenter', this.handleMouseEnter);
452
+ this === null || this === void 0 || (_this$table2 = this.table) === null || _this$table2 === void 0 || _this$table2.addEventListener('mouseenter', this.handleMouseEnter);
442
453
 
443
454
  // Ignored via go/ees005
444
455
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
445
- this === null || this === void 0 || (_this$table2 = this.table) === null || _this$table2 === void 0 || _this$table2.addEventListener('mouseout', this.handleMouseOut);
456
+ this === null || this === void 0 || (_this$table3 = this.table) === null || _this$table3 === void 0 || _this$table3.addEventListener('mouseout', this.handleMouseOut);
446
457
 
447
458
  // Ignored via go/ees005
448
459
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
449
- this === null || this === void 0 || (_this$table3 = this.table) === null || _this$table3 === void 0 || _this$table3.addEventListener('mouseover', this.handleMouseOver);
460
+ this === null || this === void 0 || (_this$table4 = this.table) === null || _this$table4 === void 0 || _this$table4.addEventListener('mouseover', this.handleMouseOver);
450
461
  if (this.wrapper) {
451
462
  this.wrapperReisizeObserver = new ResizeObserver(function (entries) {
452
463
  var _iterator2 = _createForOfIteratorHelper(entries),
@@ -505,7 +516,7 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
505
516
  }, {
506
517
  key: "componentWillUnmount",
507
518
  value: function componentWillUnmount() {
508
- var _this$resizeObserver, _this$wrapperReisizeO, _this$table4, _this$table5, _this$table6;
519
+ var _this$resizeObserver, _this$wrapperReisizeO, _this$table5, _this$table6, _this$table7;
509
520
  var _this$props7 = this.props,
510
521
  allowColumnResizing = _this$props7.allowColumnResizing,
511
522
  allowTableResizing = _this$props7.allowTableResizing,
@@ -553,15 +564,15 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
553
564
 
554
565
  // Ignored via go/ees005
555
566
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
556
- this === null || this === void 0 || (_this$table4 = this.table) === null || _this$table4 === void 0 || _this$table4.removeEventListener('mouseenter', this.handleMouseEnter);
567
+ this === null || this === void 0 || (_this$table5 = this.table) === null || _this$table5 === void 0 || _this$table5.removeEventListener('mouseenter', this.handleMouseEnter);
557
568
 
558
569
  // Ignored via go/ees005
559
570
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
560
- this === null || this === void 0 || (_this$table5 = this.table) === null || _this$table5 === void 0 || _this$table5.removeEventListener('mouseout', this.handleMouseOut);
571
+ this === null || this === void 0 || (_this$table6 = this.table) === null || _this$table6 === void 0 || _this$table6.removeEventListener('mouseout', this.handleMouseOut);
561
572
 
562
573
  // Ignored via go/ees005
563
574
  // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
564
- this === null || this === void 0 || (_this$table6 = this.table) === null || _this$table6 === void 0 || _this$table6.removeEventListener('mouseover', this.handleMouseOver);
575
+ this === null || this === void 0 || (_this$table7 = this.table) === null || _this$table7 === void 0 || _this$table7.removeEventListener('mouseover', this.handleMouseOver);
565
576
 
566
577
  // Ignored via go/ees005
567
578
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -599,6 +610,7 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
599
610
  key: "handleColgroupUpdates",
600
611
  value: function handleColgroupUpdates() {
601
612
  var _this$containerWidth,
613
+ _this$containerWidth2,
602
614
  _this4 = this;
603
615
  var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
604
616
  var _this$props9 = this.props,
@@ -635,16 +647,20 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
635
647
  containerLineLength = containerWidth.lineLength;
636
648
  var isLineLengthChanged = ((_this$containerWidth = this.containerWidth) === null || _this$containerWidth === void 0 ? void 0 : _this$containerWidth.lineLength) !== containerLineLength;
637
649
  var isFullWidthModeAndLineLengthChanged = this.updateColGroupFromFullWidthChange && isLineLengthChanged;
638
- var maybeScale = isTableSquashed || isTableWidthChanged || isTableResizedFullWidth && !(options !== null && options !== void 0 && options.isCommentEditor) || isNumberColumnChanged || isNumberOfColumnsChanged || this.state.windowResized;
650
+ var previousContainerWidth = (_this$containerWidth2 = this.containerWidth) === null || _this$containerWidth2 === void 0 ? void 0 : _this$containerWidth2.width;
651
+ var isFullPageEditor = !(options !== null && options !== void 0 && options.isCommentEditor) && !(options !== null && options !== void 0 && options.isChromelessEditor);
652
+ var isContainerResized = fg('platform_editor_table_scale_on_container_resize') && isFullPageEditor && !!previousContainerWidth && previousContainerWidth !== containerWidthValue && this.hasMeasuredColumnWidths();
653
+ var isWindowOrContainerResized = this.state.windowResized || isContainerResized;
654
+ var maybeScale = isTableSquashed || isTableWidthChanged || isTableResizedFullWidth && !(options !== null && options !== void 0 && options.isCommentEditor) || isNumberColumnChanged || isNumberOfColumnsChanged || isWindowOrContainerResized;
639
655
  if (force || maybeScale || isFullWidthModeAndLineLengthChanged) {
640
- var _this$containerWidth2, _this$props0, _this$props$options3, _this$props$options4, _this$props$options5;
641
- var isWidthChanged = ((_this$containerWidth2 = this.containerWidth) === null || _this$containerWidth2 === void 0 ? void 0 : _this$containerWidth2.width) !== containerWidthValue;
656
+ var _this$containerWidth3, _this$props0, _this$props$options3, _this$props$options4, _this$props$options5;
657
+ var isWidthChanged = ((_this$containerWidth3 = this.containerWidth) === null || _this$containerWidth3 === void 0 ? void 0 : _this$containerWidth3.width) !== containerWidthValue;
642
658
  var wasTableResized = hasTableBeenResized(this.node);
643
659
  var isTableResized = hasTableBeenResized(tableNode);
644
660
  var isColumnsDistributed = wasTableResized && !isTableResized;
645
661
  var isTableDisplayModeChanged = this.node.attrs.displayMode !== tableNode.attrs.displayMode;
646
662
  var shouldUpdateColgroup = this.shouldUpdateColgroup({
647
- isWindowResized: this.state.windowResized,
663
+ isWindowResized: isWindowOrContainerResized,
648
664
  isWidthChanged: isWidthChanged,
649
665
  isTableWidthChanged: isTableWidthChanged,
650
666
  isColumnsDistributed: isColumnsDistributed,
@@ -773,14 +789,14 @@ var TableComponent = /*#__PURE__*/function (_React$Component) {
773
789
  var view = this.props.view;
774
790
  var shouldRecreateResizeCols = !allowTableResizing || !isResizing || isNoOfColumnsChanged && isResizing;
775
791
  if (shouldRecreateResizeCols) {
776
- var _this$table7, _this$props$options10, _this$props$options11;
792
+ var _this$table8, _this$props$options10, _this$props$options11;
777
793
  var start = getPos() || 0;
778
794
  var depth = view.state.doc.resolve(start).depth;
779
795
  shouldScale = depth === 0 && shouldScale;
780
796
  insertColgroupFromNode(this.table, currentTable, shouldScale, undefined, shouldUseIncreasedScalingPercent, options === null || options === void 0 ? void 0 : options.isCommentEditor);
781
797
 
782
798
  // Deferred from setDomAttrs — remove only once colgroup is updated and table has left content mode.
783
- if ((_this$table7 = this.table) !== null && _this$table7 !== void 0 && _this$table7.hasAttribute('data-initial-width-mode') && !isTableInContentMode({
799
+ if ((_this$table8 = this.table) !== null && _this$table8 !== void 0 && _this$table8.hasAttribute('data-initial-width-mode') && !isTableInContentMode({
784
800
  tableNode: currentTable,
785
801
  isSupported: isContentModeSupported({
786
802
  allowColumnResizing: !!allowColumnResizing,
@@ -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.0.3",
3
+ "version": "42.2.0",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -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.0.0",
56
+ "@atlaskit/tmp-editor-statsig": "^248.1.0",
57
57
  "@atlaskit/toggle": "^17.3.0",
58
58
  "@atlaskit/tokens": "^20.8.0",
59
59
  "@atlaskit/tooltip": "^24.4.0",
@@ -68,7 +68,7 @@
68
68
  "uuid": "^11.1.1"
69
69
  },
70
70
  "peerDependencies": {
71
- "@atlaskit/editor-common": "^134.1.0",
71
+ "@atlaskit/editor-common": "^134.2.0",
72
72
  "react": "^18.2.0 || ^19.2.0",
73
73
  "react-dom": "^18.2.0 || ^19.2.0",
74
74
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
@@ -99,6 +99,9 @@
99
99
  "platform_editor_table_view_mode_scaling_fix": {
100
100
  "type": "boolean"
101
101
  },
102
+ "platform_editor_table_scale_on_container_resize": {
103
+ "type": "boolean"
104
+ },
102
105
  "platform-dst-top-layer-tooltip": {
103
106
  "type": "boolean"
104
107
  }