@atlaskit/editor-plugin-table 1.2.0 → 1.2.2

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cjs/i18n/en.js +43 -0
  3. package/dist/cjs/i18n/en_GB.js +43 -0
  4. package/dist/cjs/plugins/table/index.js +2 -1
  5. package/dist/cjs/plugins/table/nodeviews/OverflowShadowsObserver.js +13 -26
  6. package/dist/cjs/plugins/table/nodeviews/TableComponent.js +18 -3
  7. package/dist/cjs/plugins/table/nodeviews/table.js +7 -0
  8. package/dist/cjs/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +32 -17
  9. package/dist/cjs/plugins/table/pm-plugins/table-resizing/event-handlers.js +1 -5
  10. package/dist/cjs/plugins/table/pm-plugins/table-resizing/utils/misc.js +3 -38
  11. package/dist/cjs/plugins/table/transforms/delete-rows.js +1 -1
  12. package/dist/cjs/plugins/table/transforms/index.js +3 -3
  13. package/dist/cjs/plugins/table/transforms/merge.js +39 -54
  14. package/dist/cjs/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +16 -3
  15. package/dist/cjs/plugins/table/ui/FloatingContextualMenu/index.js +6 -3
  16. package/dist/cjs/plugins/table/ui/common-styles.js +2 -2
  17. package/dist/cjs/plugins/table/utils/row-controls.js +3 -2
  18. package/dist/cjs/types/i18n.js +5 -0
  19. package/dist/cjs/version.json +1 -1
  20. package/dist/es2019/i18n/en.js +36 -0
  21. package/dist/es2019/i18n/en_GB.js +36 -0
  22. package/dist/es2019/plugins/table/index.js +2 -1
  23. package/dist/es2019/plugins/table/nodeviews/OverflowShadowsObserver.js +13 -26
  24. package/dist/es2019/plugins/table/nodeviews/TableComponent.js +22 -3
  25. package/dist/es2019/plugins/table/nodeviews/table.js +7 -0
  26. package/dist/es2019/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +32 -17
  27. package/dist/es2019/plugins/table/pm-plugins/table-resizing/event-handlers.js +1 -5
  28. package/dist/es2019/plugins/table/pm-plugins/table-resizing/utils/misc.js +1 -37
  29. package/dist/es2019/plugins/table/transforms/delete-rows.js +2 -2
  30. package/dist/es2019/plugins/table/transforms/index.js +1 -1
  31. package/dist/es2019/plugins/table/transforms/merge.js +39 -43
  32. package/dist/es2019/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +18 -4
  33. package/dist/es2019/plugins/table/ui/FloatingContextualMenu/index.js +6 -3
  34. package/dist/es2019/plugins/table/ui/common-styles.js +21 -1
  35. package/dist/es2019/plugins/table/utils/row-controls.js +3 -2
  36. package/dist/es2019/types/i18n.js +1 -0
  37. package/dist/es2019/version.json +1 -1
  38. package/dist/esm/i18n/en.js +36 -0
  39. package/dist/esm/i18n/en_GB.js +36 -0
  40. package/dist/esm/plugins/table/index.js +2 -1
  41. package/dist/esm/plugins/table/nodeviews/OverflowShadowsObserver.js +13 -26
  42. package/dist/esm/plugins/table/nodeviews/TableComponent.js +18 -3
  43. package/dist/esm/plugins/table/nodeviews/table.js +7 -0
  44. package/dist/esm/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +32 -17
  45. package/dist/esm/plugins/table/pm-plugins/table-resizing/event-handlers.js +1 -5
  46. package/dist/esm/plugins/table/pm-plugins/table-resizing/utils/misc.js +1 -35
  47. package/dist/esm/plugins/table/transforms/delete-rows.js +2 -2
  48. package/dist/esm/plugins/table/transforms/index.js +1 -1
  49. package/dist/esm/plugins/table/transforms/merge.js +38 -53
  50. package/dist/esm/plugins/table/ui/FloatingContextualMenu/ContextualMenu.js +17 -4
  51. package/dist/esm/plugins/table/ui/FloatingContextualMenu/index.js +6 -3
  52. package/dist/esm/plugins/table/ui/common-styles.js +2 -2
  53. package/dist/esm/plugins/table/utils/row-controls.js +3 -2
  54. package/dist/esm/types/i18n.js +1 -0
  55. package/dist/esm/version.json +1 -1
  56. package/dist/types/i18n/en.d.ts +35 -0
  57. package/dist/types/i18n/en_GB.d.ts +35 -0
  58. package/dist/types/plugins/table/nodeviews/OverflowShadowsObserver.d.ts +4 -5
  59. package/dist/types/plugins/table/nodeviews/TableComponent.d.ts +1 -0
  60. package/dist/types/plugins/table/nodeviews/__mocks__/OverflowShadowsObserver.d.ts +3 -3
  61. package/dist/types/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.d.ts +2 -2
  62. package/dist/types/plugins/table/pm-plugins/table-resizing/utils/misc.d.ts +1 -2
  63. package/dist/types/plugins/table/transforms/index.d.ts +1 -1
  64. package/dist/types/plugins/table/transforms/merge.d.ts +1 -1
  65. package/dist/types/plugins/table/types.d.ts +2 -0
  66. package/dist/types/plugins/table/ui/FloatingContextualMenu/ContextualMenu.d.ts +2 -1
  67. package/dist/types/plugins/table/ui/FloatingContextualMenu/index.d.ts +3 -2
  68. package/dist/types/types/i18n.d.ts +5 -0
  69. package/package.json +10 -9
  70. package/src/__tests__/integration/__fixtures__/table-and-paragraph-adf.ts +130 -0
  71. package/src/__tests__/integration/horizontal-scroll-shadows.ts +199 -0
  72. package/src/__tests__/integration/horizontal-scroll.ts +4 -9
  73. package/src/__tests__/integration/meta-arrowup-cursor-in-first-row.ts +4 -2
  74. package/src/__tests__/integration/sticky-header.ts +61 -1
  75. package/src/__tests__/unit/commands/insert.ts +8 -8
  76. package/src/__tests__/unit/commands/sort.ts +4 -0
  77. package/src/__tests__/unit/commands.ts +2 -0
  78. package/src/__tests__/unit/index.ts +2 -0
  79. package/src/__tests__/unit/keymap.ts +4 -2
  80. package/src/__tests__/unit/layout.ts +2 -0
  81. package/src/__tests__/unit/nodeviews/OverflowShadowsObserver.ts +20 -11
  82. package/src/__tests__/unit/nodeviews/cell.ts +14 -0
  83. package/src/__tests__/unit/pm-plugins/main-with-allow-collapse.ts +2 -0
  84. package/src/__tests__/unit/pm-plugins/sticky-headers/tableRow.tsx +2 -2
  85. package/src/__tests__/unit/transforms/delete-rows.ts +45 -0
  86. package/src/__tests__/unit/ui/ContextualMenu.tsx +2 -0
  87. package/src/__tests__/unit/ui/FloatingContextualMenu.tsx +1 -0
  88. package/src/__tests__/unit/utils/collapse.ts +4 -1
  89. package/src/i18n/en.ts +36 -0
  90. package/src/i18n/en_GB.ts +36 -0
  91. package/src/plugins/table/index.tsx +4 -0
  92. package/src/plugins/table/nodeviews/OverflowShadowsObserver.ts +24 -40
  93. package/src/plugins/table/nodeviews/TableComponent.tsx +20 -4
  94. package/src/plugins/table/nodeviews/__mocks__/OverflowShadowsObserver.ts +3 -3
  95. package/src/plugins/table/nodeviews/table.tsx +12 -0
  96. package/src/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.ts +40 -23
  97. package/src/plugins/table/pm-plugins/table-resizing/event-handlers.ts +1 -9
  98. package/src/plugins/table/pm-plugins/table-resizing/utils/misc.ts +1 -57
  99. package/src/plugins/table/transforms/delete-rows.ts +2 -2
  100. package/src/plugins/table/transforms/index.ts +1 -1
  101. package/src/plugins/table/transforms/merge.ts +41 -43
  102. package/src/plugins/table/ui/FloatingContextualMenu/ContextualMenu.tsx +21 -2
  103. package/src/plugins/table/ui/FloatingContextualMenu/index.tsx +8 -1
  104. package/src/plugins/table/ui/common-styles.ts +21 -0
  105. package/src/plugins/table/utils/row-controls.ts +3 -2
  106. package/src/types/i18n.ts +5 -0
  107. package/src/__tests__/integration/__fixtures__/table-and-paragraph-adf.json +0 -130
