Giter VIP home page Giter VIP logo

react-panelgroup's Introduction

Hi there ๐Ÿ‘‹

react-panelgroup's People

Contributors

2blane avatar danfessler avatar davidzub avatar leshakoss avatar nicolas-van avatar nmccready avatar radekmie avatar shilder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-panelgroup's Issues

Is it possible to know when 'dragging' is in progress?

I have a scenario where I need to know if the divider is being dragged, to add some additional styling to some components. I can see the 'dragging' class being applied on the 'divider'. Is that the only way to know? Doesn't seem very 'React' way of finding out.

Any help appreciated. Thanks!

Scrolling within panels

I'm having trouble adding scrolling to my panels with the content exceeds the current size of the panel. Is there a simple way of doing this?

Panel touch support

It would be great if this panel would handle touch support for mobile devices.

Uncaught RangeError: Maximum call stack size exceeded

Hi,

When the window resizes to smaller than the defined minSize range then the following error is thrown.
Anything wrong being done on the configuration? or min size has to be dynamic?

[{ size: 400, minSize: 400, resize: 'dynamic' }, { size: 900, minSize: 300, resize: 'dynamic' }]

But the same is changed to

[{ size: 400, minSize: 400, resize: 'dynamic' }, { size: 900, minSize: 0, resize: 'dynamic' }]

then there are no errors.

Uncaught RangeError: Maximum call stack size exceeded
    at Object.getPanelMinSize (PanelGroup.js:241)
    at Object.resizePanel (PanelGroup.js:201)
    at Object.resizePanel (PanelGroup.js:208)
    at Object.resizePanel (PanelGroup.js:226)
    at Object.resizePanel (PanelGroup.js:208)
    at Object.resizePanel (PanelGroup.js:226)
    at Object.resizePanel (PanelGroup.js:208)
    at Object.resizePanel (PanelGroup.js:226)
    at Object.resizePanel (PanelGroup.js:208)
    at Object.resizePanel (PanelGroup.js:226)

Not sure if this is expected or requires any fix.

Thanks!

option to keep panel heights synchronized

I am using react-panelgroups with rows. The height of each panel is dynamically set by the user, and I wish to keep the panel heights synchronized while the user changes them individually. From what I can tell they change independently and require constant updating panel div heights to align with each other. It's added quite a bit of new code. Am I missing something? Does anyone have any ideas as to how to make the panel div heights talk to each other/synchronize?

Optional "Handle" for Panels

