@atlaskit/editor-plugin-table 7.16.11 → 7.16.13
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/.eslintrc.js +3 -3
- package/CHANGELOG.md +16 -0
- package/dist/cjs/commands/misc.js +3 -3
- package/dist/cjs/nodeviews/TableCell.js +10 -10
- package/dist/cjs/nodeviews/TableContainer.js +83 -27
- package/dist/cjs/nodeviews/TableResizer.js +40 -19
- package/dist/cjs/nodeviews/TableRow.js +23 -23
- package/dist/cjs/pm-plugins/table-resizing/plugin.js +3 -3
- package/dist/cjs/pm-plugins/table-resizing/utils/resize-state.js +4 -4
- package/dist/cjs/pm-plugins/table-resizing/utils/scale-table.js +3 -3
- package/dist/cjs/ui/FloatingContextualMenu/styles.js +1 -1
- package/dist/cjs/ui/FloatingDragMenu/styles.js +1 -1
- package/dist/cjs/ui/common-styles.js +13 -13
- package/dist/cjs/ui/ui-styles.js +25 -25
- package/dist/cjs/utils/guidelines.js +7 -4
- package/dist/cjs/utils/merged-cells.js +3 -3
- package/dist/cjs/utils/snapping.js +7 -8
- package/dist/es2019/commands/misc.js +3 -3
- package/dist/es2019/nodeviews/TableContainer.js +70 -9
- package/dist/es2019/nodeviews/TableResizer.js +42 -21
- package/dist/es2019/nodeviews/TableRow.js +21 -21
- package/dist/es2019/pm-plugins/table-resizing/plugin.js +3 -3
- package/dist/es2019/pm-plugins/table-resizing/utils/resize-state.js +4 -4
- package/dist/es2019/pm-plugins/table-resizing/utils/scale-table.js +3 -3
- package/dist/es2019/ui/FloatingContextualMenu/styles.js +47 -47
- package/dist/es2019/ui/FloatingDragMenu/styles.js +30 -30
- package/dist/es2019/ui/common-styles.js +802 -816
- package/dist/es2019/ui/ui-styles.js +665 -678
- package/dist/es2019/utils/guidelines.js +5 -2
- package/dist/es2019/utils/merged-cells.js +3 -3
- package/dist/es2019/utils/snapping.js +5 -6
- package/dist/esm/commands/misc.js +3 -3
- package/dist/esm/nodeviews/TableCell.js +10 -10
- package/dist/esm/nodeviews/TableContainer.js +85 -29
- package/dist/esm/nodeviews/TableResizer.js +42 -21
- package/dist/esm/nodeviews/TableRow.js +23 -23
- package/dist/esm/pm-plugins/table-resizing/plugin.js +3 -3
- package/dist/esm/pm-plugins/table-resizing/utils/resize-state.js +4 -4
- package/dist/esm/pm-plugins/table-resizing/utils/scale-table.js +3 -3
- package/dist/esm/ui/FloatingContextualMenu/styles.js +1 -1
- package/dist/esm/ui/FloatingDragMenu/styles.js +1 -1
- package/dist/esm/ui/common-styles.js +13 -13
- package/dist/esm/ui/ui-styles.js +25 -25
- package/dist/esm/utils/guidelines.js +6 -3
- package/dist/esm/utils/merged-cells.js +3 -3
- package/dist/esm/utils/snapping.js +6 -7
- package/dist/types/nodeviews/TableResizer.d.ts +2 -1
- package/dist/types/pm-plugins/decorations/utils/index.d.ts +1 -1
- package/dist/types/pm-plugins/drag-and-drop/utils/autoscrollers.d.ts +1 -1
- package/dist/types/pm-plugins/drag-and-drop/utils/getDragBehaviour.d.ts +1 -1
- package/dist/types/pm-plugins/table-resizing/utils/index.d.ts +1 -1
- package/dist/types/ui/ColumnResizeWidget/index.d.ts +1 -1
- package/dist/types/ui/FloatingAlignmentButtons/FloatingAlignmentButtons.d.ts +1 -1
- package/dist/types/ui/FloatingToolbarLabel/FloatingToolbarLabel.d.ts +1 -1
- package/dist/types/ui/TableFloatingColumnControls/ColumnDropTargets/ColumnDropTarget.d.ts +1 -1
- package/dist/types/ui/TableFloatingControls/CornerControls/index.d.ts +1 -1
- package/dist/types/ui/icons/DragHandleDisabledIcon.d.ts +1 -1
- package/dist/types/utils/guidelines.d.ts +2 -1
- package/dist/types/utils/snapping.d.ts +3 -2
- package/dist/types-ts4.5/nodeviews/TableResizer.d.ts +2 -1
- package/dist/types-ts4.5/pm-plugins/decorations/utils/index.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/drag-and-drop/utils/autoscrollers.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/drag-and-drop/utils/getDragBehaviour.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/table-resizing/utils/index.d.ts +1 -1
- package/dist/types-ts4.5/ui/ColumnResizeWidget/index.d.ts +1 -1
- package/dist/types-ts4.5/ui/FloatingAlignmentButtons/FloatingAlignmentButtons.d.ts +1 -1
- package/dist/types-ts4.5/ui/FloatingToolbarLabel/FloatingToolbarLabel.d.ts +1 -1
- package/dist/types-ts4.5/ui/TableFloatingColumnControls/ColumnDropTargets/ColumnDropTarget.d.ts +1 -1
- package/dist/types-ts4.5/ui/TableFloatingControls/CornerControls/index.d.ts +1 -1
- package/dist/types-ts4.5/ui/icons/DragHandleDisabledIcon.d.ts +1 -1
- package/dist/types-ts4.5/utils/guidelines.d.ts +2 -1
- package/dist/types-ts4.5/utils/snapping.d.ts +3 -2
- package/docs/0-intro.tsx +9 -7
- package/package.json +3 -3
- package/report.api.md +67 -66
- package/src/commands/clear.ts +36 -44
- package/src/commands/collapse.ts +8 -8
- package/src/commands/column-resize.ts +412 -452
- package/src/commands/delete.ts +14 -14
- package/src/commands/display-mode.ts +10 -11
- package/src/commands/go-to-next-cell.ts +48 -54
- package/src/commands/hover.ts +210 -227
- package/src/commands/index.ts +35 -35
- package/src/commands/insert.ts +208 -235
- package/src/commands/misc.ts +655 -748
- package/src/commands/referentiality.ts +9 -9
- package/src/commands/selection.ts +433 -563
- package/src/commands/sort.ts +68 -86
- package/src/commands/split-cell.ts +14 -14
- package/src/commands/toggle.ts +69 -67
- package/src/commands-with-analytics.ts +570 -639
- package/src/create-plugin-config.ts +13 -13
- package/src/event-handlers.ts +513 -612
- package/src/handlers.ts +120 -133
- package/src/nodeviews/ExternalDropTargets.tsx +68 -73
- package/src/nodeviews/OverflowShadowsObserver.ts +148 -157
- package/src/nodeviews/TableCell.ts +47 -54
- package/src/nodeviews/TableComponent.tsx +1018 -1112
- package/src/nodeviews/TableComponentWithSharedState.tsx +91 -94
- package/src/nodeviews/TableContainer.tsx +384 -340
- package/src/nodeviews/TableNodeViewBase.ts +19 -24
- package/src/nodeviews/TableResizer.tsx +642 -653
- package/src/nodeviews/TableRow.ts +580 -629
- package/src/nodeviews/TableStickyScrollbar.ts +173 -190
- package/src/nodeviews/__mocks__/OverflowShadowsObserver.ts +8 -8
- package/src/nodeviews/__mocks__/OverridableMock.ts +14 -15
- package/src/nodeviews/table.tsx +345 -375
- package/src/nodeviews/types.ts +21 -24
- package/src/nodeviews/update-overflow-shadows.ts +8 -14
- package/src/plugin.tsx +578 -603
- package/src/pm-plugins/analytics/actions.ts +10 -12
- package/src/pm-plugins/analytics/commands.ts +31 -37
- package/src/pm-plugins/analytics/plugin-factory.ts +4 -2
- package/src/pm-plugins/analytics/plugin-key.ts +1 -3
- package/src/pm-plugins/analytics/plugin.ts +60 -70
- package/src/pm-plugins/analytics/reducer.ts +19 -19
- package/src/pm-plugins/analytics/types.ts +10 -10
- package/src/pm-plugins/analytics/utils/moved-event.ts +38 -38
- package/src/pm-plugins/decorations/plugin.ts +58 -77
- package/src/pm-plugins/decorations/utils/column-controls.ts +59 -71
- package/src/pm-plugins/decorations/utils/column-resizing.ts +50 -57
- package/src/pm-plugins/decorations/utils/compose-decorations.ts +6 -6
- package/src/pm-plugins/decorations/utils/index.ts +3 -6
- package/src/pm-plugins/decorations/utils/types.ts +7 -12
- package/src/pm-plugins/default-table-selection.ts +3 -3
- package/src/pm-plugins/drag-and-drop/actions.ts +25 -25
- package/src/pm-plugins/drag-and-drop/commands-with-analytics.ts +158 -190
- package/src/pm-plugins/drag-and-drop/commands.ts +154 -170
- package/src/pm-plugins/drag-and-drop/consts.ts +4 -5
- package/src/pm-plugins/drag-and-drop/plugin-factory.ts +23 -20
- package/src/pm-plugins/drag-and-drop/plugin-key.ts +1 -3
- package/src/pm-plugins/drag-and-drop/plugin.ts +329 -383
- package/src/pm-plugins/drag-and-drop/reducer.ts +30 -30
- package/src/pm-plugins/drag-and-drop/types.ts +8 -8
- package/src/pm-plugins/drag-and-drop/utils/autoscrollers.ts +38 -41
- package/src/pm-plugins/drag-and-drop/utils/getDragBehaviour.ts +3 -6
- package/src/pm-plugins/drag-and-drop/utils/monitor.ts +57 -70
- package/src/pm-plugins/keymap.ts +208 -220
- package/src/pm-plugins/main.ts +348 -400
- package/src/pm-plugins/plugin-factory.ts +32 -34
- package/src/pm-plugins/safari-delete-composition-text-issue-workaround.ts +83 -97
- package/src/pm-plugins/sticky-headers/commands.ts +2 -6
- package/src/pm-plugins/sticky-headers/plugin-key.ts +1 -3
- package/src/pm-plugins/sticky-headers/plugin-state.ts +41 -44
- package/src/pm-plugins/sticky-headers/plugin.ts +4 -4
- package/src/pm-plugins/sticky-headers/types.ts +8 -8
- package/src/pm-plugins/sticky-headers/util.ts +10 -10
- package/src/pm-plugins/table-analytics.ts +70 -72
- package/src/pm-plugins/table-local-id.ts +180 -184
- package/src/pm-plugins/table-resizing/commands.ts +72 -85
- package/src/pm-plugins/table-resizing/event-handlers.ts +298 -317
- package/src/pm-plugins/table-resizing/plugin-factory.ts +10 -10
- package/src/pm-plugins/table-resizing/plugin-key.ts +1 -3
- package/src/pm-plugins/table-resizing/plugin.ts +61 -68
- package/src/pm-plugins/table-resizing/reducer.ts +30 -33
- package/src/pm-plugins/table-resizing/utils/colgroup.ts +84 -84
- package/src/pm-plugins/table-resizing/utils/column-state.ts +78 -81
- package/src/pm-plugins/table-resizing/utils/content-width.ts +94 -114
- package/src/pm-plugins/table-resizing/utils/dom.ts +93 -110
- package/src/pm-plugins/table-resizing/utils/index.ts +29 -34
- package/src/pm-plugins/table-resizing/utils/misc.ts +94 -119
- package/src/pm-plugins/table-resizing/utils/resize-column.ts +93 -106
- package/src/pm-plugins/table-resizing/utils/resize-logic.ts +240 -257
- package/src/pm-plugins/table-resizing/utils/resize-state.ts +343 -372
- package/src/pm-plugins/table-resizing/utils/scale-table.ts +202 -207
- package/src/pm-plugins/table-resizing/utils/types.ts +17 -17
- package/src/pm-plugins/table-resizing/utils/unit-to-number.ts +1 -2
- package/src/pm-plugins/table-selection-keymap.ts +25 -51
- package/src/pm-plugins/table-width.ts +191 -204
- package/src/pm-plugins/view-mode-sort/index.ts +223 -227
- package/src/pm-plugins/view-mode-sort/plugin-key.ts +3 -2
- package/src/pm-plugins/view-mode-sort/types.ts +12 -12
- package/src/pm-plugins/view-mode-sort/utils.ts +108 -117
- package/src/reducer.ts +139 -155
- package/src/toolbar.tsx +815 -905
- package/src/transforms/column-width.ts +186 -213
- package/src/transforms/delete-columns.ts +208 -222
- package/src/transforms/delete-rows.ts +117 -121
- package/src/transforms/fix-tables.ts +190 -215
- package/src/transforms/merge.ts +263 -269
- package/src/transforms/replace-table.ts +27 -43
- package/src/transforms/split.ts +65 -75
- package/src/types.ts +421 -427
- package/src/ui/ColumnResizeWidget/index.tsx +40 -47
- package/src/ui/DragHandle/HandleIconComponent.tsx +9 -13
- package/src/ui/DragHandle/index.tsx +221 -250
- package/src/ui/DragPreview/index.tsx +35 -35
- package/src/ui/FloatingAlignmentButtons/FloatingAlignmentButtons.tsx +33 -41
- package/src/ui/FloatingContextualButton/FixedButton.tsx +154 -157
- package/src/ui/FloatingContextualButton/index.tsx +109 -115
- package/src/ui/FloatingContextualButton/styles.ts +43 -46
- package/src/ui/FloatingContextualMenu/ContextualMenu.tsx +634 -694
- package/src/ui/FloatingContextualMenu/index.tsx +83 -101
- package/src/ui/FloatingContextualMenu/styles.ts +57 -65
- package/src/ui/FloatingDeleteButton/DeleteButton.tsx +37 -37
- package/src/ui/FloatingDeleteButton/getPopUpOptions.ts +47 -57
- package/src/ui/FloatingDeleteButton/index.tsx +319 -350
- package/src/ui/FloatingDragMenu/DragMenu.tsx +555 -596
- package/src/ui/FloatingDragMenu/DropdownMenu.tsx +152 -162
- package/src/ui/FloatingDragMenu/index.tsx +88 -102
- package/src/ui/FloatingDragMenu/styles.ts +51 -54
- package/src/ui/FloatingInsertButton/InsertButton.tsx +204 -217
- package/src/ui/FloatingInsertButton/getPopupOptions.ts +100 -115
- package/src/ui/FloatingInsertButton/index.tsx +248 -292
- package/src/ui/FloatingToolbarLabel/FloatingToolbarLabel.tsx +24 -29
- package/src/ui/TableFloatingColumnControls/ColumnControls/index.tsx +308 -329
- package/src/ui/TableFloatingColumnControls/ColumnDropTargets/ColumnDropTarget.tsx +85 -94
- package/src/ui/TableFloatingColumnControls/ColumnDropTargets/index.tsx +46 -46
- package/src/ui/TableFloatingColumnControls/index.tsx +116 -136
- package/src/ui/TableFloatingControls/CornerControls/ClassicCornerControls.tsx +79 -91
- package/src/ui/TableFloatingControls/CornerControls/DragCornerControls.tsx +95 -102
- package/src/ui/TableFloatingControls/CornerControls/index.tsx +1 -4
- package/src/ui/TableFloatingControls/CornerControls/types.ts +8 -8
- package/src/ui/TableFloatingControls/FloatingControlsWithSelection.tsx +50 -50
- package/src/ui/TableFloatingControls/NumberColumn/index.tsx +111 -124
- package/src/ui/TableFloatingControls/RowControls/ClassicControls.tsx +86 -105
- package/src/ui/TableFloatingControls/RowControls/DragControls.tsx +305 -341
- package/src/ui/TableFloatingControls/RowDropTarget/index.tsx +72 -75
- package/src/ui/TableFloatingControls/index.tsx +191 -193
- package/src/ui/TableFullWidthLabel/index.tsx +20 -20
- package/src/ui/common-styles.ts +880 -912
- package/src/ui/consts.ts +29 -74
- package/src/ui/icons/AddColLeftIcon.tsx +33 -39
- package/src/ui/icons/AddColRightIcon.tsx +33 -39
- package/src/ui/icons/AddRowAboveIcon.tsx +16 -22
- package/src/ui/icons/AddRowBelowIcon.tsx +33 -39
- package/src/ui/icons/DisplayModeIcon.tsx +31 -31
- package/src/ui/icons/DragHandleDisabledIcon.tsx +19 -21
- package/src/ui/icons/DragHandleIcon.tsx +12 -12
- package/src/ui/icons/DragInMotionIcon.tsx +45 -52
- package/src/ui/icons/MergeCellsIcon.tsx +22 -28
- package/src/ui/icons/MinimisedHandle.tsx +9 -9
- package/src/ui/icons/SplitCellIcon.tsx +30 -36
- package/src/ui/ui-styles.ts +769 -798
- package/src/utils/alignment.ts +1 -1
- package/src/utils/analytics.ts +192 -208
- package/src/utils/collapse.ts +55 -64
- package/src/utils/column-controls.ts +237 -254
- package/src/utils/create.ts +30 -30
- package/src/utils/decoration.ts +482 -502
- package/src/utils/dom.ts +127 -134
- package/src/utils/drag-menu.ts +322 -373
- package/src/utils/get-allow-add-column-custom-step.ts +4 -5
- package/src/utils/guidelines.ts +16 -21
- package/src/utils/index.ts +68 -68
- package/src/utils/merged-cells.ts +245 -254
- package/src/utils/nodes.ts +91 -106
- package/src/utils/paste.ts +119 -135
- package/src/utils/row-controls.ts +199 -213
- package/src/utils/selection.ts +77 -87
- package/src/utils/snapping.ts +87 -100
- package/src/utils/table.ts +44 -44
- package/src/utils/transforms.ts +5 -5
- package/src/utils/update-plugin-state-decorations.ts +5 -9
|
@@ -10,16 +10,16 @@ import { getPluginState } from '../pm-plugins/plugin-factory';
|
|
|
10
10
|
import { pluginKey as tablePluginKey } from '../pm-plugins/plugin-key';
|
|
11
11
|
import { updateStickyState } from '../pm-plugins/sticky-headers/commands';
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
syncStickyRowToTable,
|
|
14
|
+
updateStickyMargins as updateTableMargin,
|
|
15
15
|
} from '../pm-plugins/table-resizing/utils/dom';
|
|
16
16
|
import type { TablePluginState } from '../types';
|
|
17
17
|
import { TableCssClassName as ClassName, TableCssClassName } from '../types';
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
stickyHeaderBorderBottomWidth,
|
|
20
|
+
stickyRowOffsetTop,
|
|
21
|
+
tableControlsSpacing,
|
|
22
|
+
tableScrollbarOffset,
|
|
23
23
|
} from '../ui/consts';
|
|
24
24
|
import type { TableDOMElements } from '../utils/dom';
|
|
25
25
|
import { getTop, getTree } from '../utils/dom';
|
|
@@ -28,9 +28,9 @@ import { supportedHeaderRow } from '../utils/nodes';
|
|
|
28
28
|
import TableNodeView from './TableNodeViewBase';
|
|
29
29
|
|
|
30
30
|
interface SentinelData {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
isIntersecting: boolean;
|
|
32
|
+
boundingClientRect: DOMRectReadOnly | null;
|
|
33
|
+
rootBounds: DOMRectReadOnly | null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// limit scroll event calls
|
|
@@ -40,367 +40,336 @@ const HEADER_ROW_SCROLL_THROTTLE_TIMEOUT = 200;
|
|
|
40
40
|
// if too short it would trigger too many dom updates.
|
|
41
41
|
const HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT = 400;
|
|
42
42
|
|
|
43
|
-
export default class TableRow
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
targetKey === 'top' ? 'bottom' : targetKey
|
|
374
|
-
].rootBounds = rootBounds ?? entry.rootBounds;
|
|
375
|
-
});
|
|
376
|
-
this.refreshStickyState();
|
|
377
|
-
},
|
|
378
|
-
{ threshold: 0, root: this.editorScrollableElement as Element },
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
private refreshStickyState() {
|
|
383
|
-
const tree = getTree(this.dom);
|
|
384
|
-
if (!tree) {
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
const { table } = tree;
|
|
388
|
-
const shouldStick = this.isHeaderSticky();
|
|
389
|
-
if (this.isSticky !== shouldStick) {
|
|
390
|
-
if (shouldStick) {
|
|
391
|
-
// The rootRect is kept in sync across sentinels so it doesn't matter which one we use.
|
|
392
|
-
const rootRect =
|
|
393
|
-
this.sentinelData.top.rootBounds ??
|
|
394
|
-
this.sentinelData.bottom.rootBounds;
|
|
395
|
-
this.makeHeaderRowSticky(tree, rootRect?.top);
|
|
396
|
-
} else {
|
|
397
|
-
this.makeRowHeaderNotSticky(table);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
private isHeaderSticky() {
|
|
403
|
-
/*
|
|
43
|
+
export default class TableRow extends TableNodeView<HTMLTableRowElement> implements NodeView {
|
|
44
|
+
constructor(
|
|
45
|
+
node: PMNode,
|
|
46
|
+
view: EditorView,
|
|
47
|
+
getPos: () => number | undefined,
|
|
48
|
+
eventDispatcher: EventDispatcher,
|
|
49
|
+
) {
|
|
50
|
+
super(node, view, getPos, eventDispatcher);
|
|
51
|
+
|
|
52
|
+
this.isHeaderRow = supportedHeaderRow(node);
|
|
53
|
+
this.isSticky = false;
|
|
54
|
+
|
|
55
|
+
const { pluginConfig } = getPluginState(view.state);
|
|
56
|
+
|
|
57
|
+
this.isStickyHeaderEnabled = !!pluginConfig.stickyHeaders;
|
|
58
|
+
|
|
59
|
+
if (this.isHeaderRow) {
|
|
60
|
+
this.dom.setAttribute('data-header-row', 'true');
|
|
61
|
+
if (this.isStickyHeaderEnabled) {
|
|
62
|
+
this.subscribe();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Variables
|
|
69
|
+
*/
|
|
70
|
+
private isHeaderRow: boolean;
|
|
71
|
+
private isStickyHeaderEnabled: boolean;
|
|
72
|
+
private editorScrollableElement?: HTMLElement | Window;
|
|
73
|
+
private colControlsOffset = 0;
|
|
74
|
+
private focused = false;
|
|
75
|
+
private topPosEditorElement = 0;
|
|
76
|
+
private isSticky: boolean;
|
|
77
|
+
private intersectionObserver?: IntersectionObserver;
|
|
78
|
+
private resizeObserver?: ResizeObserver;
|
|
79
|
+
private sentinels: {
|
|
80
|
+
top?: HTMLElement | null;
|
|
81
|
+
bottom?: HTMLElement | null;
|
|
82
|
+
} = {};
|
|
83
|
+
private sentinelData: {
|
|
84
|
+
top: SentinelData;
|
|
85
|
+
bottom: SentinelData;
|
|
86
|
+
} = {
|
|
87
|
+
top: {
|
|
88
|
+
isIntersecting: false,
|
|
89
|
+
boundingClientRect: null,
|
|
90
|
+
rootBounds: null,
|
|
91
|
+
},
|
|
92
|
+
bottom: {
|
|
93
|
+
isIntersecting: false,
|
|
94
|
+
boundingClientRect: null,
|
|
95
|
+
rootBounds: null,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
private stickyRowHeight?: number;
|
|
99
|
+
private listening = false;
|
|
100
|
+
private padding: number = 0;
|
|
101
|
+
private top: number = 0;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Methods: Nodeview Lifecycle
|
|
105
|
+
*/
|
|
106
|
+
update(node: PMNode, ..._args: any[]) {
|
|
107
|
+
// do nothing if nodes were identical
|
|
108
|
+
if (node === this.node) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// see if we're changing into a header row or
|
|
113
|
+
// changing away from one
|
|
114
|
+
const newNodeIsHeaderRow = supportedHeaderRow(node);
|
|
115
|
+
if (this.isHeaderRow !== newNodeIsHeaderRow) {
|
|
116
|
+
return false; // re-create nodeview
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// node is different but no need to re-create nodeview
|
|
120
|
+
this.node = node;
|
|
121
|
+
|
|
122
|
+
// don't do anything if we're just a regular tr
|
|
123
|
+
if (!this.isHeaderRow) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// something changed, sync widths
|
|
128
|
+
if (this.isStickyHeaderEnabled) {
|
|
129
|
+
const tbody = this.dom.parentElement;
|
|
130
|
+
const table = tbody && tbody.parentElement;
|
|
131
|
+
syncStickyRowToTable(table);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
destroy() {
|
|
138
|
+
if (this.isStickyHeaderEnabled) {
|
|
139
|
+
this.unsubscribe();
|
|
140
|
+
|
|
141
|
+
const tree = getTree(this.dom);
|
|
142
|
+
if (tree) {
|
|
143
|
+
this.makeRowHeaderNotSticky(tree.table, true);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.emitOff(true);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ignoreMutation(mutationRecord: MutationRecord | { type: 'selection'; target: Element }) {
|
|
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' && mutationRecord.target.nodeName === 'TR';
|
|
160
|
+
/**
|
|
161
|
+
* Update: should not ignore mutations when an node is added, as this interferes with
|
|
162
|
+
* prosemirrors handling of some language inputs in Safari (ie. Pinyin, Hiragana).
|
|
163
|
+
*
|
|
164
|
+
* In paticular, when a composition occurs at the start of the first node inside a table cell, if the resulting mutation
|
|
165
|
+
* from the composition end is ignored than prosemirror will end up with; invalid table markup nesting and a misplaced
|
|
166
|
+
* selection and insertion.
|
|
167
|
+
*/
|
|
168
|
+
const isNodeInsertion =
|
|
169
|
+
mutationRecord.type === 'childList' &&
|
|
170
|
+
mutationRecord.target.nodeName === 'TR' &&
|
|
171
|
+
mutationRecord.addedNodes.length;
|
|
172
|
+
|
|
173
|
+
if (isTableSelection || isNodeInsertion) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Methods
|
|
182
|
+
*/
|
|
183
|
+
private headerRowMouseScrollEnd = debounce(() => {
|
|
184
|
+
this.dom.classList.remove('no-pointer-events');
|
|
185
|
+
}, HEADER_ROW_SCROLL_RESET_DEBOUNCE_TIMEOUT);
|
|
186
|
+
|
|
187
|
+
// When the header is sticky, the header row is set to position: fixed
|
|
188
|
+
// This prevents mouse wheel scrolling on the scroll-parent div when user's mouse is hovering the header row.
|
|
189
|
+
// This fix sets pointer-events: none on the header row briefly to avoid this behaviour
|
|
190
|
+
private headerRowMouseScroll = throttle(() => {
|
|
191
|
+
if (this.isSticky) {
|
|
192
|
+
this.dom.classList.add('no-pointer-events');
|
|
193
|
+
this.headerRowMouseScrollEnd();
|
|
194
|
+
}
|
|
195
|
+
}, HEADER_ROW_SCROLL_THROTTLE_TIMEOUT);
|
|
196
|
+
|
|
197
|
+
private subscribe() {
|
|
198
|
+
this.editorScrollableElement = findOverflowScrollParent(this.view.dom as HTMLElement) || window;
|
|
199
|
+
|
|
200
|
+
if (this.editorScrollableElement) {
|
|
201
|
+
this.initObservers();
|
|
202
|
+
this.topPosEditorElement = getTop(this.editorScrollableElement);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.eventDispatcher.on('widthPlugin', this.updateStickyHeaderWidth.bind(this));
|
|
206
|
+
|
|
207
|
+
this.eventDispatcher.on((tablePluginKey as any).key, this.onTablePluginState.bind(this));
|
|
208
|
+
|
|
209
|
+
this.listening = true;
|
|
210
|
+
|
|
211
|
+
this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
|
|
212
|
+
passive: true,
|
|
213
|
+
});
|
|
214
|
+
this.dom.addEventListener('touchmove', this.headerRowMouseScroll.bind(this), { passive: true });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private unsubscribe() {
|
|
218
|
+
if (!this.listening) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (this.intersectionObserver) {
|
|
222
|
+
this.intersectionObserver.disconnect();
|
|
223
|
+
// ED-16211 Once intersection observer is disconnected, we need to remove the isObserved from the sentinels
|
|
224
|
+
// Otherwise when newer intersection observer is created it will not observe because it thinks its already being observed
|
|
225
|
+
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
226
|
+
if (el) {
|
|
227
|
+
delete el.dataset.isObserved;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (this.resizeObserver) {
|
|
233
|
+
this.resizeObserver.disconnect();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.eventDispatcher.off('widthPlugin', this.updateStickyHeaderWidth);
|
|
237
|
+
this.eventDispatcher.off((tablePluginKey as any).key, this.onTablePluginState);
|
|
238
|
+
|
|
239
|
+
this.listening = false;
|
|
240
|
+
|
|
241
|
+
this.dom.removeEventListener('wheel', this.headerRowMouseScroll);
|
|
242
|
+
this.dom.removeEventListener('touchmove', this.headerRowMouseScroll);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// initialize intersection observer to track if table is within scroll area
|
|
246
|
+
private initObservers() {
|
|
247
|
+
if (!this.dom || this.dom.dataset.isObserved) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this.dom.dataset.isObserved = 'true';
|
|
251
|
+
this.createIntersectionObserver();
|
|
252
|
+
this.createResizeObserver();
|
|
253
|
+
|
|
254
|
+
if (!this.intersectionObserver || !this.resizeObserver) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
this.resizeObserver.observe(this.dom);
|
|
259
|
+
if (this.editorScrollableElement) {
|
|
260
|
+
this.resizeObserver.observe(this.editorScrollableElement as HTMLElement);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
window.requestAnimationFrame(() => {
|
|
264
|
+
// we expect tree to be defined after animation frame
|
|
265
|
+
const tableContainer = getTree(this.dom)?.wrapper.closest(
|
|
266
|
+
`.${TableCssClassName.NODEVIEW_WRAPPER}`,
|
|
267
|
+
);
|
|
268
|
+
if (tableContainer) {
|
|
269
|
+
this.sentinels.top = tableContainer
|
|
270
|
+
.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_TOP)
|
|
271
|
+
.item(0) as HTMLElement;
|
|
272
|
+
this.sentinels.bottom = tableContainer
|
|
273
|
+
.getElementsByClassName(ClassName.TABLE_STICKY_SENTINEL_BOTTOM)
|
|
274
|
+
.item(0) as HTMLElement;
|
|
275
|
+
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
276
|
+
// skip if already observed for another row on this table
|
|
277
|
+
if (el && !el.dataset.isObserved) {
|
|
278
|
+
el.dataset.isObserved = 'true';
|
|
279
|
+
this.intersectionObserver!.observe(el);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// updating bottom sentinel position if sticky header height changes
|
|
287
|
+
// to allocate for new header height
|
|
288
|
+
private createResizeObserver() {
|
|
289
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
290
|
+
const tree = getTree(this.dom);
|
|
291
|
+
if (!tree) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const { table } = tree;
|
|
295
|
+
entries.forEach((entry) => {
|
|
296
|
+
// On resize of the parent scroll element we need to adjust the width
|
|
297
|
+
// of the sticky header
|
|
298
|
+
if (entry.target.className === (this.editorScrollableElement as HTMLElement)?.className) {
|
|
299
|
+
this.updateStickyHeaderWidth();
|
|
300
|
+
} else {
|
|
301
|
+
const newHeight = entry.contentRect
|
|
302
|
+
? entry.contentRect.height
|
|
303
|
+
: (entry.target as HTMLElement).offsetHeight;
|
|
304
|
+
|
|
305
|
+
if (
|
|
306
|
+
this.sentinels.bottom &&
|
|
307
|
+
// When the table header is sticky, it would be taller by a 1px (border-bottom),
|
|
308
|
+
// So we adding this check to allow a 1px difference.
|
|
309
|
+
Math.abs(newHeight - (this.stickyRowHeight || 0)) > stickyHeaderBorderBottomWidth
|
|
310
|
+
) {
|
|
311
|
+
this.stickyRowHeight = newHeight;
|
|
312
|
+
this.sentinels.bottom.style.bottom = `${
|
|
313
|
+
tableScrollbarOffset + stickyRowOffsetTop + newHeight
|
|
314
|
+
}px`;
|
|
315
|
+
|
|
316
|
+
updateTableMargin(table);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private createIntersectionObserver() {
|
|
324
|
+
this.intersectionObserver = new IntersectionObserver(
|
|
325
|
+
(entries: IntersectionObserverEntry[], _: IntersectionObserver) => {
|
|
326
|
+
// IMPORTANT: please try and avoid using entry.rootBounds it's terribly inconsistent. For example; sometimes it may return
|
|
327
|
+
// 0 height. In safari it will multiply all values by the window scale factor, however chrome & firfox won't.
|
|
328
|
+
// This is why i just get the scroll view bounding rect here and use it, and fallback to the entry.rootBounds if needed.
|
|
329
|
+
const rootBounds = (this.editorScrollableElement as Element)?.getBoundingClientRect?.();
|
|
330
|
+
|
|
331
|
+
entries.forEach((entry) => {
|
|
332
|
+
const { target, isIntersecting, boundingClientRect } = entry;
|
|
333
|
+
// This observer only every looks at the top/bottom sentinels, we can assume if it's not one then it's the other.
|
|
334
|
+
const targetKey = target.classList.contains(ClassName.TABLE_STICKY_SENTINEL_TOP)
|
|
335
|
+
? 'top'
|
|
336
|
+
: 'bottom';
|
|
337
|
+
// Cache the latest sentinel information
|
|
338
|
+
this.sentinelData[targetKey] = {
|
|
339
|
+
isIntersecting,
|
|
340
|
+
boundingClientRect,
|
|
341
|
+
rootBounds: rootBounds ?? entry.rootBounds,
|
|
342
|
+
};
|
|
343
|
+
// Keep the other sentinels rootBounds in sync with this latest one
|
|
344
|
+
this.sentinelData[targetKey === 'top' ? 'bottom' : targetKey].rootBounds =
|
|
345
|
+
rootBounds ?? entry.rootBounds;
|
|
346
|
+
});
|
|
347
|
+
this.refreshStickyState();
|
|
348
|
+
},
|
|
349
|
+
{ threshold: 0, root: this.editorScrollableElement as Element },
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private refreshStickyState() {
|
|
354
|
+
const tree = getTree(this.dom);
|
|
355
|
+
if (!tree) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const { table } = tree;
|
|
359
|
+
const shouldStick = this.isHeaderSticky();
|
|
360
|
+
if (this.isSticky !== shouldStick) {
|
|
361
|
+
if (shouldStick) {
|
|
362
|
+
// The rootRect is kept in sync across sentinels so it doesn't matter which one we use.
|
|
363
|
+
const rootRect = this.sentinelData.top.rootBounds ?? this.sentinelData.bottom.rootBounds;
|
|
364
|
+
this.makeHeaderRowSticky(tree, rootRect?.top);
|
|
365
|
+
} else {
|
|
366
|
+
this.makeRowHeaderNotSticky(table);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private isHeaderSticky() {
|
|
372
|
+
/*
|
|
404
373
|
# Overview
|
|
405
374
|
I'm going to list all the view states associated with the sentinels and when they should trigger sticky headers.
|
|
406
375
|
The format of the states are; {top|bottom}:{in|above|below}
|
|
@@ -427,263 +396,245 @@ export default class TableRow
|
|
|
427
396
|
is in or below it.
|
|
428
397
|
*/
|
|
429
398
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
private emitOff(isEditorDestroyed: boolean) {
|
|
672
|
-
if (this.top === 0 && this.padding === 0) {
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
this.top = 0;
|
|
677
|
-
this.padding = 0;
|
|
678
|
-
const pos = this.getPos()!;
|
|
679
|
-
|
|
680
|
-
if (!isEditorDestroyed && Number.isFinite(pos)) {
|
|
681
|
-
updateStickyState({
|
|
682
|
-
pos,
|
|
683
|
-
sticky: false,
|
|
684
|
-
top: this.top,
|
|
685
|
-
padding: this.padding,
|
|
686
|
-
})(this.view.state, this.view.dispatch, this.view);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
399
|
+
const { top: sentinelTop, bottom: sentinelBottom } = this.sentinelData;
|
|
400
|
+
// The rootRect is kept in sync across sentinels so it doesn't matter which one we use.
|
|
401
|
+
const rootBounds = sentinelTop.rootBounds ?? sentinelBottom.rootBounds;
|
|
402
|
+
|
|
403
|
+
if (!rootBounds || !sentinelTop.boundingClientRect || !sentinelBottom.boundingClientRect) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Normalize the sentinels to y points
|
|
408
|
+
// Since the sentinels are actually rects 1px high we want make sure we normalise the inner most values closest to the table.
|
|
409
|
+
const sentinelTopY = sentinelTop.boundingClientRect.bottom;
|
|
410
|
+
const sentinelBottomY = sentinelBottom.boundingClientRect.top;
|
|
411
|
+
|
|
412
|
+
// If header row height is more than 50% of viewport height don't do this
|
|
413
|
+
const isRowHeaderTooLarge =
|
|
414
|
+
this.stickyRowHeight && this.stickyRowHeight > window.innerHeight * 0.5;
|
|
415
|
+
|
|
416
|
+
const isTopSentinelAboveScrollArea =
|
|
417
|
+
!sentinelTop.isIntersecting && sentinelTopY <= rootBounds.top;
|
|
418
|
+
|
|
419
|
+
const isBottomSentinelInOrBelowScrollArea =
|
|
420
|
+
sentinelBottom.isIntersecting || sentinelBottomY > rootBounds.bottom;
|
|
421
|
+
|
|
422
|
+
// This makes sure that the top sentinel is actually above the bottom sentinel, and that they havn't inverted.
|
|
423
|
+
const isTopSentinelAboveBottomSentinel = sentinelTopY < sentinelBottomY;
|
|
424
|
+
|
|
425
|
+
return (
|
|
426
|
+
isTopSentinelAboveScrollArea &&
|
|
427
|
+
isBottomSentinelInOrBelowScrollArea &&
|
|
428
|
+
isTopSentinelAboveBottomSentinel &&
|
|
429
|
+
!isRowHeaderTooLarge
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* receive external events */
|
|
434
|
+
|
|
435
|
+
private onTablePluginState(state: TablePluginState) {
|
|
436
|
+
const tableRef = state.tableRef;
|
|
437
|
+
|
|
438
|
+
const tree = getTree(this.dom);
|
|
439
|
+
if (!tree) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// when header rows are toggled off - mark sentinels as unobserved
|
|
444
|
+
if (!state.isHeaderRowEnabled) {
|
|
445
|
+
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
446
|
+
if (el) {
|
|
447
|
+
delete el.dataset.isObserved;
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const isCurrentTableSelected = tableRef === tree.table;
|
|
453
|
+
|
|
454
|
+
// If current table selected and header row is toggled off, turn off sticky header
|
|
455
|
+
if (isCurrentTableSelected && !state.isHeaderRowEnabled && tree) {
|
|
456
|
+
this.makeRowHeaderNotSticky(tree.table);
|
|
457
|
+
}
|
|
458
|
+
this.focused = isCurrentTableSelected;
|
|
459
|
+
|
|
460
|
+
const { wrapper } = tree;
|
|
461
|
+
|
|
462
|
+
const tableContainer = wrapper.parentElement;
|
|
463
|
+
const tableContentWrapper = tableContainer?.parentElement;
|
|
464
|
+
|
|
465
|
+
const layoutContainer = tableContentWrapper && tableContentWrapper.parentElement;
|
|
466
|
+
|
|
467
|
+
if (isCurrentTableSelected) {
|
|
468
|
+
this.colControlsOffset = tableControlsSpacing;
|
|
469
|
+
|
|
470
|
+
if (layoutContainer && layoutContainer.getAttribute('data-layout-content')) {
|
|
471
|
+
// move table a little out of the way
|
|
472
|
+
// to provide spacing for table controls
|
|
473
|
+
tableContentWrapper!.style.paddingLeft = '11px';
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
this.colControlsOffset = 0;
|
|
477
|
+
if (layoutContainer && layoutContainer.getAttribute('data-layout-content')) {
|
|
478
|
+
tableContentWrapper!.style.removeProperty('padding-left');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// run after table style changes have been committed
|
|
483
|
+
setTimeout(() => {
|
|
484
|
+
syncStickyRowToTable(tree.table);
|
|
485
|
+
}, 0);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
private updateStickyHeaderWidth() {
|
|
489
|
+
// table width might have changed, sync that back to sticky row
|
|
490
|
+
const tree = getTree(this.dom);
|
|
491
|
+
if (!tree) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
syncStickyRowToTable(tree.table);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Manually refire the intersection observers.
|
|
500
|
+
* Useful when the header may have detached from the table.
|
|
501
|
+
*/
|
|
502
|
+
private refireIntersectionObservers() {
|
|
503
|
+
if (this.isSticky) {
|
|
504
|
+
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
505
|
+
if (el && this.intersectionObserver) {
|
|
506
|
+
this.intersectionObserver.unobserve(el);
|
|
507
|
+
this.intersectionObserver.observe(el);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private makeHeaderRowSticky(tree: TableDOMElements, scrollTop?: number) {
|
|
514
|
+
// If header row height is more than 50% of viewport height don't do this
|
|
515
|
+
if (this.isSticky || (this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2)) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const { table, wrapper } = tree;
|
|
520
|
+
|
|
521
|
+
// ED-16035 Make sure sticky header is only applied to first row
|
|
522
|
+
const tbody = this.dom.parentElement;
|
|
523
|
+
const isFirstHeader = tbody?.firstChild?.isEqualNode(this.dom);
|
|
524
|
+
if (!isFirstHeader) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const currentTableTop = this.getCurrentTableTop(tree);
|
|
529
|
+
|
|
530
|
+
if (!scrollTop) {
|
|
531
|
+
scrollTop = getTop(this.editorScrollableElement);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const domTop = currentTableTop > 0 ? scrollTop : scrollTop + currentTableTop;
|
|
535
|
+
|
|
536
|
+
if (!this.isSticky) {
|
|
537
|
+
syncStickyRowToTable(table);
|
|
538
|
+
this.dom.classList.add('sticky');
|
|
539
|
+
table.classList.add(ClassName.TABLE_STICKY);
|
|
540
|
+
|
|
541
|
+
this.isSticky = true;
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
|
|
545
|
+
* detaches from the table. This typically happens during a fast scroll by the user which causes
|
|
546
|
+
* the intersection observer logic to not fire as expected.
|
|
547
|
+
*/
|
|
548
|
+
this.editorScrollableElement?.addEventListener(
|
|
549
|
+
'scrollend',
|
|
550
|
+
this.refireIntersectionObservers,
|
|
551
|
+
{ passive: true, once: true },
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
const fastScrollThresholdMs = 500;
|
|
555
|
+
setTimeout(() => {
|
|
556
|
+
this.refireIntersectionObservers();
|
|
557
|
+
}, fastScrollThresholdMs);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
this.dom.style.top = `${domTop}px`;
|
|
561
|
+
updateTableMargin(table);
|
|
562
|
+
this.dom.scrollLeft = wrapper.scrollLeft;
|
|
563
|
+
|
|
564
|
+
this.emitOn(domTop, this.colControlsOffset);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private makeRowHeaderNotSticky(table: HTMLElement, isEditorDestroyed: boolean = false) {
|
|
568
|
+
if (!this.isSticky || !table || !this.dom) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
this.dom.style.removeProperty('width');
|
|
573
|
+
this.dom.classList.remove('sticky');
|
|
574
|
+
table.classList.remove(ClassName.TABLE_STICKY);
|
|
575
|
+
|
|
576
|
+
this.isSticky = false;
|
|
577
|
+
this.dom.style.top = '';
|
|
578
|
+
table.style.removeProperty('margin-top');
|
|
579
|
+
|
|
580
|
+
this.emitOff(isEditorDestroyed);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
private getWrapperoffset(inverse: boolean = false): number {
|
|
584
|
+
const focusValue = inverse ? !this.focused : this.focused;
|
|
585
|
+
return focusValue ? 0 : tableControlsSpacing;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private getWrapperRefTop(wrapper: HTMLElement): number {
|
|
589
|
+
return Math.round(getTop(wrapper)) + this.getWrapperoffset();
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// TODO: rename!
|
|
593
|
+
private getScrolledTableTop(wrapper: HTMLElement): number {
|
|
594
|
+
return this.getWrapperRefTop(wrapper) - this.topPosEditorElement;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
private getCurrentTableTop(tree: TableDOMElements): number {
|
|
598
|
+
return this.getScrolledTableTop(tree.wrapper) + tree.table.clientHeight;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* emit external events */
|
|
602
|
+
|
|
603
|
+
private emitOn(top: number, padding: number) {
|
|
604
|
+
if (top === this.top && padding === this.padding) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
this.top = top;
|
|
609
|
+
this.padding = padding;
|
|
610
|
+
const pos = this.getPos()!;
|
|
611
|
+
|
|
612
|
+
if (Number.isFinite(pos)) {
|
|
613
|
+
updateStickyState({
|
|
614
|
+
pos,
|
|
615
|
+
top,
|
|
616
|
+
sticky: true,
|
|
617
|
+
padding,
|
|
618
|
+
})(this.view.state, this.view.dispatch, this.view);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
private emitOff(isEditorDestroyed: boolean) {
|
|
623
|
+
if (this.top === 0 && this.padding === 0) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
this.top = 0;
|
|
628
|
+
this.padding = 0;
|
|
629
|
+
const pos = this.getPos()!;
|
|
630
|
+
|
|
631
|
+
if (!isEditorDestroyed && Number.isFinite(pos)) {
|
|
632
|
+
updateStickyState({
|
|
633
|
+
pos,
|
|
634
|
+
sticky: false,
|
|
635
|
+
top: this.top,
|
|
636
|
+
padding: this.padding,
|
|
637
|
+
})(this.view.state, this.view.dispatch, this.view);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
689
640
|
}
|