@atlaskit/editor-plugin-table 3.2.0 → 4.0.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,21 @@
1
1
  # @atlaskit/editor-plugin-table
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`151b0d45db4`](https://bitbucket.org/atlassian/atlassian-frontend/commits/151b0d45db4) - Changed Resizer API. Removed handleComponent, innerPadding & handleMarginTop. Also renamed HandleHeightSizeType to HandleSize. The resizer should be opionated and control the handle component itself. innerPadding & handleMarginTop can also be controlled via the handleStyles override property.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+
13
+ ## 3.2.1
14
+
15
+ ### Patch Changes
16
+
17
+ - [`b1a93f61747`](https://bitbucket.org/atlassian/atlassian-frontend/commits/b1a93f61747) - [ED-20091] add logic to refire intersection observers and prevent detached table sticky headers
18
+
3
19
  ## 3.2.0
4
20
 
5
21
  ### Minor Changes
@@ -38,15 +38,16 @@ var messages = (0, _reactIntlNext.defineMessages)({
38
38
  var handles = {
39
39
  right: true
40
40
  };
41
- var tableHandleMarginTop = 12;
42
- var tableHandlePosition = 14;
43
- var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
44
- var tableHeight = tableRef === null || tableRef === void 0 ? void 0 : tableRef.clientHeight;
45
- var handleHeightSize = 'small';
46
- if (!tableHeight) {
47
- return handleHeightSize;
41
+ var handleStyles = {
42
+ right: {
43
+ // eslint-disable-next-line
44
+ right: '-14px',
45
+ marginTop: "var(--ds-space-150, 12px)"
48
46
  }
49
-
47
+ };
48
+ var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
49
+ var _tableRef$clientHeigh;
50
+ var tableHeight = (_tableRef$clientHeigh = tableRef === null || tableRef === void 0 ? void 0 : tableRef.clientHeight) !== null && _tableRef$clientHeigh !== void 0 ? _tableRef$clientHeigh : 0;
50
51
  /*
51
52
  - One row table height (minimum possible table height) ~ 45px
52
53
  - Two row table height ~ 90px
@@ -55,12 +56,13 @@ var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
55
56
  - > 46 because the height of the table can be a float number like 45.44.
56
57
  - < 96 is the height of large resize handle.
57
58
  */
58
- if (tableHeight > 46 && tableHeight < 96) {
59
- handleHeightSize = 'medium';
60
- } else if (tableHeight >= 96) {
61
- handleHeightSize = 'large';
59
+ if (tableHeight >= 96) {
60
+ return 'large';
61
+ }
62
+ if (tableHeight > 46) {
63
+ return 'medium';
62
64
  }
63
- return handleHeightSize;
65
+ return 'small';
64
66
  };
65
67
  var getResizerMinWidth = function getResizerMinWidth(node) {
66
68
  var currentColumnCount = (0, _utils2.getColgroupChildrenLength)(node);
@@ -107,7 +109,7 @@ var TableResizer = function TableResizer(_ref) {
107
109
  var _useIntl = (0, _reactIntlNext.useIntl)(),
108
110
  formatMessage = _useIntl.formatMessage;
109
111
  var resizerMinWidth = getResizerMinWidth(node);
110
- var handleHeightSize = getResizerHandleHeight(tableRef);
112
+ var handleSize = getResizerHandleHeight(tableRef);
111
113
  var _getPluginState = (0, _pluginFactory.getPluginState)(editorView.state),
112
114
  isInDanger = _getPluginState.isInDanger;
113
115
  var _useMeasureFramerate = (0, _analytics2.useMeasureFramerate)(),
@@ -244,8 +246,8 @@ var TableResizer = function TableResizer(_ref) {
244
246
  enable: handles,
245
247
  width: width,
246
248
  handleAlignmentMethod: "sticky",
247
- handleHeightSize: handleHeightSize,
248
- handleMarginTop: tableHandleMarginTop,
249
+ handleSize: handleSize,
250
+ handleStyles: handleStyles,
249
251
  handleResizeStart: handleResizeStart,
250
252
  handleResize: scheduleResize,
251
253
  handleResizeStop: handleResizeStop,
@@ -255,7 +257,6 @@ var TableResizer = function TableResizer(_ref) {
255
257
  snapGap: _consts.TABLE_SNAP_GAP,
256
258
  snap: guidelineSnaps,
257
259
  handlePositioning: "adjacent",
258
- innerPadding: tableHandlePosition,
259
260
  isHandleVisible: isTableSelected,
260
261
  appearance: isInDanger && isTableSelected ? 'danger' : undefined,
261
262
  handleHighlight: "shadow",
@@ -149,10 +149,24 @@ var TableRowNodeView = /*#__PURE__*/function () {
149
149
  // otherwise make it non-sticky
150
150
  return false;
151
151
  });
152
+ /**
153
+ * Manually refire the intersection observers.
154
+ * Useful when the header may have detached from the table.
155
+ */
156
+ (0, _defineProperty2.default)(this, "refireIntersectionObservers", function () {
157
+ if (_this.isSticky) {
158
+ [_this.sentinels.top, _this.sentinels.bottom].forEach(function (el) {
159
+ if (el && _this.intersectionObserver) {
160
+ _this.intersectionObserver.unobserve(el);
161
+ _this.intersectionObserver.observe(el);
162
+ }
163
+ });
164
+ }
165
+ });
152
166
  (0, _defineProperty2.default)(this, "makeHeaderRowSticky", function (tree, scrollTop) {
153
167
  var _tbody$firstChild;
154
168
  // If header row height is more than 50% of viewport height don't do this
155
- if (_this.stickyRowHeight && _this.stickyRowHeight > window.innerHeight / 2) {
169
+ if (_this.isSticky || _this.stickyRowHeight && _this.stickyRowHeight > window.innerHeight / 2) {
156
170
  return;
157
171
  }
158
172
  var table = tree.table,
@@ -170,10 +184,25 @@ var TableRowNodeView = /*#__PURE__*/function () {
170
184
  }
171
185
  var domTop = currentTableTop > 0 ? scrollTop : scrollTop + currentTableTop;
172
186
  if (!_this.isSticky) {
187
+ var _this$editorScrollabl;
173
188
  (0, _dom.syncStickyRowToTable)(table);
174
189
  _this.dom.classList.add('sticky');
175
190
  table.classList.add(_types.TableCssClassName.TABLE_STICKY);
176
191
  _this.isSticky = true;
192
+
193
+ /**
194
+ * The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
195
+ * detaches from the table. This typically happens during a fast scroll by the user which causes
196
+ * the intersection observer logic to not fire as expected.
197
+ */
198
+ (_this$editorScrollabl = _this.editorScrollableElement) === null || _this$editorScrollabl === void 0 ? void 0 : _this$editorScrollabl.addEventListener('scrollend', _this.refireIntersectionObservers, {
199
+ passive: true,
200
+ once: true
201
+ });
202
+ var fastScrollThresholdMs = 500;
203
+ setTimeout(function () {
204
+ _this.refireIntersectionObservers();
205
+ }, fastScrollThresholdMs);
177
206
  }
178
207
  _this.dom.style.top = "".concat(domTop, "px");
179
208
  (0, _dom.updateStickyMargins)(table);
@@ -270,8 +299,12 @@ var TableRowNodeView = /*#__PURE__*/function () {
270
299
  this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth);
271
300
  this.eventDispatcher.on(_pluginKey.pluginKey.key, this.onTablePluginState);
272
301
  this.listening = true;
273
- this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this));
274
- this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this));
302
+ this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
303
+ passive: true
304
+ });
305
+ this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this), {
306
+ passive: true
307
+ });
275
308
  }
276
309
  }, {
277
310
  key: "unsubscribe",
@@ -282,7 +315,7 @@ var TableRowNodeView = /*#__PURE__*/function () {
282
315
  if (this.intersectionObserver) {
283
316
  this.intersectionObserver.disconnect();
284
317
  // ED-16211 Once intersection observer is disconnected, we need to remove the isObserved from the sentinels
285
- // Otherwise when new intersection observer is created it will not observe because it thinks its already being observed
318
+ // Otherwise when newer intersection observer is created it will not observe because it thinks its already being observed
286
319
  [this.sentinels.top, this.sentinels.bottom].forEach(function (el) {
287
320
  if (el) {
288
321
  delete el.dataset.isObserved;
@@ -24,15 +24,16 @@ const messages = defineMessages({
24
24
  const handles = {
25
25
  right: true
26
26
  };
27
- const tableHandleMarginTop = 12;
28
- const tableHandlePosition = 14;
29
- const getResizerHandleHeight = tableRef => {
30
- const tableHeight = tableRef === null || tableRef === void 0 ? void 0 : tableRef.clientHeight;
31
- let handleHeightSize = 'small';
32
- if (!tableHeight) {
33
- return handleHeightSize;
27
+ const handleStyles = {
28
+ right: {
29
+ // eslint-disable-next-line
30
+ right: '-14px',
31
+ marginTop: "var(--ds-space-150, 12px)"
34
32
  }
35
-
33
+ };
34
+ const getResizerHandleHeight = tableRef => {
35
+ var _tableRef$clientHeigh;
36
+ const tableHeight = (_tableRef$clientHeigh = tableRef === null || tableRef === void 0 ? void 0 : tableRef.clientHeight) !== null && _tableRef$clientHeigh !== void 0 ? _tableRef$clientHeigh : 0;
36
37
  /*
37
38
  - One row table height (minimum possible table height) ~ 45px
38
39
  - Two row table height ~ 90px
@@ -41,12 +42,13 @@ const getResizerHandleHeight = tableRef => {
41
42
  - > 46 because the height of the table can be a float number like 45.44.
42
43
  - < 96 is the height of large resize handle.
43
44
  */
44
- if (tableHeight > 46 && tableHeight < 96) {
45
- handleHeightSize = 'medium';
46
- } else if (tableHeight >= 96) {
47
- handleHeightSize = 'large';
45
+ if (tableHeight >= 96) {
46
+ return 'large';
47
+ }
48
+ if (tableHeight > 46) {
49
+ return 'medium';
48
50
  }
49
- return handleHeightSize;
51
+ return 'small';
50
52
  };
51
53
  const getResizerMinWidth = node => {
52
54
  const currentColumnCount = getColgroupChildrenLength(node);
@@ -92,7 +94,7 @@ export const TableResizer = ({
92
94
  formatMessage
93
95
  } = useIntl();
94
96
  const resizerMinWidth = getResizerMinWidth(node);
95
- const handleHeightSize = getResizerHandleHeight(tableRef);
97
+ const handleSize = getResizerHandleHeight(tableRef);
96
98
  const {
97
99
  isInDanger
98
100
  } = getPluginState(editorView.state);
@@ -239,8 +241,8 @@ export const TableResizer = ({
239
241
  enable: handles,
240
242
  width: width,
241
243
  handleAlignmentMethod: "sticky",
242
- handleHeightSize: handleHeightSize,
243
- handleMarginTop: tableHandleMarginTop,
244
+ handleSize: handleSize,
245
+ handleStyles: handleStyles,
244
246
  handleResizeStart: handleResizeStart,
245
247
  handleResize: scheduleResize,
246
248
  handleResizeStop: handleResizeStop,
@@ -250,7 +252,6 @@ export const TableResizer = ({
250
252
  snapGap: TABLE_SNAP_GAP,
251
253
  snap: guidelineSnaps,
252
254
  handlePositioning: "adjacent",
253
- innerPadding: tableHandlePosition,
254
255
  isHandleVisible: isTableSelected,
255
256
  appearance: isInDanger && isTableSelected ? 'danger' : undefined,
256
257
  handleHighlight: "shadow",
@@ -137,10 +137,24 @@ export class TableRowNodeView {
137
137
  // otherwise make it non-sticky
138
138
  return false;
139
139
  });
140
+ /**
141
+ * Manually refire the intersection observers.
142
+ * Useful when the header may have detached from the table.
143
+ */
144
+ _defineProperty(this, "refireIntersectionObservers", () => {
145
+ if (this.isSticky) {
146
+ [this.sentinels.top, this.sentinels.bottom].forEach(el => {
147
+ if (el && this.intersectionObserver) {
148
+ this.intersectionObserver.unobserve(el);
149
+ this.intersectionObserver.observe(el);
150
+ }
151
+ });
152
+ }
153
+ });
140
154
  _defineProperty(this, "makeHeaderRowSticky", (tree, scrollTop) => {
141
155
  var _tbody$firstChild;
142
156
  // If header row height is more than 50% of viewport height don't do this
143
- if (this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2) {
157
+ if (this.isSticky || this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2) {
144
158
  return;
145
159
  }
146
160
  const {
@@ -160,10 +174,25 @@ export class TableRowNodeView {
160
174
  }
161
175
  const domTop = currentTableTop > 0 ? scrollTop : scrollTop + currentTableTop;
162
176
  if (!this.isSticky) {
177
+ var _this$editorScrollabl;
163
178
  syncStickyRowToTable(table);
164
179
  this.dom.classList.add('sticky');
165
180
  table.classList.add(ClassName.TABLE_STICKY);
166
181
  this.isSticky = true;
182
+
183
+ /**
184
+ * The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
185
+ * detaches from the table. This typically happens during a fast scroll by the user which causes
186
+ * the intersection observer logic to not fire as expected.
187
+ */
188
+ (_this$editorScrollabl = this.editorScrollableElement) === null || _this$editorScrollabl === void 0 ? void 0 : _this$editorScrollabl.addEventListener('scrollend', this.refireIntersectionObservers, {
189
+ passive: true,
190
+ once: true
191
+ });
192
+ const fastScrollThresholdMs = 500;
193
+ setTimeout(() => {
194
+ this.refireIntersectionObservers();
195
+ }, fastScrollThresholdMs);
167
196
  }
168
197
  this.dom.style.top = `${domTop}px`;
169
198
  updateTableMargin(table);
@@ -245,8 +274,12 @@ export class TableRowNodeView {
245
274
  this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth);
246
275
  this.eventDispatcher.on(tablePluginKey.key, this.onTablePluginState);
247
276
  this.listening = true;
248
- this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this));
249
- this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this));
277
+ this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
278
+ passive: true
279
+ });
280
+ this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this), {
281
+ passive: true
282
+ });
250
283
  }
251
284
  unsubscribe() {
252
285
  if (!this.listening) {
@@ -255,7 +288,7 @@ export class TableRowNodeView {
255
288
  if (this.intersectionObserver) {
256
289
  this.intersectionObserver.disconnect();
257
290
  // ED-16211 Once intersection observer is disconnected, we need to remove the isObserved from the sentinels
258
- // Otherwise when new intersection observer is created it will not observe because it thinks its already being observed
291
+ // Otherwise when newer intersection observer is created it will not observe because it thinks its already being observed
259
292
  [this.sentinels.top, this.sentinels.bottom].forEach(el => {
260
293
  if (el) {
261
294
  delete el.dataset.isObserved;
@@ -316,10 +349,10 @@ export class TableRowNodeView {
316
349
  table
317
350
  } = this.tree;
318
351
  entries.forEach(entry => {
319
- var _this$editorScrollabl;
352
+ var _this$editorScrollabl2;
320
353
  // On resize of the parent scroll element we need to adjust the width
321
354
  // of the sticky header
322
- if (entry.target.className === ((_this$editorScrollabl = this.editorScrollableElement) === null || _this$editorScrollabl === void 0 ? void 0 : _this$editorScrollabl.className)) {
355
+ if (entry.target.className === ((_this$editorScrollabl2 = this.editorScrollableElement) === null || _this$editorScrollabl2 === void 0 ? void 0 : _this$editorScrollabl2.className)) {
323
356
  this.updateStickyHeaderWidth();
324
357
  } else {
325
358
  const newHeight = entry.contentRect ? entry.contentRect.height : entry.target.offsetHeight;
@@ -28,15 +28,16 @@ var messages = defineMessages({
28
28
  var handles = {
29
29
  right: true
30
30
  };
31
- var tableHandleMarginTop = 12;
32
- var tableHandlePosition = 14;
33
- var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
34
- var tableHeight = tableRef === null || tableRef === void 0 ? void 0 : tableRef.clientHeight;
35
- var handleHeightSize = 'small';
36
- if (!tableHeight) {
37
- return handleHeightSize;
31
+ var handleStyles = {
32
+ right: {
33
+ // eslint-disable-next-line
34
+ right: '-14px',
35
+ marginTop: "var(--ds-space-150, 12px)"
38
36
  }
39
-
37
+ };
38
+ var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
39
+ var _tableRef$clientHeigh;
40
+ var tableHeight = (_tableRef$clientHeigh = tableRef === null || tableRef === void 0 ? void 0 : tableRef.clientHeight) !== null && _tableRef$clientHeigh !== void 0 ? _tableRef$clientHeigh : 0;
40
41
  /*
41
42
  - One row table height (minimum possible table height) ~ 45px
42
43
  - Two row table height ~ 90px
@@ -45,12 +46,13 @@ var getResizerHandleHeight = function getResizerHandleHeight(tableRef) {
45
46
  - > 46 because the height of the table can be a float number like 45.44.
46
47
  - < 96 is the height of large resize handle.
47
48
  */
48
- if (tableHeight > 46 && tableHeight < 96) {
49
- handleHeightSize = 'medium';
50
- } else if (tableHeight >= 96) {
51
- handleHeightSize = 'large';
49
+ if (tableHeight >= 96) {
50
+ return 'large';
51
+ }
52
+ if (tableHeight > 46) {
53
+ return 'medium';
52
54
  }
53
- return handleHeightSize;
55
+ return 'small';
54
56
  };
55
57
  var getResizerMinWidth = function getResizerMinWidth(node) {
56
58
  var currentColumnCount = getColgroupChildrenLength(node);
@@ -97,7 +99,7 @@ export var TableResizer = function TableResizer(_ref) {
97
99
  var _useIntl = useIntl(),
98
100
  formatMessage = _useIntl.formatMessage;
99
101
  var resizerMinWidth = getResizerMinWidth(node);
100
- var handleHeightSize = getResizerHandleHeight(tableRef);
102
+ var handleSize = getResizerHandleHeight(tableRef);
101
103
  var _getPluginState = getPluginState(editorView.state),
102
104
  isInDanger = _getPluginState.isInDanger;
103
105
  var _useMeasureFramerate = useMeasureFramerate(),
@@ -234,8 +236,8 @@ export var TableResizer = function TableResizer(_ref) {
234
236
  enable: handles,
235
237
  width: width,
236
238
  handleAlignmentMethod: "sticky",
237
- handleHeightSize: handleHeightSize,
238
- handleMarginTop: tableHandleMarginTop,
239
+ handleSize: handleSize,
240
+ handleStyles: handleStyles,
239
241
  handleResizeStart: handleResizeStart,
240
242
  handleResize: scheduleResize,
241
243
  handleResizeStop: handleResizeStop,
@@ -245,7 +247,6 @@ export var TableResizer = function TableResizer(_ref) {
245
247
  snapGap: TABLE_SNAP_GAP,
246
248
  snap: guidelineSnaps,
247
249
  handlePositioning: "adjacent",
248
- innerPadding: tableHandlePosition,
249
250
  isHandleVisible: isTableSelected,
250
251
  appearance: isInDanger && isTableSelected ? 'danger' : undefined,
251
252
  handleHighlight: "shadow",
@@ -142,10 +142,24 @@ export var TableRowNodeView = /*#__PURE__*/function () {
142
142
  // otherwise make it non-sticky
143
143
  return false;
144
144
  });
145
+ /**
146
+ * Manually refire the intersection observers.
147
+ * Useful when the header may have detached from the table.
148
+ */
149
+ _defineProperty(this, "refireIntersectionObservers", function () {
150
+ if (_this.isSticky) {
151
+ [_this.sentinels.top, _this.sentinels.bottom].forEach(function (el) {
152
+ if (el && _this.intersectionObserver) {
153
+ _this.intersectionObserver.unobserve(el);
154
+ _this.intersectionObserver.observe(el);
155
+ }
156
+ });
157
+ }
158
+ });
145
159
  _defineProperty(this, "makeHeaderRowSticky", function (tree, scrollTop) {
146
160
  var _tbody$firstChild;
147
161
  // If header row height is more than 50% of viewport height don't do this
148
- if (_this.stickyRowHeight && _this.stickyRowHeight > window.innerHeight / 2) {
162
+ if (_this.isSticky || _this.stickyRowHeight && _this.stickyRowHeight > window.innerHeight / 2) {
149
163
  return;
150
164
  }
151
165
  var table = tree.table,
@@ -163,10 +177,25 @@ export var TableRowNodeView = /*#__PURE__*/function () {
163
177
  }
164
178
  var domTop = currentTableTop > 0 ? scrollTop : scrollTop + currentTableTop;
165
179
  if (!_this.isSticky) {
180
+ var _this$editorScrollabl;
166
181
  syncStickyRowToTable(table);
167
182
  _this.dom.classList.add('sticky');
168
183
  table.classList.add(ClassName.TABLE_STICKY);
169
184
  _this.isSticky = true;
185
+
186
+ /**
187
+ * The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
188
+ * detaches from the table. This typically happens during a fast scroll by the user which causes
189
+ * the intersection observer logic to not fire as expected.
190
+ */
191
+ (_this$editorScrollabl = _this.editorScrollableElement) === null || _this$editorScrollabl === void 0 ? void 0 : _this$editorScrollabl.addEventListener('scrollend', _this.refireIntersectionObservers, {
192
+ passive: true,
193
+ once: true
194
+ });
195
+ var fastScrollThresholdMs = 500;
196
+ setTimeout(function () {
197
+ _this.refireIntersectionObservers();
198
+ }, fastScrollThresholdMs);
170
199
  }
171
200
  _this.dom.style.top = "".concat(domTop, "px");
172
201
  updateTableMargin(table);
@@ -263,8 +292,12 @@ export var TableRowNodeView = /*#__PURE__*/function () {
263
292
  this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth);
264
293
  this.eventDispatcher.on(tablePluginKey.key, this.onTablePluginState);
265
294
  this.listening = true;
266
- this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this));
267
- this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this));
295
+ this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
296
+ passive: true
297
+ });
298
+ this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this), {
299
+ passive: true
300
+ });
268
301
  }
269
302
  }, {
270
303
  key: "unsubscribe",
@@ -275,7 +308,7 @@ export var TableRowNodeView = /*#__PURE__*/function () {
275
308
  if (this.intersectionObserver) {
276
309
  this.intersectionObserver.disconnect();
277
310
  // ED-16211 Once intersection observer is disconnected, we need to remove the isObserved from the sentinels
278
- // Otherwise when new intersection observer is created it will not observe because it thinks its already being observed
311
+ // Otherwise when newer intersection observer is created it will not observe because it thinks its already being observed
279
312
  [this.sentinels.top, this.sentinels.bottom].forEach(function (el) {
280
313
  if (el) {
281
314
  delete el.dataset.isObserved;
@@ -55,6 +55,11 @@ export declare class TableRowNodeView implements NodeView {
55
55
  onTablePluginState: (state: TablePluginState) => void;
56
56
  updateStickyHeaderWidth: () => void;
57
57
  shouldHeaderStick: (tree: TableDOMElements) => boolean;
58
+ /**
59
+ * Manually refire the intersection observers.
60
+ * Useful when the header may have detached from the table.
61
+ */
62
+ refireIntersectionObservers: () => void;
58
63
  makeHeaderRowSticky: (tree: TableDOMElements, scrollTop?: number) => void;
59
64
  makeRowHeaderNotSticky: (table: HTMLElement, isEditorDestroyed?: boolean) => void;
60
65
  getWrapperoffset: (inverse?: boolean) => number;
@@ -55,6 +55,11 @@ export declare class TableRowNodeView implements NodeView {
55
55
  onTablePluginState: (state: TablePluginState) => void;
56
56
  updateStickyHeaderWidth: () => void;
57
57
  shouldHeaderStick: (tree: TableDOMElements) => boolean;
58
+ /**
59
+ * Manually refire the intersection observers.
60
+ * Useful when the header may have detached from the table.
61
+ */
62
+ refireIntersectionObservers: () => void;
58
63
  makeHeaderRowSticky: (tree: TableDOMElements, scrollTop?: number) => void;
59
64
  makeRowHeaderNotSticky: (table: HTMLElement, isEditorDestroyed?: boolean) => void;
60
65
  getWrapperoffset: (inverse?: boolean) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-table",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Table plugin for the @atlaskit/editor",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@atlaskit/adf-schema": "^29.1.0",
31
- "@atlaskit/editor-common": "^75.8.0",
31
+ "@atlaskit/editor-common": "^76.0.0",
32
32
  "@atlaskit/editor-palette": "1.5.1",
33
33
  "@atlaskit/editor-plugin-analytics": "^0.2.0",
34
34
  "@atlaskit/editor-plugin-content-insertion": "^0.1.0",
@@ -14,16 +14,14 @@ import type { TableEventPayload } from '@atlaskit/editor-common/analytics';
14
14
  import { TABLE_OVERFLOW_CHANGE_TRIGGER } from '@atlaskit/editor-common/analytics';
15
15
  import { getGuidelinesWithHighlights } from '@atlaskit/editor-common/guideline';
16
16
  import type { GuidelineConfig } from '@atlaskit/editor-common/guideline';
17
- import type {
18
- HandleHeightSizeType,
19
- HandleResize,
20
- } from '@atlaskit/editor-common/resizer';
17
+ import type { HandleResize, HandleSize } from '@atlaskit/editor-common/resizer';
21
18
  import { ResizerNext } from '@atlaskit/editor-common/resizer';
22
19
  import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
23
20
  import type { Transaction } from '@atlaskit/editor-prosemirror/state';
24
21
  import type { EditorView } from '@atlaskit/editor-prosemirror/view';
25
22
  import { findTable } from '@atlaskit/editor-tables/utils';
26
23
  import { getBooleanFF } from '@atlaskit/platform-feature-flags';
24
+ import { token } from '@atlaskit/tokens';
27
25
 
28
26
  import { getPluginState } from '../pm-plugins/plugin-factory';
29
27
  import { META_KEYS } from '../pm-plugins/table-analytics';
@@ -77,17 +75,18 @@ const messages = defineMessages({
77
75
  });
78
76
 
79
77
  const handles = { right: true };
80
- const tableHandleMarginTop = 12;
81
- const tableHandlePosition = 14;
82
-
83
- const getResizerHandleHeight = (tableRef: HTMLTableElement | undefined) => {
84
- const tableHeight = tableRef?.clientHeight;
85
- let handleHeightSize: HandleHeightSizeType | undefined = 'small';
86
-
87
- if (!tableHeight) {
88
- return handleHeightSize;
89
- }
78
+ const handleStyles = {
79
+ right: {
80
+ // eslint-disable-next-line
81
+ right: '-14px',
82
+ marginTop: token('space.150', '12px'),
83
+ },
84
+ };
90
85
 
86
+ const getResizerHandleHeight = (
87
+ tableRef: HTMLTableElement | undefined,
88
+ ): HandleSize | undefined => {
89
+ const tableHeight = tableRef?.clientHeight ?? 0;
91
90
  /*
92
91
  - One row table height (minimum possible table height) ~ 45px
93
92
  - Two row table height ~ 90px
@@ -97,13 +96,15 @@ const getResizerHandleHeight = (tableRef: HTMLTableElement | undefined) => {
97
96
  - > 46 because the height of the table can be a float number like 45.44.
98
97
  - < 96 is the height of large resize handle.
99
98
  */
100
- if (tableHeight > 46 && tableHeight < 96) {
101
- handleHeightSize = 'medium';
102
- } else if (tableHeight >= 96) {
103
- handleHeightSize = 'large';
99
+ if (tableHeight >= 96) {
100
+ return 'large';
101
+ }
102
+
103
+ if (tableHeight > 46) {
104
+ return 'medium';
104
105
  }
105
106
 
106
- return handleHeightSize;
107
+ return 'small';
107
108
  };
108
109
 
109
110
  const getResizerMinWidth = (node: PMNode) => {
@@ -160,7 +161,7 @@ export const TableResizer = ({
160
161
  const { formatMessage } = useIntl();
161
162
 
162
163
  const resizerMinWidth = getResizerMinWidth(node);
163
- const handleHeightSize = getResizerHandleHeight(tableRef);
164
+ const handleSize = getResizerHandleHeight(tableRef);
164
165
  const { isInDanger } = getPluginState(editorView.state);
165
166
 
166
167
  const { startMeasure, endMeasure, countFrames } = useMeasureFramerate();
@@ -389,8 +390,8 @@ export const TableResizer = ({
389
390
  enable={handles}
390
391
  width={width}
391
392
  handleAlignmentMethod="sticky"
392
- handleHeightSize={handleHeightSize}
393
- handleMarginTop={tableHandleMarginTop}
393
+ handleSize={handleSize}
394
+ handleStyles={handleStyles}
394
395
  handleResizeStart={handleResizeStart}
395
396
  handleResize={scheduleResize}
396
397
  handleResizeStop={handleResizeStop}
@@ -400,7 +401,6 @@ export const TableResizer = ({
400
401
  snapGap={TABLE_SNAP_GAP}
401
402
  snap={guidelineSnaps}
402
403
  handlePositioning="adjacent"
403
- innerPadding={tableHandlePosition}
404
404
  isHandleVisible={isTableSelected}
405
405
  appearance={isInDanger && isTableSelected ? 'danger' : undefined}
406
406
  handleHighlight="shadow"
@@ -150,10 +150,13 @@ export class TableRowNodeView implements NodeView {
150
150
 
151
151
  this.listening = true;
152
152
 
153
- this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this));
153
+ this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
154
+ passive: true,
155
+ });
154
156
  this.dom.addEventListener(
155
157
  'touchmove',
156
158
  this.headerRowMouseScroll.bind(this),
159
+ { passive: true },
157
160
  );
158
161
  }
159
162
 
@@ -164,7 +167,7 @@ export class TableRowNodeView implements NodeView {
164
167
  if (this.intersectionObserver) {
165
168
  this.intersectionObserver.disconnect();
166
169
  // ED-16211 Once intersection observer is disconnected, we need to remove the isObserved from the sentinels
167
- // Otherwise when new intersection observer is created it will not observe because it thinks its already being observed
170
+ // Otherwise when newer intersection observer is created it will not observe because it thinks its already being observed
168
171
  [this.sentinels.top, this.sentinels.bottom].forEach((el) => {
169
172
  if (el) {
170
173
  delete el.dataset.isObserved;
@@ -522,9 +525,27 @@ export class TableRowNodeView implements NodeView {
522
525
  return false;
523
526
  };
524
527
 
528
+ /**
529
+ * Manually refire the intersection observers.
530
+ * Useful when the header may have detached from the table.
531
+ */
532
+ refireIntersectionObservers = () => {
533
+ if (this.isSticky) {
534
+ [this.sentinels.top, this.sentinels.bottom].forEach((el) => {
535
+ if (el && this.intersectionObserver) {
536
+ this.intersectionObserver.unobserve(el);
537
+ this.intersectionObserver.observe(el);
538
+ }
539
+ });
540
+ }
541
+ };
542
+
525
543
  makeHeaderRowSticky = (tree: TableDOMElements, scrollTop?: number) => {
526
544
  // If header row height is more than 50% of viewport height don't do this
527
- if (this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2) {
545
+ if (
546
+ this.isSticky ||
547
+ (this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2)
548
+ ) {
528
549
  return;
529
550
  }
530
551
 
@@ -552,6 +573,22 @@ export class TableRowNodeView implements NodeView {
552
573
  table.classList.add(ClassName.TABLE_STICKY);
553
574
 
554
575
  this.isSticky = true;
576
+
577
+ /**
578
+ * The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
579
+ * detaches from the table. This typically happens during a fast scroll by the user which causes
580
+ * the intersection observer logic to not fire as expected.
581
+ */
582
+ this.editorScrollableElement?.addEventListener(
583
+ 'scrollend',
584
+ this.refireIntersectionObservers,
585
+ { passive: true, once: true },
586
+ );
587
+
588
+ const fastScrollThresholdMs = 500;
589
+ setTimeout(() => {
590
+ this.refireIntersectionObservers();
591
+ }, fastScrollThresholdMs);
555
592
  }
556
593
 
557
594
  this.dom.style.top = `${domTop}px`;