@@ -55,7 +55,6 @@ import type { EventDispatcher } from '@atlaskit/editor-common/event-dispatcher';
55
55
 
56
56
  import memoizeOne from 'memoize-one';
57
57
  import { OverflowShadowsObserver } from './OverflowShadowsObserver';
58
- import { getParentWidthWithoutPadding } from '../pm-plugins/table-resizing/utils/misc';
59
58
 
60
59
  const isIE11 = browser.ie_version === 11;
61
60
  const NOOP = () => undefined;
@@ -226,9 +225,8 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
226
225
  }
227
226
 
228
227
  if (this.overflowShadowsObserver) {
229
- this.overflowShadowsObserver.observeCells(
228
+ this.overflowShadowsObserver.observeShadowSentinels(
230
229
  this.state.stickyHeader?.sticky,
231
- containsHeaderRow(getNode()),
232
230
  );
233
231
  }
234
232
  }
@@ -288,6 +286,17 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
288
286
  this.setState({ [shadowKey]: value });
289
287
  };
290
288
 
289
+ private createShadowSentinels = (table: HTMLTableElement | null) => {
290
+ if (table) {
291
+ const shadowSentinelLeft = document.createElement('span');
292
+ shadowSentinelLeft.className = ClassName.TABLE_SHADOW_SENTINEL_LEFT;
293
+ const shadowSentinelRight = document.createElement('span');
294
+ shadowSentinelRight.className = ClassName.TABLE_SHADOW_SENTINEL_RIGHT;
295
+ table.prepend(shadowSentinelLeft);
296
+ table.prepend(shadowSentinelRight);
297
+ }
298
+ };
299
+
291
300
  onStickyState = (state: StickyPluginState) => {
292
301
  const { tableOverflowShadowsOptimization } =
293
302
  this.props.getEditorFeatureFlags();
@@ -441,6 +450,7 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
441
450
  const tableElement = elem.querySelector('table');
442
451
  if (tableElement !== this.table) {
443
452
  this.table = tableElement;
453
+ this.createShadowSentinels(this.table);
444
454
  }
445
455
  }
