@atlaskit/editor-plugin-table 28.1.15 → 29.1.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,49 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 29.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`aec3fcfe13ff9`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/aec3fcfe13ff9) -
8
+ EDITOR-8303 Add runtime input-latency trigger for limited mode behind
9
+ platform_editor_dynamic_limited_mode. Limited mode can now latch mid-session when sustained slow
10
+ input or repeated browser freezes indicate the device is struggling, in addition to the existing
11
+ document-size decision. The latch is one-way. Nothing is constructed when the experiment is off.
12
+
13
+ How much evidence is needed is a single tunable, `requiredConfirmations`: that many qualifying
14
+ windows, each separated from the last by `confirmationGapMs`. The hardware no longer feeds into
15
+ the decision — `navigator.hardwareConcurrency` and `navigator.deviceMemory` are reported with the
16
+ `limitedModeLatched` event instead, so which devices latch can be answered from the data rather
17
+ than assumed up front.
18
+
19
+ `LimitedModePluginState` gains a derived `enabled` flag, which is now the single thing consumers
20
+ should branch on — the individual reasons (`documentSizeBreachesThreshold`, `latchPolicyBreached`)
21
+ no longer need to be combined at each call site, so a future reason needs no change outside this
22
+ plugin. `sharedState.enabled` reads from it.
23
+
24
+ Under the experiment the document decision is also evaluated on load and on document replacement
25
+ only, rather than on every transaction that changes the document. That check walks the whole
26
+ document, so it was a full-document scan per keystroke on exactly the pages least able to afford
27
+ one. The trade-off is that a document editing its way past the thresholds is not re-judged until
28
+ it next loads.
29
+
30
+ Consumers updated to react to a mid-session change: table sticky headers now subscribe instead of
31
+ reading once at construction, block-controls resets rather than freezes its state on entry, and
32
+ the expand, media and table nodeviews read the derived flag so they no longer miss a runtime
33
+ latch.
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies
38
+
39
+ ## 29.0.0
40
+
41
+ ### Patch Changes
42
+
43
+ - [`7c7646bee3e68`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7c7646bee3e68) -
44
+ Clean up feature gate `platform_editor_table_height_analytics_event`
45
+ - Updated dependencies
46
+
3
47
  ## 28.1.15
4
48
 
5
49
  ### Patch Changes
@@ -30,8 +30,8 @@ var TableComponentWithSharedState = exports.TableComponentWithSharedState = func
30
30
  allowTableAlignment = _ref.allowTableAlignment,
31
31
  allowTableResizing = _ref.allowTableResizing,
32
32
  allowFixedColumnWidthOption = _ref.allowFixedColumnWidthOption;
33
- var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['table', 'width', 'media', 'selection', 'editorViewMode', 'interaction'], function (states) {
34
- var _states$tableState, _states$tableState2, _states$tableState3, _states$tableState4, _states$tableState5, _states$tableState6, _states$tableState7, _states$tableState8, _states$tableState9, _states$tableState0, _states$tableState1, _states$mediaState, _states$selectionStat, _states$editorViewMod, _states$widthState, _states$widthState2, _states$interactionSt;
33
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['table', 'width', 'media', 'selection', 'editorViewMode', 'interaction', 'limitedMode'], function (states) {
34
+ var _states$tableState, _states$tableState2, _states$tableState3, _states$tableState4, _states$tableState5, _states$tableState6, _states$tableState7, _states$tableState8, _states$tableState9, _states$tableState0, _states$tableState1, _states$mediaState, _states$selectionStat, _states$editorViewMod, _states$widthState, _states$widthState2, _states$interactionSt, _states$limitedModeSt;
35
35
  return {
36
36
  // tableState
37
37
  isTableResizing: (_states$tableState = states.tableState) === null || _states$tableState === void 0 ? void 0 : _states$tableState.isTableResizing,
@@ -56,7 +56,10 @@ var TableComponentWithSharedState = exports.TableComponentWithSharedState = func
56
56
  width: (_states$widthState = states.widthState) === null || _states$widthState === void 0 ? void 0 : _states$widthState.width,
57
57
  lineLength: (_states$widthState2 = states.widthState) === null || _states$widthState2 === void 0 ? void 0 : _states$widthState2.lineLength,
58
58
  // interactionState
59
- interaction: (_states$interactionSt = states.interactionState) === null || _states$interactionSt === void 0 ? void 0 : _states$interactionSt.interactionState
59
+ interaction: (_states$interactionSt = states.interactionState) === null || _states$interactionSt === void 0 ? void 0 : _states$interactionSt.interactionState,
60
+ // limitedModeState — selected (rather than read once) so sticky headers stop when limited
61
+ // mode latches mid-session, not only when it was already on as the table mounted.
62
+ limitedModeEnabled: (_states$limitedModeSt = states.limitedModeState) === null || _states$limitedModeSt === void 0 ? void 0 : _states$limitedModeSt.enabled
60
63
  };
61
64
  }),
