Skip to content

Commit dc22c99

Browse files
committed
Improve handling of scroll shadow.
1 parent 15456fa commit dc22c99

File tree

11 files changed

+139
-77
lines changed

11 files changed

+139
-77
lines changed

graylog2-web-interface/src/components/common/EntityDataTable/EntityDataTable.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,9 @@ const EntityDataTable = <Entity extends EntityBase, Meta = unknown>({
198198
const authorizedColumnSchemas = useAuthorizedColumnSchemas(columnSchemas);
199199
const columnRenderersByAttribute = useColumnRenderers<Entity, Meta>(authorizedColumnSchemas, customColumnRenderers);
200200
const { headerMinWidths, handleHeaderSectionResize } = useHeaderMinWidths();
201+
const scrollContainerRef = useRef<HTMLDivElement>(null);
201202
const scrolledToRightIndicator = useRef<HTMLDivElement>();
202-
const scrolledToRight = useIntersectionObserver(scrolledToRightIndicator);
203+
const scrolledToRight = useIntersectionObserver(scrollContainerRef, scrolledToRightIndicator);
203204

204205
const [internalAttributeColumnOrder, setInternalAttributeColumnOrder] = useState<Array<string>>(
205206
layoutPreferences?.order ?? defaultColumnOrder,
@@ -221,17 +222,19 @@ const EntityDataTable = <Entity extends EntityBase, Meta = unknown>({
221222
displayBulkSelectCol,
222223
);
223224

224-
const { tableRef, columnWidths, handleActionsWidthChange, tableIsCompressed, actionsColMinWidth } = useElementWidths<
225+
const { columnWidths, handleActionsWidthChange, tableIsCompressed, actionsColMinWidth } = useElementWidths<
225226
Entity,
226227
Meta
227228
>({
228229
columnRenderersByAttribute,
229230
columnSchemas: authorizedColumnSchemas,
230231
columnWidthPreferences: internalColumnWidthPreferences,
231232
displayBulkSelectCol,
233+
entities,
234+
hasRowActions,
232235
headerMinWidths,
236+
scrollContainerRef,
233237
visibleColumns: columnOrder,
234-
entities,
235238
});
236239

237240
const columnDefinitions = useColumnDefinitions<Entity, Meta>({
@@ -302,7 +305,7 @@ const EntityDataTable = <Entity extends EntityBase, Meta = unknown>({
302305
{({ activeColId, columnTransform }) => (
303306
<ScrollContainer
304307
id="scroll-container"
305-
ref={tableRef}
308+
ref={scrollContainerRef}
306309
$actionsHeaderWidth={actionsColMinWidth}
307310
$activeColId={activeColId}
308311
$columnTransform={columnTransform}

graylog2-web-interface/src/components/common/EntityDataTable/PinnedColScrollShadow.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

graylog2-web-interface/src/components/common/EntityDataTable/Table.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ import { Table as BaseTable } from 'components/bootstrap';
2323
import ExpandedSections from 'components/common/EntityDataTable/ExpandedSections';
2424
import { CELL_PADDING, ACTIONS_COL_ID } from 'components/common/EntityDataTable/Constants';
2525
import type { EntityBase, ExpandedSectionRenderers, ColumnMetaContext } from 'components/common/EntityDataTable/types';
26-
import { columnOpacityVar, columnTransformVar, columnTransition } from 'components/common/EntityDataTable/CSSVariables';
27-
import PinnedColScrollShadow from 'components/common/EntityDataTable/PinnedColScrollShadow';
26+
import {
27+
columnOpacityVar,
28+
columnTransformVar,
29+
columnTransition,
30+
displayScrollRightIndicatorVar,
31+
} from 'components/common/EntityDataTable/CSSVariables';
32+
import ScrollShadow from 'theme/box-shadows/ScrollShadow';
2833

2934
import TableHead from './TableHead';
3035

@@ -78,7 +83,10 @@ const Td = styled.td<{
7883
${$colId === ACTIONS_COL_ID &&
7984
css`
8085
position: sticky;
81-
${PinnedColScrollShadow}
86+
${ScrollShadow('left')}
87+
&::before {
88+
display: var(${displayScrollRightIndicatorVar}, none);
89+
}
8290
`}
8391
`,
8492
);

graylog2-web-interface/src/components/common/EntityDataTable/TableHead.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ import {
2424
columnOpacityVar,
2525
columnWidthVar,
2626
columnTransition,
27+
displayScrollRightIndicatorVar,
2728
} from 'components/common/EntityDataTable/CSSVariables';
2829
import { ACTIONS_COL_ID } from 'components/common/EntityDataTable/Constants';
29-
import PinnedColScrollShadow from 'components/common/EntityDataTable/PinnedColScrollShadow';
30+
import ScrollShadow from 'theme/box-shadows/ScrollShadow';
3031

3132
import type { EntityBase, ColumnMetaContext } from './types';
3233

@@ -65,7 +66,10 @@ export const Th = styled.th<{
6566
${$colId === ACTIONS_COL_ID &&
6667
css`
6768
position: sticky;
68-
${PinnedColScrollShadow}
69+
${ScrollShadow('left')}
70+
&::before {
71+
display: var(${displayScrollRightIndicatorVar}, none);
72+
}
6973
`}
7074
`,
7175
);

graylog2-web-interface/src/components/common/EntityDataTable/hooks/useActionsColumnDefinition.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ const useActionsColumnDefinition = <Entity extends EntityBase>({
129129
enableHiding: false,
130130
enablePinning: true,
131131
enableResizing: false,
132-
header,
132+
header: hasRowActions ? header : undefined,
133133
cell: hasRowActions ? cell : undefined,
134134
meta: {
135135
hideCellPadding: true,

graylog2-web-interface/src/components/common/EntityDataTable/hooks/useActionsColumnWidth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { CELL_PADDING } from 'components/common/EntityDataTable/Constants';
2121

2222
import type { EntityBase } from '../types';
2323

24-
const useActionsColumnWidth = <Entity extends EntityBase>(entities: ReadonlyArray<Entity>) => {
24+
const useActionsColumnWidth = <Entity extends EntityBase>(entities: ReadonlyArray<Entity>, hasRowActions: boolean) => {
2525
const [rowWidths, setRowWidths] = useState<{ [rowId: string]: number }>({});
2626
const visibleRowIdsSet = useMemo(() => new Set(entities.map(({ id }) => id)), [entities]);
2727

@@ -64,7 +64,7 @@ const useActionsColumnWidth = <Entity extends EntityBase>(entities: ReadonlyArra
6464
.filter((width) => !!width),
6565
);
6666

67-
return maxWidth + CELL_PADDING * 2;
67+
return hasRowActions ? maxWidth + CELL_PADDING * 2 : 0;
6868
}, [rowWidths, visibleRowIdsSet]);
6969

7070
return { colMinWidth, handleWidthChange };

graylog2-web-interface/src/components/common/EntityDataTable/hooks/useColumnWidths.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import type { EntityBase, ColumnRenderersByAttribute } from '../types';
2727

2828
const assignableTableWidth = ({
29-
tableWidth,
29+
scrollContainerWidth,
3030
actionsColMinWidth,
3131
bulkSelectColWidth,
3232
columnIds,
@@ -35,12 +35,12 @@ const assignableTableWidth = ({
3535
actionsColMinWidth: number;
3636
bulkSelectColWidth: number;
3737
columnIds: Array<string>;
38-
tableWidth: number;
38+
scrollContainerWidth: number;
3939
staticColumnWidths: { [colId: string]: number };
4040
}) => {
4141
const staticColWidths = columnIds.reduce((total, id) => total + (staticColumnWidths[id] ?? 0), 0);
4242

43-
return tableWidth - bulkSelectColWidth - actionsColMinWidth - staticColWidths;
43+
return scrollContainerWidth - bulkSelectColWidth - actionsColMinWidth - staticColWidths;
4444
};
4545

4646
const calculateColumnWidths = ({
@@ -118,15 +118,15 @@ const useColumnWidths = <Entity extends EntityBase>({
118118
bulkSelectColWidth,
119119
columnRenderersByAttribute,
120120
columnIds,
121-
tableWidth,
121+
scrollContainerWidth,
122122
columnWidthPreferences,
123123
headerMinWidths,
124124
}: {
125125
actionsColMinWidth: number;
126126
bulkSelectColWidth: number;
127127
columnRenderersByAttribute: ColumnRenderersByAttribute<Entity>;
128128
columnIds: Array<string>;
129-
tableWidth: number;
129+
scrollContainerWidth: number;
130130
columnWidthPreferences: { [key: string]: number } | undefined;
131131
headerMinWidths: { [colId: string]: number };
132132
}) => {
@@ -143,7 +143,7 @@ const useColumnWidths = <Entity extends EntityBase>({
143143
);
144144

145145
useLayoutEffect(() => {
146-
if (!tableWidth) {
146+
if (!scrollContainerWidth) {
147147
return;
148148
}
149149

@@ -152,7 +152,7 @@ const useColumnWidths = <Entity extends EntityBase>({
152152
actionsColMinWidth,
153153
columnIds,
154154
bulkSelectColWidth,
155-
tableWidth,
155+
scrollContainerWidth,
156156
staticColumnWidths,
157157
});
158158

@@ -173,7 +173,7 @@ const useColumnWidths = <Entity extends EntityBase>({
173173
bulkSelectColWidth,
174174
columnRenderersByAttribute,
175175
columnIds,
176-
tableWidth,
176+
scrollContainerWidth,
177177
columnWidthPreferences,
178178
staticColumnWidths,
179179
headerMinWidths,

graylog2-web-interface/src/components/common/EntityDataTable/hooks/useElementWidths.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* <http://www.mongodb.com/licensing/server-side-public-license>.
1616
*/
1717

18-
import { useRef, useMemo } from 'react';
18+
import { useMemo } from 'react';
1919

2020
import type { EntityBase, ColumnRenderersByAttribute } from 'components/common/EntityDataTable/types';
2121
import type { ColumnSchema } from 'components/common/EntityDataTable';
@@ -29,24 +29,27 @@ type Props<Entity extends EntityBase, Meta> = {
2929
columnSchemas: Array<ColumnSchema>;
3030
columnWidthPreferences: { [colId: string]: number };
3131
displayBulkSelectCol: boolean;
32+
hasRowActions: boolean;
3233
headerMinWidths: { [colId: string]: number };
3334
visibleColumns: Array<string>;
3435
entities: ReadonlyArray<Entity>;
36+
scrollContainerRef: React.RefObject<HTMLDivElement>;
3537
};
3638

3739
const useElementWidths = <Entity extends EntityBase, Meta>({
3840
columnRenderersByAttribute,
3941
columnSchemas,
4042
columnWidthPreferences,
4143
displayBulkSelectCol,
44+
entities,
45+
hasRowActions,
4246
headerMinWidths,
47+
scrollContainerRef,
4348
visibleColumns,
44-
entities,
4549
}: Props<Entity, Meta>) => {
46-
const tableRef = useRef<HTMLDivElement>(null);
4750
const { colMinWidth: actionsColMinWidth, handleWidthChange: handleActionsWidthChange } =
48-
useActionsColumnWidth<Entity>(entities);
49-
const { width: tableWidth } = useElementDimensions(tableRef);
51+
useActionsColumnWidth<Entity>(entities, hasRowActions);
52+
const { width: scrollContainerWidth } = useElementDimensions(scrollContainerRef);
5053
const columnIds = useMemo(
5154
() => columnSchemas.filter(({ id }) => visibleColumns.includes(id)).map(({ id }) => id),
5255
[columnSchemas, visibleColumns],
@@ -58,12 +61,10 @@ const useElementWidths = <Entity extends EntityBase, Meta>({
5861
columnRenderersByAttribute,
5962
columnWidthPreferences,
6063
headerMinWidths,
61-
tableWidth,
64+
scrollContainerWidth,
6265
});
6366

6467
return {
65-
tableRef,
66-
tableWidth,
6768
handleActionsWidthChange,
6869
columnWidths,
6970
tableIsCompressed: actionsColMinWidth === columnWidths[ACTIONS_COL_ID],

graylog2-web-interface/src/hooks/useIntersectionObserver.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717

1818
import { useEffect, useState } from 'react';
1919

20-
const useIntersectionObserver = (ref: React.RefObject<HTMLDivElement>) => {
20+
const useIntersectionObserver = (
21+
containerRef: React.RefObject<HTMLDivElement>,
22+
targetElementRef: React.RefObject<HTMLDivElement>,
23+
) => {
2124
const [elementIsVisible, setElementIsVisible] = useState(false);
2225

2326
useEffect(() => {
2427
const observer = new IntersectionObserver(
2528
([entry]) => {
2629
setElementIsVisible(!entry.isIntersecting);
2730
},
28-
{ threshold: 0.9 },
31+
{ threshold: 0.9, root: containerRef.current },
2932
);
3033

31-
const indicatorElement = ref.current;
34+
const indicatorElement = targetElementRef.current;
3235

3336
if (indicatorElement) {
3437
observer.observe(indicatorElement);
@@ -39,7 +42,7 @@ const useIntersectionObserver = (ref: React.RefObject<HTMLDivElement>) => {
3942
observer.unobserve(indicatorElement);
4043
}
4144
};
42-
}, [ref]);
45+
}, [containerRef, targetElementRef]);
4346

4447
return elementIsVisible;
4548
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (C) 2020 Graylog, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*/
17+
import { css } from 'styled-components';
18+
19+
const SIZE = 5;
20+
type Side = 'left' | 'right' | 'top' | 'bottom';
21+
22+
const shadowXY = (side: Side) => {
23+
switch (side) {
24+
case 'left':
25+
return { x: -4, y: 0 };
26+
case 'right':
27+
return { x: 4, y: 0 };
28+
case 'top':
29+
return { x: 0, y: -4 };
30+
case 'bottom':
31+
return { x: 0, y: 4 };
32+
}
33+
};
34+
35+
const sideGeometry = (side: Side) => {
36+
switch (side) {
37+
case 'left':
38+
return css`
39+
left: 0;
40+
top: 0;
41+
bottom: 0;
42+
width: ${SIZE}px;
43+
`;
44+
case 'right':
45+
return css`
46+
right: 0;
47+
top: 0;
48+
bottom: 0;
49+
width: ${SIZE}px;
50+
`;
51+
case 'top':
52+
return css`
53+
top: 0;
54+
left: 0;
55+
right: 0;
56+
height: ${SIZE}px;
57+
`;
58+
case 'bottom':
59+
return css`
60+
bottom: 0;
61+
left: 0;
62+
right: 0;
63+
height: ${SIZE}px;
64+
`;
65+
}
66+
};
67+
68+
const ScrollShadow = (side: Side) => {
69+
const { x, y } = shadowXY(side);
70+
71+
return css`
72+
&::before {
73+
content: '';
74+
position: absolute;
75+
pointer-events: none;
76+
z-index: -1;
77+
box-shadow: ${x}px ${y}px 8px rgb(0 0 0 / 10%);
78+
${sideGeometry(side)}
79+
}
80+
`;
81+
};
82+
83+
export default ScrollShadow;

0 commit comments

Comments
 (0)