@atlaskit/editor-plugin-table 5.1.0 → 5.2.1
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 +12 -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} +363 -303
- 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/types.js +4 -1
- 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} +324 -280
- 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/types.js +5 -1
- 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} +364 -303
- 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/types.js +5 -1
- 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 +62 -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 +15 -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 +62 -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 +15 -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 +4 -2
- 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} +246 -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 +18 -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/tsconfig.app.json +6 -0
- 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
package/src/plugins/table/{pm-plugins/sticky-headers/nodeviews/tableRow.ts → nodeviews/TableRow.ts}
RENAMED
|
@@ -3,31 +3,33 @@ import throttle from 'lodash/throttle';
|
|
|
3
3
|
|
|
4
4
|
import type { EventDispatcher } from '@atlaskit/editor-common/event-dispatcher';
|
|
5
5
|
import { findOverflowScrollParent } from '@atlaskit/editor-common/ui';
|
|
6
|
-
import { browser
|
|
7
|
-
import type { Node as
|
|
6
|
+
import { browser } from '@atlaskit/editor-common/utils';
|
|
7
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
8
8
|
import type { EditorView, NodeView } from '@atlaskit/editor-prosemirror/view';
|
|
9
|
+
import { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/addon/closest-edge';
|
|
10
|
+
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/adapter/element';
|
|
9
11
|
|
|
10
|
-
import
|
|
12
|
+
import { getPluginState } from '../pm-plugins/plugin-factory';
|
|
13
|
+
import { pluginKey as tablePluginKey } from '../pm-plugins/plugin-key';
|
|
14
|
+
import { updateStickyState } from '../pm-plugins/sticky-headers/commands';
|
|
11
15
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from '
|
|
16
|
+
syncStickyRowToTable,
|
|
17
|
+
updateStickyMargins as updateTableMargin,
|
|
18
|
+
} from '../pm-plugins/table-resizing/utils/dom';
|
|
19
|
+
import type { DraggableSourceData, TablePluginState } from '../types';
|
|
20
|
+
import { TableCssClassName as ClassName, TableCssClassName } from '../types';
|
|
15
21
|
import {
|
|
16
22
|
STICKY_HEADER_TOGGLE_TOLERANCE_MS,
|
|
17
23
|
stickyHeaderBorderBottomWidth,
|
|
18
24
|
stickyRowOffsetTop,
|
|
19
25
|
tableControlsSpacing,
|
|
20
26
|
tableScrollbarOffset,
|
|
21
|
-
} from '
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
updateStickyMargins as updateTableMargin,
|
|
26
|
-
} from '../../table-resizing/utils/dom';
|
|
27
|
-
import { updateStickyState } from '../commands';
|
|
27
|
+
} from '../ui/consts';
|
|
28
|
+
import type { TableDOMElements } from '../utils/dom';
|
|
29
|
+
import { getTop, getTree } from '../utils/dom';
|
|
30
|
+
import { supportedHeaderRow } from '../utils/nodes';
|
|
28
31
|
|
|
29
|
-
import
|
|
30
|
-
import { getTop, getTree } from './dom';
|
|
32
|
+
import TableNodeView from './TableNodeViewBase';
|
|
31
33
|
|
|
32
34
|
// limit scroll event calls
|
|
33
35
|
const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
|
|
@@ -36,48 +38,51 @@ const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
|
|
|
36
38
|
// if too short it would trigger too many dom updates.
|
|
37
39
|
const HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
(child) => child.type.name === 'tableHeader',
|
|
56
|
-
).every(Boolean);
|
|
57
|
-
|
|
58
|
-
const someMerged = anyChildCellMergedAcrossRow(node);
|
|
59
|
-
|
|
60
|
-
return allHeaders && !someMerged;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export class TableRowNodeView implements NodeView {
|
|
64
|
-
view: EditorView;
|
|
65
|
-
node: PmNode;
|
|
66
|
-
getPos: () => number;
|
|
67
|
-
eventDispatcher: EventDispatcher;
|
|
68
|
-
|
|
69
|
-
dom: HTMLTableRowElement; // this is the sticky header table row
|
|
70
|
-
contentDOM: HTMLElement;
|
|
71
|
-
|
|
72
|
-
isHeaderRow: boolean;
|
|
73
|
-
editorScrollableElement?: HTMLElement | Window;
|
|
74
|
-
colControlsOffset = 0;
|
|
75
|
-
focused = false;
|
|
76
|
-
topPosEditorElement = 0;
|
|
77
|
-
isSticky: boolean;
|
|
78
|
-
lastStickyTimestamp: number | undefined;
|
|
79
|
-
lastTimePainted: number;
|
|
41
|
+
export default class TableRow
|
|
42
|
+
extends TableNodeView<HTMLTableRowElement>
|
|
43
|
+
implements NodeView
|
|
44
|
+
{
|
|
45
|
+
constructor(
|
|
46
|
+
node: PMNode,
|
|
47
|
+
view: EditorView,
|
|
48
|
+
getPos: () => number | undefined,
|
|
49
|
+
eventDispatcher: EventDispatcher,
|
|
50
|
+
) {
|
|
51
|
+
super(node, view, getPos, eventDispatcher);
|
|
52
|
+
|
|
53
|
+
this.isHeaderRow = supportedHeaderRow(node);
|
|
54
|
+
this.isSticky = false;
|
|
55
|
+
|
|
56
|
+
const { pluginConfig, isDragAndDropEnabled } = getPluginState(view.state);
|
|
80
57
|
|
|
58
|
+
this.isStickyHeaderEnabled = !!pluginConfig.stickyHeaders;
|
|
59
|
+
this.isDragAndDropEnabled = !!isDragAndDropEnabled;
|
|
60
|
+
|
|
61
|
+
if (this.isHeaderRow) {
|
|
62
|
+
this.dom.setAttribute('data-header-row', 'true');
|
|
63
|
+
if (this.isStickyHeaderEnabled) {
|
|
64
|
+
this.subscribe();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.isDragAndDropEnabled) {
|
|
69
|
+
this.addDropTarget(this.contentDOM);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Variables
|
|
75
|
+
*/
|
|
76
|
+
private isHeaderRow: boolean;
|
|
77
|
+
private isStickyHeaderEnabled: boolean;
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
private isDragAndDropEnabled: boolean;
|
|
80
|
+
private editorScrollableElement?: HTMLElement | Window;
|
|
81
|
+
private colControlsOffset = 0;
|
|
82
|
+
private focused = false;
|
|
83
|
+
private topPosEditorElement = 0;
|
|
84
|
+
private isSticky: boolean;
|
|
85
|
+
private lastStickyTimestamp: number | undefined;
|
|
81
86
|
private intersectionObserver?: IntersectionObserver;
|
|
82
87
|
private resizeObserver?: ResizeObserver;
|
|
83
88
|
private sentinels: {
|
|
@@ -85,54 +90,156 @@ export class TableRowNodeView implements NodeView {
|
|
|
85
90
|
bottom?: HTMLElement | null;
|
|
86
91
|
} = {};
|
|
87
92
|
private stickyRowHeight?: number;
|
|
93
|
+
private listening = false;
|
|
94
|
+
private padding: number = 0;
|
|
95
|
+
private top: number = 0;
|
|
96
|
+
private dropTargetCleanup?: () => void;
|
|
88
97
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Methods: Nodeview Lifecycle
|
|
100
|
+
*/
|
|
101
|
+
update(node: PMNode, ..._args: any[]) {
|
|
102
|
+
// do nothing if nodes were identical
|
|
103
|
+
if (node === this.node) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
// see if we're changing into a header row or
|
|
108
|
+
// changing away from one
|
|
109
|
+
const newNodeIsHeaderRow = supportedHeaderRow(node);
|
|
110
|
+
if (this.isHeaderRow !== newNodeIsHeaderRow) {
|
|
111
|
+
return false; // re-create nodeview
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// node is different but no need to re-create nodeview
|
|
100
115
|
this.node = node;
|
|
101
|
-
this.getPos = getPos;
|
|
102
|
-
this.eventDispatcher = eventDispatcher;
|
|
103
116
|
|
|
104
|
-
|
|
105
|
-
this.
|
|
117
|
+
// don't do anything if we're just a regular tr
|
|
118
|
+
if (!this.isHeaderRow) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
106
121
|
|
|
107
|
-
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
// something changed, sync widths
|
|
123
|
+
if (this.isStickyHeaderEnabled) {
|
|
124
|
+
const tbody = this.dom.parentElement;
|
|
125
|
+
const table = tbody && tbody.parentElement;
|
|
126
|
+
syncStickyRowToTable(table);
|
|
127
|
+
}
|
|
111
128
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
destroy() {
|
|
133
|
+
if (this.isStickyHeaderEnabled) {
|
|
134
|
+
this.unsubscribe();
|
|
135
|
+
|
|
136
|
+
const tree = getTree(this.dom);
|
|
137
|
+
if (tree) {
|
|
138
|
+
this.makeRowHeaderNotSticky(tree.table, true);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.emitOff(true);
|
|
115
142
|
}
|
|
143
|
+
|
|
144
|
+
// If a drop target cleanup method has been set then we should call it.
|
|
145
|
+
this.dropTargetCleanup?.();
|
|
116
146
|
}
|
|
117
147
|
|
|
118
|
-
|
|
119
|
-
|
|
148
|
+
ignoreMutation(
|
|
149
|
+
mutationRecord: MutationRecord | { type: 'selection'; target: Element },
|
|
150
|
+
) {
|
|
151
|
+
/* tableRows are not directly editable by the user
|
|
152
|
+
* so it should be safe to ignore mutations that we cause
|
|
153
|
+
* by updating styles and classnames on this DOM element
|
|
154
|
+
*
|
|
155
|
+
* Update: should not ignore mutations for row selection to avoid known issue with table selection highlight in firefox
|
|
156
|
+
* Related bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=1289673
|
|
157
|
+
* */
|
|
158
|
+
const isTableSelection =
|
|
159
|
+
mutationRecord.type === 'selection' &&
|
|
160
|
+
mutationRecord.target.nodeName === 'TR';
|
|
161
|
+
/**
|
|
162
|
+
* Update: should not ignore mutations when an node is added, as this interferes with
|
|
163
|
+
* prosemirrors handling of some language inputs in Safari (ie. Pinyin, Hiragana).
|
|
164
|
+
*
|
|
165
|
+
* In paticular, when a composition occurs at the start of the first node inside a table cell, if the resulting mutation
|
|
166
|
+
* from the composition end is ignored than prosemirror will end up with; invalid table markup nesting and a misplaced
|
|
167
|
+
* selection and insertion.
|
|
168
|
+
*/
|
|
169
|
+
const isNodeInsertion =
|
|
170
|
+
mutationRecord.type === 'childList' &&
|
|
171
|
+
mutationRecord.target.nodeName === 'TR' &&
|
|
172
|
+
mutationRecord.addedNodes.length;
|
|
173
|
+
|
|
174
|
+
if (isTableSelection || isNodeInsertion) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Methods
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
private addDropTarget(element: HTMLElement) {
|
|
186
|
+
const pos = this.getPos()!;
|
|
187
|
+
if (!Number.isFinite(pos)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (this.dropTargetCleanup) {
|
|
192
|
+
this.dropTargetCleanup();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const resolvedPos = this.view.state.doc.resolve(pos);
|
|
196
|
+
const targetIndex = resolvedPos.index();
|
|
197
|
+
const localId = resolvedPos.parent.attrs.localId;
|
|
198
|
+
|
|
199
|
+
this.dropTargetCleanup = dropTargetForElements({
|
|
200
|
+
element: element,
|
|
201
|
+
canDrop({ source }) {
|
|
202
|
+
const data = source.data as DraggableSourceData;
|
|
203
|
+
return (
|
|
204
|
+
// Only draggables of row type can be dropped on this target
|
|
205
|
+
data.type === 'table-row' &&
|
|
206
|
+
// Only draggables which came from the same table can be dropped on this target
|
|
207
|
+
data.localId === localId &&
|
|
208
|
+
// Only draggables which DO NOT include this drop targets index can be dropped
|
|
209
|
+
!!data.indexes?.length &&
|
|
210
|
+
data.indexes?.indexOf(targetIndex) === -1
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
getData({ input, element }) {
|
|
214
|
+
const data = {
|
|
215
|
+
localId,
|
|
216
|
+
type: 'table-row',
|
|
217
|
+
targetIndex,
|
|
218
|
+
};
|
|
219
|
+
return attachClosestEdge(data, {
|
|
220
|
+
input,
|
|
221
|
+
element,
|
|
222
|
+
allowedEdges: ['top', 'bottom'],
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
120
227
|
|
|
121
|
-
headerRowMouseScrollEnd = debounce(() => {
|
|
228
|
+
private headerRowMouseScrollEnd = debounce(() => {
|
|
122
229
|
this.dom.classList.remove('no-pointer-events');
|
|
123
230
|
}, HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT);
|
|
124
231
|
|
|
125
232
|
// When the header is sticky, the header row is set to position: fixed
|
|
126
233
|
// This prevents mouse wheel scrolling on the scroll-parent div when user's mouse is hovering the header row.
|
|
127
234
|
// This fix sets pointer-events: none on the header row briefly to avoid this behaviour
|
|
128
|
-
headerRowMouseScroll = throttle(() => {
|
|
235
|
+
private headerRowMouseScroll = throttle(() => {
|
|
129
236
|
if (this.isSticky) {
|
|
130
237
|
this.dom.classList.add('no-pointer-events');
|
|
131
238
|
this.headerRowMouseScrollEnd();
|
|
132
239
|
}
|
|
133
240
|
}, HEADER_ROW_SCROLL_THROTTLE_TIMEOUT);
|
|
134
241
|
|
|
135
|
-
subscribe() {
|
|
242
|
+
private subscribe() {
|
|
136
243
|
this.editorScrollableElement =
|
|
137
244
|
findOverflowScrollParent(this.view.dom as HTMLElement) || window;
|
|
138
245
|
|
|
@@ -141,11 +248,14 @@ export class TableRowNodeView implements NodeView {
|
|
|
141
248
|
this.topPosEditorElement = getTop(this.editorScrollableElement);
|
|
142
249
|
}
|
|
143
250
|
|
|
144
|
-
this.eventDispatcher.on(
|
|
251
|
+
this.eventDispatcher.on(
|
|
252
|
+
'widthPlugin',
|
|
253
|
+
this.updateStickyHeaderWidth.bind(this),
|
|
254
|
+
);
|
|
145
255
|
|
|
146
256
|
this.eventDispatcher.on(
|
|
147
257
|
(tablePluginKey as any).key,
|
|
148
|
-
this.onTablePluginState,
|
|
258
|
+
this.onTablePluginState.bind(this),
|
|
149
259
|
);
|
|
150
260
|
|
|
151
261
|
this.listening = true;
|
|
@@ -160,7 +270,7 @@ export class TableRowNodeView implements NodeView {
|
|
|
160
270
|
);
|
|
161
271
|
}
|
|
162
272
|
|
|
163
|
-
unsubscribe() {
|
|
273
|
+
private unsubscribe() {
|
|
164
274
|
if (!this.listening) {
|
|
165
275
|
return;
|
|
166
276
|
}
|
|
@@ -211,7 +321,7 @@ export class TableRowNodeView implements NodeView {
|
|
|
211
321
|
|
|
212
322
|
window.requestAnimationFrame(() => {
|
|
213
323
|
// we expect tree to be defined after animation frame
|
|
214
|
-
const tableContainer = this.
|
|
324
|
+
const tableContainer = getTree(this.dom)?.wrapper.closest(
|
|
215
325
|
`.${TableCssClassName.NODEVIEW_WRAPPER}`,
|
|
216
326
|
);
|
|
217
327
|
if (tableContainer) {
|
|
@@ -236,10 +346,11 @@ export class TableRowNodeView implements NodeView {
|
|
|
236
346
|
// to allocate for new header height
|
|
237
347
|
private createResizeObserver() {
|
|
238
348
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
239
|
-
|
|
349
|
+
const tree = getTree(this.dom);
|
|
350
|
+
if (!tree) {
|
|
240
351
|
return;
|
|
241
352
|
}
|
|
242
|
-
const { table } =
|
|
353
|
+
const { table } = tree;
|
|
243
354
|
entries.forEach((entry) => {
|
|
244
355
|
// On resize of the parent scroll element we need to adjust the width
|
|
245
356
|
// of the sticky header
|
|
@@ -275,10 +386,11 @@ export class TableRowNodeView implements NodeView {
|
|
|
275
386
|
private createIntersectionObserver() {
|
|
276
387
|
this.intersectionObserver = new IntersectionObserver(
|
|
277
388
|
(entries: IntersectionObserverEntry[], _: IntersectionObserver) => {
|
|
278
|
-
|
|
389
|
+
const tree = getTree(this.dom);
|
|
390
|
+
if (!tree) {
|
|
279
391
|
return;
|
|
280
392
|
}
|
|
281
|
-
const { table } =
|
|
393
|
+
const { table } = tree;
|
|
282
394
|
|
|
283
395
|
if (table.rows.length < 2) {
|
|
284
396
|
// ED-19307 - When there's only one row in a table the top & bottom sentinels become inverted. This creates some nasty visiblity
|
|
@@ -299,8 +411,7 @@ export class TableRowNodeView implements NodeView {
|
|
|
299
411
|
(entry.rootBounds?.bottom || 0) < entry.boundingClientRect.bottom;
|
|
300
412
|
|
|
301
413
|
if (!entry.isIntersecting && !sentinelIsBelowScrollArea) {
|
|
302
|
-
this.tree
|
|
303
|
-
this.makeHeaderRowSticky(this.tree, entry.rootBounds?.top);
|
|
414
|
+
tree && this.makeHeaderRowSticky(tree, entry.rootBounds?.top);
|
|
304
415
|
this.lastStickyTimestamp = Date.now();
|
|
305
416
|
} else {
|
|
306
417
|
table && this.makeRowHeaderNotSticky(table);
|
|
@@ -328,8 +439,7 @@ export class TableRowNodeView implements NodeView {
|
|
|
328
439
|
this.makeRowHeaderNotSticky(table);
|
|
329
440
|
}
|
|
330
441
|
} else if (entry.isIntersecting && sentinelIsAboveScrollArea) {
|
|
331
|
-
this.tree
|
|
332
|
-
this.makeHeaderRowSticky(this.tree, entry?.rootBounds?.top);
|
|
442
|
+
tree && this.makeHeaderRowSticky(tree, entry?.rootBounds?.top);
|
|
333
443
|
this.lastStickyTimestamp = Date.now();
|
|
334
444
|
}
|
|
335
445
|
}
|
|
@@ -339,94 +449,12 @@ export class TableRowNodeView implements NodeView {
|
|
|
339
449
|
{ root: this.editorScrollableElement as Element },
|
|
340
450
|
);
|
|
341
451
|
}
|
|
342
|
-
|
|
343
|
-
/* paint/update loop */
|
|
344
|
-
previousDomTop: number | undefined;
|
|
345
|
-
previousPadding: number | undefined;
|
|
346
|
-
|
|
347
|
-
latestDomTop: number | undefined;
|
|
348
|
-
|
|
349
|
-
nextFrame: number | undefined;
|
|
350
|
-
|
|
351
|
-
/* nodeview lifecycle */
|
|
352
|
-
update(node: PmNode, ..._args: any[]) {
|
|
353
|
-
// do nothing if nodes were identical
|
|
354
|
-
if (node === this.node) {
|
|
355
|
-
return true;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// see if we're changing into a header row or
|
|
359
|
-
// changing away from one
|
|
360
|
-
const newNodeIsHeaderRow = supportedHeaderRow(node);
|
|
361
|
-
if (this.isHeaderRow !== newNodeIsHeaderRow) {
|
|
362
|
-
return false; // re-create nodeview
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// node is different but no need to re-create nodeview
|
|
366
|
-
this.node = node;
|
|
367
|
-
|
|
368
|
-
// don't do anything if we're just a regular tr
|
|
369
|
-
if (!this.isHeaderRow) {
|
|
370
|
-
return true;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// something changed, sync widths
|
|
374
|
-
const tbody = this.dom.parentElement;
|
|
375
|
-
const table = tbody && tbody.parentElement;
|
|
376
|
-
syncStickyRowToTable(table);
|
|
377
|
-
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
destroy() {
|
|
382
|
-
this.unsubscribe();
|
|
383
|
-
|
|
384
|
-
if (this.tree) {
|
|
385
|
-
this.makeRowHeaderNotSticky(this.tree.table, true);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
this.emitOff(true);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
ignoreMutation(
|
|
392
|
-
mutationRecord: MutationRecord | { type: 'selection'; target: Element },
|
|
393
|
-
) {
|
|
394
|
-
/* tableRows are not directly editable by the user
|
|
395
|
-
* so it should be safe to ignore mutations that we cause
|
|
396
|
-
* by updating styles and classnames on this DOM element
|
|
397
|
-
*
|
|
398
|
-
* Update: should not ignore mutations for row selection to avoid known issue with table selection highlight in firefox
|
|
399
|
-
* Related bug report: https://bugzilla.mozilla.org/show_bug.cgi?id=1289673
|
|
400
|
-
* */
|
|
401
|
-
const isTableSelection =
|
|
402
|
-
mutationRecord.type === 'selection' &&
|
|
403
|
-
mutationRecord.target.nodeName === 'TR';
|
|
404
|
-
/**
|
|
405
|
-
* Update: should not ignore mutations when an node is added, as this interferes with
|
|
406
|
-
* prosemirrors handling of some language inputs in Safari (ie. Pinyin, Hiragana).
|
|
407
|
-
*
|
|
408
|
-
* In paticular, when a composition occurs at the start of the first node inside a table cell, if the resulting mutation
|
|
409
|
-
* from the composition end is ignored than prosemirror will end up with; invalid table markup nesting and a misplaced
|
|
410
|
-
* selection and insertion.
|
|
411
|
-
*/
|
|
412
|
-
const isNodeInsertion =
|
|
413
|
-
mutationRecord.type === 'childList' &&
|
|
414
|
-
mutationRecord.target.nodeName === 'TR' &&
|
|
415
|
-
mutationRecord.addedNodes.length;
|
|
416
|
-
|
|
417
|
-
if (isTableSelection || isNodeInsertion) {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
452
|
/* receive external events */
|
|
425
453
|
|
|
426
|
-
onTablePluginState
|
|
454
|
+
private onTablePluginState(state: TablePluginState) {
|
|
427
455
|
const tableRef = state.tableRef;
|
|
428
456
|
|
|
429
|
-
const tree = this.
|
|
457
|
+
const tree = getTree(this.dom);
|
|
430
458
|
if (!tree) {
|
|
431
459
|
return;
|
|
432
460
|
}
|
|
@@ -443,8 +471,8 @@ export class TableRowNodeView implements NodeView {
|
|
|
443
471
|
const isCurrentTableSelected = tableRef === tree.table;
|
|
444
472
|
|
|
445
473
|
// If current table selected and header row is toggled off, turn off sticky header
|
|
446
|
-
if (isCurrentTableSelected && !state.isHeaderRowEnabled &&
|
|
447
|
-
this.makeRowHeaderNotSticky(
|
|
474
|
+
if (isCurrentTableSelected && !state.isHeaderRowEnabled && tree) {
|
|
475
|
+
this.makeRowHeaderNotSticky(tree.table);
|
|
448
476
|
}
|
|
449
477
|
this.focused = isCurrentTableSelected;
|
|
450
478
|
|
|
@@ -481,55 +509,23 @@ export class TableRowNodeView implements NodeView {
|
|
|
481
509
|
setTimeout(() => {
|
|
482
510
|
syncStickyRowToTable(tree.table);
|
|
483
511
|
}, 0);
|
|
484
|
-
}
|
|
512
|
+
}
|
|
485
513
|
|
|
486
|
-
updateStickyHeaderWidth
|
|
514
|
+
private updateStickyHeaderWidth() {
|
|
487
515
|
// table width might have changed, sync that back to sticky row
|
|
488
|
-
const tree = this.
|
|
516
|
+
const tree = getTree(this.dom);
|
|
489
517
|
if (!tree) {
|
|
490
518
|
return;
|
|
491
519
|
}
|
|
492
520
|
|
|
493
521
|
syncStickyRowToTable(tree.table);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
shouldHeaderStick = (tree: TableDOMElements): boolean => {
|
|
497
|
-
const { wrapper } = tree;
|
|
498
|
-
const tableWrapperRect = wrapper.getBoundingClientRect();
|
|
499
|
-
const editorAreaRect = (
|
|
500
|
-
this.editorScrollableElement as HTMLElement
|
|
501
|
-
).getBoundingClientRect();
|
|
502
|
-
|
|
503
|
-
const stickyHeaderRect = this.contentDOM.getBoundingClientRect();
|
|
504
|
-
const firstHeaderRow = !this.dom.previousElementSibling;
|
|
505
|
-
const subsequentRows = !!this.dom.nextElementSibling;
|
|
506
|
-
const isHeaderValid = firstHeaderRow && subsequentRows;
|
|
507
|
-
|
|
508
|
-
// if the table wrapper is less than the editor top pos then make it sticky
|
|
509
|
-
// Make header sticky if table wrapper top is outside viewport
|
|
510
|
-
// but bottom is still in the viewport.
|
|
511
|
-
if (
|
|
512
|
-
tableWrapperRect.top < editorAreaRect.top &&
|
|
513
|
-
tableWrapperRect.bottom > editorAreaRect.top &&
|
|
514
|
-
isHeaderValid
|
|
515
|
-
) {
|
|
516
|
-
return true;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// if the sticky header is below the editor area make it non-sticky
|
|
520
|
-
if (stickyHeaderRect.top > editorAreaRect.top) {
|
|
521
|
-
return false;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// otherwise make it non-sticky
|
|
525
|
-
return false;
|
|
526
|
-
};
|
|
522
|
+
}
|
|
527
523
|
|
|
528
524
|
/**
|
|
529
525
|
* Manually refire the intersection observers.
|
|
530
526
|
* Useful when the header may have detached from the table.
|
|
531
527
|
*/
|
|
532
|
-
refireIntersectionObservers
|
|
528
|
+
private refireIntersectionObservers() {
|
|
533
529
|
if (this.isSticky) {
|
|
534
530
|
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
535
531
|
if (el && this.intersectionObserver) {
|
|
@@ -538,9 +534,9 @@ export class TableRowNodeView implements NodeView {
|
|
|
538
534
|
}
|
|
539
535
|
});
|
|
540
536
|
}
|
|
541
|
-
}
|
|
537
|
+
}
|
|
542
538
|
|
|
543
|
-
makeHeaderRowSticky
|
|
539
|
+
private makeHeaderRowSticky(tree: TableDOMElements, scrollTop?: number) {
|
|
544
540
|
// If header row height is more than 50% of viewport height don't do this
|
|
545
541
|
if (
|
|
546
542
|
this.isSticky ||
|
|
@@ -596,12 +592,12 @@ export class TableRowNodeView implements NodeView {
|
|
|
596
592
|
this.dom.scrollLeft = wrapper.scrollLeft;
|
|
597
593
|
|
|
598
594
|
this.emitOn(domTop, this.colControlsOffset);
|
|
599
|
-
}
|
|
595
|
+
}
|
|
600
596
|
|
|
601
|
-
makeRowHeaderNotSticky
|
|
597
|
+
private makeRowHeaderNotSticky(
|
|
602
598
|
table: HTMLElement,
|
|
603
599
|
isEditorDestroyed: boolean = false,
|
|
604
|
-
)
|
|
600
|
+
) {
|
|
605
601
|
if (!this.isSticky || !table || !this.dom) {
|
|
606
602
|
return;
|
|
607
603
|
}
|
|
@@ -615,59 +611,63 @@ export class TableRowNodeView implements NodeView {
|
|
|
615
611
|
table.style.removeProperty('margin-top');
|
|
616
612
|
|
|
617
613
|
this.emitOff(isEditorDestroyed);
|
|
618
|
-
}
|
|
614
|
+
}
|
|
619
615
|
|
|
620
|
-
getWrapperoffset
|
|
616
|
+
private getWrapperoffset(inverse: boolean = false): number {
|
|
621
617
|
const focusValue = inverse ? !this.focused : this.focused;
|
|
622
618
|
return focusValue ? 0 : tableControlsSpacing;
|
|
623
|
-
}
|
|
619
|
+
}
|
|
624
620
|
|
|
625
|
-
getWrapperRefTop
|
|
626
|
-
Math.round(getTop(wrapper)) + this.getWrapperoffset();
|
|
621
|
+
private getWrapperRefTop(wrapper: HTMLElement): number {
|
|
622
|
+
return Math.round(getTop(wrapper)) + this.getWrapperoffset();
|
|
623
|
+
}
|
|
627
624
|
|
|
628
625
|
// TODO: rename!
|
|
629
|
-
getScrolledTableTop
|
|
630
|
-
this.getWrapperRefTop(wrapper) - this.topPosEditorElement;
|
|
626
|
+
private getScrolledTableTop(wrapper: HTMLElement): number {
|
|
627
|
+
return this.getWrapperRefTop(wrapper) - this.topPosEditorElement;
|
|
628
|
+
}
|
|
631
629
|
|
|
632
|
-
getCurrentTableTop
|
|
633
|
-
this.getScrolledTableTop(tree.wrapper) + tree.table.clientHeight;
|
|
630
|
+
private getCurrentTableTop(tree: TableDOMElements): number {
|
|
631
|
+
return this.getScrolledTableTop(tree.wrapper) + tree.table.clientHeight;
|
|
632
|
+
}
|
|
634
633
|
|
|
635
634
|
/* emit external events */
|
|
636
635
|
|
|
637
|
-
padding
|
|
638
|
-
top = 0;
|
|
639
|
-
|
|
640
|
-
emitOn = (top: number, padding: number) => {
|
|
636
|
+
private emitOn(top: number, padding: number) {
|
|
641
637
|
if (top === this.top && padding === this.padding) {
|
|
642
638
|
return;
|
|
643
639
|
}
|
|
644
640
|
|
|
645
641
|
this.top = top;
|
|
646
642
|
this.padding = padding;
|
|
643
|
+
const pos = this.getPos()!;
|
|
647
644
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
645
|
+
if (Number.isFinite(pos)) {
|
|
646
|
+
updateStickyState({
|
|
647
|
+
pos,
|
|
648
|
+
top,
|
|
649
|
+
sticky: true,
|
|
650
|
+
padding,
|
|
651
|
+
})(this.view.state, this.view.dispatch, this.view);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
655
654
|
|
|
656
|
-
emitOff
|
|
655
|
+
private emitOff(isEditorDestroyed: boolean) {
|
|
657
656
|
if (this.top === 0 && this.padding === 0) {
|
|
658
657
|
return;
|
|
659
658
|
}
|
|
660
659
|
|
|
661
660
|
this.top = 0;
|
|
662
661
|
this.padding = 0;
|
|
662
|
+
const pos = this.getPos()!;
|
|
663
663
|
|
|
664
|
-
if (!isEditorDestroyed) {
|
|
664
|
+
if (!isEditorDestroyed && Number.isFinite(pos)) {
|
|
665
665
|
updateStickyState({
|
|
666
|
-
pos
|
|
666
|
+
pos,
|
|
667
667
|
sticky: false,
|
|
668
668
|
top: this.top,
|
|
669
669
|
padding: this.padding,
|
|
670
670
|
})(this.view.state, this.view.dispatch, this.view);
|
|
671
671
|
}
|
|
672
|
-
}
|
|
672
|
+
}
|
|
673
673
|
}
|