62
65
  isTableResizing = _useSharedPluginState.isTableResizing,
@@ -74,7 +77,8 @@ var TableComponentWithSharedState = exports.TableComponentWithSharedState = func
74
77
  lineLength = _useSharedPluginState.lineLength,
75
78
  mode = _useSharedPluginState.mode,
76
79
  selection = _useSharedPluginState.selection,
77
- width = _useSharedPluginState.width;
80
+ width = _useSharedPluginState.width,
81
+ limitedModeEnabled = _useSharedPluginState.limitedModeEnabled;
78
82
  var isLivePageViewMode = mode === 'view';
79
83
 
80
84
  /**
@@ -126,6 +130,7 @@ var TableComponentWithSharedState = exports.TableComponentWithSharedState = func
126
130
  hoveredCell: hoveredCell,
127
131
  isTableHovered: isTableHovered,
128
132
  isWholeTableInDanger: isWholeTableInDanger,
129
- selection: selection
133
+ selection: selection,
134
+ limitedMode: limitedModeEnabled
130
135
  });
131
136
  };
@@ -17,6 +17,7 @@ var _nesting = require("@atlaskit/editor-common/nesting");
17
17
  var _nodeVisibility = require("@atlaskit/editor-common/node-visibility");
18
18
  var _ui = require("@atlaskit/editor-common/ui");
19
19
  var _utils = require("@atlaskit/editor-prosemirror/utils");
20
+ var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
20
21
  var _fg = require("@atlaskit/platform-feature-flags/fg");
21
22
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
22
23
  var _pluginFactory = require("../pm-plugins/plugin-factory");
@@ -38,7 +39,6 @@ var HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
38
39
  var HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
39
40
  var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
40
41
  function TableRow(node, view, getPos, eventDispatcher, api) {
41
- var _api$limitedMode;
42
42
  var _this;
43
43
  (0, _classCallCheck2.default)(this, TableRow);
44
44
  _this = _callSuper(this, TableRow, [node, view, getPos, eventDispatcher]);
@@ -94,10 +94,38 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
94
94
  var _getPluginState = (0, _pluginFactory.getPluginState)(view.state),
95
95
  pluginConfig = _getPluginState.pluginConfig;
96
96
  _this.isStickyHeaderEnabled = !!pluginConfig.stickyHeaders;
97
- if (api !== null && api !== void 0 && (_api$limitedMode = api.limitedMode) !== null && _api$limitedMode !== void 0 && (_api$limitedMode = _api$limitedMode.sharedState.currentState()) !== null && _api$limitedMode !== void 0 && (_api$limitedMode = _api$limitedMode.limitedModePluginKey.getState(view.state)) !== null && _api$limitedMode !== void 0 && _api$limitedMode.documentSizeBreachesThreshold) {
98
- _this.isStickyHeaderEnabled = false;
99
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
100
- document.addEventListener('limited-mode-activated', _this.cleanup);
97
+
98
+ // Sticky headers are the only thing limited mode turns off here, so there is nothing to read
99
+ // or subscribe to when they were never enabled for this editor.
100
+ if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_dynamic_limited_mode')) {
101
+ var _api$limitedMode, _limitedMode$currentS;
102
+ var limitedMode = _this.isStickyHeaderEnabled ? api === null || api === void 0 || (_api$limitedMode = api.limitedMode) === null || _api$limitedMode === void 0 ? void 0 : _api$limitedMode.sharedState : undefined;
103
+ if (limitedMode !== null && limitedMode !== void 0 && (_limitedMode$currentS = limitedMode.currentState()) !== null && _limitedMode$currentS !== void 0 && _limitedMode$currentS.enabled) {
104
+ _this.isStickyHeaderEnabled = false;
105
+ } else {
106
+ _this.limitedModeUnsubscribe = limitedMode === null || limitedMode === void 0 ? void 0 : limitedMode.onChange(function (_ref) {
107
+ var _this$limitedModeUnsu, _this2;
108
+ var nextSharedState = _ref.nextSharedState;
109
+ if (!(nextSharedState !== null && nextSharedState !== void 0 && nextSharedState.enabled)) {
110
+ return;
111
+ }
112
+
113
+ // Order matters: cleanup() short-circuits unless sticky headers are still marked enabled.
114
+ _this.cleanup();
115
+ _this.isStickyHeaderEnabled = false;
116
+
117
+ // Limited mode never turns back off, so this listener has done its job.
118
+ (_this$limitedModeUnsu = (_this2 = _this).limitedModeUnsubscribe) === null || _this$limitedModeUnsu === void 0 || _this$limitedModeUnsu.call(_this2);
119
+ _this.limitedModeUnsubscribe = undefined;
120
+ });
121
+ }
122
+ } else {
123
+ var _api$limitedMode2;
124
+ if (api !== null && api !== void 0 && (_api$limitedMode2 = api.limitedMode) !== null && _api$limitedMode2 !== void 0 && (_api$limitedMode2 = _api$limitedMode2.sharedState.currentState()) !== null && _api$limitedMode2 !== void 0 && (_api$limitedMode2 = _api$limitedMode2.limitedModePluginKey.getState(view.state)) !== null && _api$limitedMode2 !== void 0 && _api$limitedMode2.documentSizeBreachesThreshold) {
125
+ _this.isStickyHeaderEnabled = false;
126
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
127
+ document.addEventListener('limited-mode-activated', _this.cleanup);
128
+ }
101
129
  }
102
130
  var pos = _this.getPos();
103
131
  _this.isInNestedTable = false;
@@ -195,9 +223,14 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
195
223
  }
196
224
  this.emitOff(true);
197
225
  }
198
-
199
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
200
- document.removeEventListener('limited-mode-activated', this.cleanup);
226
+ if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_dynamic_limited_mode')) {
227
+ var _this$limitedModeUnsu2;
228
+ (_this$limitedModeUnsu2 = this.limitedModeUnsubscribe) === null || _this$limitedModeUnsu2 === void 0 || _this$limitedModeUnsu2.call(this);
229
+ this.limitedModeUnsubscribe = undefined;
230
+ } else {
231
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
232
+ document.removeEventListener('limited-mode-activated', this.cleanup);
233
+ }
201
234
  if (this.tableContainerObserver) {
202
235
  this.tableContainerObserver.disconnect();
203
236
  }
@@ -292,7 +325,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
292
325
  }, {
293
326
  key: "initObservers",
294
327
  value: function initObservers() {
295
- var _this2 = this;
328
+ var _this3 = this;
296
329
  if (!this.dom || this.dom.dataset.isObserved) {
297
330
  return;
298
331
  }
@@ -311,7 +344,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
311
344
  window.requestAnimationFrame(function () {
312
345
  var getTableContainer = function getTableContainer() {
313
346
  var _getTree;
314
- return (_getTree = (0, _dom2.getTree)(_this2.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(".".concat(_types.TableCssClassName.NODEVIEW_WRAPPER));
347
+ return (_getTree = (0, _dom2.getTree)(_this3.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(".".concat(_types.TableCssClassName.NODEVIEW_WRAPPER));
315
348
  };
316
349
 
317
350
  // we expect tree to be defined after animation frame
@@ -337,16 +370,16 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
337
370
  return getSentinelTop() !== null && getSentinelBottom() !== null;
338
371
  };
339
372
  var observeStickySentinels = function observeStickySentinels() {
340
- _this2.sentinels.top = getSentinelTop();
341
- _this2.sentinels.bottom = getSentinelBottom();
342
- [_this2.sentinels.top, _this2.sentinels.bottom].forEach(function (el) {
373
+ _this3.sentinels.top = getSentinelTop();
374
+ _this3.sentinels.bottom = getSentinelBottom();
375
+ [_this3.sentinels.top, _this3.sentinels.bottom].forEach(function (el) {
343
376
  // skip if already observed for another row on this table
344
377
  if (el && !el.dataset.isObserved) {
345
378
  el.dataset.isObserved = 'true';
346
379
 
347
380
  // Ignored via go/ees005
348
381
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
349
- _this2.intersectionObserver.observe(el);
382
+ _this3.intersectionObserver.observe(el);
350
383
  }
351
384
  });
352
385
  };
@@ -361,7 +394,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
361
394
  // observe sentinels before they are in the DOM, use MutationObserver
362
395
  // to wait for sentinels to be added to the parent Table node DOM
363
396
  // then attach the IntersectionObserver
364
- _this2.tableContainerObserver = new MutationObserver(function () {
397
+ _this3.tableContainerObserver = new MutationObserver(function () {
365
398
  // Check if the tableContainer is still connected to the DOM. It can become disconnected when the placholder
366
399
  // prosemirror node is replaced with the fully rendered React node (see _handleTableRef).
367
400
 
@@ -369,14 +402,14 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
369
402
  tableContainer = getTableContainer();
370
403
  }
371
404
  if (sentinelsInDom()) {
372
- var _this2$tableContainer;
405
+ var _this3$tableContainer;
373
406
  observeStickySentinels();
374
- (_this2$tableContainer = _this2.tableContainerObserver) === null || _this2$tableContainer === void 0 || _this2$tableContainer.disconnect();
407
+ (_this3$tableContainer = _this3.tableContainerObserver) === null || _this3$tableContainer === void 0 || _this3$tableContainer.disconnect();
375
408
  }
376
409
  });
377
410
  var mutatingNode = tableContainer;
378
- if (mutatingNode && _this2.tableContainerObserver) {
379
- _this2.tableContainerObserver.observe(mutatingNode, {
411
+ if (mutatingNode && _this3.tableContainerObserver) {
412
+ _this3.tableContainerObserver.observe(mutatingNode, {
380
413
  subtree: true,
381
414
  childList: true
382
415
  });
@@ -391,32 +424,32 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
391
424
  }, {
392
425
  key: "createResizeObserver",
393
426
  value: function createResizeObserver() {
394
- var _this3 = this;
427
+ var _this4 = this;
395
428
  this.resizeObserver = new ResizeObserver(function (entries) {
396
- var tree = (0, _dom2.getTree)(_this3.dom);
429
+ var tree = (0, _dom2.getTree)(_this4.dom);
397
430
  if (!tree) {
398
431
  return;
399
432
  }
400
433
  var table = tree.table;
401
434
  entries.forEach(function (entry) {
402
- var _this3$editorScrollab;
435
+ var _this4$editorScrollab;
403
436
  // On resize of the parent scroll element we need to adjust the width
404
437
  // of the sticky header
405
438
  // Ignored via go/ees005
406
439
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
407
- if (entry.target.className === ((_this3$editorScrollab = _this3.editorScrollableElement) === null || _this3$editorScrollab === void 0 ? void 0 : _this3$editorScrollab.className)) {
408
- _this3.updateStickyHeaderWidth();
440
+ if (entry.target.className === ((_this4$editorScrollab = _this4.editorScrollableElement) === null || _this4$editorScrollab === void 0 ? void 0 : _this4$editorScrollab.className)) {
441
+ _this4.updateStickyHeaderWidth();
409
442
  } else {
410
443
  var newHeight = entry.contentRect ? entry.contentRect.height :
411
444
  // Ignored via go/ees005
412
445
  // eslint-disable-next-line @atlaskit/editor/no-as-casting
413
446
  entry.target.offsetHeight;
414
- if (_this3.sentinels.bottom &&
447
+ if (_this4.sentinels.bottom &&
415
448
  // When the table header is sticky, it would be taller by a 1px (border-bottom),
416
449
  // So we adding this check to allow a 1px difference.
417
- Math.abs(newHeight - (_this3.stickyRowHeight || 0)) > _consts.stickyHeaderBorderBottomWidth) {
418
- _this3.stickyRowHeight = newHeight;
419
- _this3.sentinels.bottom.style.bottom = "".concat(_consts.tableScrollbarOffset + _consts.stickyRowOffsetTop + newHeight, "px");
450
+ Math.abs(newHeight - (_this4.stickyRowHeight || 0)) > _consts.stickyHeaderBorderBottomWidth) {
451
+ _this4.stickyRowHeight = newHeight;
452
+ _this4.sentinels.bottom.style.bottom = "".concat(_consts.tableScrollbarOffset + _consts.stickyRowOffsetTop + newHeight, "px");
420
453
  (0, _dom.updateStickyMargins)(table);
421
454
  }
422
455
  }
@@ -426,13 +459,13 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
426
459
  }, {
427
460
  key: "createIntersectionObserver",
428
461
  value: function createIntersectionObserver() {
429
- var _this4 = this;
462
+ var _this5 = this;
430
463
  this.intersectionObserver = new IntersectionObserver(function (entries, _) {
431
- var _this4$editorScrollab, _this4$editorScrollab2;
464
+ var _this5$editorScrollab, _this5$editorScrollab2;
432
465
  // IMPORTANT: please try and avoid using entry.rootBounds it's terribly inconsistent. For example; sometimes it may return
433
466
  // 0 height. In safari it will multiply all values by the window scale factor, however chrome & firfox won't.
434
467
  // This is why i just get the scroll view bounding rect here and use it, and fallback to the entry.rootBounds if needed.
435
- var rootBounds = (_this4$editorScrollab = _this4.editorScrollableElement) === null || _this4$editorScrollab === void 0 || (_this4$editorScrollab2 = _this4$editorScrollab.getBoundingClientRect) === null || _this4$editorScrollab2 === void 0 ? void 0 : _this4$editorScrollab2.call(_this4$editorScrollab);
468
+ var rootBounds = (_this5$editorScrollab = _this5.editorScrollableElement) === null || _this5$editorScrollab === void 0 || (_this5$editorScrollab2 = _this5$editorScrollab.getBoundingClientRect) === null || _this5$editorScrollab2 === void 0 ? void 0 : _this5$editorScrollab2.call(_this5$editorScrollab);
436
469
  entries.forEach(function (entry) {
437
470
  var target = entry.target,
438
471
  isIntersecting = entry.isIntersecting,
@@ -440,15 +473,15 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
440
473
  // This observer only every looks at the top/bottom sentinels, we can assume if it's not one then it's the other.
441
474
  var targetKey = target.classList.contains(_types.TableCssClassName.TABLE_STICKY_SENTINEL_TOP) ? 'top' : 'bottom';
442
475
  // Cache the latest sentinel information
443
- _this4.sentinelData[targetKey] = {
476
+ _this5.sentinelData[targetKey] = {
444
477
  isIntersecting: isIntersecting,
445
478
  boundingClientRect: boundingClientRect,
446
479
  rootBounds: rootBounds !== null && rootBounds !== void 0 ? rootBounds : entry.rootBounds
447
480
  };
448
481
  // Keep the other sentinels rootBounds in sync with this latest one
449
- _this4.sentinelData[targetKey === 'top' ? 'bottom' : targetKey].rootBounds = rootBounds !== null && rootBounds !== void 0 ? rootBounds : entry.rootBounds;
482
+ _this5.sentinelData[targetKey === 'top' ? 'bottom' : targetKey].rootBounds = rootBounds !== null && rootBounds !== void 0 ? rootBounds : entry.rootBounds;
450
483
  });
451
- _this4.refreshStickyState();
484
+ _this5.refreshStickyState();
452
485
  }, {
453
486
  threshold: 0,
454
487
  root: this.editorScrollableElement
@@ -654,12 +687,12 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
654
687
  }, {
655
688
  key: "refireIntersectionObservers",
656
689
  value: function refireIntersectionObservers() {
657
- var _this5 = this;
690
+ var _this6 = this;
658
691
  if (this.isSticky) {
659
692
  [this.sentinels.top, this.sentinels.bottom].forEach(function (el) {
660
- if (el && _this5.intersectionObserver) {
661
- _this5.intersectionObserver.unobserve(el);
662
- _this5.intersectionObserver.observe(el);
693
+ if (el && _this6.intersectionObserver) {
694
+ _this6.intersectionObserver.unobserve(el);
695
+ _this6.intersectionObserver.observe(el);
663
696
  }
664
697
  });
665
698
  }
@@ -668,7 +701,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
668
701
  key: "makeHeaderRowSticky",
669
702
  value: function makeHeaderRowSticky(tree, scrollTop) {
670
703
  var _tbody$firstChild,
671
- _this6 = this;
704
+ _this7 = this;
672
705
  // If header row height is more than 50% of viewport height don't do this
673
706
  if (this.isSticky || this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2 || this.isInNestedTable) {
674
707
  return;
@@ -707,7 +740,7 @@ var TableRow = exports.default = /*#__PURE__*/function (_TableNodeView) {
707
740
  });
708
741
  var fastScrollThresholdMs = 500;
709
742
  setTimeout(function () {
710
- _this6.refireIntersectionObservers();
743
+ _this7.refireIntersectionObservers();
711
744
  }, fastScrollThresholdMs);
712
745
  }
713
746
  this.dom.style.top = "0px";