446
456
  }}
@@ -520,6 +530,12 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
520
530
  if (!tableOverflowShadowsOptimization) {
521
531
  this.updateShadows();
522
532
  }
533
+
534
+ if (this.wrapper.scrollLeft === 0) {
535
+ this.setState({ [ShadowEvent.SHOW_BEFORE_SHADOW]: false });
536
+ } else {
537
+ this.setState({ [ShadowEvent.SHOW_BEFORE_SHADOW]: true });
538
+ }
523
539
  };
524
540
 
525
541
  private handleTableResizing = () => {
@@ -689,7 +705,7 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
689
705
  options && options.isFullWidthModeEnabled,
690
706
  );
691
707
 
692
- return getParentWidthWithoutPadding(parentNodeWith, pos, state);
708
+ return parentNodeWith;
693
709
  };
694
710
 
695
711
  private updateParentWidth = (width?: number) => {
@@ -1,13 +1,13 @@
1
1
  import { OverridableMock } from './OverridableMock';
2
2
 
3
3
  export class OverflowShadowsObserver extends OverridableMock {
4
- firstCell: HTMLElement | null = null;
5
- lastCell: HTMLElement | null = null;
4
+ leftShadowSentinel: HTMLElement | null = null;
5
+ rightShadowSentinel: HTMLElement | null = null;
6
6
  constructor(...inputs: any[]) {
7
7
  super(...inputs);
8
8
  }
9
9
 
10
- observeCells = this.getMock('observeCells');
10
+ observeShadowSentinels = this.getMock('observeShadowSentinels');
11
11
  updateStickyShadows = this.getMock('updateStickyShadows');
12
12
  dispose = this.getMock('dispose');
13
13
  }
@@ -225,6 +225,18 @@ export default class TableView extends ReactNodeView<Props> {
225
225
  return false;
226
226
  }
227
227
 
228
+ // ED-16668
229
+ // Do not remove this fixes an issue with windows firefox that relates to
230
+ // the addition of the shadow sentinels
231
+ if (
232
+ type === 'selection' &&
233
+ nodeName?.toUpperCase() === 'TABLE' &&
234
+ (firstChild?.nodeName.toUpperCase() === 'COLGROUP' ||
235
+ firstChild?.nodeName.toUpperCase() === 'SPAN')
236
+ ) {
237
+ return false;
238
+ }
239
+
228
240
  return true;
229
241
  }
230
242
 
@@ -170,7 +170,7 @@ export class TableRowNodeView implements NodeView {
170
170
  this.topPosEditorElement = getTop(this.editorScrollableElement);
171
171
  }
172
172
 
173
- this.eventDispatcher.on('widthPlugin', this.onWidthPluginState);
173
+ this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth);
174
174
 
