@atlaskit/editor-plugin-table 3.1.3 → 3.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 +16 -0
- package/dist/cjs/plugins/table/nodeviews/TableComponent.js +36 -2
- package/dist/cjs/plugins/table/nodeviews/TableStickyScrollbar.js +154 -0
- package/dist/cjs/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +37 -4
- package/dist/cjs/plugins/table/toolbar.js +6 -0
- package/dist/cjs/plugins/table/ui/common-styles.js +9 -2
- package/dist/es2019/plugins/table/nodeviews/TableComponent.js +37 -3
- package/dist/es2019/plugins/table/nodeviews/TableStickyScrollbar.js +112 -0
- package/dist/es2019/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +39 -6
- package/dist/es2019/plugins/table/toolbar.js +6 -0
- package/dist/es2019/plugins/table/ui/common-styles.js +33 -1
- package/dist/esm/plugins/table/nodeviews/TableComponent.js +37 -3
- package/dist/esm/plugins/table/nodeviews/TableStickyScrollbar.js +146 -0
- package/dist/esm/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.js +37 -4
- package/dist/esm/plugins/table/toolbar.js +6 -0
- package/dist/esm/plugins/table/ui/common-styles.js +8 -2
- package/dist/types/plugins/table/nodeviews/TableComponent.d.ts +1 -0
- package/dist/types/plugins/table/nodeviews/TableStickyScrollbar.d.ts +24 -0
- package/dist/types/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.d.ts +5 -0
- package/dist/types/plugins/table/toolbar.d.ts +4 -4
- package/dist/types/plugins/table/types.d.ts +3 -0
- package/dist/types/plugins/table/ui/common-styles.d.ts +1 -0
- package/dist/types-ts4.5/plugins/table/nodeviews/TableComponent.d.ts +1 -0
- package/dist/types-ts4.5/plugins/table/nodeviews/TableStickyScrollbar.d.ts +24 -0
- package/dist/types-ts4.5/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.d.ts +5 -0
- package/dist/types-ts4.5/plugins/table/toolbar.d.ts +4 -4
- package/dist/types-ts4.5/plugins/table/types.d.ts +3 -0
- package/dist/types-ts4.5/plugins/table/ui/common-styles.d.ts +1 -0
- package/package.json +6 -3
- package/src/plugins/table/nodeviews/TableComponent.tsx +54 -3
- package/src/plugins/table/nodeviews/TableStickyScrollbar.ts +204 -0
- package/src/plugins/table/pm-plugins/sticky-headers/nodeviews/tableRow.ts +40 -3
- package/src/plugins/table/toolbar.tsx +10 -6
- package/src/plugins/table/ui/common-styles.ts +38 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import rafSchedule from 'raf-schd';
|
|
2
|
+
|
|
3
|
+
import { findOverflowScrollParent } from '@atlaskit/editor-common/ui';
|
|
4
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
|
|
6
|
+
import { TableCssClassName as ClassName } from '../types';
|
|
7
|
+
|
|
8
|
+
type SentinelState = 'above' | 'visible' | 'below';
|
|
9
|
+
|
|
10
|
+
export class TableStickyScrollbar {
|
|
11
|
+
private wrapper: HTMLDivElement;
|
|
12
|
+
private view: EditorView;
|
|
13
|
+
private editorScrollableElement?: HTMLElement | Document;
|
|
14
|
+
private intersectionObserver?: IntersectionObserver;
|
|
15
|
+
private stickyScrollbarContainerElement?: HTMLDivElement | null;
|
|
16
|
+
|
|
17
|
+
private sentinels: {
|
|
18
|
+
bottom?: HTMLElement | null;
|
|
19
|
+
top?: HTMLElement | null;
|
|
20
|
+
} = {};
|
|
21
|
+
|
|
22
|
+
private topSentinelState?: SentinelState;
|
|
23
|
+
private bottomSentinelState?: SentinelState;
|
|
24
|
+
|
|
25
|
+
constructor(wrapper: HTMLDivElement, view: EditorView) {
|
|
26
|
+
this.wrapper = wrapper;
|
|
27
|
+
this.view = view;
|
|
28
|
+
|
|
29
|
+
this.init();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dispose() {
|
|
33
|
+
if (this.stickyScrollbarContainerElement) {
|
|
34
|
+
this.stickyScrollbarContainerElement.removeEventListener(
|
|
35
|
+
'scroll',
|
|
36
|
+
this.handleScrollDebounced,
|
|
37
|
+
);
|
|
38
|
+
this.handleScrollDebounced.cancel();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.deleteIntesactionObserver();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
scrollLeft(left: number) {
|
|
45
|
+
if (this.stickyScrollbarContainerElement) {
|
|
46
|
+
this.stickyScrollbarContainerElement.scrollLeft = left;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private init() {
|
|
51
|
+
if (!this.wrapper) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.stickyScrollbarContainerElement =
|
|
56
|
+
this.wrapper.parentElement?.querySelector(
|
|
57
|
+
`.${ClassName.TABLE_STICKY_SCROLLBAR_CONTAINER}`,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (this.stickyScrollbarContainerElement) {
|
|
61
|
+
this.stickyScrollbarContainerElement.addEventListener(
|
|
62
|
+
'scroll',
|
|
63
|
+
this.handleScrollDebounced,
|
|
64
|
+
{ passive: true },
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.createIntersectionObserver();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private createIntersectionObserver() {
|
|
72
|
+
this.editorScrollableElement =
|
|
73
|
+
(findOverflowScrollParent(this.view.dom) as HTMLElement) ||
|
|
74
|
+
window.document;
|
|
75
|
+
|
|
76
|
+
if (!this.editorScrollableElement || !this.wrapper) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.intersectionObserver = new IntersectionObserver(
|
|
81
|
+
(entries: IntersectionObserverEntry[], _: IntersectionObserver) => {
|
|
82
|
+
if (!this.stickyScrollbarContainerElement) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
entries.forEach((entry) => {
|
|
87
|
+
const target = entry.target as HTMLElement;
|
|
88
|
+
// if the rootBounds has 0 height, e.g. confluence preview mode, we do nothing.
|
|
89
|
+
if (entry.rootBounds?.height === 0) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
target.classList.contains(
|
|
95
|
+
ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_BOTTOM,
|
|
96
|
+
)
|
|
97
|
+
) {
|
|
98
|
+
this.sentenialBottomCallback(entry);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
target.classList.contains(
|
|
103
|
+
ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_TOP,
|
|
104
|
+
)
|
|
105
|
+
) {
|
|
106
|
+
this.sentenialTopCallback(entry);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
{ root: this.editorScrollableElement },
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
this.sentinels.bottom = this.wrapper?.parentElement
|
|
114
|
+
?.getElementsByClassName(ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_BOTTOM)
|
|
115
|
+
?.item(0) as HTMLElement;
|
|
116
|
+
|
|
117
|
+
this.sentinels.top = this.wrapper?.parentElement
|
|
118
|
+
?.getElementsByClassName(ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_TOP)
|
|
119
|
+
?.item(0) as HTMLElement;
|
|
120
|
+
|
|
121
|
+
[this.sentinels.bottom, this.sentinels.top].forEach((el) =>
|
|
122
|
+
this.intersectionObserver!.observe(el),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private deleteIntesactionObserver() {
|
|
127
|
+
if (this.intersectionObserver) {
|
|
128
|
+
if (this.sentinels.bottom) {
|
|
129
|
+
this.intersectionObserver.unobserve(this.sentinels.bottom);
|
|
130
|
+
}
|
|
131
|
+
this.intersectionObserver.disconnect();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private sentenialBottomCallback(entry: IntersectionObserverEntry) {
|
|
136
|
+
const sentinelIsAboveScrollArea =
|
|
137
|
+
entry.boundingClientRect.top < (entry.rootBounds?.top || 0);
|
|
138
|
+
|
|
139
|
+
this.bottomSentinelState = sentinelIsAboveScrollArea
|
|
140
|
+
? 'above'
|
|
141
|
+
: entry.isIntersecting
|
|
142
|
+
? 'visible'
|
|
143
|
+
: 'below';
|
|
144
|
+
|
|
145
|
+
this.toggle();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private sentenialTopCallback(entry: IntersectionObserverEntry) {
|
|
149
|
+
const sentinelIsBelowScrollArea =
|
|
150
|
+
(entry.rootBounds?.bottom || 0) < entry.boundingClientRect.top;
|
|
151
|
+
|
|
152
|
+
this.topSentinelState = sentinelIsBelowScrollArea
|
|
153
|
+
? 'below'
|
|
154
|
+
: entry.isIntersecting
|
|
155
|
+
? 'visible'
|
|
156
|
+
: 'above';
|
|
157
|
+
|
|
158
|
+
this.toggle();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private toggle() {
|
|
162
|
+
if (
|
|
163
|
+
(this.topSentinelState === 'visible' ||
|
|
164
|
+
this.topSentinelState === 'above') &&
|
|
165
|
+
this.bottomSentinelState === 'below'
|
|
166
|
+
) {
|
|
167
|
+
this.show();
|
|
168
|
+
} else {
|
|
169
|
+
this.hide();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private hide() {
|
|
174
|
+
if (
|
|
175
|
+
this.stickyScrollbarContainerElement &&
|
|
176
|
+
this.stickyScrollbarContainerElement.style.display !== 'none'
|
|
177
|
+
) {
|
|
178
|
+
this.stickyScrollbarContainerElement.style.display = 'none';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private show() {
|
|
183
|
+
if (
|
|
184
|
+
this.stickyScrollbarContainerElement &&
|
|
185
|
+
this.stickyScrollbarContainerElement.style.display !== 'block'
|
|
186
|
+
) {
|
|
187
|
+
this.stickyScrollbarContainerElement.style.display = 'block';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private handleScroll = (event: Event) => {
|
|
192
|
+
if (
|
|
193
|
+
!this.stickyScrollbarContainerElement ||
|
|
194
|
+
!this.wrapper ||
|
|
195
|
+
event.target !== this.stickyScrollbarContainerElement
|
|
196
|
+
) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.wrapper.scrollLeft = this.stickyScrollbarContainerElement.scrollLeft;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
private handleScrollDebounced = rafSchedule(this.handleScroll);
|
|
204
|
+
}
|
|
@@ -150,10 +150,13 @@ export class TableRowNodeView implements NodeView {
|
|
|
150
150
|
|
|
151
151
|
this.listening = true;
|
|
152
152
|
|
|
153
|
-
this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this)
|
|
153
|
+
this.dom.addEventListener('wheel', this.headerRowMouseScroll.bind(this), {
|
|
154
|
+
passive: true,
|
|
155
|
+
});
|
|
154
156
|
this.dom.addEventListener(
|
|
155
157
|
'touchmove',
|
|
156
158
|
this.headerRowMouseScroll.bind(this),
|
|
159
|
+
{ passive: true },
|
|
157
160
|
);
|
|
158
161
|
}
|
|
159
162
|
|
|
@@ -164,7 +167,7 @@ export class TableRowNodeView implements NodeView {
|
|
|
164
167
|
if (this.intersectionObserver) {
|
|
165
168
|
this.intersectionObserver.disconnect();
|
|
166
169
|
// ED-16211 Once intersection observer is disconnected, we need to remove the isObserved from the sentinels
|
|
167
|
-
// Otherwise when
|
|
170
|
+
// Otherwise when newer intersection observer is created it will not observe because it thinks its already being observed
|
|
168
171
|
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
169
172
|
if (el) {
|
|
170
173
|
delete el.dataset.isObserved;
|
|
@@ -522,9 +525,27 @@ export class TableRowNodeView implements NodeView {
|
|
|
522
525
|
return false;
|
|
523
526
|
};
|
|
524
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Manually refire the intersection observers.
|
|
530
|
+
* Useful when the header may have detached from the table.
|
|
531
|
+
*/
|
|
532
|
+
refireIntersectionObservers = () => {
|
|
533
|
+
if (this.isSticky) {
|
|
534
|
+
[this.sentinels.top, this.sentinels.bottom].forEach((el) => {
|
|
535
|
+
if (el && this.intersectionObserver) {
|
|
536
|
+
this.intersectionObserver.unobserve(el);
|
|
537
|
+
this.intersectionObserver.observe(el);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
|
|
525
543
|
makeHeaderRowSticky = (tree: TableDOMElements, scrollTop?: number) => {
|
|
526
544
|
// If header row height is more than 50% of viewport height don't do this
|
|
527
|
-
if (
|
|
545
|
+
if (
|
|
546
|
+
this.isSticky ||
|
|
547
|
+
(this.stickyRowHeight && this.stickyRowHeight > window.innerHeight / 2)
|
|
548
|
+
) {
|
|
528
549
|
return;
|
|
529
550
|
}
|
|
530
551
|
|
|
@@ -552,6 +573,22 @@ export class TableRowNodeView implements NodeView {
|
|
|
552
573
|
table.classList.add(ClassName.TABLE_STICKY);
|
|
553
574
|
|
|
554
575
|
this.isSticky = true;
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* The logic below is not desirable, but acts as a fail safe for scenarios where the sticky header
|
|
579
|
+
* detaches from the table. This typically happens during a fast scroll by the user which causes
|
|
580
|
+
* the intersection observer logic to not fire as expected.
|
|
581
|
+
*/
|
|
582
|
+
this.editorScrollableElement?.addEventListener(
|
|
583
|
+
'scrollend',
|
|
584
|
+
this.refireIntersectionObservers,
|
|
585
|
+
{ passive: true, once: true },
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
const fastScrollThresholdMs = 500;
|
|
589
|
+
setTimeout(() => {
|
|
590
|
+
this.refireIntersectionObservers();
|
|
591
|
+
}, fastScrollThresholdMs);
|
|
555
592
|
}
|
|
556
593
|
|
|
557
594
|
this.dom.style.top = `${domTop}px`;
|
|
@@ -33,13 +33,13 @@ import {
|
|
|
33
33
|
getNodeName,
|
|
34
34
|
isReferencedSource,
|
|
35
35
|
} from '@atlaskit/editor-common/utils';
|
|
36
|
-
import { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
37
|
-
import { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
36
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
37
|
+
import type { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
38
38
|
import { findParentDomRefOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
39
|
-
import { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
39
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
40
40
|
import { akEditorFloatingPanelZIndex } from '@atlaskit/editor-shared-styles';
|
|
41
41
|
import { shortcutStyle } from '@atlaskit/editor-shared-styles/shortcut';
|
|
42
|
-
import { Rect } from '@atlaskit/editor-tables/table-map';
|
|
42
|
+
import type { Rect } from '@atlaskit/editor-tables/table-map';
|
|
43
43
|
import {
|
|
44
44
|
findCellRectClosestToPos,
|
|
45
45
|
findTable,
|
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
splitCell,
|
|
49
49
|
} from '@atlaskit/editor-tables/utils';
|
|
50
50
|
import RemoveIcon from '@atlaskit/icon/glyph/editor/remove';
|
|
51
|
+
import { getBooleanFF } from '@atlaskit/platform-feature-flags';
|
|
51
52
|
|
|
52
53
|
import {
|
|
53
54
|
clearHoverSelection,
|
|
@@ -79,13 +80,13 @@ import { pluginKey as tableResizingPluginKey } from './pm-plugins/table-resizing
|
|
|
79
80
|
import { getNewResizeStateFromSelectedColumns } from './pm-plugins/table-resizing/utils/resize-state';
|
|
80
81
|
import { pluginKey as tableWidthPluginKey } from './pm-plugins/table-width';
|
|
81
82
|
import { canMergeCells } from './transforms';
|
|
82
|
-
import {
|
|
83
|
+
import type {
|
|
83
84
|
PluginConfig,
|
|
84
|
-
TableCssClassName,
|
|
85
85
|
ToolbarMenuConfig,
|
|
86
86
|
ToolbarMenuContext,
|
|
87
87
|
ToolbarMenuState,
|
|
88
88
|
} from './types';
|
|
89
|
+
import { TableCssClassName } from './types';
|
|
89
90
|
import { messages as ContextualMenuMessages } from './ui/FloatingContextualMenu/ContextualMenu';
|
|
90
91
|
import tableMessages from './ui/messages';
|
|
91
92
|
import {
|
|
@@ -520,6 +521,9 @@ export const getToolbarConfig =
|
|
|
520
521
|
getDomRef,
|
|
521
522
|
nodeType,
|
|
522
523
|
offset: [0, 18],
|
|
524
|
+
absoluteOffset: getBooleanFF('platform.editor.table-sticky-scrollbar')
|
|
525
|
+
? { top: -6 }
|
|
526
|
+
: { top: 0 },
|
|
523
527
|
zIndex: akEditorFloatingPanelZIndex + 1, // Place the context menu slightly above the others
|
|
524
528
|
items: [
|
|
525
529
|
menu,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
akEditorTableToolbarSize,
|
|
15
15
|
akEditorUnitZIndex,
|
|
16
16
|
getSelectionStyles,
|
|
17
|
+
MAX_BROWSER_SCROLLBAR_HEIGHT,
|
|
17
18
|
relativeFontSizeToBase16,
|
|
18
19
|
SelectionStyle,
|
|
19
20
|
} from '@atlaskit/editor-shared-styles';
|
|
@@ -75,6 +76,7 @@ const cornerControlHeight = tableToolbarSize + 1;
|
|
|
75
76
|
its center should be aligned with the edge
|
|
76
77
|
*/
|
|
77
78
|
export const insertColumnButtonOffset = tableInsertColumnButtonSize / 2;
|
|
79
|
+
export const tableRowHeight = 44;
|
|
78
80
|
|
|
79
81
|
const rangeSelectionStyles = `
|
|
80
82
|
.${ClassName.NODEVIEW_WRAPPER}.${akEditorSelectedNodeClassName} table tbody tr {
|
|
@@ -114,6 +116,41 @@ const sentinelStyles = `.${ClassName.TABLE_CONTAINER} {
|
|
|
114
116
|
}
|
|
115
117
|
}`;
|
|
116
118
|
|
|
119
|
+
const stickyScrollbarSentinelStyles = `.${ClassName.TABLE_CONTAINER} {
|
|
120
|
+
> .${ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_BOTTOM},
|
|
121
|
+
> .${ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_TOP} {
|
|
122
|
+
position: absolute;
|
|
123
|
+
width: 100%;
|
|
124
|
+
height: 1px;
|
|
125
|
+
margin-top: -1px;
|
|
126
|
+
// need this to avoid sentinel being focused via keyboard
|
|
127
|
+
// this still allows it to be detected by intersection observer
|
|
128
|
+
visibility: hidden;
|
|
129
|
+
}
|
|
130
|
+
> .${ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_TOP} {
|
|
131
|
+
top: ${columnControlsDecorationHeight + tableRowHeight * 3}px;
|
|
132
|
+
}
|
|
133
|
+
> .${ClassName.TABLE_STICKY_SCROLLBAR_SENTINEL_BOTTOM} {
|
|
134
|
+
bottom: ${MAX_BROWSER_SCROLLBAR_HEIGHT}px;
|
|
135
|
+
}
|
|
136
|
+
}`;
|
|
137
|
+
|
|
138
|
+
const stickyScrollbarContainerStyles = `.${ClassName.TABLE_CONTAINER} {
|
|
139
|
+
> .${ClassName.TABLE_STICKY_SCROLLBAR_CONTAINER} {
|
|
140
|
+
width: 100%;
|
|
141
|
+
display: none;
|
|
142
|
+
overflow-x: auto;
|
|
143
|
+
position: sticky;
|
|
144
|
+
bottom: 0;
|
|
145
|
+
}
|
|
146
|
+
}`;
|
|
147
|
+
|
|
148
|
+
const stickyScrollbarStyles = () => {
|
|
149
|
+
return getBooleanFF('platform.editor.table-sticky-scrollbar')
|
|
150
|
+
? `${stickyScrollbarContainerStyles} ${stickyScrollbarSentinelStyles}`
|
|
151
|
+
: '';
|
|
152
|
+
};
|
|
153
|
+
|
|
117
154
|
const shadowSentinelStyles = `
|
|
118
155
|
.${ClassName.TABLE_SHADOW_SENTINEL_LEFT},
|
|
119
156
|
.${ClassName.TABLE_SHADOW_SENTINEL_RIGHT} {
|
|
@@ -417,6 +454,7 @@ export const tableStyles = (
|
|
|
417
454
|
|
|
418
455
|
${sentinelStyles}
|
|
419
456
|
${OverflowShadow(props)}
|
|
457
|
+
${stickyScrollbarStyles()}
|
|
420
458
|
|
|
421
459
|
.${ClassName.TABLE_STICKY} .${ClassName.TABLE_STICKY_SHADOW} {
|
|
422
460
|
height: 0; // stop overflow flash & set correct height in update-overflow-shadows.ts
|