bvaughn / react-resizable-panels

Home Page:https://react-resizable-panels.vercel.app/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Re-calculations after Conditional not behaving as expected

Psvensso opened this issue · comments

The re-calculations after dynamically change panels don't behave as expected, however this might be fully intentional sorry for the noise if thats the case. I have not been able to narrow it down exactly to the root cause so im describing a few unexpected behaviors.

Scenario 1

  1. Add collapsible={true} on the Left panel in the Conditional example
  2. Drag to collapse the Left panel
  3. Click to remove the Right panel
    Expected:
    The left panel should remain collapsed and the main panel should take up remaning space.
    Actuall:
    Left and Right "resets" to 50/50

Scenario 2

  1. Resize Left to 50 Middle to 25 and right to 25.
  2. Hide Right panel

Expected:
Left to take up ~2/3
Middle to take up ~1/3
Actuall:
Panel "resets" to 50/50

Interesting observation:
When restoring the Right to visible again the layout are restored to the "correct" previous layout.
So without digging to deep into the re-calcuations it appears to be some "missmatch previous state" that causes it to reset the calculations instead of reusing the individual resized state by id.

Observation:
In PanelGroup.ts ~530 we try to load the state for the new panels but get a not hit (since we removed a panel), it then "re-calculates" a new state with calculateUnsafeDefaultLayout({panelDataArray,}) PanelGroup.ts:535

Perhaps we could re-use current state when calculating the new state or keep each panel state separate instead of beeing a composition.

Something more in the line of how expand (panelSizeBeforeCollapseRef) works

The behavior you describe in your scenarios are expected/intentional, even though I understand why it's perhaps subjective or unintuitive. Panel group stores layouts using both the autoSaveId (if one is provided) and the combination of panels (panel attributes really). That's because the same group can be used to contain different combinations of panels/things, and persisted values are not meaningful between different combinations.

To illustrate this, let's say we have 3 panels in a group– each 33.3% width. Remove one panel and it makes no sense for the remaining two panels to both be 33% width.

I could rely on Flex behavior to paper over this, except that it's also important for this library to apply panel size constraints to panels in the group at all time. For example, take the same scenario above– three panels in a group: A is 33.3%, B is 33.3%, and C is 33.3%. Now remove C, but assume that A has a max size constraint of 40%. Group can't just make A and B both 50% of the visible width, it has to make A 40% and B 60% because of the constraints.

There are many unit tests in this library that verify the expected behaviors, as I believe your PR highlighted when you tried removing this constraint.

"That's because the same group can be used to contain different combinations of panels/things, and persisted values are not meaningful between different combinations."

It is very meaningfull to try and maintain the state within a panel group. It feels very unexpected to always reset the state to null without ever trying to calculate the new state based on the old.

"it's also important for this library to apply panel size constraints to panels in the group at all time"
Yes but this is the same problem regardless if you calculate new state based on the previous one. Lets say the user adds a new panel with 120, then ofcourse this fails also.

If you dynamically add more and more panels you will ofcourse come over 100 and then the recalc method have to "reset" the state again so they get assigned new states within the boundrary.

This already works great today and none of that is changed in this PR.

The calculateUnsafeDefaultLayout handles it great.

It seems like this just boils down to a subjective opinion/preference.

To be clear, I could update the logic to try to maintain relative proportions and then re-apply constraints to handle anything that would become invalid if it wasn't tweaked. (Presumably that's what you did in the PR? I didn't look into the test failure to see if it was "expected" or a regression.)

"Presumably that's what you did in the PR?"
In the PR i just make it simple by returning the previous state of each panel if available. And then yes, you may end up with a "overflow". For example [50,50,50] but validatePanelGroupLayout already handles that and turns 50,50,50 into relative sizes. ~33,33,33.

However the constraints problem can never be fixed if you have dynamic panels. The user could always add additional panels that "overflows" max width and i dont think it's very productive to try and stop it from happening. So you will end up with "invalid layout total size: xxx. Layout normalization will be applied." But thats expected with dynamic add/remove panels and i would see that as a feature.

Do the best effort, if it doesent work, do a normalization.

For 99% of scenarios i think it will work without a "Layout normalization" and for the other uses cases where normalization has to be applied i cant see what damage it does.

"I could update the logic to try to maintain relative proportions and then re-apply constraints to handle anything that would become invalid if it wasn't tweaked."
To be clear, do you really have to? I think it already works as-is.

However the constraints problem can never be fixed if you have dynamic panels. The user could always add additional panels that "overflows" max width and i dont think it's very productive to try and stop it from happening. So you will end up with "invalid layout total size: xxx. Layout normalization will be applied." But thats expected with dynamic add/remove panels and i would see that as a feature.

To be clear, do you really have to? I think it already works as-is.

Yes. The library should detect and warn about this, because it can definitely break user interactions. This is not really something I'm willing to compromise on.

I don't know if I'll be able to support this feature. I think the way I would need to implement it would be something like this:

In an effect, PanelGroup detects that the panel configuration has changed (eagerValuesRef.current.panelDataArrayChanged). There are a few cases it needs to handle:

  1. Panels have been removed
  2. Panels have been added
  3. Some combination of adding and removing
  4. Totally different panels

The easiest case to handle is the first one. If panels have been removed, it should calculate the total size of all remaining panels, and try to scale up their previous sizes by that amount. (Then apply validation rules to make sure this change doesn't violate min/max size constraints.)

The next easiest case to handle is when panels are added. Assuming the new panels have defaultSize params, we can assign those as their initial sizes, then scale the remaining (pre-existing) panels down to fit within the remaining space. (Then apply validation rules.) This may not work as well if default sizes are oddly big, etc. If there are no default sizes for the new panels (a possibility!) then this fails entirely, because I don't know what their initial sizes should be– and min/max size constraints makes calculating reasonable defaults non-trivial.

The fourth case should basically work the way this library currently works (no change).

Given the above complexity, I'm choosing not to support this feature. I think the "right" behavior is kind of subjective, and I'm going to stick with the well-tested existing behavior.

Thanks for understanding 🙇🏼