Skip to content

Commit 767d63e

Browse files
Remove reflows
1 parent c4047b3 commit 767d63e

File tree

4 files changed

+72
-66
lines changed

4 files changed

+72
-66
lines changed

polaris-react/src/components/IndexTable/IndexTable.tsx

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ export interface TableHeadingRect {
5858
}
5959

6060
const SCROLL_BAR_PADDING = 4;
61-
const SIXTY_FPS = 1000 / 60;
6261
const SCROLL_BAR_DEBOUNCE_PERIOD = 300;
6362
const SMALL_SCREEN_WIDTH = 458;
6463

@@ -151,59 +150,52 @@ function IndexTableBase({
151150

152151
const resizeTableHeadings = useMemo(
153152
() =>
154-
debounce(
155-
() => {
156-
if (!tableElement.current || !scrollableContainerElement.current) {
157-
return;
158-
}
153+
debounce(() => {
154+
if (!tableElement.current || !scrollableContainerElement.current) {
155+
return;
156+
}
159157

160-
const boundingRect =
161-
scrollableContainerElement.current.getBoundingClientRect();
162-
tablePosition.current = {
163-
top: boundingRect.top,
164-
left: boundingRect.left,
165-
};
158+
const boundingRect =
159+
scrollableContainerElement.current.getBoundingClientRect();
160+
tablePosition.current = {
161+
top: boundingRect.top,
162+
left: boundingRect.left,
163+
};
166164

167-
tableHeadingRects.current = tableHeadings.current.map((heading) => ({
168-
offsetWidth: heading.offsetWidth || 0,
169-
offsetLeft: heading.offsetLeft || 0,
170-
}));
165+
tableHeadingRects.current = tableHeadings.current.map((heading) => ({
166+
offsetWidth: heading.offsetWidth || 0,
167+
offsetLeft: heading.offsetLeft || 0,
168+
}));
171169

172-
if (tableHeadings.current.length === 0) {
173-
return;
174-
}
170+
if (tableHeadings.current.length === 0) {
171+
return;
172+
}
173+
174+
// update left offset for first column
175+
if (selectable && tableHeadings.current.length > 1)
176+
tableHeadings.current[1].style.left = `${tableHeadingRects.current[0].offsetWidth}px`;
175177

176-
// update left offset for first column
177-
if (selectable && tableHeadings.current.length > 1)
178-
tableHeadings.current[1].style.left = `${tableHeadingRects.current[0].offsetWidth}px`;
178+
// update the min width of the checkbox to be the be the un-padded width of the first heading
179+
if (selectable && firstStickyHeaderElement?.current) {
180+
const elementStyle = getComputedStyle(tableHeadings.current[0]);
181+
const boxWidth = tableHeadings.current[0].offsetWidth;
182+
firstStickyHeaderElement.current.style.minWidth = `calc(${boxWidth}px - ${elementStyle.paddingLeft} - ${elementStyle.paddingRight} + 2px)`;
183+
}
179184

180-
// update the min width of the checkbox to be the be the un-padded width of the first heading
181-
if (selectable && firstStickyHeaderElement?.current) {
182-
const elementStyle = getComputedStyle(tableHeadings.current[0]);
183-
const boxWidth = tableHeadings.current[0].offsetWidth;
184-
firstStickyHeaderElement.current.style.minWidth = `calc(${boxWidth}px - ${elementStyle.paddingLeft} - ${elementStyle.paddingRight} + 2px)`;
185+
// update sticky header min-widths
186+
stickyTableHeadings.current.forEach((heading, index) => {
187+
let minWidth = 0;
188+
if (index === 0 && (!isSmallScreen() || !selectable)) {
189+
minWidth = calculateFirstHeaderOffset();
190+
} else if (selectable && tableHeadingRects.current.length > index) {
191+
minWidth = tableHeadingRects.current[index]?.offsetWidth || 0;
192+
} else if (!selectable && tableHeadingRects.current.length >= index) {
193+
minWidth = tableHeadingRects.current[index - 1]?.offsetWidth || 0;
185194
}
186195

187-
// update sticky header min-widths
188-
stickyTableHeadings.current.forEach((heading, index) => {
189-
let minWidth = 0;
190-
if (index === 0 && (!isSmallScreen() || !selectable)) {
191-
minWidth = calculateFirstHeaderOffset();
192-
} else if (selectable && tableHeadingRects.current.length > index) {
193-
minWidth = tableHeadingRects.current[index]?.offsetWidth || 0;
194-
} else if (
195-
!selectable &&
196-
tableHeadingRects.current.length >= index
197-
) {
198-
minWidth = tableHeadingRects.current[index - 1]?.offsetWidth || 0;
199-
}
200-
201-
heading.style.minWidth = `${minWidth}px`;
202-
});
203-
},
204-
SIXTY_FPS,
205-
{leading: true, trailing: true, maxWait: SIXTY_FPS},
206-
),
196+
heading.style.minWidth = `${minWidth}px`;
197+
});
198+
}),
207199
[calculateFirstHeaderOffset, selectable],
208200
);
209201

@@ -231,21 +223,25 @@ function IndexTableBase({
231223

232224
const [canScrollRight, setCanScrollRight] = useState(true);
233225

234-
const handleCanScrollRight = useCallback(() => {
235-
if (
236-
!lastColumnSticky ||
237-
!tableElement.current ||
238-
!scrollableContainerElement.current
239-
) {
240-
return;
241-
}
226+
const handleCanScrollRight = useCallback(
227+
() =>
228+
debounce(() => {
229+
if (
230+
!lastColumnSticky ||
231+
!tableElement.current ||
232+
!scrollableContainerElement.current
233+
) {
234+
return;
235+
}
242236

243-
const tableRect = tableElement.current.getBoundingClientRect();
244-
const scrollableRect =
245-
scrollableContainerElement.current.getBoundingClientRect();
237+
const tableRect = tableElement.current.getBoundingClientRect();
238+
const scrollableRect =
239+
scrollableContainerElement.current.getBoundingClientRect();
246240

247-
setCanScrollRight(tableRect.width > scrollableRect.width);
248-
}, [lastColumnSticky]);
241+
setCanScrollRight(tableRect.width > scrollableRect.width);
242+
}),
243+
[lastColumnSticky],
244+
);
249245

250246
useEffect(() => {
251247
handleCanScrollRight();

polaris-react/src/components/IndexTable/components/Checkbox/Checkbox.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import React, {
88
ReactNode,
99
} from 'react';
1010

11+
import {debounce} from '../../../../utilities/debounce';
1112
import {useI18n} from '../../../../utilities/i18n';
1213
import {classNames} from '../../../../utilities/css';
1314
import {RowContext} from '../../../../utilities/index-table';
@@ -57,14 +58,21 @@ interface CheckboxWrapperProps {
5758
}
5859

5960
export function CheckboxWrapper({children}: CheckboxWrapperProps) {
61+
const {position} = useContext(RowContext);
6062
const checkboxNode = useRef<HTMLTableDataCellElement>(null);
6163

62-
const handleResize = useCallback(() => {
63-
if (!checkboxNode.current) return;
64+
// eslint-disable-next-line react-hooks/exhaustive-deps
65+
const handleResize = useCallback(
66+
debounce(() => {
67+
if (position !== 0) return;
68+
69+
if (!checkboxNode.current) return;
6470

65-
const {width} = checkboxNode.current.getBoundingClientRect();
66-
setRootProperty('--pc-checkbox-offset', `${width}px`);
67-
}, []);
71+
const {width} = checkboxNode.current.getBoundingClientRect();
72+
setRootProperty('--pc-checkbox-offset', `${width}px`);
73+
}),
74+
[position],
75+
);
6876

6977
useEffect(() => {
7078
handleResize();

polaris-react/src/components/IndexTable/components/Row/Row.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ export const Row = memo(function Row({
6060
() => ({
6161
itemId: id,
6262
selected,
63+
position,
6364
onInteraction: handleInteraction,
6465
}),
65-
[id, selected, handleInteraction],
66+
[id, selected, handleInteraction, position],
6667
);
6768

6869
const primaryLinkElement = useRef<HTMLAnchorElement | null>(null);

polaris-react/src/utilities/index-table/context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {createContext} from 'react';
33
interface RowContextType {
44
itemId?: string;
55
selected?: boolean;
6+
position?: number;
67
onInteraction?: (event: React.MouseEvent | React.KeyboardEvent) => void;
78
}
89

0 commit comments

Comments
 (0)