@atlaskit/editor-plugin-table 1.2.1 → 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 (67) hide show
  1. package/CHANGELOG.md +9 -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/nodeviews/OverflowShadowsObserver.js +13 -26
  5. package/dist/cjs/plugins/table/nodeviews/TableComponent.js +17 -1
  6. package/dist/cjs/plugins/table/nodeviews/table.js +7 -0
  7. package/dist/cjs/plugins/table/transforms/delete-rows.js +1 -1
  8. package/dist/cjs/plugins/table/transforms/index.js +3 -3
  9. package/dist/cjs/plugins/table/transforms/merge.js +39 -54
  10. package/dist/cjs/plugins/table/ui/common-styles.js +2 -2
  11. package/dist/cjs/plugins/table/utils/row-controls.js +3 -2
  12. package/dist/cjs/version.json +1 -1
  13. package/dist/es2019/i18n/en.js +36 -0
  14. package/dist/es2019/i18n/en_GB.js +36 -0
  15. package/dist/es2019/plugins/table/nodeviews/OverflowShadowsObserver.js +13 -26
  16. package/dist/es2019/plugins/table/nodeviews/TableComponent.js +21 -1
  17. package/dist/es2019/plugins/table/nodeviews/table.js +7 -0
  18. package/dist/es2019/plugins/table/transforms/delete-rows.js +2 -2
  19. package/dist/es2019/plugins/table/transforms/index.js +1 -1
  20. package/dist/es2019/plugins/table/transforms/merge.js +39 -43
  21. package/dist/es2019/plugins/table/ui/common-styles.js +20 -1
  22. package/dist/es2019/plugins/table/utils/row-controls.js +3 -2
  23. package/dist/es2019/version.json +1 -1
  24. package/dist/esm/i18n/en.js +36 -0
  25. package/dist/esm/i18n/en_GB.js +36 -0
  26. package/dist/esm/plugins/table/nodeviews/OverflowShadowsObserver.js +13 -26
  27. package/dist/esm/plugins/table/nodeviews/TableComponent.js +17 -1
  28. package/dist/esm/plugins/table/nodeviews/table.js +7 -0
  29. package/dist/esm/plugins/table/transforms/delete-rows.js +2 -2
  30. package/dist/esm/plugins/table/transforms/index.js +1 -1
  31. package/dist/esm/plugins/table/transforms/merge.js +38 -53
  32. package/dist/esm/plugins/table/ui/common-styles.js +2 -2
  33. package/dist/esm/plugins/table/utils/row-controls.js +3 -2
  34. package/dist/esm/version.json +1 -1
  35. package/dist/types/i18n/en.d.ts +35 -0
  36. package/dist/types/i18n/en_GB.d.ts +35 -0
  37. package/dist/types/plugins/table/nodeviews/OverflowShadowsObserver.d.ts +4 -5
  38. package/dist/types/plugins/table/nodeviews/TableComponent.d.ts +1 -0
  39. package/dist/types/plugins/table/nodeviews/__mocks__/OverflowShadowsObserver.d.ts +3 -3
  40. package/dist/types/plugins/table/transforms/index.d.ts +1 -1
  41. package/dist/types/plugins/table/transforms/merge.d.ts +1 -1
  42. package/dist/types/plugins/table/types.d.ts +2 -0
  43. package/package.json +7 -6
  44. package/src/__tests__/integration/__fixtures__/table-and-paragraph-adf.ts +130 -0
  45. package/src/__tests__/integration/horizontal-scroll-shadows.ts +199 -0
  46. package/src/__tests__/integration/meta-arrowup-cursor-in-first-row.ts +4 -2
  47. package/src/__tests__/unit/commands/sort.ts +4 -0
  48. package/src/__tests__/unit/commands.ts +2 -0
  49. package/src/__tests__/unit/index.ts +2 -0
  50. package/src/__tests__/unit/keymap.ts +4 -2
  51. package/src/__tests__/unit/layout.ts +2 -0
  52. package/src/__tests__/unit/nodeviews/OverflowShadowsObserver.ts +20 -11
  53. package/src/__tests__/unit/pm-plugins/main-with-allow-collapse.ts +2 -0
  54. package/src/__tests__/unit/transforms/delete-rows.ts +45 -0
  55. package/src/__tests__/unit/utils/collapse.ts +4 -1
  56. package/src/i18n/en.ts +36 -0
  57. package/src/i18n/en_GB.ts +36 -0
  58. package/src/plugins/table/nodeviews/OverflowShadowsObserver.ts +24 -40
  59. package/src/plugins/table/nodeviews/TableComponent.tsx +19 -2
  60. package/src/plugins/table/nodeviews/__mocks__/OverflowShadowsObserver.ts +3 -3
  61. package/src/plugins/table/nodeviews/table.tsx +12 -0
  62. package/src/plugins/table/transforms/delete-rows.ts +2 -2
  63. package/src/plugins/table/transforms/index.ts +1 -1
  64. package/src/plugins/table/transforms/merge.ts +41 -43
  65. package/src/plugins/table/ui/common-styles.ts +20 -0
  66. package/src/plugins/table/utils/row-controls.ts +3 -2
  67. package/src/__tests__/integration/__fixtures__/table-and-paragraph-adf.json +0 -130
