Header flickering and freezing issue with material table (implemented with react window + react window infinite loader) - javascript

I have implemented an Infinite scrolling table with react-window (based on #60) and material UI.
The table seems to be fulfilling the main objective but there are few issues with it -
header is not sticking on the top in spite of having styles in place (stickyHeader prop).
header is flashing/flickering while scrolling quickly towards up direction.
[Steps to reproduce - scroll 50% down then do fast scroll up and observe the header movement ].
Here's the codesandbox example - https://codesandbox.io/s/react-window-with-table-elements-forked-huti6?file=/src/index.tsx:514-542

Use default value for TableContainer overflow-x and the sticky header should work
import { makeStyles } from '#material-ui/core/styles'
const useStyles = makeStyles({
tableContainer: {
overflowX: "initial"
}
})
function Inner({ children, ...rest }, ref) {
const { header, footer, top } = useContext(VirtualTableContext)
const classes = useStyles();
return (
<TableContainer classes={{root: classes.tableContainer}} {...rest} ref={ref}>
...

As an alternative to your implementation, the Material UI docs have a working example of a virtualized table which achieves what you're looking for using react-virtualized. That is, the header is sticky and there is no flashing/flickering of the header during scroll.
Below is a codesandbox
containing a working example to reflect your desired layout.

First about the react-window: The Scollbars in the react-window are for the outercontainer not for InnerContainer. Now don't try to replace the outer container itself, unless you want to handle everything yourself. All the scroll events a few more things are attached to the outercontainer.
Think of it like outer container decides/takes the size and the larger inner container gets scrolled inside it.
Now looking at your case I assume you are trying to make the table behave like any other grid where headers are fixed and content is scrolled if it's getting overflow. your table header is way lower in the element hierarchy than the inner container, so no way you can write any simple css (or js logic) to achieve in current hierarchy.
that's why even though you have set the stickyheader for the MaterialUI table, it won't stick. Cause your whole table (MaterialUI table) is getting scrolled inside the outer container of react-window.
I would suggest you to move your table header outside of the react-window and only place the rows in the react-widow. that's the way it's suppose to behave (i.e. react window treats everything in it as scrollable content). See one of the presentation below:
A little tip on redesigning of your table (guess the alignment can be improved by additional css)

Related

How to make react-window FixedSizeList scroll with the viewport instead of the component's scroll bar?

I am trying to integrate react-window's FixedSizeList and FixedSizeGrid components to increase the initial rendering speed of my page. Is there some way for me to let the user scroll down the react-window component using the viewport's scrolling area? I was also
wondering if there is some way to remove the scrollbar from the react-window component and only use the viewport's scrolling as I described above.
I tried integrating the documentation version of FixedSizeList into my page and as you can see, since the total height of all my rows is greater than the height I specified in the component so the vertical scrollbar beside the component appears, which I want to remove. I also cannot figure out how to let scrolling downwards on the viewport make the react-window component scroll down the rest of its rows.
From looking online, I think I might need to modify the CSS style of the FixedSizeList to have overflow:hidden to remove the scrollbar but how can I ensure that I keep the scrolling functionality and that the user can scroll down the component from anywhere in the viewport?
Current version with no react-window
FixedSizeList version
const Row = ({ index, style }) => (
<div style={style}>Row {index}</div>
);
<FixedSizeList
height={500}
itemCount={38}
itemSize={35}
width={"100%"}
>
{Row}
</FixedSizeList>
One solution is to use a package linked from the react-window github page called react-virtualized-auto-sizer. It is also made by bvaughn and is a good solution to the problem.
This solves the issue by allowing you to set the height of your FixedSizedList to the height of its content, so it does not get a scrollbar. Here's how that would look:
<AutoSizer>
{({ height, width }) => (
<FixedSizeList
className="List"
height={height}
itemCount={1000}
itemSize={35}
width={width}
>
{Row}
</FixedSizeList>
)}
</AutoSizer>
Here's a full example on codesandbox:

ReactJS MaterialUI Stepper breaks on overflow

I have been trying to convince the Material UI Stepper component to fit my needs, that is to say display severl steps and overflow, but it continues doing this:
I don't know how to explain the error... the separators are doing weird things on horizontal overflow. Here is the sandbox. How can I get this working?
TL;DR: 1) Word-break your labels (<StepLabel style={{ wordBreak: "break-all" }}>) so they don't invade layouts among steps and 2) provide an initial width to the stepper (<Stepper ... style={{ width: 64*16 }}>) so it does not use the parent width to do so.
I fixed your pastebin:
NL;PR: You had two styles missing to handle long words and content within a scrollable container:
For 1) the labels' words were bigger than the allotted space to render the steps since 550px is too tight for 16 steps, so they broke the layout, play it safe by not letting lengthy words overflow the label.
For 2) consider given the Stepper component a proportional value (64 (stepper min-width)* 16(stepper count)), and since you want to have a scroller, the width of the scrollable container is used as the initial width of the content, thus you must specify the content(Stepper) so it is not shrunk too much before overflowing.
Note: Those styles I provided are examples, you can customize them to your needs. Also, consider passing those changes via classes.

Scrolling element scrollbar jumping to top after re-render

