Closed mdingena closed 6 years ago
Yes, that seems like a valid point. There's no official api to do this yet, but something like this should work:
class MyComp extends Component {
handleRef = n => this.stickyBoxInstance = n
recalculatePosition = () => {
this.stickyBoxInstance.latestScrollY = -1
this.stickyBoxInstance.handleScroll()
}
render() {
return <StickyBox ref={this.handleRef}>{content}</StickyBox>
}
}
recalculatePosition
simulates a scroll event. Setting latestScrollY
to -1
implies that the handleScroll
event will behave as if you scrolled down.
I've tried implementing your suggestion, but maybe I'm not doing it right. I'm trying to call recalculatePosition()
from a child component, like so:
export default class extends Component {
[... stuff omitted for brevity ...]
// Recalculate StickyBox position with custom trigger
// https://github.com/codecks-io/react-sticky-box/issues/16
filterStickyBoxRef = n => this.stickyBoxInstance = n;
recalculatePosition = () => {
console.log( "recalc called" );
this.stickyBoxInstance.latestScrollY = -1;
this.stickyBoxInstance.handleScroll();
}
render() {
return (
<div className="wrap layout">
<StickyBox className="sidebar small" ref={ this.filterStickyBoxRef } offset={ 56 }>
<Filter
filter={ this.state.query }
onFilterChange={ this.filterChanged }
onRelationChange={ this.relationChanged }
onCollapse={ this.recalculatePosition }
{ ...this.props }
/>
</StickyBox>
<section className="main">
[...]
</section>
<StickyBox className="sidebar medium" offset={ 56 }>
<Details { ...this.state.details } isLoading={ this.state.loadingDetails } />
</StickyBox>
</div>
);
}
Then inside <Filter />
I'm calling this.props.onCollapse()
. I've confirmed that this works because the console logs "recalc called"
when the list collapses, but the list doesn't appear back on the screen. When I scroll a little bit, it immediately jumps back into the right position. So the idea is good, but I'm not executing it right.
It might be that simulating scrollDown
might be the wrong event in your context. So what you could try instead is replacing this.stickyBoxInstance.latestScrollY = -1
with a very high value to simulate a scroll down.
Thinking about it, you probably also need to wait with calling this method until the filterChange is flushed to the DOM. So you might want to try putting the call in a componentDidUpdate
.
I'm well aware that all this is all quite hacky and I'm very open to having a clean API that "just works". But this process definitely helps understanding the problem space!
Inside <Filter />
, I only proceeded with calling this.props.onCollapse()
after the state (expanded / collapsed) was changed. I do this using the setState
callback.
<button
className="expand"
onClick={ () => this.setState({ genresHidden : !this.state.genresHidden }, () => this.props.onCollapse() ) }
>
Show { this.state.genresHidden ? 'all' : 'less' } genres
</button>
Do you think this is still too early to call it?
I'm not too certain about this part of the life cycle. But you could test it by adding a timeout of like 20ms. If it makes a difference, then the setState callback might not be the best place for this.
A 20ms timeout does the trick, but it's still a dirty hack ;)
I'd love to see something official for this. I have a second use case for it, too:
When I click items in the sidebar filter, I reload the page results and scroll the page back to the top. But the sidebar is fixed to its previous location. Also here, a manual trigger to recalculate the new sidebar position would be excellent!
I just released v0.7.1 which automatically should do the right thing if the the sticky box's contents change in size. You can try out the behaviour in the new docs. Maybe this solves your underlying issue as well?
I believe the behaviour for expanding and collapsing works like a charm (at least in the docs, I still need to test this in my app later this week).
However, I'm not sure if a jump in the page itself would correctly reposition the StickyBox. Could you add this use case to the documentation if you already support it?
Basically a "Jump to top" button somewhere in the content.
In my particular use case, there is a list of blocks with infinite scrolling. The StickyBox sidebar contains filter options. When you change the filters, the list reloads with the new matching items, so I use window.scrollTo
to go all the way back up. Right now, I have to make additional calls to your 'hack' to reposition the StickyBox after 20ms, otherwise it will float below the viewport at its old position. Of course it immediately corrects itself when I start scrolling, but as long as I don't scroll, the filter sidebar is not visible.
I just added that button to the full page example. And I could reproduce it for the behaviour: 'instant'
case.
I've added a section in the troubleshooting that solved the problem for me.
Excellent, I will report my findings when I implement 0.7.1 (or 0.7.2) later this week.
My findings so far, using 0.7.2
:
Does not work:
window.scrollTo(0, 0);
window.dispatchEvent(new Event('scroll'));
Works:
window.scrollTo({top: 0, behaviour: 'smooth'});
Repositioning the StickyBox after collapsing its contents does not seem to work on my end. I've tried examining your setup and you don't seem to call an API to StickyBox anywhere, it's simply the content that disappears?
My initial setup was like this (pseudo):
{ this.state.loading ? null : topTenItems }
<div className={ this.state.genresHidden ? 'hidden' : '' }>
{
this.state.loading ? null : this.state.filters.genre
.slice( 10 )
.map( genre => <Item key={ `genre-${ genre.id }` } name={ genre.name } /> ))
}
</div>
{ this.expandButton() }
And so I thought, maybe it's because the additional items that can be collapsed are tucked in a container. I made them direct siblings, but it's still not affecting the StickyBox position.
{ this.state.loading ? null : topTenItems }
{
this.state.loading || this.state.genresHidden ? null : this.state.filters.genre
.slice( 10 )
.map( genre => <Item key={ `genre-${ genre.id }` } name={ genre.name } /> ))
}
{ this.expandButton() }
Can you try cloning my repo and reproduce it?
npm install
(You will need to use some Code-Fu if you don't have a Font Awesome Pro subscription 🤐)npm start
http://localhost:3000/search
I think I just found the main issue: the scrollPane (the html tag in your case) has a height of 0
.
If I add this to your css it seems to be working as intended. Even without all the reposition hooks.
html {
...
min-height: 100vh;
}
But then I'm not 100% sure whats really going on in your html
and body
classes!
In any case I'll add a warning to this library should it detect a scrollPane of height 0
You're right, didn't even notice that. It happens because body { position: absolute }
which must have been a brainfart many commits back. I can't really justify why I need(ed) that, so I'm taking it out.
However, even after taking it out, and also even after adding html { height: 100vh; }
, StickyBox is not behaving as intended.
Interesting! What browser have you been using? I was checking it out on chrome. When using your master branch, the sticky box wouldn't move at all, because of the height=0
issue. All I then changed was adding the min-height: 100vh;
and removing all reposition logic and it seemed to work as expected.
Here's the changes I made you can try and run: https://github.com/danielberndt/tjoonz-client/tree/sticky-box-fix
(sorry it's a bit messy due to me removing all icons to make it work. (and prettier remformatting every file I've touched)
Oh and v0.7.3 could probably have some impact for your use case as well (fixing #24)
Sorry, it would've been better to commit the reformatted files first, then commit your changes. They are in the same commit now, and I can't really figure out the lines you changed. Could you add some comments to it? I will run a test with 0.7.3
later this week.
Yeah, sorry, I didn't pay too much attention here, since I didn't attend to publish and just wanted to make it work. I've added two comments to my commit indicating where I changed things.
I've upgraded to 0.7.5
, taken out all the hacks from before, and it seems to work out of the box now. Great, thanks!
Can I force StickyBox to recalculate its
top
position?I'm displaying an expandable list in a sidebar. When the list expands, the StickyBox sticks nicely when necessary. However, when I click the button at the bottom of the list to collapse it back into showing the top 10 items only, the StickyBox disappears off-screen. This is because the content is now much less high, and its
top
position is still offset from its longer previous height.I'd like to be able to trigger a recalculation of the
top
position after collapsing my list. Is there a way to do that? I've thought about faking a browser window resize event, but that seems very dirty...