@@ -225,9 +225,8 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
225
225
  }
226
226
 
227
227
  if (this.overflowShadowsObserver) {
228
- this.overflowShadowsObserver.observeCells(
228
+ this.overflowShadowsObserver.observeShadowSentinels(
229
229
  this.state.stickyHeader?.sticky,
230
- containsHeaderRow(getNode()),
231
230
  );
232
231
  }
233
232
  }
@@ -287,6 +286,17 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
287
286
  this.setState({ [shadowKey]: value });
288
287
  };
289
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
+
290
300
  onStickyState = (state: StickyPluginState) => {
291
301
  const { tableOverflowShadowsOptimization } =
292
302
  this.props.getEditorFeatureFlags();
@@ -440,6 +450,7 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
440
450
  const tableElement = elem.querySelector('table');
441
451
  if (tableElement !== this.table) {
442
452
  this.table = tableElement;
453
+ this.createShadowSentinels(this.table);
443
454
  }
444
455
  }
445
456
  }}
@@ -519,6 +530,12 @@ class TableComponent extends React.Component<ComponentProps, TableState> {
519
530
  if (!tableOverflowShadowsOptimization) {
520
531
  this.updateShadows();
521
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
+ }
522
539
  };
523
540
 
524
541
  private handleTableResizing = () => {
@@ -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
 
@@ -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));
@@ -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;
@@ -741,6 +759,8 @@ export const tableStyles = (
741
759
  ${props?.featureFlags?.restartNumberedLists
742
760
  ? ``
743
761
  : listLargeNumericMarkersOldStyles}
762
+
763
+ ${shadowSentinelStyles}
744
764
  `;
745
765
 
746
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;
@@ -1,130 +0,0 @@
1
- {
2
- "version": 1,
3
- "type": "doc",
4
- "content": [
5
- {
6
- "type": "table",
7
- "attrs": {
8
- "isNumberColumnEnabled": false,
9
- "layout": "default",
10
- "localId": "e1365400-6226-4bb9-b3f1-b80fd3ae640a"
11
- },
12
- "content": [
13
- {
14
- "type": "tableRow",
15
- "content": [
16
- {
17
- "type": "tableHeader",
18
- "attrs": {},
19
- "content": [
20
- {
21
- "type": "paragraph",
22
- "content": []
23
- }
24
- ]
25
- },
26
- {
27
- "type": "tableHeader",
28
- "attrs": {},
29
- "content": [
30
- {
31
- "type": "paragraph",
32
- "content": []
33
- }
34
- ]
35
- },
36
- {
37
- "type": "tableHeader",
38
- "attrs": {},
39
- "content": [
40
- {
41
- "type": "paragraph",
42
- "content": []
43
- }
44
- ]
45
- }
46
- ]
47
- },
48
- {
49
- "type": "tableRow",
50
- "content": [
51
- {
52
- "type": "tableCell",
53
- "attrs": {},
54
- "content": [
55
- {
56
- "type": "paragraph",
57
- "content": []
58
- }
59
- ]
60
- },
61
- {
62
- "type": "tableCell",
63
- "attrs": {},
64
- "content": [
65
- {
66
- "type": "paragraph",
67
- "content": []
68
- }
69
- ]
70
- },
71
- {
72
- "type": "tableCell",
73
- "attrs": {},
74
- "content": [
75
- {
76
- "type": "paragraph",
77
- "content": []
78
- }
79
- ]
80
- }
81
- ]
82
- },
83
- {
84
- "type": "tableRow",
85
- "content": [
86
- {
87
- "type": "tableCell",
88
- "attrs": {},
89
- "content": [
90
- {
91
- "type": "paragraph",
92
- "content": []
93
- }
94
- ]
95
- },
96
- {
97
- "type": "tableCell",
98
- "attrs": {},
99
- "content": [
100
- {
101
- "type": "paragraph",
102
- "content": []
103
- }
104
- ]
105
- },
106
- {
107
- "type": "tableCell",
108
- "attrs": {},
109
- "content": [
110
- {
111
- "type": "paragraph",
112
- "content": []
113
- }
114
- ]
115
- }
116
- ]
117
- }
118
- ]
119
- },
120
- {
121
- "type": "paragraph",
122
- "content": [
123
- {
124
- "type": "text",
125
- "text": "text"
126
- }
127
- ]
128
- }
129
- ]
130
- }