Skip to content
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

Flex reverse #173

Closed
stecb opened this issue Jul 14, 2023 · 18 comments
Closed

Flex reverse #173

stecb opened this issue Jul 14, 2023 · 18 comments
Labels
question Further information is requested

Comments

@stecb
Copy link

stecb commented Jul 14, 2023

Hey @bvaughn, first of all thanks a lot for the lib, it's excellent! You deserve way more than just a coffee!

So, we implemented a "resizable" layout, and it works perfectly, but we're trying to achieve something probably unsupported by the lib. As inside of the panels there're some components w/ some (complex) state/interactions, and we also want to be able to "swap" them (i.e. left w/ the right one), we naively thought about "hacking" it via setting flex-direction: row-reverse to the GroupContainer. It actually works, but of course the resize handlers do not work as expected, as they go in the opposite direction (see attached screen rec).

resize-reverse.mov

Given we can't actually easily take a snapshot of the state inside of the panels and re-hydrate them if we actually "swap" them via a re-render, i.e.

const leftContent = isMainContentLeft ? MainContent : SideContent
const rightContent = isMainContentLeft ? SideContent : MainContent

return (
  <PanelGroup direction="horizontal">
    <Panel>{leftContent}</Panel>
    <PanelResizeHandle />
    <Panel>{rightContent}</Panel>
  </PanelGroup>
);

Do you see any other possible "hacks" to make it work by "visually" swapping the panels? 🤔 Or maybe there's something hidden in the props we can use.

Thanks again.

@stecb
Copy link
Author

stecb commented Jul 14, 2023

I haven't said, but it's obvious I will try to contribute and support this in the case we can see there are no options available RN.

@bvaughn
Copy link
Owner

bvaughn commented Jul 14, 2023

Can you give me a narrowed down repro to look at? (Like a link to that Code Sandbox the video was made from?)

You deserve way more than just a coffee!

Well okay if you insist

@stecb
Copy link
Author

stecb commented Jul 14, 2023

Can you give me a narrowed down repro to look at? (Like a link to that Code Sandbox the video was made from?)

Well that video was just from your codesandbox's demo, I just added a style obj to it + made it horizontal.

If you want I can make another one.

You deserve way more than just a coffee!

Well okay if you insist

Bear w me!

Thanks again! 🙏

@bvaughn
Copy link
Owner

bvaughn commented Jul 14, 2023

I see. Yes, I think it would be useful for you to give me an example that is close to what you want as possible, and then I can try an idea or two. Otherwise I might be solving the wrong problem.

@stecb
Copy link
Author

stecb commented Jul 14, 2023

@bvaughn
Copy link
Owner

bvaughn commented Jul 15, 2023

Thanks. This is very helpful.

I was on-call this week so I didn't have a lot of extra time but I'll try to take a look this afternoon.

@bvaughn bvaughn closed this as completed Jul 15, 2023
@bvaughn
Copy link
Owner

bvaughn commented Jul 15, 2023

Here you go: https://codesandbox.io/s/react-resizable-panels-forked-7zzz83

Based on what you've described, I think this should work for you:

  • Render panel content as portals, so you can move it around without losing component state.
  • Use onLayout and panel.resize() to swap sizes when you swap panels.

❤️ → ☕ givebrian.coffee

@bvaughn bvaughn reopened this Jul 15, 2023
@bvaughn bvaughn closed this as completed Jul 15, 2023
@bvaughn bvaughn added the question Further information is requested label Jul 15, 2023
@stecb
Copy link
Author

stecb commented Jul 16, 2023

Here you go: https://codesandbox.io/s/react-resizable-panels-forked-7zzz83

Based on what you've described, I think this should work for you:

  • Render panel content as portals, so you can move it around without losing component state.

  • Use onLayout and panel.resize() to swap sizes when you swap panels.


❤️ → ☕ givebrian.coffee

N1! Thanks 🙏

@stecb
Copy link
Author

stecb commented Jul 17, 2023

Hey @bvaughn sorry to bother again, I think I still have the same issue, as the actual content ("holding" the state) is coming as a prop, and I actually need to "inject" it into the Panels, see example here

Do you have any ideas? 🤔

@bvaughn
Copy link
Owner

bvaughn commented Jul 17, 2023

Don't really understand what you're saying, sorry.

@stecb
Copy link
Author

stecb commented Jul 17, 2023

https://codesandbox.io/s/react-resizable-panels-forked-7zzz83

Let me rephrase: in your working example, you had both SampleContentLeft and SampleContentRight with some state inside. After swapping them, the state was kept intact.

The problem is: if I want to render another component inside SampleContentLeft or SampleContentLeft that has its own state, that state resets every time we swap them (here's it).

@bvaughn
Copy link
Owner

bvaughn commented Jul 17, 2023

State is state. It shouldn't matter if it's in the immediate component or a nested one. I suspect something else is going wrong.

@bvaughn
Copy link
Owner

bvaughn commented Jul 17, 2023

I suspect what's happening is that this–

if (container == null) {
  return null;
}

–is rendering in between when you swap, which is unmounting and remounting your nested component.

@stecb
Copy link
Author

stecb commented Jul 17, 2023

Tried that already, but it looks it's never null, so that isn't the problem it seems

@bvaughn
Copy link
Owner

bvaughn commented Jul 17, 2023

Hmm. Maybe there's a quirk about portal behavior that I was not familiar with :( Possibly related to facebook/react#12247?

If a portal doesn't work for this, another fallback solution would be to lift this state outside of the component (by passing it in as a prop or using something like context)

I don't think this is really within the realm of resizable panels at this point. (Even if it were, unfortunately I don't really have the bandwidth to dig into it anymore at the moment.)

@stecb
Copy link
Author

stecb commented Jul 17, 2023

Ah thanks, and I totally agree about the latter sentence. Even tho I think I could give it a try w/ the (horizontal/vertical)-reverse behaviour and see if I can get it working just via reversing the flex.

Thanks 🙏🏼

@stecb
Copy link
Author

stecb commented Jul 17, 2023

FYI, I've made it working flawlessly w/ https://github.com/httptoolkit/react-reverse-portal

👇🏼

// ...

const swappedPortalLeft = swappedPanels ? (
  <portals.OutPortal node={portalNodeLeft} />
) : (
  <portals.OutPortal node={portalNodeRight} />
);
const swappedPortalRight = swappedPanels ? (
  <portals.OutPortal node={portalNodeRight} />
) : (
  <portals.OutPortal node={portalNodeLeft} />
);

return (
  <>
    <PanelGroup direction="horizontal" onLayout={setLayout}>
      <Panel
        ref={panelLeftRef}
        style={{
          ...((!showSplitPanel || isDigitalResults) && !contentNavRight && { flexGrow: 0 }),
        }}
      >
        {swappedPortalLeft}
      </Panel>
      {!isDigitalResults && (
        <PanelResizer
          key={swappedPanels ? "swapped" : "normal"}
          showSplitPanel={showSplitPanel}
          setIsDragging={setIsDragging}
          isDragging={isDragging}
        />
      )}
      <Panel
        ref={panelRightRef}
        style={{
          ...((!showSplitPanel || isDigitalResults) && contentNavRight && { flexGrow: 0 }),
        }}
      >
        {swappedPortalRight}
      </Panel>
    </PanelGroup>
    <portals.InPortal node={portalNodeLeft}>{mainContent}</portals.InPortal>
    <portals.InPortal node={portalNodeRight}>{extraContent}</portals.InPortal>
  </>
);

@bvaughn
Copy link
Owner

bvaughn commented Jul 17, 2023

Glad you got it sorted out! Thanks for sharing the solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants