Search code examples
cssreactjspanel

Set initial size and memorize last size of allotments


I have made a draggable split panel by https://github.com/johnwalley/allotment.

I would like to realize several things:

  1. Clicking on Expand or collapse the allotment B will expand or collapse the allotment B.
  2. We can drag the splitter, the position of the splitter is saved before collapsing the allotment B so that we can go back to that position when expanding allotment B.
  3. The first time when we expand the allotment B, I would like the height of the allotment B to be 1/5 of the entire height.

I wrote the code as follows (https://codesandbox.io/s/reset-forked-f2f386?file=/src/App.js). I could not realize 2) and 3) at the same time.

Does anyone know why it's like that?

import React from "react";
import { Allotment } from "allotment";
import "allotment/dist/style.css";
import "./style.css";

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      toExpand: true,
      lastExpandedSize: [200, 50]
    };
    this.myRef = React.createRef();
  }

  handleChangeAllotment = (sizes) => {
    if (sizes.length > 1) {
      if (sizes[1] < 31) {
        this.setState({ toExpand: true });
      } else {
        this.setState({
          toExpand: false,
          lastExpandedSize: sizes // removing this line will set the initial sizes correctly, but will not be able to save last sizes.
        });
      }
    }
  };

  undertakeExpandOrCollapse = () => {
    if (this.state.toExpand) {
      this.myRef.current.resize(this.state.lastExpandedSize);
    } else {
      this.myRef.current.resize([10000, 0]);
    }
  };

  render() {
    return (
      <div
        style={{
          minWidth: 200,
          height: "100vh",
          overflowX: "auto",
          width: "auto",
          margin: "0px 0px 0px 0px"
        }}
      >
        <Allotment
          vertical
          onChange={this.handleChangeAllotment}
          ref={this.myRef}
        >
          <Allotment.Pane>
            <span onClick={this.undertakeExpandOrCollapse}>
              Expand or collapse the allotment B
            </span>
          </Allotment.Pane>
          <Allotment.Pane preferredSize="0%" minSize={30}>
            the allotment B
          </Allotment.Pane>
        </Allotment>
      </div>
    );
  }
}

Solution

  • My quick idea is that by default, there is no saved value, and the value of lastExpandedSize is null. Sensing this, we pass one-fifth of the screen height as the saved height. When closing, there will already be a saved value, so the useState function will return that value from then on.

    // ...
    
    constructor(props) {
      super(props);
      this.state = {
        toExpand: true,
        lastExpandedSize: null // set null to default
      };
      this.containerRef = React.createRef();
      this.myRef = React.createRef();
    }
    
    /**
     * Check to has LastExpandedSize
     * @return {bool}
     */
    hasLastExpandedSize = () => {
      return this.state.lastExpandedSize !== null;
    }
    
    /**
     * Get DefaultExpandedSize
     * @param {number} ratio
     * @param {number} height
     * @return {number[]}
     */
    getDefaultExpandedSize = (ratio = 5, height = 0) => {
      if (height < 1) {
        height = this.containerRef.current.clientHeight;
      }
    
      return [
        height * (ratio - 1) / ratio, 
        height / ratio
      ];
    }
    
    handleChangeAllotment = (sizes) => {
      if (sizes.length > 1) {
        if (sizes[1] < 31) {
          this.setState({ toExpand: true });
        }
        else {
          this.setState({
            toExpand: false,
            lastExpandedSize: this.hasLastExpandedSize()
              ? sizes
              : this.getDefaultExpandedSize()
          });
        }
      }
    };
    
    // ...
    
    <div
      ref={this.containerRef}
      style={{
        minWidth: 200,
        height: "100vh",
        overflowX: "auto",
        width: "auto",
        margin: "0px 0px 0px 0px"
      }}
    >
      ...
    </div>