@atlaskit/editor-plugin-table 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/plugins/table/index.js +3 -2
- package/dist/cjs/plugins/table/nodeviews/TableCell.js +111 -0
- package/dist/cjs/plugins/table/nodeviews/TableNodeViewBase.js +30 -0
- package/dist/cjs/plugins/table/{pm-plugins/sticky-headers/nodeviews/tableRow.js → nodeviews/TableRow.js} +313 -307
- package/dist/cjs/plugins/table/pm-plugins/main.js +16 -13
- package/dist/cjs/plugins/table/pm-plugins/sticky-headers/index.js +1 -8
- package/dist/cjs/plugins/table/pm-plugins/sticky-headers/plugin.js +1 -9
- package/dist/cjs/plugins/table/utils/dom.js +31 -1
- package/dist/cjs/plugins/table/utils/index.js +12 -0
- package/dist/cjs/plugins/table/utils/nodes.js +31 -7
- package/dist/es2019/plugins/table/index.js +3 -2
- package/dist/es2019/plugins/table/nodeviews/{tableCell.js → TableCell.js} +28 -24
- package/dist/es2019/plugins/table/nodeviews/TableNodeViewBase.js +22 -0
- package/dist/es2019/plugins/table/{pm-plugins/sticky-headers/nodeviews/tableRow.js → nodeviews/TableRow.js} +273 -284
- package/dist/es2019/plugins/table/pm-plugins/main.js +8 -8
- package/dist/es2019/plugins/table/pm-plugins/sticky-headers/index.js +1 -2
- package/dist/es2019/plugins/table/pm-plugins/sticky-headers/plugin.js +1 -9
- package/dist/es2019/plugins/table/utils/dom.js +30 -0
- package/dist/es2019/plugins/table/utils/index.js +1 -1
- package/dist/es2019/plugins/table/utils/nodes.js +16 -0
- package/dist/esm/plugins/table/index.js +3 -2
- package/dist/esm/plugins/table/nodeviews/TableCell.js +105 -0
- package/dist/esm/plugins/table/nodeviews/TableNodeViewBase.js +24 -0
- package/dist/esm/plugins/table/{pm-plugins/sticky-headers/nodeviews/tableRow.js → nodeviews/TableRow.js} +314 -307
- package/dist/esm/plugins/table/pm-plugins/main.js +16 -13
- package/dist/esm/plugins/table/pm-plugins/sticky-headers/index.js +1 -2
- package/dist/esm/plugins/table/pm-plugins/sticky-headers/plugin.js +1 -9
- package/dist/esm/plugins/table/utils/dom.js +30 -0
- package/dist/esm/plugins/table/utils/index.js +1 -1
- package/dist/esm/plugins/table/utils/nodes.js +24 -0
- package/dist/types/plugins/table/nodeviews/TableCell.d.ts +13 -0
- package/dist/types/plugins/table/nodeviews/TableNodeViewBase.d.ts +18 -0
- package/dist/types/plugins/table/nodeviews/TableRow.d.ts +60 -0
- package/dist/types/plugins/table/pm-plugins/main.d.ts +1 -1
- package/dist/types/plugins/table/pm-plugins/sticky-headers/index.d.ts +0 -1
- package/dist/types/plugins/table/pm-plugins/sticky-headers/plugin.d.ts +1 -1
- package/dist/types/plugins/table/types.d.ts +1 -0
- package/dist/types/plugins/table/utils/dom.d.ts +6 -0
- package/dist/types/plugins/table/utils/index.d.ts +1 -1
- package/dist/types/plugins/table/utils/nodes.d.ts +12 -2
- package/dist/types-ts4.5/plugins/table/nodeviews/TableCell.d.ts +13 -0
- package/dist/types-ts4.5/plugins/table/nodeviews/TableNodeViewBase.d.ts +18 -0
- package/dist/types-ts4.5/plugins/table/nodeviews/TableRow.d.ts +60 -0
- package/dist/types-ts4.5/plugins/table/pm-plugins/main.d.ts +1 -1
- package/dist/types-ts4.5/plugins/table/pm-plugins/sticky-headers/index.d.ts +0 -1
- package/dist/types-ts4.5/plugins/table/pm-plugins/sticky-headers/plugin.d.ts +1 -1
- package/dist/types-ts4.5/plugins/table/types.d.ts +1 -0
- package/dist/types-ts4.5/plugins/table/utils/dom.d.ts +6 -0
- package/dist/types-ts4.5/plugins/table/utils/index.d.ts +1 -1
- package/dist/types-ts4.5/plugins/table/utils/nodes.d.ts +12 -2
- package/package.json +1 -1
- package/src/__tests__/unit/nodeviews/cell.ts +2 -2
- package/src/__tests__/unit/pm-plugins/sticky-headers/tableRow.tsx +25 -148
- package/src/plugins/table/index.tsx +2 -0
- package/src/plugins/table/nodeviews/{tableCell.tsx → TableCell.ts} +41 -46
- package/src/plugins/table/nodeviews/TableNodeViewBase.ts +32 -0
- package/src/plugins/table/{pm-plugins/sticky-headers/nodeviews/tableRow.ts → nodeviews/TableRow.ts} +193 -246
- package/src/plugins/table/pm-plugins/main.ts +10 -19
- package/src/plugins/table/pm-plugins/sticky-headers/index.ts +0 -1
- package/src/plugins/table/pm-plugins/sticky-headers/plugin.ts +1 -9
- package/src/plugins/table/types.ts +2 -0
- package/src/plugins/table/utils/dom.ts +38 -0
- package/src/plugins/table/utils/index.ts +2 -0
- package/src/plugins/table/utils/nodes.ts +30 -2
- package/dist/cjs/plugins/table/nodeviews/tableCell.js +0 -99
- package/dist/cjs/plugins/table/pm-plugins/sticky-headers/nodeviews/dom.js +0 -35
- package/dist/es2019/plugins/table/pm-plugins/sticky-headers/nodeviews/dom.js +0 -29
- package/dist/esm/plugins/table/nodeviews/tableCell.js +0 -93
- package/dist/esm/plugins/table/pm-plugins/sticky-headers/nodeviews/dom.js +0 -29
- package/dist/types/plugins/table/nodeviews/tableCell.d.ts +0 -17
- package/dist/types/plugins/table/pm-plugins/sticky-headers/nodeviews/dom.d.ts +0 -6
- package/dist/types/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.d.ts +0 -73
- package/dist/types-ts4.5/plugins/table/nodeviews/tableCell.d.ts +0 -17
- package/dist/types-ts4.5/plugins/table/pm-plugins/sticky-headers/nodeviews/dom.d.ts +0 -6
- package/dist/types-ts4.5/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.d.ts +0 -73
- package/src/plugins/table/pm-plugins/sticky-headers/nodeviews/dom.ts +0 -37
|
@@ -2,13 +2,16 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
import debounce from 'lodash/debounce';
|
|
3
3
|
import throttle from 'lodash/throttle';
|
|
4
4
|
import { findOverflowScrollParent } from '@atlaskit/editor-common/ui';
|
|
5
|
-
import { browser
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { syncStickyRowToTable, updateStickyMargins as updateTableMargin } from '
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
5
|
+
import { browser } from '@atlaskit/editor-common/utils';
|
|
6
|
+
import { getPluginState } from '../pm-plugins/plugin-factory';
|
|
7
|
+
import { pluginKey as tablePluginKey } from '../pm-plugins/plugin-key';
|
|
8
|
+
import { updateStickyState } from '../pm-plugins/sticky-headers/commands';
|
|
9
|
+
import { syncStickyRowToTable, updateStickyMargins as updateTableMargin } from '../pm-plugins/table-resizing/utils/dom';
|
|
10
|
+
import { TableCssClassName as ClassName, TableCssClassName } from '../types';
|
|
11
|
+
import { STICKY_HEADER_TOGGLE_TOLERANCE_MS, stickyHeaderBorderBottomWidth, stickyRowOffsetTop, tableControlsSpacing, tableScrollbarOffset } from '../ui/consts';
|
|
12
|
+
import { getTop, getTree } from '../utils/dom';
|
|
13
|
+
import { supportedHeaderRow } from '../utils/nodes';
|
|
14
|
+
import TableNodeView from './TableNodeViewBase';
|
|
12
15
|
|
|
13
16
|
// limit scroll event calls
|
|
14
17
|
const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
|
|
@@ -16,33 +19,19 @@ const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
|
|
|
16
19
|
// timeout for resetting the scroll class - if it’s too long then users won’t be able to click on the header cells,
|
|
17
20
|
// if too short it would trigger too many dom updates.
|
|
18
21
|
const HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Check if a given node is a header row with this definition:
|
|
23
|
-
* - all children are tableHeader cells
|
|
24
|
-
* - no table cells have been have merged with other table row cells
|
|
25
|
-
*
|
|
26
|
-
* @param node ProseMirror node
|
|
27
|
-
* @return boolean if it meets definition
|
|
28
|
-
*/
|
|
29
|
-
export const supportedHeaderRow = node => {
|
|
30
|
-
const allHeaders = mapChildren(node, child => child.type.name === 'tableHeader').every(Boolean);
|
|
31
|
-
const someMerged = anyChildCellMergedAcrossRow(node);
|
|
32
|
-
return allHeaders && !someMerged;
|
|
33
|
-
};
|
|
34
|
-
export class TableRowNodeView {
|
|
35
|
-
get tree() {
|
|
36
|
-
return getTree(this.dom);
|
|
37
|
-
}
|
|
22
|
+
export default class TableRow extends TableNodeView {
|
|
38
23
|
constructor(node, view, getPos, eventDispatcher) {
|
|
39
|
-
|
|
24
|
+
super(node, view, getPos, eventDispatcher);
|
|
40
25
|
_defineProperty(this, "colControlsOffset", 0);
|
|
41
26
|
_defineProperty(this, "focused", false);
|
|
42
27
|
_defineProperty(this, "topPosEditorElement", 0);
|
|
43
28
|
_defineProperty(this, "sentinels", {});
|
|
44
|
-
/* external events */
|
|
45
29
|
_defineProperty(this, "listening", false);
|
|
30
|
+
_defineProperty(this, "padding", 0);
|
|
31
|
+
_defineProperty(this, "top", 0);
|
|
32
|
+
/**
|
|
33
|
+
* Methods
|
|
34
|
+
*/
|
|
46
35
|
_defineProperty(this, "headerRowMouseScrollEnd", debounce(() => {
|
|
47
36
|
this.dom.classList.remove('no-pointer-events');
|
|
48
37
|
}, HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT));
|
|
@@ -55,215 +44,92 @@ export class TableRowNodeView {
|
|
|
55
44
|
this.headerRowMouseScrollEnd();
|
|
56
45
|
}
|
|
57
46
|
}, HEADER_ROW_SCROLL_THROTTLE_TIMEOUT));
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
this.isHeaderRow = supportedHeaderRow(node);
|
|
48
|
+
this.isSticky = false;
|
|
49
|
+
const {
|
|
50
|
+
pluginConfig,
|
|
51
|
+
isDragAndDropEnabled
|
|
52
|
+
} = getPluginState(view.state);
|
|
53
|
+
this.isStickyHeaderEnabled = !!pluginConfig.stickyHeaders;
|
|
54
|
+
this.isDragAndDropEnabled = !!isDragAndDropEnabled;
|
|
55
|
+
if (this.isHeaderRow) {
|
|
56
|
+
this.dom.setAttribute('data-header-row', 'true');
|
|
57
|
+
if (this.isStickyHeaderEnabled) {
|
|
58
|
+
this.subscribe();
|
|
64
59
|
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (el) {
|
|
70
|
-
delete el.dataset.isObserved;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
const isCurrentTableSelected = tableRef === tree.table;
|
|
63
|
+
/**
|
|
64
|
+
* Variables
|
|
65
|
+
*/
|
|
75
66
|
|
|
76
|
-
|
|
77
|
-
if (isCurrentTableSelected && !state.isHeaderRowEnabled && this.tree) {
|
|
78
|
-
this.makeRowHeaderNotSticky(this.tree.table);
|
|
79
|
-
}
|
|
80
|
-
this.focused = isCurrentTableSelected;
|
|
81
|
-
const {
|
|
82
|
-
wrapper
|
|
83
|
-
} = tree;
|
|
84
|
-
const tableContainer = wrapper.parentElement;
|
|
85
|
-
const tableContentWrapper = tableContainer.parentElement;
|
|
86
|
-
const layoutContainer = tableContentWrapper && tableContentWrapper.parentElement;
|
|
87
|
-
if (isCurrentTableSelected) {
|
|
88
|
-
this.colControlsOffset = tableControlsSpacing;
|
|
89
|
-
if (layoutContainer && layoutContainer.getAttribute('data-layout-content')) {
|
|
90
|
-
// move table a little out of the way
|
|
91
|
-
// to provide spacing for table controls
|
|
92
|
-
tableContentWrapper.style.paddingLeft = '11px';
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
this.colControlsOffset = 0;
|
|
96
|
-
if (layoutContainer && layoutContainer.getAttribute('data-layout-content')) {
|
|
97
|
-
tableContentWrapper.style.removeProperty('padding-left');
|
|
98
|
-
}
|
|
99
|
-
}
|
|
67
|
+
// @ts-ignore
|
|
100
68
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (!tree) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
syncStickyRowToTable(tree.table);
|
|
113
|
-
});
|
|
114
|
-
_defineProperty(this, "shouldHeaderStick", tree => {
|
|
115
|
-
const {
|
|
116
|
-
wrapper
|
|
117
|
-
} = tree;
|
|
118
|
-
const tableWrapperRect = wrapper.getBoundingClientRect();
|
|
119
|
-
const editorAreaRect = this.editorScrollableElement.getBoundingClientRect();
|
|
120
|
-
const stickyHeaderRect = this.contentDOM.getBoundingClientRect();
|
|
121
|
-
const firstHeaderRow = !this.dom.previousElementSibling;
|
|
122
|
-
const subsequentRows = !!this.dom.nextElementSibling;
|
|
123
|
-
const isHeaderValid = firstHeaderRow && subsequentRows;
|
|
69
|
+
/**
|
|
70
|
+
* Methods: Nodeview Lifecycle
|
|
71
|
+
*/
|
|
72
|
+
update(node, ..._args) {
|
|
73
|
+
// do nothing if nodes were identical
|
|
74
|
+
if (node === this.node) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
124
77
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
78
|
+
// see if we're changing into a header row or
|
|
79
|
+
// changing away from one
|
|
80
|
+
const newNodeIsHeaderRow = supportedHeaderRow(node);
|
|
81
|
+
if (this.isHeaderRow !== newNodeIsHeaderRow) {
|
|
82
|
+
return false; // re-create nodeview
|
|
83
|
+
}
|
|
131
84
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
85
|
+
// node is different but no need to re-create nodeview
|
|
86
|
+
this.node = node;
|
|
136
87
|
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
});
|
|
154
|
-
_defineProperty(this, "makeHeaderRowSticky", (tree, scrollTop) => {
|
|
155
|
-
var _tbody$firstChild;
|
|
156
|
-
// If header row height is more than 50% of viewport height don't do this
|
|
157
|
-
if (this.isSticky || this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
const {
|
|
161
|
-
table,
|
|
162
|
-
wrapper
|
|
163
|
-
} = tree;
|
|
88
|
+
// don't do anything if we're just a regular tr
|
|
89
|
+
if (!this.isHeaderRow) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
164
92
|
|
|
165
|
-
|
|
93
|
+
// something changed, sync widths
|
|
94
|
+
if (this.isStickyHeaderEnabled) {
|
|
166
95
|
const tbody = this.dom.parentElement;
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
syncStickyRowToTable(table);
|
|
179
|
-
this.dom.classList.add('sticky');
|
|
180
|
-
table.classList.add(ClassName.TABLE_STICKY);
|
|
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);
|
|
196
|
-
}
|
|
197
|
-
this.dom.style.top = `${domTop}px`;
|
|
198
|
-
updateTableMargin(table);
|
|
199
|
-
this.dom.scrollLeft = wrapper.scrollLeft;
|
|
200
|
-
this.emitOn(domTop, this.colControlsOffset);
|
|
201
|
-
});
|
|
202
|
-
_defineProperty(this, "makeRowHeaderNotSticky", (table, isEditorDestroyed = false) => {
|
|
203
|
-
if (!this.isSticky || !table || !this.dom) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
this.dom.style.removeProperty('width');
|
|
207
|
-
this.dom.classList.remove('sticky');
|
|
208
|
-
table.classList.remove(ClassName.TABLE_STICKY);
|
|
209
|
-
this.isSticky = false;
|
|
210
|
-
this.dom.style.top = '';
|
|
211
|
-
table.style.removeProperty('margin-top');
|
|
212
|
-
this.emitOff(isEditorDestroyed);
|
|
213
|
-
});
|
|
214
|
-
_defineProperty(this, "getWrapperoffset", (inverse = false) => {
|
|
215
|
-
const focusValue = inverse ? !this.focused : this.focused;
|
|
216
|
-
return focusValue ? 0 : tableControlsSpacing;
|
|
217
|
-
});
|
|
218
|
-
_defineProperty(this, "getWrapperRefTop", wrapper => Math.round(getTop(wrapper)) + this.getWrapperoffset());
|
|
219
|
-
// TODO: rename!
|
|
220
|
-
_defineProperty(this, "getScrolledTableTop", wrapper => this.getWrapperRefTop(wrapper) - this.topPosEditorElement);
|
|
221
|
-
_defineProperty(this, "getCurrentTableTop", tree => this.getScrolledTableTop(tree.wrapper) + tree.table.clientHeight);
|
|
222
|
-
/* emit external events */
|
|
223
|
-
_defineProperty(this, "padding", 0);
|
|
224
|
-
_defineProperty(this, "top", 0);
|
|
225
|
-
_defineProperty(this, "emitOn", (top, padding) => {
|
|
226
|
-
if (top === this.top && padding === this.padding) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
this.top = top;
|
|
230
|
-
this.padding = padding;
|
|
231
|
-
updateStickyState({
|
|
232
|
-
pos: this.getPos(),
|
|
233
|
-
top,
|
|
234
|
-
sticky: true,
|
|
235
|
-
padding
|
|
236
|
-
})(this.view.state, this.view.dispatch, this.view);
|
|
237
|
-
});
|
|
238
|
-
_defineProperty(this, "emitOff", isEditorDestroyed => {
|
|
239
|
-
if (this.top === 0 && this.padding === 0) {
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
this.top = 0;
|
|
243
|
-
this.padding = 0;
|
|
244
|
-
if (!isEditorDestroyed) {
|
|
245
|
-
updateStickyState({
|
|
246
|
-
pos: this.getPos(),
|
|
247
|
-
sticky: false,
|
|
248
|
-
top: this.top,
|
|
249
|
-
padding: this.padding
|
|
250
|
-
})(this.view.state, this.view.dispatch, this.view);
|
|
96
|
+
const table = tbody && tbody.parentElement;
|
|
97
|
+
syncStickyRowToTable(table);
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
destroy() {
|
|
102
|
+
if (this.isStickyHeaderEnabled) {
|
|
103
|
+
this.unsubscribe();
|
|
104
|
+
const tree = getTree(this.dom);
|
|
105
|
+
if (tree) {
|
|
106
|
+
this.makeRowHeaderNotSticky(tree.table, true);
|
|
251
107
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
108
|
+
this.emitOff(true);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
ignoreMutation(mutationRecord) {
|
|
112
|
+
/* tableRows are not directly editable by the user
|
|
113
|
+
* so it should be safe to ignore mutations that we cause
|
|
114
|
+
* by updating styles and classnames on this DOM element
|
|
115
|
+
*
|
|
116
|
+
* Update: should not ignore mutations for row selection to avoid known issue with table selection highlight in firefox
|
|
117
|
+
* Related bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=1289673
|
|
118
|
+
* */
|
|
119
|
+
const isTableSelection = mutationRecord.type === 'selection' && mutationRecord.target.nodeName === 'TR';
|
|
120
|
+
/**
|
|
121
|
+
* Update: should not ignore mutations when an node is added, as this interferes with
|
|
122
|
+
* prosemirrors handling of some language inputs in Safari (ie. Pinyin, Hiragana).
|
|
123
|
+
*
|
|
124
|
+
* In paticular, when a composition occurs at the start of the first node inside a table cell, if the resulting mutation
|
|
125
|
+
* from the composition end is ignored than prosemirror will end up with; invalid table markup nesting and a misplaced
|
|
126
|
+
* selection and insertion.
|
|
127
|
+
*/
|
|
128
|
+
const isNodeInsertion = mutationRecord.type === 'childList' && mutationRecord.target.nodeName === 'TR' && mutationRecord.addedNodes.length;
|
|
129
|
+
if (isTableSelection || isNodeInsertion) {
|
|
130
|
+
return false;
|
|
266
131
|
}
|
|
132
|
+
return true;
|
|
267
133
|
}
|
|
268
134
|
subscribe() {
|
|
269
135
|
this.editorScrollableElement = findOverflowScrollParent(this.view.dom) || window;
|
|
@@ -271,8 +137,8 @@ export class TableRowNodeView {
|
|
|
271
137
|
this.initObservers();
|
|
272
138
|
this.topPosEditorElement = getTop(this.editorScrollableElement);
|
|
273
139
|
}
|
|
274
|
-
this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth);
|
|
275
|
-
this.eventDispatcher.on(tablePluginKey.key, this.onTablePluginState);
|
|
140
|
+
this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth.bind(this));
|
|
141
|
+
this.eventDispatcher.on(tablePluginKey.key, this.onTablePluginState.bind(this));
|
|
276
142
|
this.listening = true;
|
|
277
143
|
this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
|
|
278
144
|
passive: true
|
|
@@ -321,9 +187,9 @@ export class TableRowNodeView {
|
|
|
321
187
|
this.resizeObserver.observe(this.editorScrollableElement);
|
|
322
188
|
}
|
|
323
189
|
window.requestAnimationFrame(() => {
|
|
324
|
-
var
|
|
190
|
+
var _getTree;
|
|
325
191
|
// we expect tree to be defined after animation frame
|
|
326
|
-
const tableContainer = (
|
|
192
|
+
const tableContainer = (_getTree = getTree(this.dom)) === null || _getTree === void 0 ? void 0 : _getTree.wrapper.closest(`.${TableCssClassName.NODEVIEW_WRAPPER}`);
|
|
327
193
|
if (tableContainer) {
|
|
328
194
|
this.sentinels.top = tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_TOP).item(0);
|
|
329
195
|
this.sentinels.bottom = tableContainer.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM).item(0);
|
|
@@ -342,17 +208,18 @@ export class TableRowNodeView {
|
|
|
342
208
|
// to allocate for new header height
|
|
343
209
|
createResizeObserver() {
|
|
344
210
|
this.resizeObserver = new ResizeObserver(entries => {
|
|
345
|
-
|
|
211
|
+
const tree = getTree(this.dom);
|
|
212
|
+
if (!tree) {
|
|
346
213
|
return;
|
|
347
214
|
}
|
|
348
215
|
const {
|
|
349
216
|
table
|
|
350
|
-
} =
|
|
217
|
+
} = tree;
|
|
351
218
|
entries.forEach(entry => {
|
|
352
|
-
var _this$
|
|
219
|
+
var _this$editorScrollabl;
|
|
353
220
|
// On resize of the parent scroll element we need to adjust the width
|
|
354
221
|
// of the sticky header
|
|
355
|
-
if (entry.target.className === ((_this$
|
|
222
|
+
if (entry.target.className === ((_this$editorScrollabl = this.editorScrollableElement) === null || _this$editorScrollabl === void 0 ? void 0 : _this$editorScrollabl.className)) {
|
|
356
223
|
this.updateStickyHeaderWidth();
|
|
357
224
|
} else {
|
|
358
225
|
const newHeight = entry.contentRect ? entry.contentRect.height : entry.target.offsetHeight;
|
|
@@ -370,12 +237,13 @@ export class TableRowNodeView {
|
|
|
370
237
|
}
|
|
371
238
|
createIntersectionObserver() {
|
|
372
239
|
this.intersectionObserver = new IntersectionObserver((entries, _) => {
|
|
373
|
-
|
|
240
|
+
const tree = getTree(this.dom);
|
|
241
|
+
if (!tree) {
|
|
374
242
|
return;
|
|
375
243
|
}
|
|
376
244
|
const {
|
|
377
245
|
table
|
|
378
|
-
} =
|
|
246
|
+
} = tree;
|
|
379
247
|
if (table.rows.length < 2) {
|
|
380
248
|
// ED-19307 - When there's only one row in a table the top & bottom sentinels become inverted. This creates some nasty visiblity
|
|
381
249
|
// toggling side-effects because the intersection observers gets confused.
|
|
@@ -394,7 +262,7 @@ export class TableRowNodeView {
|
|
|
394
262
|
const sentinelIsBelowScrollArea = (((_entry$rootBounds2 = entry.rootBounds) === null || _entry$rootBounds2 === void 0 ? void 0 : _entry$rootBounds2.bottom) || 0) < entry.boundingClientRect.bottom;
|
|
395
263
|
if (!entry.isIntersecting && !sentinelIsBelowScrollArea) {
|
|
396
264
|
var _entry$rootBounds3;
|
|
397
|
-
|
|
265
|
+
tree && this.makeHeaderRowSticky(tree, (_entry$rootBounds3 = entry.rootBounds) === null || _entry$rootBounds3 === void 0 ? void 0 : _entry$rootBounds3.top);
|
|
398
266
|
this.lastStickyTimestamp = Date.now();
|
|
399
267
|
} else {
|
|
400
268
|
table && this.makeRowHeaderNotSticky(table);
|
|
@@ -414,7 +282,7 @@ export class TableRowNodeView {
|
|
|
414
282
|
}
|
|
415
283
|
} else if (entry.isIntersecting && sentinelIsAboveScrollArea) {
|
|
416
284
|
var _entry$rootBounds5;
|
|
417
|
-
|
|
285
|
+
tree && this.makeHeaderRowSticky(tree, entry === null || entry === void 0 ? void 0 : (_entry$rootBounds5 = entry.rootBounds) === null || _entry$rootBounds5 === void 0 ? void 0 : _entry$rootBounds5.top);
|
|
418
286
|
this.lastStickyTimestamp = Date.now();
|
|
419
287
|
}
|
|
420
288
|
}
|
|
@@ -424,65 +292,186 @@ export class TableRowNodeView {
|
|
|
424
292
|
root: this.editorScrollableElement
|
|
425
293
|
});
|
|
426
294
|
}
|
|
295
|
+
/* receive external events */
|
|
427
296
|
|
|
428
|
-
|
|
297
|
+
onTablePluginState(state) {
|
|
298
|
+
const tableRef = state.tableRef;
|
|
299
|
+
const tree = getTree(this.dom);
|
|
300
|
+
if (!tree) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
429
303
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
304
|
+
// when header rows are toggled off - mark sentinels as unobserved
|
|
305
|
+
if (!state.isHeaderRowEnabled) {
|
|
306
|
+
[this.sentinels.top, this.sentinels.bottom].forEach(el => {
|
|
307
|
+
if (el) {
|
|
308
|
+
delete el.dataset.isObserved;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
435
311
|
}
|
|
312
|
+
const isCurrentTableSelected = tableRef === tree.table;
|
|
436
313
|
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
314
|
+
// If current table selected and header row is toggled off, turn off sticky header
|
|
315
|
+
if (isCurrentTableSelected && !state.isHeaderRowEnabled && tree) {
|
|
316
|
+
this.makeRowHeaderNotSticky(tree.table);
|
|
317
|
+
}
|
|
318
|
+
this.focused = isCurrentTableSelected;
|
|
319
|
+
const {
|
|
320
|
+
wrapper
|
|
321
|
+
} = tree;
|
|
322
|
+
const tableContainer = wrapper.parentElement;
|
|
323
|
+
const tableContentWrapper = tableContainer.parentElement;
|
|
324
|
+
const layoutContainer = tableContentWrapper && tableContentWrapper.parentElement;
|
|
325
|
+
if (isCurrentTableSelected) {
|
|
326
|
+
this.colControlsOffset = tableControlsSpacing;
|
|
327
|
+
if (layoutContainer && layoutContainer.getAttribute('data-layout-content')) {
|
|
328
|
+
// move table a little out of the way
|
|
329
|
+
// to provide spacing for table controls
|
|
330
|
+
tableContentWrapper.style.paddingLeft = '11px';
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
this.colControlsOffset = 0;
|
|
334
|
+
if (layoutContainer && layoutContainer.getAttribute('data-layout-content')) {
|
|
335
|
+
tableContentWrapper.style.removeProperty('padding-left');
|
|
336
|
+
}
|
|
442
337
|
}
|
|
443
338
|
|
|
444
|
-
//
|
|
445
|
-
|
|
339
|
+
// run after table style changes have been committed
|
|
340
|
+
setTimeout(() => {
|
|
341
|
+
syncStickyRowToTable(tree.table);
|
|
342
|
+
}, 0);
|
|
343
|
+
}
|
|
344
|
+
updateStickyHeaderWidth() {
|
|
345
|
+
// table width might have changed, sync that back to sticky row
|
|
346
|
+
const tree = getTree(this.dom);
|
|
347
|
+
if (!tree) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
syncStickyRowToTable(tree.table);
|
|
351
|
+
}
|
|
446
352
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Manually refire the intersection observers.
|
|
355
|
+
* Useful when the header may have detached from the table.
|
|
356
|
+
*/
|
|
357
|
+
refireIntersectionObservers() {
|
|
358
|
+
if (this.isSticky) {
|
|
359
|
+
[this.sentinels.top, this.sentinels.bottom].forEach(el => {
|
|
360
|
+
if (el && this.intersectionObserver) {
|
|
361
|
+
this.intersectionObserver.unobserve(el);
|
|
362
|
+
this.intersectionObserver.observe(el);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
450
365
|
}
|
|
366
|
+
}
|
|
367
|
+
makeHeaderRowSticky(tree, scrollTop) {
|
|
368
|
+
var _tbody$firstChild;
|
|
369
|
+
// If header row height is more than 50% of viewport height don't do this
|
|
370
|
+
if (this.isSticky || this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const {
|
|
374
|
+
table,
|
|
375
|
+
wrapper
|
|
376
|
+
} = tree;
|
|
451
377
|
|
|
452
|
-
//
|
|
378
|
+
// ED-16035 Make sure sticky header is only applied to first row
|
|
453
379
|
const tbody = this.dom.parentElement;
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
380
|
+
const isFirstHeader = tbody === null || tbody === void 0 ? void 0 : (_tbody$firstChild = tbody.firstChild) === null || _tbody$firstChild === void 0 ? void 0 : _tbody$firstChild.isEqualNode(this.dom);
|
|
381
|
+
if (!isFirstHeader) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const currentTableTop = this.getCurrentTableTop(tree);
|
|
385
|
+
if (!scrollTop) {
|
|
386
|
+
scrollTop = getTop(this.editorScrollableElement);
|
|
387
|
+
}
|
|
388
|
+
const domTop = currentTableTop > 0 ? scrollTop : scrollTop + currentTableTop;
|
|
389
|
+
if (!this.isSticky) {
|
|
390
|
+
var _this$editorScrollabl2;
|
|
391
|
+
syncStickyRowToTable(table);
|
|
392
|
+
this.dom.classList.add('sticky');
|
|
393
|
+
table.classList.add(ClassName.TABLE_STICKY);
|
|
394
|
+
this.isSticky = true;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
|
|
398
|
+
* detaches from the table. This typically happens during a fast scroll by the user which causes
|
|
399
|
+
* the intersection observer logic to not fire as expected.
|
|
400
|
+
*/
|
|
401
|
+
(_this$editorScrollabl2 = this.editorScrollableElement) === null || _this$editorScrollabl2 === void 0 ? void 0 : _this$editorScrollabl2.addEventListener('scrollend', this.refireIntersectionObservers, {
|
|
402
|
+
passive: true,
|
|
403
|
+
once: true
|
|
404
|
+
});
|
|
405
|
+
const fastScrollThresholdMs = 500;
|
|
406
|
+
setTimeout(() => {
|
|
407
|
+
this.refireIntersectionObservers();
|
|
408
|
+
}, fastScrollThresholdMs);
|
|
409
|
+
}
|
|
410
|
+
this.dom.style.top = `${domTop}px`;
|
|
411
|
+
updateTableMargin(table);
|
|
412
|
+
this.dom.scrollLeft = wrapper.scrollLeft;
|
|
413
|
+
this.emitOn(domTop, this.colControlsOffset);
|
|
457
414
|
}
|
|
458
|
-
|
|
459
|
-
this.
|
|
460
|
-
|
|
461
|
-
this.makeRowHeaderNotSticky(this.tree.table, true);
|
|
415
|
+
makeRowHeaderNotSticky(table, isEditorDestroyed = false) {
|
|
416
|
+
if (!this.isSticky || !table || !this.dom) {
|
|
417
|
+
return;
|
|
462
418
|
}
|
|
463
|
-
this.
|
|
419
|
+
this.dom.style.removeProperty('width');
|
|
420
|
+
this.dom.classList.remove('sticky');
|
|
421
|
+
table.classList.remove(ClassName.TABLE_STICKY);
|
|
422
|
+
this.isSticky = false;
|
|
423
|
+
this.dom.style.top = '';
|
|
424
|
+
table.style.removeProperty('margin-top');
|
|
425
|
+
this.emitOff(isEditorDestroyed);
|
|
464
426
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
427
|
+
getWrapperoffset(inverse = false) {
|
|
428
|
+
const focusValue = inverse ? !this.focused : this.focused;
|
|
429
|
+
return focusValue ? 0 : tableControlsSpacing;
|
|
430
|
+
}
|
|
431
|
+
getWrapperRefTop(wrapper) {
|
|
432
|
+
return Math.round(getTop(wrapper)) + this.getWrapperoffset();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// TODO: rename!
|
|
436
|
+
getScrolledTableTop(wrapper) {
|
|
437
|
+
return this.getWrapperRefTop(wrapper) - this.topPosEditorElement;
|
|
438
|
+
}
|
|
439
|
+
getCurrentTableTop(tree) {
|
|
440
|
+
return this.getScrolledTableTop(tree.wrapper) + tree.table.clientHeight;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* emit external events */
|
|
444
|
+
|
|
445
|
+
emitOn(top, padding) {
|
|
446
|
+
if (top === this.top && padding === this.padding) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
this.top = top;
|
|
450
|
+
this.padding = padding;
|
|
451
|
+
const pos = this.getPos();
|
|
452
|
+
if (Number.isFinite(pos)) {
|
|
453
|
+
updateStickyState({
|
|
454
|
+
pos,
|
|
455
|
+
top,
|
|
456
|
+
sticky: true,
|
|
457
|
+
padding
|
|
458
|
+
})(this.view.state, this.view.dispatch, this.view);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
emitOff(isEditorDestroyed) {
|
|
462
|
+
if (this.top === 0 && this.padding === 0) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
this.top = 0;
|
|
466
|
+
this.padding = 0;
|
|
467
|
+
const pos = this.getPos();
|
|
468
|
+
if (!isEditorDestroyed && Number.isFinite(pos)) {
|
|
469
|
+
updateStickyState({
|
|
470
|
+
pos,
|
|
471
|
+
sticky: false,
|
|
472
|
+
top: this.top,
|
|
473
|
+
padding: this.padding
|
|
474
|
+
})(this.view.state, this.view.dispatch, this.view);
|
|
485
475
|
}
|
|
486
|
-
return true;
|
|
487
476
|
}
|
|
488
477
|
}
|