175
175
  this.eventDispatcher.on(
176
176
  (tablePluginKey as any).key,
@@ -209,7 +209,7 @@ export class TableRowNodeView implements NodeView {
209
209
  this.editorScrollableElement.removeEventListener('scroll', this.onScroll);
210
210
  }
211
211
 
212
- this.eventDispatcher.off('widthPlugin', this.onWidthPluginState);
212
+ this.eventDispatcher.off('widthPlugin', this.updateStickyHeaderWidth);
213
213
  this.eventDispatcher.off(
214
214
  (tablePluginKey as any).key,
215
215
  this.onTablePluginState,
@@ -235,6 +235,9 @@ export class TableRowNodeView implements NodeView {
235
235
  }
236
236
 
237
237
  this.resizeObserver.observe(this.dom);
238
+ if (this.editorScrollableElement) {
239
+ this.resizeObserver.observe(this.editorScrollableElement as HTMLElement);
240
+ }
238
241
 
239
242
  window.requestAnimationFrame(() => {
240
243
  // we expect tree to be defined after animation frame
@@ -268,22 +271,31 @@ export class TableRowNodeView implements NodeView {
268
271
  }
269
272
  const { table } = this.tree;
270
273
  entries.forEach((entry) => {
271
- const newHeight = entry.contentRect
272
- ? entry.contentRect.height
273
- : (entry.target as HTMLElement).offsetHeight;
274
-
274
+ // On resize of the parent scroll element we need to adjust the width
275
+ // of the sticky header
275
276
  if (
276
- this.sentinels.bottom &&
277
- // When the table header is sticky, it would be taller by a 1px (border-bottom),
278
- // So we adding this check to allow a 1px difference.
279
- Math.abs(newHeight - (this.stickyRowHeight || 0)) >
280
- stickyHeaderBorderBottomWidth
277
+ entry.target.className ===
278
+ (this.editorScrollableElement as HTMLElement)?.className
281
279
  ) {
282
- this.stickyRowHeight = newHeight;
283
- this.sentinels.bottom.style.bottom = `${
284
- tableScrollbarOffset + stickyRowOffsetTop + newHeight
285
- }px`;
286
- updateTableMargin(table);
280
+ this.updateStickyHeaderWidth();
281
+ } else {
282
+ const newHeight = entry.contentRect
283
+ ? entry.contentRect.height
284
+ : (entry.target as HTMLElement).offsetHeight;
285
+
286
+ if (
287
+ this.sentinels.bottom &&
288
+ // When the table header is sticky, it would be taller by a 1px (border-bottom),
289
+ // So we adding this check to allow a 1px difference.
290
+ Math.abs(newHeight - (this.stickyRowHeight || 0)) >
291
+ stickyHeaderBorderBottomWidth
292
+ ) {
293
+ this.stickyRowHeight = newHeight;
294
+ this.sentinels.bottom.style.bottom = `${
295
+ tableScrollbarOffset + stickyRowOffsetTop + newHeight
296
+ }px`;
297
+ updateTableMargin(table);
298
+ }
287
299
  }
288
300
  });
289
301
  });
@@ -309,7 +321,8 @@ export class TableRowNodeView implements NodeView {
309
321
  (entry.rootBounds?.bottom || 0) < entry.boundingClientRect.bottom;
310
322
 
311
323
  if (!entry.isIntersecting && !sentinelIsBelowScrollArea) {
312
- this.tree && this.makeHeaderRowSticky(this.tree);
324
+ this.tree &&
325
+ this.makeHeaderRowSticky(this.tree, entry.rootBounds?.top);
313
326
  } else {
314
327
  table && this.makeRowHeaderNotSticky(table);
315
328
  }
@@ -325,7 +338,8 @@ export class TableRowNodeView implements NodeView {
325
338
  if (table && !entry.isIntersecting && sentinelIsAboveScrollArea) {
326
339
  this.makeRowHeaderNotSticky(table);
327
340
  } else if (entry.isIntersecting && sentinelIsAboveScrollArea) {
328
- this.tree && this.makeHeaderRowSticky(this.tree);
341
+ this.tree &&
342
+ this.makeHeaderRowSticky(this.tree, entry?.rootBounds?.top);
329
343
  }
330
344
  }
331
345
  });
@@ -549,7 +563,7 @@ export class TableRowNodeView implements NodeView {
549
563
  }, 0);
550
564
  };
551
565
 
