Feature Request: Greedy Panels
BrodyRas opened this issue ยท 14 comments
Problem
I'm creating a list of collapsible hierarchies (similar to the left panel in VS Code), and this package is working well for it! There is one slight issue in functionality, which I want to discuss:
Seemingly, the "preference" for which panel takes up the remaining space when a panel collapsed is the next panel in order i.e. if <Panel order=3>
collapses, then <Panel order=4>
takes the remaining space. I'm wondering if there's a way to customize this behavior. Giving preference to the previous panel, instead of the next one, would provide the functionality I'm looking for: collapsing a panel shoves it to the bottom of the PanelGroup.
I tried to do some trickery to make this work (i.e. giving the PanelGroup flexDirection: column-reverse
, reversing the order values, etc), but that didn't quite work (reversed the dragging direction ๐
). If there's a quick fix I'm missing, I'll be happy to learn what it is โค๏ธ Thanks for the help!
Feature Request
Create an option for PanelGroups which can reverse the direction of preferred panels on collapse/expansion, or provide the Panels a "greediness" option, which controls how much of the new space they take
Current Behavior
Collapsing a panel "pulls up" the panel below it, giving it's space to the next panel.
Desired Behavior
Collapsing a panel "pushes" it down, giving it's space to the previous panel.
Collapsing a panel "pushes" it down, giving it's space to the previous panel.
Seems like the video you showed from VS Code should already be possible using panelRef.collapse
?
I am using panelRef.collapse
, it works great! But the issue is how the remaining space is filled. VS Code "pushes down", giving space to the panel above. This package seems to "pull up", giving space to the panel below.
Watch the first GIF, while I'm toggling the "Tab Hierarchy" panel: I'd expect that tab to go down to the bottom, but instead it pulls up the "Third Hierarchy" tab.
Ah, I see what you're saying.
Have you consider using panelGroup.setLayout()
for this? I think that would let you do what you're describing.
Oh, that makes sense! On click, I'll add the current panel's percentage to the previous panel's percentage, and zero out the current panel. Thanks!
Yup! :)
Sorry to reopen, I think the layout is working, but for some reason, it seems like the panels are 1 render behind after using setLayout()
. Is there another step to trigger the render?
See the video: clicking on the "Tab Hierarchy" calls setLayout()
with the updated layout (as described in the previous comment). But nothing happens visually. Then, when I attempt to resize the panel, it jumps to where it should've been: 0.
Huh. Can you share a repro?
Here's a reproduction with the simplest case:
const DummyPanels = () => {
const groupRef = useRef(null)
// Give the clicked panel's percentage to the previous panel
const handleClick = (order: number) => {
const anyGroupRef = groupRef as any
const layout = anyGroupRef.current.getLayout()
const currentSize = layout[order]
layout[order] = 0
layout[order - 1] += currentSize
anyGroupRef.current.setLayout(layout)
}
return <PanelGroup ref={groupRef} direction='vertical'>
<Panel order={0} collapsible><></></Panel>
<CollapseOrDragHandle title='First Panel' onMouseDown={() => { handleClick(1) }} />
<Panel order={1} collapsible>First Panel's content</Panel>
<CollapseOrDragHandle title='Second Panel' onMouseDown={() => { handleClick(2) }} />
<Panel order={2} collapsible>Second Panel's content</Panel>
<CollapseOrDragHandle title='Third Panel' onMouseDown={() => { handleClick(3) }} />
<Panel order={3} collapsible>Third Panel's content</Panel>
</PanelGroup>
}
interface CollapseOrDragHandleProps {
title: string
onMouseDown: any
}
const CollapseOrDragHandle = ({ title, onMouseDown }: CollapseOrDragHandleProps) => {
return <>
{/* Drag to Resize */}
<PanelResizeHandle>
<Box sx={{ height: '10px', backgroundColor: 'gray' }} />
</PanelResizeHandle>
{/* Click to Collapse */}
<Box sx={{ backgroundColor: 'gray', cursor: 'pointer' }} onMouseDown={onMouseDown}>{title}</Box>
</>
}
The "handle" is two Boxes: dragging the top will resize the panel, and clicking the bottom will collapse the panel. Notice how clicking will technically trigger a new layout, but you don't see it until making another change (i.e. dragging that panel, or another panel)
If there's a better way to structure the clickable handles w/ title text, I'm happy to hear it too โค๏ธ should the clickable title be a part of the panel itself, and the resize handle only manages resizing?
@BrodyRas setLayout does an equality check see
So I believe you might need to pass a new array to setLayout ie
let nextLayout = [...group.getLayout()];
// modify nextLayout
nextLayout[0] = 20;
group.setLayout(nextLayout);
Yeah, mutating state is always a good idea to avoid because of equality checks like this. Should work okay if you pass it a new layout array!
D'oh, that makes sense. Silly mistake, I definitely thought I was just grabbing the values of the array, not mutating the group's state ๐
That fixed it ๐ Thanks so much for the help!
Glad you got it sorted out!