Fixed grid headers with CSS position sticky
See original GitHub issueHi @bvaughn,
I’m sure you’re really tired of this request, but per your comment here I think I might have a lightweight-enough solution for you to consider.
This solution keeps browser-native scrolling, doesn’t require any ScrollSync-esque behavior, and only requires a few changes to the createGridComponent
render method. However, IE11 is not supported. I think this is OK as long as it’s explicitly called out in the documentation.
You can see a demo here: https://codesandbox.io/s/xjzrzmxv8q
This is what I have locally as a prototype - if you’re interested in a PR for this, there’s definitely some more work to be done as several things are hardcoded. I can do the work here, but would appreciate some guidance.
I totally understand if you don’t want to add this - in the worst case, this issue can at least document the approach for others.
An overview of my changes:
First, when generating items
, we want to skip row == 0 and column == 0 (the Math.max() calls). We also push into leftStickyItems and topStickyItems for the visible range. Those are the only changes here.
const items = [];
const topStickyItems = [];
const leftStickyItems = [];
if (columnCount > 0 && rowCount) {
for (
let columnIndex = Math.max(1, columnStartIndex); // Skip column 0
columnIndex <= columnStopIndex;
columnIndex++
) {
topStickyItems.push(
createElement(children, {
columnIndex,
data: itemData,
isScrolling: useIsScrolling ? isScrolling : undefined,
key: itemKey({ columnIndex, data: itemData, rowIndex: 0 }),
rowIndex: 0,
style: this._getItemStyle(0, columnIndex),
})
);
}
for (
let rowIndex = Math.max(1, rowStartIndex); // Skip row 0
rowIndex <= rowStopIndex;
rowIndex++
) {
// I'm leveraging this already existing loop, but this is probably
// better as its own loop for clarity.
leftStickyItems.push(
createElement(children, {
columnIndex: 0,
data: itemData,
isScrolling: useIsScrolling ? isScrolling : undefined,
key: itemKey({ columnIndex: 0, data: itemData, rowIndex }),
rowIndex,
style: this._getItemStyle(rowIndex, 0),
})
);
for (
let columnIndex = Math.max(1, columnStartIndex); // Skip column 0
columnIndex <= columnStopIndex;
columnIndex++
) {
items.push(
createElement(children, {
columnIndex,
data: itemData,
isScrolling: useIsScrolling ? isScrolling : undefined,
key: itemKey({ columnIndex, data: itemData, rowIndex }),
rowIndex,
style: this._getItemStyle(rowIndex, columnIndex),
})
);
}
}
}
Once we’ve generated those items, before the return call, we add the sticky containers to the items array.
const topLeftStyle = this._getItemStyle(0, 0);
items.unshift(
createElement('div', {
children: leftStickyItems,
key: 'left-sticky',
className: 'left-sticky',
style: {
height: estimatedTotalHeight,
width: topLeftStyle.width,
position: 'sticky',
left: 0,
zIndex: 1,
transform: `translateY(-${topLeftStyle.height}px)`,
},
})
);
items.unshift(
createElement('div', {
children: topStickyItems,
key: 'top-sticky',
className: 'top-sticky',
style: {
height: topLeftStyle.height,
width: estimatedTotalWidth,
position: 'sticky',
top: 0,
zIndex: 1,
},
})
);
There’s one unsolved part - the (0, 0) position. I’ve gotten around this by rendering it outside of the grid (the grey box in the top left). This is fine for my use case but would merit a better solution if you want a PR for this.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:6
- Comments:14 (4 by maintainers)
Top GitHub Comments
@bvaughn First of all man I would like to thank you for this awesome lib. Provided abstraction level allows to resolve almost whatever you want. Now back to the topic. My production case is pretty complicated: multiple grouping against rows and columns which should be sticky. So I tried to simplify it as it was possible and made this sandbox based on sticky list implementation: https://codesandbox.io/s/react-window-sticky-grid-liwsd.
@apazzolini I just wanted to weigh in here and say that I’ve been playing with your branch all day and it works really well! My particular use case has to render a 5000-cell (100 rows x 50 columns) table and my first attempt with scroll-syncing was okay, but noticeably laggy. This native CSS solution is really smooth.
I understand this probably wouldn’t (and shouldn’t) be a core library feature, but hopefully it’ll find more exposure for other devs who need stick/frozen columns and rows.
Thanks for the great work, @apazzolini!