I'm using Gatsby and i made a vertical side navigation menu. I used useRef for navigation wrapper and for every item inside menu for getting some height values. Then I calculated the scroll position for active navigation link as you can see below and set scrollTop value for navigation wrapper and i can align scroll bar to active link item.
function calculateScrollPosition(wrapper, link) {
const distance = wrapper.current.scrollHeight - wrapper.current.offsetHeight
const scrollUnit = wrapper.current.scrollHeight / distance
const itemOffset =
wrapper.current.scrollHeight / 2 < link.current.offsetTop
? link.current.offsetTop + link.current.clientHeight
: link.current.offsetTop - link.current.clientHeight
const position = Math.floor(itemOffset / scrollUnit)
wrapper.current.scrollTop = position
}
and i am calling it here
useEffect(() => {
if (activeLink) {
calculateScrollPosition(list, activeLink)
}
}, [activeLink])
But each time I click the link inside sidebar and change the page, scrollbar of navigation jumping to top for a second and then finding the right place. I expect it to go to new position from last position, not jumping to top. Any help i will appreciated.
Thank you for your comments. I actually fixed my problem so i want to explain how i did.
As i mentioned, I'm using Gatsby and i have multi-lang website. Pages creating dynamically with .md files. Gatsby re-renders when the top level component changes between pages. Actually that was broke my sidebar transitions too because it was unmount and mount layout again when i change page.
Gatsby doesn't automatically wrap pages in a layout component. So I wrapped pages with layout using gatsby-plugin-layout. I made 2 different layout. One of it main layout with sidebar and header, other one for my dynamically created pages. I can use one of inside another. Now its not re-render my main layout, it is just re-render layout which i created for pages inside main layout, scrollbar not jumping to top and transitions are works fine.
I don't know if i explained well. I hope it helps others who have the same problem.

Using Parent Scrollbar instead of Sub Component Scrollbar with Angular Virtual Scroll

Context
I'm using angular virtual scroll to display a long list of elements. My design is very simple and can be seen in the image below.
Current Behaviour
Currently, the virtual scroll has its own scrollbar which operates differently from the parent scroll bar. This makes it very tedious to scroll to the footer. i.e. When you scroll to the end of the list it does not scroll to the footer.
Desired Behaviour
I want the virtual scroll container to use the parent scroll bar instead of its own such that when the end of the list is reached, regular scrolling is resumed.
A Stackblitz can be found here illustrating the undesired behaviour: https://stackblitz.com/angular/aemdyrjmebn
This is default scrolling behavior native to the browser (I tried it on Chrome). For example the same thing will happen with the overflow: scroll example on MDN: after the container has been fully scrolled down, the scrolling of the parent/window will not begin until the mouse is moved. This is also what happens with Angular Virtual Scrolling
If there is another way to achieve what you're looking for you would need to do it when the virtual container has been scrolled to the bottom - and you can get this event with the following code:
#ViewChild(CdkVirtualScrollViewport, {static: false}) virtualScroll: CdkVirtualScrollViewport;
ngAfterViewInit() {
this.virtualScroll.elementScrolled().pipe(
filter(event => {
return this.virtualScroll.measureScrollOffset('bottom') === 0;
}),
tap((event)=> {
// do something here
})
).subscribe()
}

Resizable divs within a container w/o Jquery

Ok - So I am kind of struggling with this. I am trying to achieve something pretty straight forward I think. I need 3 resizable divs within a fixed width container div without using Jquery. The resize needs to occur for all divs only horizontally as they all have the parent divs height. Here is the layout
<div id="container">
<div id="m1">M1</div>
<div id="m2">M2</div>
<div id="m3">M3</div>
</div>
My object here is to wrap this into a ReactJS component and therefore not wanting to muddy the waters with Jquery. Any help/direction would be most appreciated. If someone can mock up something like this in React, that would be awesome too! :)
Not sure whether this can be a CSS only solution but I am open to ideas
Thanks
Your question isn't exactly clear to me, if your container div is fixed width (as in a constant value) then your child divs should not resize dynamically no matter what the width of anything beyond the container is.
The only other way I could interpret your question, is that you need some sort of grid system, where you can drag to resize the width of the child divs within the confines of the container. In that case, I might be wrong but I don't think the solution is that simple, you might want to take a look at this package: https://github.com/STRML/react-grid-layout
More specifically, their simplest implementation allows you to define a fixed width container (1200 in this case) like so:
<ReactGridLayout className="layout" layout={layout} cols={12} rowHeight={30} width={1200}>
<div key={'a'}>a</div>
<div key={'b'}>b</div>
<div key={'c'}>c</div>
</ReactGridLayout>
If you want to fix the parent div's size and then have the inner divs automatically resize based on the parent div you can use the flex box layout which you can find here: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
In your case you can set the styling of the first div to:
.container {
display: flex;
}
And for the children (in your case identified by m1, m2, and m3) you need to set the css property flex-grow to how big do you want it to be relative to the others. Say, if you want m1 and m2 to have the same size and m3 to be twice as big, you would do the following:
#m1, #m2 {
flex-grow: 1;
}
#m3 {
flex-grow: 2;
}
Depending on what you want to do, you might want to consider renaming the ids/classes.
In case you are looking to get the exact size of the parent of a div you can do this as following:
render() {
return <div ref="node"></div>
}
componentDidMount() {
const parentWidth = this.refs['node'].parentNode.clientWidth;
// Do whatever you need here (like setting state of the react component)
}

Categories