I've seen a handful of implementations of panels like this where there's a small "handle" on the resizer divider. This can be really useful for mobile (eg: #16 )since it is often more useful to "toggle" panels on mobile rather than drag to resize.

This is something I'm looking at doing, but wanted to file an issue first to see if anyone in the community had implemented something like this.

Proposed Behavior:

  1. Add an optional parameter "handle" to the panelWidths configs object
  2. Default behavior would toggle between the "minSize" and the "maxSize" (see #20 & my associated PR)
  3. Additional callback onHandleClick called with arguments panels (like current onUpdate method), along with the panelIndex of the selected panel divider.
  4. Dragging a panel handle would act the same as dragging the panel divider (eg: onHandleClick would only fire onMouseUp if isDragging === false

Questions:

What does panelIndex refer to?

If a handle is placed on panel 0, it is placed on the divider between panels 0 and 1. This is straightforward.
In a three column layout with a stretch column between two sidebars, does the handle for the righthand sidebar go on panel 1 or panel 2?

possible solution: handle could take either a single DOM element (as described above, ie: "basic config mode"), or it could take an object with properties: handle.component, handle.direction, etc

Support React 16

Hello, @DanFessler!

Is there a plan to support React 16? The only problem is the React.createClass. If you are OK with it, I'd submit a PR.

Console warning componentWillReceiveProps

Hi guys,

I receive in the console this error:

console.warn node_modules/react-dom/cjs/react-dom.development.js:12386
      Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
      
      * Move data fetching code or side effects to componentDidUpdate.
      * If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
      * Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.
      
      Please update the following components: PanelGroup

Can you do something about it?

Thanks!

Support for TS typings?

I'm curious if you have any plans for publishing Typescript typings for this, or if for the library itself using Typescript?

Ability to customize CSS class names?

Right now, the handle/divider for resizing the panels just has a CSS class of "divider" which is likely to clash with existing styling we have in our site. Is there a way to customize this?

Divider: allow custom styling when dragged

Hi,

First, thanks for you lib !
I have an issue and would like to propose a solution.

Use Case

You often need to custom Divider style when you drag it, like a visual feedback to let user knows he's inside a draggable area. This is currently possible thanks to the divider CSS class and a :hover on this class.

Issue

Except that when you're dragging the divider, it happens that your mouse leave -for a second- the Divider area (delay between new panelWidths and render), which cause the lost of the .divider:hover .

See this GIF for demo (maybe it's not clear due to low fps, but :hover is often lost when dragging quickly):

Solution

Add a custom divider-dragging class when a Divider is dragging. This way, we can custom style of the Divider when hovered AND when dragged.

I can make a PR if you agree on this one.

Unable to find node on an unmounted component error

Hello,

When using version 1.0.7 of the PanelGroup, we get an "Unable to find node on an unmounted component." error from time to time.

image

I could easily reproduce it by creating a test project where the React component using the PanelGroup is replaced by another React component when clicking a button. When clicking this button rapidly, the unmounted errors are shown in the console. It does seem to happen more frequently when my browser/pc is slower or the component using the PanelGroup is more complex.

react-panelgroup.zip

The error seems to be introduced when this.onNextFrame(this.calculateStretchWidth); was put in the Panel.componentDidMount.

Thanks for the help.

Pieter

Option to avoid recursive resizing

Hi! I'd like to know if there is an option to avoid recursive resizing. It would be useful to have a prop on the PanelGroup component to avoid it and only allow resizing of two adjacent panels.
I see there are some cases (I configured the snap in panelWidths) in which recursive resizing causes RangeError: Maximum call stack size exceeded:

PanelGroup.js:125 Uncaught RangeError: Maximum call stack size exceeded
    at PanelGroup._this.resizePanel

Thank you.

Cannot read properties of undefined (reading 'style') when dynamically adding an additional panel

In our application, by default, two panels are used. But on pressing the button of an event one more panel is added.
Panel sizes are also saved in store mobx.

When the component is loaded in the store for the first time, the panelWidths array is saved using onUpdate , but when after that I try to call another panel on click, I get an error

 <PanelGroup
         onUpdate={(e: Array<any>) => {                              // when updating panels, write update state
           accountStore.setSizesPanel(e);
         }}
         panelWidths={accountStore.panelWidthsSettings}               // read state from store
       >

         <Panel0 / >

         {accountStore.isVisibleCustomPanel() ? <Panel1 /> : null}.     // appears on user click

         <Panel2 />

  </PanelGroup>

Typing it up with Flow

Here's a patch to apply on Reactv15 state:

--- index.js
+++ index.js
@@ -1,28 +1,58 @@
 // @flow
-import React from 'react';
+// @flow-ignore
+import React, { MouseEvent } from 'react';
 import ReactDOM from 'react-dom';

-/**
- * From https://github.com/DanFessler/react-panelgroup
- */
-var PanelGroup = React.createClass({
-
-  // Default props
-  getDefaultProps: function() {
-    return {
-      spacing: 1,
-      direction: "row",
-      panelWidths: []
-    };
-  },
+type PanelProp =
+  { snap?: number[],
+    size: number,
+    resize?: 'stretch' | 'dynamic',
+    minSize: number
+  }
+
+type PanelT =
+  { snap: number[],
+    size: number,
+    resize: 'stretch' | 'dynamic',
+    minSize: number
+  }
+
+type PanelGroupState =
+  { panels: PanelT[] }
+
+type Direction =
+  | 'row'
+  | 'column'
+
+type Delta =
+  { x: number,
+    y: number }
+
+type PanelGroupProps =
+  { panelWidths: PanelProp[],
+    children?: any, //React.ReactChildren
+    borderColor?: string,
+    spacing: number,
+    direction: Direction,
+    onUpdate?: PanelT[] => void,
+    onWindowResize?: (/* panel id */ number, Delta, ?(void => void)) => void,
+    panelColor?: string,
+    showHandles: boolean
+  }
+
+class PanelGroup extends React.Component {
+  state: PanelGroupState
+  props: PanelGroupProps
+
+  static defaultProps = {
+    spacing: 1,
+    direction: "row",
+    panelWidths: [],
+    showHandles: false
+  }

   // Load initial panel configuration from props
-  getInitialState: function() {
-    return this.loadPanels(this.props)
-  },
+  constructor () {
+    super(...arguments);
+
+    this.state = this.loadPanels(this.props)
+  }

   // reload panel configuration if props update
-  componentWillReceiveProps: function(nextProps) {
+  componentWillReceiveProps (nextProps: PanelGroupProps) {

     var nextPanels = nextProps.panelWidths;

@@ -49,13 +79,15 @@ var PanelGroup = React.createClass({

     }

-  },
+  }

-  // load provided props into state
-  loadPanels: function(props) {
+  /**
+   * Load provided props into state
+   */
+  loadPanels = (props: PanelGroupProps) => {
     var panels = []

-    if (props.children) {
+    if (props.children != null) {

       // Default values if none were provided
       var defaultSize = 256;
@@ -69,15 +101,16 @@ var PanelGroup = React.createClass({

         if (i < props.panelWidths.length && props.panelWidths[i]) {
           var widthObj = {
-            size:    props.panelWidths[i].size !== null ? props.panelWidths[i].size : defaultSize,
-            minSize: props.panelWidths[i].minSize !== null ? props.panelWidths[i].minSize : defaultMinSize,
+            size:    props.panelWidths[i].size != null ? props.panelWidths[i].size : defaultSize,
+            minSize: props.panelWidths[i].minSize != null ? props.panelWidths[i].minSize : defaultMinSize,
             resize:  props.panelWidths[i].resize? props.panelWidths[i].resize :
                      props.panelWidths[i].size? "dynamic" : defaultResize,
+            snap:    props.panelWidths[i].snap != null ? props.panelWidths[i].snap : []
           }
           panels.push(widthObj);
         } else {
           // default values if no props are given
-          panels.push({size: defaultSize, resize: defaultResize, minSize: defaultMinSize})
+          panels.push({size: defaultSize, resize: defaultResize, minSize: defaultMinSize, snap:[]})
         }

         // if none of the panels included was stretchy, make the last one stretchy
@@ -86,29 +119,28 @@ var PanelGroup = React.createClass({
       }
     }

-    return {
-      panels: panels
-    }
-  },
+    return { panels }
+  };

   // Pass internal state out if there's a callback for it
   // Useful for saving panel configuration
-  onUpdate: function(panels) {
-    if (this.props.onUpdate) {
-      this.props.onUpdate(panels.slice())
+  onUpdate = (panels: PanelT[]) => {
+    const fn = this.props.onUpdate;
+    if (fn != null) {
+      fn(panels.slice());
     }
-  },
+  };

   // For styling, track which direction to apply sizing to
-  getSizeDirection: function(caps) {
+  getSizeDirection = (caps: boolean = false) => {
     if (caps)
       return this.props.direction === "column" ? "Height" : "Width";
     else
       return this.props.direction === "column" ? "height" : "width";
-  },
+  };

   // Render component
-  render: function() {
+  render () {

     var style = {
       container: {
@@ -144,10 +176,10 @@ var PanelGroup = React.createClass({
         display: "flex",
         overflow: "hidden",
         position: "relative",
+        backgroundColor: '#555555'
       }

       // patch in the background color if it was supplied as a prop
-      // @flow-ignore CSS
       Object.assign(panelStyle, {backgroundColor: this.props.panelColor});

       // give position info to children
@@ -176,22 +208,22 @@ var PanelGroup = React.createClass({
     }

     return <div className="panel-group" style={style.container}>{newChildren}</div>
-  },
+  }

   // Entry point for resizing panels.
   // We clone the panel array and perform operations on it so we can
   // setState after the recursive operations are finished
-  handleResize: function(i, delta) {
+  handleResize = (i: number, delta: Delta) => {
     var tempPanels = this.state.panels.slice();
     var returnDelta = this.resizePanel(i, this.props.direction === "row" ? delta.x : delta.y, tempPanels);
     this.setState({panels: tempPanels});
     this.onUpdate(tempPanels)
     return returnDelta;
-  },
+  };

   // Recursive panel resizing so we can push other panels out of the way
   // if we've exceeded the target panel's extents
-  resizePanel: function(panelIndex, delta, panels) {
+  resizePanel = (panelIndex: number, delta: number, panels: PanelT[]) => {

     var minsize; var maxsize;

@@ -209,22 +241,22 @@ var PanelGroup = React.createClass({

     // if we made the left panel too small
     if (panels[panelIndex].size < minsize) {
-      delta = minsize - panels[panelIndex].size;
+      let delta = minsize - panels[panelIndex].size;

       if (panelIndex === 0)
-        resultDelta += this.resizePanel(panelIndex, delta, panels);
+        resultDelta = this.resizePanel(panelIndex, delta, panels);
       else
-        resultDelta += this.resizePanel(panelIndex-1, -delta, panels);
+        resultDelta = this.resizePanel(panelIndex-1, -delta, panels);
     };

     // if we made the left panel too big
     if (maxsize !== 0 && panels[panelIndex].size > maxsize) {
-      delta = panels[panelIndex].size - maxsize;
+      let delta = panels[panelIndex].size - maxsize;

       if (panelIndex === 0)
-        resultDelta += this.resizePanel(panelIndex, -delta, panels);
+        resultDelta = this.resizePanel(panelIndex, -delta, panels);
       else
-        resultDelta += this.resizePanel(panelIndex-1, delta, panels);
+        resultDelta = this.resizePanel(panelIndex-1, delta, panels);
     };


@@ -234,30 +266,60 @@ var PanelGroup = React.createClass({

     // if we made the right panel too small
     if (panels[panelIndex+1].size < minsize) {
-      delta = minsize - panels[panelIndex+1].size;
+      let delta = minsize - panels[panelIndex+1].size;

       if (panelIndex+1 === panels.length-1)
-        resultDelta += this.resizePanel(panelIndex, -delta, panels);
+        resultDelta = this.resizePanel(panelIndex, -delta, panels);
       else
-        resultDelta += this.resizePanel(panelIndex+1, delta, panels);
+        resultDelta = this.resizePanel(panelIndex+1, delta, panels);
     };

     // if we made the right panel too big
     if (maxsize !== 0 && panels[panelIndex+1].size > maxsize) {
-      delta = panels[panelIndex+1].size - maxsize;
+      let delta = panels[panelIndex+1].size - maxsize;

       if (panelIndex+1 === panels.length-1)
-        resultDelta += this.resizePanel(panelIndex, delta, panels);
+        resultDelta = this.resizePanel(panelIndex, delta, panels);
       else
-        resultDelta += this.resizePanel(panelIndex+1, -delta, panels);
+        resultDelta = this.resizePanel(panelIndex+1, -delta, panels);
     };

+    // Iterate through left panel's snap positions
+    for (let i=0; i<panels[panelIndex].snap.length; i++) {
+      if (Math.abs(panels[panelIndex].snap[i] - panels[panelIndex].size) < 20) {
+
+        let delta = panels[panelIndex].snap[i] - panels[panelIndex].size;
+
+        if (
+          delta !== 0 &&
+          panels[panelIndex].size  +  delta >= this.getPanelMinSize(panelIndex, panels) &&
+          panels[panelIndex+1].size - delta >= this.getPanelMinSize(panelIndex+1, panels)
+        )
+          resultDelta = this.resizePanel(panelIndex, delta, panels);
+      }
+    }
+
+    // Iterate through right panel's snap positions
+    for (let i=0; i<panels[panelIndex+1].snap.length; i++) {
+      if (Math.abs(panels[panelIndex+1].snap[i] - panels[panelIndex+1].size) < 20) {
+
+        let delta = panels[panelIndex+1].snap[i] - panels[panelIndex+1].size;
+
+        if (
+          delta !== 0 &&
+          panels[panelIndex].size  +  delta >= this.getPanelMinSize(panelIndex, panels) &&
+          panels[panelIndex+1].size - delta >= this.getPanelMinSize(panelIndex+1, panels)
+        )
+          resultDelta = this.resizePanel(panelIndex, -delta, panels);
+      }
+    }
+
     // return how much this panel actually resized
     return resultDelta;
-  },
+  };

   // Utility function for getting min pixel size of panel
-  getPanelMinSize: function(panelIndex: number, panels: any[]) {
+  getPanelMinSize = (panelIndex: number, panels: PanelT[]) => {
     if (panels[panelIndex].resize === "fixed") {
       if (!panels[panelIndex].fixedSize) {
         panels[panelIndex].fixedSize = panels[panelIndex].size;
@@ -265,10 +327,10 @@ var PanelGroup = React.createClass({
       return panels[panelIndex].fixedSize;
     }
     return panels[panelIndex].minSize;
-  },
+  };

   // Utility function for getting max pixel size of panel
-  getPanelMaxSize: function(panelIndex, panels) {
+  getPanelMaxSize = (panelIndex: number, panels: PanelT[]) => {
     if (panels[panelIndex].resize === "fixed") {
       if (!panels[panelIndex].fixedSize) {
         panels[panelIndex].fixedSize = panels[panelIndex].size;
@@ -276,21 +338,21 @@ var PanelGroup = React.createClass({
       return panels[panelIndex].fixedSize;
     }
     return 0;
-  },
+  };

   // Utility function for getting min pixel size of the entire panel group
-  getPanelGroupMinSize: function(spacing) {
+  getPanelGroupMinSize = (spacing: number) => {
     var size = 0;
     for (var i = 0; i < this.state.panels.length; i++) {
       size += this.getPanelMinSize(i, this.state.panels);
     }
     return size + ((this.state.panels.length-1) * spacing)
-  },
+  };

   // Hard-set a panel's size
   // Used to recalculate a stretchy panel when the window is resized
-  setPanelSize: function(panelIndex, size, callback) {
-    size = this.props.direction === "column"? size.y : size.x;
+  setPanelSize = (panelIndex: number, sizeXY: Delta, callback: void => void = () => {}) => {
+    const size = this.props.direction === "column"? sizeXY.y : sizeXY.x;

     if (size !== this.state.panels[panelIndex].size){
       var tempPanels = this.state.panels;
@@ -308,56 +370,56 @@ var PanelGroup = React.createClass({
         callback();
       }
     }
-  },
-})
-
+  };
+}

-var Panel = React.createClass({
+class Panel extends React.Component {

   // Find the resizeObject if it has one
-  componentDidMount: function() {
+  componentDidMount () {
     if (this.props.resize === "stretch") {
       this.refs.resizeObject.addEventListener("load", () => this.onResizeObjectLoad());
       this.refs.resizeObject.data = "about:blank";
       this.calculateStretchWidth();
     }
-  },
+  }

   // Attach resize event listener to resizeObject
-  onResizeObjectLoad() {
+  onResizeObjectLoad = () => {
     this.refs.resizeObject.contentDocument.defaultView.addEventListener(
     "resize", () => this.calculateStretchWidth());
-  },
+  };

   // Utility function to wait for next render before executing a function
-  onNextFrame: function(callback) {
+  onNextFrame = (callback) => {
     setTimeout(function () {
         window.requestAnimationFrame(callback)
     }, 0)
-  },
+  };

   // Recalculate the stretchy panel if it's container has been resized
-  calculateStretchWidth: function() {
-    if (this.props.onWindowResize !== null) {
-      var node = ReactDOM.findDOMNode(this)
-      if (node === null) { throw new Error("Couldn't find Panel DOM node"); }
-      if (node instanceof Text) { throw new Error("Expected DOM node to be of type Element"); }
-      var rect = node.getBoundingClientRect();
-
-      this.props.onWindowResize(
+  calculateStretchWidth = () => {
+    const onResize = this.props.onWindowResize;
+    if (onResize != null) {
+      const node = ReactDOM.findDOMNode(this);
+      if (node == null) { throw new Error('ReactDOM.findDOMNode(this) => null') }
+      if (node instanceof Text) { throw new Error("Node was of Text type, but it should've been Element"); }
+      const rect = node.getBoundingClientRect();
+
+      onResize(
         this.props.panelID,
         {x:rect.width, y:rect.height},

         // recalcalculate again if the width is below minimum
         // Kinda hacky, but for large resizes like fullscreen/Restore
         // it can't solve it in one pass.
-        function() {this.onNextFrame(this.calculateStretchWidth)}.bind(this)
+        // function() {this.onNextFrame(this.calculateStretchWidth)}.bind(this)
       );
     }
-  },
+  };

   // Render component
-  render: function() {
+  render () {

     var style = {
       resizeObject: {
@@ -381,42 +443,51 @@ var Panel = React.createClass({
       </div>
     )
   }
-})
-
-
-var Divider = React.createClass({
+}
+
+type DividerProps =
+  { dividerWidth: number,
+    handleBleed: number,
+    panelID: number,
+    handleResize: (panelId: number, delta: Delta) => number,
+    borderColor?: string,
+    showHandles: boolean }
+
+type DividerState =
+  { dragging: boolean,
+    initPos: Delta }
+
+class Divider extends React.Component {
+  props: DividerProps
+  state: DividerState
+
+  static defaultProps = {
+    dividerWidth: 1,
+    handleBleed: 4,
+  }

-  getDefaultProps: function() {
-    return {
-      dividerWidth: 1,
-      handleBleed: 4,
-    };
-  },
+  constructor () {
+    super(...arguments);

-  getInitialState: function () {
-    return {
+    this.state = {
       dragging: false,
-      initPos: {x:null,y:null},
-    }
-  },
+      initPos: {x:0,y:0},
+    };
+  }

   // Add/remove event listeners based on drag state
-  componentDidUpdate: function (props, state) {
+  componentDidUpdate (props: DividerProps, state: DividerState) {
     if (this.state.dragging && !state.dragging) {
-      // @flow-ignore
       document.addEventListener('mousemove', this.onMouseMove)
-      // @flow-ignore
       document.addEventListener('mouseup', this.onMouseUp)
     } else if (!this.state.dragging && state.dragging) {
-      // @flow-ignore
       document.removeEventListener('mousemove', this.onMouseMove)
-      // @flow-ignore
       document.removeEventListener('mouseup', this.onMouseUp)
     }
-  },
+  }

   // Start drag state and set initial position
-  onMouseDown: function (e) {
+  onMouseDown = (e) => {

     // only left mouse button
     if (e.button !== 0) return
@@ -431,54 +502,72 @@ var Divider = React.createClass({

     e.stopPropagation()
     e.preventDefault()
-  },
+  };

   // End drag state
-  onMouseUp: function (e) {
+  onMouseUp = (e: MouseEvent) => {
     this.setState({dragging: false})
     e.stopPropagation()
     e.preventDefault()
-  },
+  };

   // Call resize handler if we're dragging
-  onMouseMove: function (e) {
+  onMouseMove = (e: MouseEvent) => {
     if (!this.state.dragging) return

+    let initDelta = {
+      x: e.pageX - this.state.initPos.x,
+      y: e.pageY - this.state.initPos.y
+    }
+
+    let flowMask = {
+      x: (this.props.direction === "row"    ? 1 : 0),
+      y: (this.props.direction === "column" ? 1 : 0)
+    }
+
+    let flowDelta = (initDelta.x * flowMask.x) + (initDelta.y * flowMask.y);
+
+    // Resize the panels
     var resultDelta = this.handleResize(
       this.props.panelID,
-      {x: e.pageX - this.state.initPos.x, y: e.pageY - this.state.initPos.y}
+      initDelta
     );

-    // if we've resized the panel like intended, reset the initPos
-    if (resultDelta !== 0) {
+    // if the divider moved, reset the initPos
+    if (resultDelta + flowDelta !== 0) {
+
+      // Did we move the expected amount? (snapping will result in a larger delta)
+      let expectedDelta = (resultDelta === flowDelta);
+
       this.setState({
         initPos: {
-          x: e.pageX,
-          y: e.pageY
+          // if we moved more than expected, add the difference to the Position
+          x: e.pageX + (expectedDelta? 0 : resultDelta * flowMask.x),
+          y: e.pageY + (expectedDelta? 0 : resultDelta * flowMask.y)
         },
       })
     }

     e.stopPropagation()
     e.preventDefault()
-  },
+  };

   // Handle resizing
-  handleResize(i, delta) {
+  handleResize =(i, delta) => {
     return this.props.handleResize(i, delta);
-  },
+  };

   // Utility functions for handle size provided how much bleed
   // we want outside of the actual divider div
-  getHandleWidth: function() {
+  getHandleWidth = () => {
     return (this.props.dividerWidth + (this.props.handleBleed * 2));
-  },
-  getHandleOffset: function() {
+  };
+  getHandleOffset = () => {
     return (this.props.dividerWidth/2) - (this.getHandleWidth()/2);
-  },
+  };

   // Render component
-  render: function() {
+  render () {
     var style = {
       divider: {
         width:    this.props.direction === "row" ? this.props.dividerWidth : "auto",
@@ -489,6 +578,7 @@ var Divider = React.createClass({
         maxHeight: this.props.direction === "column" ? this.props.dividerWidth : "auto",
         flexGrow: 0,
         position: "relative",
+        backgroundColor: "#cccccc"
       },
       handle: {
         position: "absolute",
@@ -496,12 +586,12 @@ var Divider = React.createClass({
         height: this.props.direction === "column" ? this.getHandleWidth() : "100%",
         left:   this.props.direction === "row"    ? this.getHandleOffset() : 0,
         top:    this.props.direction === "column" ? this.getHandleOffset() : 0,
-        backgroundColor: this.props.showHandles? "rgba(0,128,255,0.25)" : "auto",
-        cursor: this.props.direction === "row" ? "ew-resize" : "ns-resize",
+        backgroundColor: this.props.showHandles ? "rgba(0,128,255,0.25)" : "auto",
+        cursor: this.props.direction === "row" ? "col-resize" : "row-resize",
         zIndex: 100,
       }
     }
-    // @flow-ignore CSS
+
     Object.assign(style.divider, {backgroundColor: this.props.borderColor});

     return (
@@ -510,7 +600,7 @@ var Divider = React.createClass({
       </div>
     );
   }
-})
+}


-export default PanelGroup;
\ No newline at end of file
+export default PanelGroup;

(NPM) Release Cycle?

Thank you for your library!

I was wondering what the release cycle is like? How often do you publish to NPM? I'd really like to use this library (and even contribute) for one of my React 16 projects, but the React 16 fixes are not available in NPM yet.

Currently I need to workaround this by cloning the project and pushing to a private NPM repo.

Regards,
Tim

Persist panel sizes after re-render

When I update some child component of a panel, the panel is re-rendered with the initial sizes. I am sure that this is not working as intended, but I can't find away past it.

I have tried to tie the onUpdate to a state change, figuring that I could catch the new sizes, and apply it to the component when react re-renders. However, it seems that onUpdate is triggered by the re-rendering, and I have a loop on my hands. onUpdate is triggered even before i tie the panelWidths to the state.

What am I doing wrong? I am fairly new to react and I might be missing some key insight here.




 windowResize(sizes) {
//setting the state to something, forcing a re-render, and then onUpdate triggers
       this.setState({test:"test"}); 
   }

render() { 
...
return <PanelGroup style={{height:"100%"}} borderColor="grey" onUpdate={this.windowResize} panelWidths={[
            {size: 100, minSize:50, resize: "dynamic"},
            {minSize:100, resize: "stretch"},
            {size: 100, minSize:50, resize: "dynamic"}]}>
...
</PanelGroup>
}

//I added this to stop an infinite loop. Counter defined elsewhere
 shouldComponentUpdate(nextProps, nextState) {
        counter++;
        return counter<100;
    }

onUpdate bug

Great component, thanks!

What argument are you intending to pass to onUpdate callback as this.state.props doesn't exist?
this.state.panels?

 componentWillUpdate: function componentWillUpdate() {
    if (this.props.onUpdate) {
      this.props.onUpdate(this.state.props.slice());  
    }
  }

Panel maxSize

I am working on an app which requires 3 panes. But the two panes at the end should not expand after a particular limit. It would be good to have maxSize to be set for each pane.

Snapping feature

Hello,

I'm trying add a snapping feature to react-panelgroup. So far I've tried to change the size of each panel object based on where it is, the container width and how many panels there are. But when I change the size it gets stuck in a never ending loop of updating.

Figured I'd come on here and see if you know of any projects that tried to add some kind of snapping or constraining feature to react-panelgroup?

Thanks,
Michael

Don't understand the stretch property

Hi, first of all, I really like your library thanks form making it!. But I have a problem, I don't understand how does the "weight" works when the resize property is stretch, Could you care to explain me?

Expose defaults for panel widthss

Currently, defaults for sizes are hardcoded in, but it'd be nice to be able to specify the defaults for all panels for situations where you want the same defaultMinSize for all panels but you don't know the number of panels ahead of time and don't want to keep re-creating the panelWidths array.

https://github.com/DanFessler/react-panelgroup/blob/master/src/PanelGroup.js#L81-L84

      const defaultSize = 256;
      const defaultMinSize = 48;
      const defaultMaxSize = 0;
      const defaultResize = 'stretch';

to

      const defaultSize = this.props.defaultSize || 256;
      const defaultMinSize = this.props.defaultMinSize || 48;
      const defaultMaxSize = this.props.defaultMinSize || 0;
      const defaultResize = 'stretch';

Changing direction programmatically causes resize function to stop working

Hello! As the title says, changing the direction of panel group causes the resize functionality to stop working. I was able to reproduce this on the demo site by using React DevTools to change the direction of the panels and the same issue occurred as seen below.


Before changing the property:

image

After changing the property:

image

You'll notice the separators are no longer visible and you can no longer resize the panels.


The below code should be enough to reproduce this.

const [isColumnMode, setIsColumnMode] = useState(true);

return (
  <>
    <PanelGroup direction={isColumnMode ? 'column' : 'row'}>
      <div>Panel 1</div>
      <div>Panel 2</div>
    </PanelGroup>
    <button onClick={() => setIsColumnMode(!isColumnMode)}>
      Toggle View Mode
    </button>
  </>
);

I had one possible "fix" without changing the library, albeit a very non-performant one. Essentially adding a key to a parent component such that it changed every time the panel direction changed (ie <div key={isColumnMode}>), forces React to re-render all of that div's children including the PanelGroup. And since it would be re-rendered, it would act as a newly formed PanelGroup and work fine. However, this operation is very heavy and is not optimal for our use case.

Any help with this would be greatly appreciated. Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.