react-split-pane icon indicating copy to clipboard operation
react-split-pane copied to clipboard

Respond to window resize

Open davidmason opened this issue 8 years ago • 9 comments

When the window size changes, I want to keep the same proportional size of my panes.

For example:

  • I make a resizable pane at the bottom.
  • The user has a window 600px high
  • The user drags the bottom panel to take up 200px of the height, and the top pane is sized by the browser to 400px.
  • The user maximized their browser and the window height is now 1200px

In this case the bottom pane was taking 1/3 of the height and the top pane 2/3 of the height, but after resize the bottom pane is only 1/6 of the height. I want to be able to keep the proportions so the bottom pane would grow to 400px high on window resize.

I can make it work with code outside the component, but it would feel pretty natural to either pass in a prop that says to use proportional sizing when the window size changes, or to pass in a callback to use when the window size changes. Edit: it is actually pretty tricky to get it to change size on window resize. I have a default height of 30%, which I use to set defaultSize prop to defaultHeight * window.innerHeight and that works initially, and resizes to 30% whenever I change the window height, but as soon as I drag the panel to a different size, it ignores defaultHeight so it is stuck at the pixel size it has been dragged to. If I set size prop I can no longer drag it to resize.

davidmason avatar Mar 31 '16 06:03 davidmason

If one really needs that, I think the better alternative would be to introduce another attribute flex (as the similarly named from a css flexbox model) which would conflict with the defaultSize (so that it was only valid to specify either).

It is also not clear whether to rely on the window size, or the current document size or the current element container size. I tend to think the latter is the most acceptable one.

Thoughts?

zerkms avatar Apr 05 '16 20:04 zerkms

I think the container size would be best.

I have managed to get something working with this, but I had to reach in and mess with some of the internals from the parent component - super fragile but it works. I did these things:

  • onDragFinished: look up this.refs.mySplitPane.state.draggedSize and save as a percentage of my height (e.g. percentageSize = draggedSize / window.innerHeight might give 0.33)
  • listen to window resize events to tweak the size to the percentage size (var pixelHeight = window.innerHeight * percentageSize)
  • update the size with this.refs.mySplitPane.setState({ draggedSize: pixelHeight }) since I could not find any other way to override the size after a drag resize
  • this.forceUpdate() to force a rerender

so what I did would be a lot easier if I had a callback that triggered whenever the container size changed, and gave me the current element size, previous container size and next container size and let me return the size I want it to use. The default implementation would just return the current element size:

function onContainerSizeChange (elementPixelSize, previousContainerSize, currentContainerSize) {
  // default could just be this:
  // return elementPixelSize

  // my case would just be this:
  return elementPixelSize * (currentContainerSize / previousContainerSize)
}

davidmason avatar Apr 08 '16 02:04 davidmason

I haven't reached a 100% working solution yet, but setting the panes to: window.innerHeight / 2

and setting widths of divs to 100% and heights to 100vh has me pretty close.

Just learned about vh. They are like magic: http://stackoverflow.com/a/16837667/6025788

Nantris avatar Aug 21 '16 14:08 Nantris

vh might only be useful when your pane occupies the whole width of the height (or both), in other cases it would not work.

zerkms avatar Aug 21 '16 21:08 zerkms

True, it's mostly helpful in that case, but it is helpful as a pure CSS solution that gives your body and wrapper a defined height so height: 100% has some use inside of them when needed.

Nantris avatar Aug 22 '16 13:08 Nantris

image

Nantris avatar Apr 14 '17 23:04 Nantris

I managed to get it working in a different approach, without touching the source


        componentDidMount () {
            window.addEventListener('resize', this.updateSize)
        }

        componentWillUnmount () {
            window.removeEventListener('resize', this.updateSize)
        }

        componentDidUpdate () {
            if (this.state.windowResize) this.setState({windowResize: false})
        }

        updateSize = () => this.setState({windowResize: true})

        onChange = () => {
            const draggedSize = _.get(this.splitpane.current, 'state.draggedSize')

            if (draggedSize && this.splitpane.current.splitPane.clientHeight) {
                const percentage = draggedSize / this.splitpane.current.splitPane.clientHeight

                this.setState({panelSize: `${percentage * 100}%`})
            }
        }

and on the render function there's something like

<SplitPane
    ref={this.splitpane}
    split="horizontal"
    minSize={50}
    maxSize={-50}
    defaultSize={this.state.panelSize}
    size={this.state.windowResize ? this.state.panelSize : undefined}
    onChange={this.onChange}
>

Rationale is this: when the window updates, I set the boolean so on the render function the splitpane is updated with the size prop. In the next render, the componentDidUpdate function sets the boolean to false, meaning the drag is back online

BernardoFBBraga avatar Jul 06 '18 14:07 BernardoFBBraga

Thanks @BernardoFBBraga, with some slight modifications that worked great 👍

My slightly less dynamic version:

 onChange = () => {
    const draggedSize = this.splitpane.state.pane1Size

    if (draggedSize && this.splitpane.pane1.clientWidth) {
      const percentage = draggedSize / window.innerWidth
      const panelSize = `${percentage * 100}%`
      this.setState({ panelSize })
    }
  }

damassi avatar Jan 23 '20 02:01 damassi

Is there a plan to do this natively in this component?

yotamberk avatar Sep 26 '20 17:09 yotamberk