Repository navigation
fix: Keep sticky header cols in sync with grouped table columns #5194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1b80207
e6f093f
995fc44
37fbce9
df4a588
d5912d3
80d3275
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,21 +34,24 @@ function readWidths( | |
| return result; | ||
| } | ||
|
|
||
| function getMinWidth(column: ColumnWidthDefinition | undefined) { | ||
| if (typeof column?.minWidth === 'number') { | ||
| return column.minWidth; | ||
| } | ||
| if (typeof column?.width === 'number' && column.width < DEFAULT_COLUMN_WIDTH) { | ||
| return column.width; | ||
| } | ||
| return DEFAULT_COLUMN_WIDTH; | ||
| } | ||
|
|
||
| function updateWidths( | ||
| visibleColumns: readonly ColumnWidthDefinition[], | ||
| oldWidths: Map<PropertyKey, number>, | ||
| newWidth: number, | ||
| columnId: PropertyKey | ||
| ): Map<PropertyKey, number> { | ||
| const column = visibleColumns.find(column => column.id === columnId); | ||
| let minWidth = DEFAULT_COLUMN_WIDTH; | ||
| if (typeof column?.width === 'number' && column.width < DEFAULT_COLUMN_WIDTH) { | ||
| minWidth = column?.width; | ||
| } | ||
| if (typeof column?.minWidth === 'number') { | ||
| minWidth = column?.minWidth; | ||
| } | ||
| newWidth = Math.max(newWidth, minWidth); | ||
| newWidth = Math.max(newWidth, getMinWidth(column)); | ||
| if (oldWidths.get(columnId) === newWidth) { | ||
| return oldWidths; | ||
| } | ||
|
|
@@ -60,15 +63,17 @@ function updateWidths( | |
| interface WidthsContext { | ||
| getColumnStyles(sticky: boolean, columnId: PropertyKey): ColumnWidthStyle; | ||
| columnWidths: Map<PropertyKey, number>; | ||
| getColumnMinWidth: (columnId: PropertyKey) => number; | ||
| updateColumn: (columnId: PropertyKey, newWidth: number) => void; | ||
| updateGroup: (groupId: PropertyKey, newWidth: number) => void; | ||
| setCell: (sticky: boolean, columnId: PropertyKey, node: null | HTMLElement) => void; | ||
| setCol: (columnId: PropertyKey, node: null | HTMLElement) => void; | ||
| setCol: (sticky: boolean, columnId: PropertyKey, node: null | HTMLElement) => void; | ||
| } | ||
|
|
||
| const WidthsContext = createContext<WidthsContext>({ | ||
| getColumnStyles: () => ({}), | ||
| columnWidths: new Map(), | ||
| getColumnMinWidth: () => DEFAULT_COLUMN_WIDTH, | ||
| updateColumn: () => {}, | ||
| updateGroup: () => {}, | ||
| setCell: () => {}, | ||
|
|
@@ -97,6 +102,8 @@ export function ColumnWidthsProvider({ | |
| const cellsRef = useRef(new Map<PropertyKey, HTMLElement>()); | ||
| const stickyCellsRef = useRef(new Map<PropertyKey, HTMLElement>()); | ||
| const colsRef = useRef(new Map<PropertyKey, HTMLElement>()); | ||
| // Only filled with column groups, where the sticky <col> widths take priority over the sticky cell widths. | ||
| const stickyColsRef = useRef(new Map<PropertyKey, HTMLElement>()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is this different from stickyCellsRef we already have?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. stickyCellsRef holds the sticky header cells, and this holds the sticky s. When s exist, the browser takes column widths from them, not from the cells. We were only updating the cells, so the s kept their first-render width, and that's the bug. we're using col for grouped ones: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/col
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand correctly, when we have grouped columns the widths measurement should be done differently - with respect to the groups. When grouping is not used - then technically stickyCellsRef and stickyColsRef should be identical. If thats the case, case we remove the stickyCellsRef then? If we cannot do this optimisation and we have to keep both - let's add code comments to explain why is that needed.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agree, I'll add a comment. They're not quite the identical, the sticky table only renders a |
||
| const hasColElements = useRef(false); | ||
| const getCell = (columnId: PropertyKey): null | HTMLElement => cellsRef.current.get(columnId) ?? null; | ||
| const setCell = (sticky: boolean, columnId: PropertyKey, node: null | HTMLElement) => { | ||
|
|
@@ -107,7 +114,15 @@ export function ColumnWidthsProvider({ | |
| ref.current.delete(columnId); | ||
| } | ||
| }; | ||
| const setCol = (columnId: PropertyKey, node: null | HTMLElement) => { | ||
| const setCol = (sticky: boolean, columnId: PropertyKey, node: null | HTMLElement) => { | ||
| if (sticky) { | ||
| if (node) { | ||
| stickyColsRef.current.set(columnId, node); | ||
| } else { | ||
| stickyColsRef.current.delete(columnId); | ||
| } | ||
| return; | ||
| } | ||
| if (node) { | ||
| colsRef.current.set(columnId, node); | ||
| hasColElements.current = true; | ||
|
|
@@ -158,8 +173,16 @@ export function ColumnWidthsProvider({ | |
| const syncStickyColumnWidths = useStableCallback(() => { | ||
| for (const { id } of visibleColumns) { | ||
| const element = stickyCellsRef.current.get(id); | ||
| const colElement = stickyColsRef.current.get(id); | ||
| if (!element && !colElement) { | ||
| continue; | ||
| } | ||
| const styles = getColumnStyles(true, id); | ||
| if (element) { | ||
| setElementWidths(element, getColumnStyles(true, id)); | ||
| setElementWidths(element, styles); | ||
| } | ||
| if (colElement) { | ||
| setElementWidths(colElement, { width: styles.width }); | ||
| } | ||
| } | ||
| }); | ||
|
|
@@ -255,6 +278,8 @@ export function ColumnWidthsProvider({ | |
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, []); | ||
|
|
||
| const getColumnMinWidth = (columnId: PropertyKey) => getMinWidth(visibleColumns.find(col => col.id === columnId)); | ||
|
|
||
| function updateColumn(columnId: PropertyKey, newWidth: number) { | ||
| setColumnWidths(columnWidths => updateWidths(visibleColumns, columnWidths ?? new Map(), newWidth, columnId)); | ||
| } | ||
|
|
@@ -282,7 +307,15 @@ export function ColumnWidthsProvider({ | |
|
|
||
| return ( | ||
| <WidthsContext.Provider | ||
| value={{ getColumnStyles, columnWidths: columnWidths ?? new Map(), updateColumn, updateGroup, setCell, setCol }} | ||
| value={{ | ||
| getColumnStyles, | ||
| columnWidths: columnWidths ?? new Map(), | ||
| getColumnMinWidth, | ||
| updateColumn, | ||
| updateGroup, | ||
| setCell, | ||
| setCol, | ||
| }} | ||
| > | ||
| {children} | ||
| </WidthsContext.Provider> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the bug 2, this used to use the 120 default width every time. That's what made the tracker and the column edge drift apart. using the actual min width instead.