552
- onWidthPluginState = () => {
566
+ updateStickyHeaderWidth = () => {
553
567
  // table width might have changed, sync that back to sticky row
554
568
  const tree = this.tree;
555
569
  if (!tree) {
@@ -591,7 +605,7 @@ export class TableRowNodeView implements NodeView {
591
605
  return false;
592
606
  };
593
607
 
594
- makeHeaderRowSticky = (tree: TableDOMElements) => {
608
+ makeHeaderRowSticky = (tree: TableDOMElements, scrollTop?: number) => {
595
609
  // If header row height is more than 50% of viewport height don't do this
596
610
  if (this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2) {
597
611
  return;
@@ -607,10 +621,13 @@ export class TableRowNodeView implements NodeView {
607
621
  }
608
622
 
609
623
  const currentTableTop = this.getCurrentTableTop(tree);
624
+
625
+ if (!scrollTop) {
626
+ scrollTop = getTop(this.editorScrollableElement);
627
+ }
628
+
610
629
  const domTop =
611
- currentTableTop > 0
612
- ? this.topPosEditorElement
613
- : this.topPosEditorElement + currentTableTop;
630
+ currentTableTop > 0 ? scrollTop : scrollTop + currentTableTop;
614
631
 
615
632
  if (!this.isSticky) {
616
633
  syncStickyRowToTable(table);
@@ -31,7 +31,6 @@ import type {
31
31
  GetEditorContainerWidth,
32
32
  GetEditorFeatureFlags,
33
33
  } from '@atlaskit/editor-common/types';
34
- import { getParentWidthWithoutPadding } from './utils/misc';
35
34
 
36
35
  export const handleMouseDown = (
37
36
  view: EditorView,
@@ -68,15 +67,8 @@ export const handleMouseDown = (
68
67
  const containerWidth = getEditorContainerWidth();
69
68
  const parentWidth = getParentNodeWidth(start, state, containerWidth);
70
69
 
71
- // TODO - refactor this logic into getParentNodeWidth() in editor-common [ED-16718]
72
- const parentActualWidth = getParentWidthWithoutPadding(
73
- parentWidth,
74
- start,
75
- state,
76
- );
77
-
78
70
  let maxSize =
79
- parentActualWidth ||
71
+ parentWidth ||
80
72
  getLayoutSize(
81
73
  dom.getAttribute('data-layout') as TableLayout,
82
74
  containerWidth.width,
@@ -21,8 +21,6 @@ import { Node as PMNode } from 'prosemirror-model';
21
21
  import { EditorState } from 'prosemirror-state';
22
22
  import type { GetEditorContainerWidth } from '@atlaskit/editor-common/types';
23
23
  import { getParentNodeWidth } from '@atlaskit/editor-common/node-width';
24
- import { gridSize } from '@atlaskit/theme/constants';
25
- import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils';
26
24
 
27
25
  export const tableLayoutToSize: Record<string, number> = {
28
26
  default: akEditorDefaultLayoutWidth,
@@ -124,15 +122,7 @@ export const getTableMaxWidth = ({
124
122
  const containerWidth = getEditorContainerWidth();
125
123
  const parentWidth = getParentNodeWidth(tableStart, state, containerWidth);
126
124
 
127
- // TODO - refactor this logic into getParentNodeWidth() in editor-common [ED-16718]
128
- const parentActualWidth = getParentWidthWithoutPadding(
129
- parentWidth,
130
- tableStart,
131
- state,
132
- );
133
-
134
- let maxWidth =
135
- parentActualWidth || getLayoutSize(layout, containerWidth.width, {});
125
+ let maxWidth = parentWidth || getLayoutSize(layout, containerWidth.width, {});
136
126
 
137
127
  if (table.attrs.isNumberColumnEnabled) {
138
128
  maxWidth -= akEditorTableNumberColumnWidth;
@@ -140,49 +130,3 @@ export const getTableMaxWidth = ({
140
130
 
141
131
  return maxWidth;
142
132
  };
143
-
144
- export const getParentWidthWithoutPadding = (
145
- parentWidth: number,
146
- tableStartPos: number,
147
- state: EditorState,
148
- ) => {
149
- const node = getNestedParentNode(tableStartPos, state);
150
- if (!node) {
151
- return;
152
- }
153
-
154
- const { schema } = state;
155
-
156
- if (node.type === schema.nodes.expand) {
157
- // padding
158
- parentWidth -= gridSize() * 2;
159
- // gutter offset
160
- parentWidth += gridSize() * 1.5 * 2;
161
- // padding right
162
- parentWidth -= gridSize();
163
- // padding left
164
- parentWidth -= gridSize() * 4 - gridSize() / 2;
165
- }
166
-
167
- return parentWidth;
168
- };
169
-
170
- // copy of getNestedParentNode() from packages/editor/editor-common/src/node-width/index.ts
171
- // to be removed later when we will move getParentWidthWithoutPadding() logic to editor-common
172
- const getNestedParentNode = (
173
- tablePos: number,
174
- state: EditorState,
175
- ): PMNode | null => {
176
- if (tablePos === undefined) {
177
- return null;
178
- }
179
-
180
- const $pos = state.doc.resolve(tablePos);
181
- const parent = findParentNodeOfTypeClosestToPos($pos, [
182
- state.schema.nodes.bodiedExtension,
183
- state.schema.nodes.layoutSection,
184
- state.schema.nodes.expand,
185
- ]);
186
-
187
- return parent ? parent.node : null;
188
- };
@@ -5,7 +5,7 @@ import { findTable } from '@atlaskit/editor-tables/utils';
5
5
 
6
6
  import { CellAttributes } from '@atlaskit/adf-schema';
7
7
 
8
- import { removeEmptyColumns } from './merge';
8
+ import { mergeEmptyColumns } from './merge';
9
9
  import { setMeta } from './metadata';
10
10
 
11
11
  export const deleteRows =
@@ -125,7 +125,7 @@ export const deleteRows =
125
125
  rows,
126
126
  table.node.marks,
127
127
  );
128
- const fixedTable = removeEmptyColumns(newTable);
128
+ const fixedTable = mergeEmptyColumns(newTable);
129
129
  if (fixedTable === null) {
130
130
  return setMeta({ type: 'DELETE_ROWS', problem: 'REMOVE_EMPTY_COLUMNS' })(
131
131
  tr,
@@ -1,5 +1,5 @@
1
- export { mergeCells, canMergeCells, removeEmptyColumns } from './merge';
2
1
  export { fireAnalytics, fixTables, fixAutoSizedTable } from './fix-tables';
2
+ export { mergeCells, canMergeCells, mergeEmptyColumns } from './merge';
3
3
  export { deleteColumns } from './delete-columns';
4
4
  export { deleteRows } from './delete-rows';
5
5
  export { updateColumnWidths } from './column-width';
@@ -131,7 +131,8 @@ export function mergeCells(tr: Transaction): Transaction {
131
131
  rows,
132
132
  table.node.marks,
133
133
  );
134
- const fixedTable = removeEmptyColumns(newTable);
134
+
135
+ const fixedTable = mergeEmptyColumns(newTable);
135
136
  if (fixedTable === null) {
136
137
  return setMeta({ type: 'MERGE_CELLS', problem: 'REMOVE_EMPTY_COLUMNS' })(
137
138
  tr,
@@ -227,77 +228,74 @@ function cellsOverlapRectangle({ width, height, map }: TableMap, rect: Rect) {
227
228
  }
228
229
 
229
230
  // returns an array of numbers, each number indicates the minimum colSpan in each column
230
- function getMinColSpans(table: PMNode): number[] {
231
+ function getEmptyColumnIndexes(table: PMNode): Set<number> {
231
232
  const map = TableMap.get(table);
232
- const minColspans: number[] = [];
233
- for (let colIndex = map.width - 1; colIndex >= 0; colIndex--) {
234
- const cellsPositions = map.cellsInRect({
233
+ const emptyColumnIndexes = new Set<number>();
234
+
235
+ // Loop throuh each column
236
+ for (let colIndex = 0; colIndex < map.width; colIndex++) {
237
+ // Get the cells in each row for this column
238
+ const cellPositions = map.cellsInRect({
235
239
  left: colIndex,
236
240
  right: colIndex + 1,
237
241
  top: 0,
238
242
  bottom: map.height,
239
243
  });
240
- if (cellsPositions.length) {
241
- const colspans = cellsPositions.map((cellPos) => {
242
- const cell = table.nodeAt(cellPos);
243
- if (cell) {
244
- return cell.attrs.colspan;
245
- }
246
- });
247
- const minColspan = Math.min(...colspans);
248
- // only care about the case when the next column is invisible
249
- if (!minColspans[colIndex + 1]) {
250
- minColspans[colIndex] = minColspan;
251
- } else {
252
- minColspans[colIndex] = 1;
253
- }
244
+
245
+ // If no cells exist in that column it is empty
246
+ if (!cellPositions.length) {
247
+ emptyColumnIndexes.add(colIndex);
254
248
  }
255
249
  }
256
250
 
257
- return minColspans;
251
+ return emptyColumnIndexes;
258
252
  }
259
253
 
260
- export function removeEmptyColumns(table: PMNode): PMNode | null {
254
+ export function mergeEmptyColumns(table: PMNode): PMNode | null {
255
+ const rows: PMNode[] = [];
261
256
  const map = TableMap.get(table);
262
- const minColSpans = getMinColSpans(table);
263
- if (!minColSpans.some((colspan) => colspan > 1)) {
257
+ const emptyColumnIndexes = getEmptyColumnIndexes(table);
258
+
259
+ // We don't need to remove any so return early.
260
+ if (emptyColumnIndexes.size === 0) {
264
261
  return table;
265
262
  }
266
- const rows: PMNode[] = [];
263
+
267
264
  for (let rowIndex = 0; rowIndex < map.height; rowIndex++) {
268
265
  const cellsByCols: Record<string, PMNode> = {};
269
- const cols = Object.keys(minColSpans).map(Number);
270
- for (let idx in cols) {
271
- const colIndex = cols[idx];
266
+
267
+ // Work backwards so that calculating colwidths is easier with Array.slice
268
+ for (let colIndex = map.width - 1; colIndex >= 0; colIndex--) {
272
269
  const cellPos = map.map[colIndex + rowIndex * map.width];
273
270
  const rect = map.findCell(cellPos);
274
- const cell = cellsByCols[rect.left] || table.nodeAt(cellPos);
275
- if (cell && rect.top === rowIndex) {
276
- if (minColSpans[colIndex] > 1) {
277
- const colspan = cell.attrs.colspan - minColSpans[colIndex] + 1;
278
- if (colspan < 1) {
279
- return null;
280
- }
281
- const { colwidth } = cell.attrs;
282
- const newCell = cell.type.createChecked(
271
+ let cell = cellsByCols[rect.left] || table.nodeAt(cellPos);
272
+
273
+ if (rect.top !== rowIndex) {
274
+ continue;
275
+ }
276
+
277
+ // If this column is empty, we want to decrement the colspan of its corresponding
278
+ // cell as this column is being "merged"
279
+ if (emptyColumnIndexes.has(colIndex)) {
280
+ const { colspan, colwidth } = cell.attrs;
281
+
282
+ if (colspan > 1) {
283
+ cell = cell.type.createChecked(
283
284
  {
284
285
  ...cell.attrs,
285
- colspan,
286
+ colspan: colspan - 1,
286
287
  colwidth: colwidth ? colwidth.slice(0, colspan) : null,
287
288
  },
288
289
  cell.content,
289
290
  cell.marks,
290
291
  );
291
- cellsByCols[rect.left] = newCell;
292
- } else {
293
- cellsByCols[rect.left] = cell;
294
292
  }
295
293
  }
294
+
295
+ cellsByCols[rect.left] = cell;
296
296
  }
297
297
 
298
- const rowCells: PMNode[] = Object.keys(cellsByCols).map(
299
- (col) => cellsByCols[col],
300
- );
298
+ const rowCells = Object.values(cellsByCols);
301
299
  const row = table.child(rowIndex);
302
300
  if (row) {
303
301
  rows.push(row.type.createChecked(row.attrs, rowCells, row.marks));
@@ -18,6 +18,7 @@ import {
18
18
  } from '@atlaskit/editor-common/keymaps';
19
19
  import {
20
20
  ColorPalette,
21
+ backgroundPaletteTooltipMessages,
21
22
  cellBackgroundColorPalette,
22
23
  } from '@atlaskit/editor-common/ui-color';
23
24
  import { DropdownMenuSharedCssClassName } from '@atlaskit/editor-common/styles';
@@ -32,7 +33,10 @@ import { cellColourPreviewStyles } from './styles';
32
33
  import { closestElement } from '@atlaskit/editor-common/utils';
33
34
 
34
35
  import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
35
- import type { GetEditorContainerWidth } from '@atlaskit/editor-common/types';
36
+ import type {
37
+ GetEditorContainerWidth,
38
+ GetEditorFeatureFlags,
39
+ } from '@atlaskit/editor-common/types';
36
40
 
37
41
  import type { MenuItem } from '@atlaskit/editor-common/ui-menu';
38
42
  type DropdownItem = MenuItem & {
@@ -131,6 +135,7 @@ export interface Props {
131
135
  offset?: Array<number>;
132
136
  editorAnalyticsAPI?: EditorAnalyticsAPI;
133
137
  getEditorContainerWidth: GetEditorContainerWidth;
138
+ getEditorFeatureFlags: GetEditorFeatureFlags;
134
139
  }
135
140
 
136
141
  export interface State {
@@ -209,9 +214,11 @@ export class ContextualMenu extends Component<
209
214
  selectionRect,
210
215
  intl: { formatMessage },
211
216
  editorView,
217
+ getEditorFeatureFlags,
212
218
  } = this.props;
213
219
  const items: any[] = [];
214
220
  const { isSubmenuOpen } = this.state;
221
+ const { useSomewhatSemanticTextColorNames } = getEditorFeatureFlags();
215
222
  // TargetCellPosition could be outdated: https://product-fabric.atlassian.net/browse/ED-8129
216
223
  const {
217
224
  targetCellPosition,
@@ -239,9 +246,21 @@ export class ContextualMenu extends Component<
239
246
  >
240
247
  <ColorPalette
241
248
  cols={7}
242
- palette={cellBackgroundColorPalette}
243
249
  onClick={this.setColor}
244
250
  selectedColor={background}
251
+ paletteOptions={{
252
+ palette: cellBackgroundColorPalette,
253
+ paletteColorTooltipMessages:
254
+ backgroundPaletteTooltipMessages,
255
+ // We did not want to create new FF or update
256
+ // useSomewhatSemanticTextColorNames name
257
+ // because it is temporary and require extra work.
258
+ // So even though it says text color names,
259
+ // we are going to use for all color pickers
260
+ // such as text, background and table charts.
261
+ showSomewhatSemanticTooltips:
262
+ useSomewhatSemanticTextColorNames,
263
+ }}
245
264
  />
246
265
  </div>
247
266
  )}
@@ -23,7 +23,10 @@ import {
23
23
  import { tablePopupStyles } from './styles';
24
24
  import type { EditorAnalyticsAPI } from '@atlaskit/editor-common/analytics';
25
25
 
26
- import type { GetEditorContainerWidth } from '@atlaskit/editor-common/types';
26
+ import type {
27
+ GetEditorContainerWidth,
28
+ GetEditorFeatureFlags,
29
+ } from '@atlaskit/editor-common/types';
27
30
  import ContextualMenu from './ContextualMenu';
28
31
 
29
32
  // offset of the contextual menu dropdown
@@ -46,6 +49,7 @@ export interface Props {
46
49
  editorView: EditorView;
47
50
  isOpen: boolean;
48
51
  getEditorContainerWidth: GetEditorContainerWidth;
52
+ getEditorFeatureFlags: GetEditorFeatureFlags;
49
53
  targetCellPosition?: number;
50
54
  mountPoint?: HTMLElement;
51
55
  boundariesElement?: HTMLElement;
@@ -63,6 +67,7 @@ const FloatingContextualMenu = ({
63
67
  pluginConfig,
64
68
  editorAnalyticsAPI,
65
69
  getEditorContainerWidth,
70
+ getEditorFeatureFlags,
66
71
  }: Props) => {
67
72
  // TargetCellPosition could be outdated: https://product-fabric.atlassian.net/browse/ED-8129
68
73
  const { targetCellPosition } = getPluginState(editorView.state);
@@ -103,6 +108,7 @@ const FloatingContextualMenu = ({
103
108
  zIndex={akEditorFloatingOverlapPanelZIndex}
104
109
  forcePlacement={true}
105
110
  offset={[-7, 0]}
111
+ stick={true}
106
112
  >
107
113
  <div css={tablePopupStyles}>
108
114
  <ContextualMenu
@@ -120,6 +126,7 @@ const FloatingContextualMenu = ({
120
126
  boundariesElement={boundariesElement}
121
127
  editorAnalyticsAPI={editorAnalyticsAPI}
122
128
  getEditorContainerWidth={getEditorContainerWidth}
129
+ getEditorFeatureFlags={getEditorFeatureFlags}
123
130
  />
124
131
  </div>
125
132
  </Popup>
@@ -113,6 +113,22 @@ const sentinelStyles = `.${ClassName.TABLE_CONTAINER} {
113
113
  }
114
114
  }`;
115
115
 
116
+ const shadowSentinelStyles = `
117
+ .${ClassName.TABLE_SHADOW_SENTINEL_LEFT},
118
+ .${ClassName.TABLE_SHADOW_SENTINEL_RIGHT} {
119
+ position: absolute;
120
+ top: 0;
121
+ height: 100%;
122
+ width: 1px;
123
+ visibility: hidden;
124
+ }
125
+ .${ClassName.TABLE_SHADOW_SENTINEL_LEFT} {
126
+ left: 0;
127
+ }
128
+ .${ClassName.TABLE_SHADOW_SENTINEL_RIGHT} {
129
+ right: 0;
130
+ }
131
+ `;
116
132
  // previous styles to add spacing to numbered lists with
117
133
  // large item markers (e.g. 101, 102, ...) when nested inside tables
118
134
  const listLargeNumericMarkersOldStyles = `
@@ -624,6 +640,8 @@ export const tableStyles = (
624
640
  border-top: none;
625
641
  // 1px border width offset added here to prevent unwanted overflow and scolling - ED-16212
626
642
  margin-right: -1px;
643
+ // Allows better positioning for the shadow sentinels - ED-16668
644
+ position: relative;
627
645
 
628
646
  > tbody > tr {
629
647
  white-space: pre-wrap;
@@ -717,6 +735,7 @@ export const tableStyles = (
717
735
  padding-bottom: 0px;
718
736
  /* fixes gap cursor height */
719
737
  overflow: auto;
738
+ overflow-y: hidden;
720
739
  position: relative;
721
740
  }
722
741
  }
@@ -740,6 +759,8 @@ export const tableStyles = (
740
759
  ${props?.featureFlags?.restartNumberedLists
741
760
  ? ``
742
761
  : listLargeNumericMarkersOldStyles}
762
+
763
+ ${shadowSentinelStyles}
743
764
  `;
744
765
 
745
766
  export const tableFullPageEditorStyles = css`
@@ -22,8 +22,9 @@ export interface RowParams {
22
22
 
23
23
  export const getRowHeights = (tableRef: HTMLTableElement): number[] => {
24
24
  const heights: number[] = [];
25
- if (tableRef.lastChild) {
26
- const rows = tableRef.lastChild.childNodes;
25
+ const tableBody = tableRef.querySelector('tbody');
26
+ if (tableBody) {
27
+ const rows = tableBody.childNodes;
27
28
  for (let i = 0, count = rows.length; i < count; i++) {
28
29
  const row = rows[i] as HTMLTableRowElement;
29
30
  heights[i] = row.getBoundingClientRect().height + 1;
@@ -0,0 +1,5 @@
1
+ export interface MessageDescriptor {
2
+ id: string;
3
+ description: string;
4
+ defaultMessage: string;
5
+ }