Dragging moving of panels in React Dashboard layout component

25 Jul 202324 minutes to read

The Dashboard Layout component is provided with dragging functionality to drag and reorder the panels within the layout. While dragging a panel, a holder will be highlighted below the panel indicating the panel placement on panel drop. This helps the user to decide whether to place the panel in the current position or revert to previous position without disturbing the layout.

If one or more panels collide while dragging, then the colliding panels will be pushed towards the left or right or top or bottom direction where an adaptive space for the collided panel is available. The position changes of these collided panels will be updated dynamically during dragging of a panel, so the user can conclude whether to place the panel in the current position or not.

While dragging a panel in Dashboard layout the following dragging events will be triggered,

  • dragStart - Triggers when panel drag starts
  • drag - Triggers when panel is being dragged
  • dragStop - Triggers when panel drag stops

The following sample demonstrates dragging and pushing of panels. For example, while dragging the panel 0 over panel 1, these panels get collided and push the panel 1 towards the feasible direction, so that, the panel 0 gets placed in the panel 1 position.

import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import * as React from 'react';
function App() {
    const cellSpacing = [10, 10];
    let panels = [
        { 'sizeX': 1, 'sizeY': 1, 'row': 0, 'col': 0, content: '<div class="content">0</div>' },
        { 'sizeX': 3, 'sizeY': 2, 'row': 0, 'col': 1, content: '<div class="content">1</div>' },
        { 'sizeX': 1, 'sizeY': 3, 'row': 0, 'col': 4, content: '<div class="content">2</div>' },
        { 'sizeX': 1, 'sizeY': 1, 'row': 1, 'col': 0, content: '<div class="content">3</div>' },
        { 'sizeX': 2, 'sizeY': 1, 'row': 2, 'col': 0, content: '<div class="content">4</div>' },
        { 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 2, content: '<div class="content">5</div>' },
        { 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 3, content: '<div class="content">6</div>' }
    ];
    function onDragStart() {
        console.log("Drag start");
    }
    //Dashboard Layout's drag event function
    function onDrag(args) {
        console.log("Dragging");
    }
    //Dashboard Layout's dragstop event function
    function onDragStop(args) {
        console.log("Drag stop");
    }
    return (<div>
    <div className="container">
      <DashboardLayoutComponent id='defaultLayout' cellSpacing={cellSpacing} panels={panels} columns={5} dragStart={onDragStart.bind(this)} drag={onDrag.bind(this)} dragStop={onDragStop.bind(this)}/>
    </div>
  </div>);
}
export default App;
import {  DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import * as React from 'react';

function App() {
  const cellSpacing: number[] = [10, 10];
  let panels: any =  [
    {'sizeX': 1, 'sizeY': 1, 'row': 0, 'col': 0, content:'<div class="content">0</div>'},
    {'sizeX': 3, 'sizeY': 2, 'row': 0, 'col': 1, content:'<div class="content">1</div>'},
    {'sizeX': 1, 'sizeY': 3, 'row': 0, 'col': 4, content:'<div class="content">2</div>'},
    {'sizeX': 1, 'sizeY': 1, 'row': 1, 'col': 0, content:'<div class="content">3</div>'},
    {'sizeX': 2, 'sizeY': 1, 'row': 2, 'col': 0, content:'<div class="content">4</div>'},
    {'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 2, content:'<div class="content">5</div>'},
    {'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 3, content:'<div class="content">6</div>'}
  ];
  function onDragStart(){
    console.log("Drag start");
  }
    //Dashboard Layout's drag event function
  function onDrag(args: any) {
    console.log("Dragging");
  }
  //Dashboard Layout's dragstop event function
  function onDragStop(args: any) {
    console.log("Drag stop");
  }
  return (
    <div>
    <div className="container">
      <DashboardLayoutComponent id='defaultLayout' cellSpacing={cellSpacing}  panels={panels} columns={5}
      dragStart={onDragStart.bind(this)} drag={onDrag.bind(this)} dragStop={onDragStop.bind(this)} />
    </div>
  </div>
  );
}
export default App;
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App  from './App';

ReactDOM.render(<App />, document.getElementById('root'));

Customizing the dragging handler

Initially, the complete panel will act as the handler for dragging the panel such that the dragging action occurs on clicking anywhere over a panel. However, this dragging handler for the panels can be customized using thedraggableHandle property to restrict the dragging action within a particular element in the panel.

The following sample demonstrates customizing the dragging handler of the panels where the dragging action of panel occurs only with the header of the panel.

import { AccumulationChartComponent, AccumulationSeriesCollectionDirective, AccumulationSeriesDirective, AccumulationTooltip, Category, ChartComponent, ColumnSeries, DataLabel, Inject, Legend, LineSeries, SeriesCollectionDirective, SeriesDirective, Tooltip } from "@syncfusion/ej2-react-charts";
import { DashboardLayoutComponent, PanelDirective, PanelsDirective } from '@syncfusion/ej2-react-layouts';
import * as React from 'react';
function App() {
    const cellSpacing = [10, 10];
    // Template for line Chart 
    function lineTemplate() {
        const lineData = [
            { x: 2013, y: 28 }, { x: 2014, y: 25 }, { x: 2015, y: 26 }, { x: 2016, y: 27 },
            { x: 2017, y: 32 }, { x: 2018, y: 35 },
        ];
        return (<div className="template">
            <ChartComponent style={{ "height": "162px" }}>
                <Inject services={[LineSeries]}/>
                    <SeriesCollectionDirective>
                        <SeriesDirective dataSource={lineData} xName='x' yName='y' type='Line'/>
                    </SeriesCollectionDirective>
            </ChartComponent>
            </div>);
    }
    // Template for Pie Chart
    function pieTemplate() {
        const pieData = [
            { x: 'TypeScript', y: 13, text: 'TS 13%' },
            { x: 'React', y: 12.5, text: 'Reat 12.5%' },
            { x: 'MVC', y: 12, text: 'MVC 12%' },
            { x: 'Core', y: 12.5, text: 'Core 12.5%' },
            { x: 'Vue', y: 10, text: 'Vue 10%' },
            { x: 'Angular', y: 40, text: 'Angular 40%' }
        ];
        return (<div className="template">
            <AccumulationChartComponent style={{ "height": "162px" }} tooltip={{ enable: true }}>
                <Inject services={[AccumulationTooltip]}/>
                <AccumulationSeriesCollectionDirective>
                    <AccumulationSeriesDirective dataSource={pieData} xName='x' yName='y' innerRadius="40%"/>
                </AccumulationSeriesCollectionDirective>
            </AccumulationChartComponent>
            </div>);
    }
    // Template for Pie Chart 1
    function pieTemplate1() {
        const pieData = [
            { 'x': 'Chrome', y: 37, text: '37%' },
            { 'x': 'UC Browser', y: 17, text: '17%' },
            { 'x': 'iPhone', y: 19, text: '19%' },
            { 'x': 'Others', y: 4, text: '4%' },
            { 'x': 'Opera', y: 11, text: '11%' },
            { 'x': 'Android', y: 12, text: '12%' }
        ];
        const dataLabel = { visible: true, position: 'Inside', name: 'text', font: { fontWeight: '600' } };
        const enableAnimation = false;
        return (<div className="template">
                <AccumulationChartComponent style={{ "height": "162px" }} enableAnimation={enableAnimation} legendSettings={{ visible: false }} tooltip={{ enable: true, format: '${point.x} : <b>${point.y}%</b>' }}>
                    <Inject services={[AccumulationTooltip]}/>
                    <AccumulationSeriesCollectionDirective>
                        <AccumulationSeriesDirective dataSource={pieData} dataLabel={dataLabel} xName='x' yName='y' radius="70%" name='Browser'/>
                    </AccumulationSeriesCollectionDirective>
                </AccumulationChartComponent>
            </div>);
    }
    function columnTemplate() {
        const chartData = [
            { month: 'Jan', sales: 35 }, { month: 'Feb', sales: 28 },
            { month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
            { month: 'May', sales: 40 }, { month: 'Jun', sales: 32 },
            { month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
            { month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
            { month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
        ];
        return (<div className="template">
                <ChartComponent style={{ "height": "162px" }} primaryXAxis={{ valueType: 'Category' }}>
                    <Inject services={[ColumnSeries, Legend, Tooltip, Category, DataLabel]}/>
                        <SeriesCollectionDirective>
                            <SeriesDirective dataSource={chartData} xName='month' yName='sales' type='Column'/>
                        </SeriesCollectionDirective>
                </ChartComponent>
            </div>);
    }
    return (<div id='container'>
           <DashboardLayoutComponent id="dashboard_default" draggableHandle='.e-panel-header' columns={6} cellSpacing={cellSpacing} allowResizing={true}>
                <PanelsDirective>
                    <PanelDirective sizeX={3} sizeY={2} row={0} col={0} content={pieTemplate} header="<div class='header'>Product usage ratio</div><span class='handler e-icons burg-icon'></span>"/>
                    <PanelDirective sizeX={3} sizeY={2} row={0} col={3} content={columnTemplate} header="<div class='header'>Last year Sales Comparison</div><span class='handler e-icons burg-icon'></span>"/>
                    <PanelDirective sizeX={3} sizeY={2} row={0} col={3} content={pieTemplate1} header="<div class='header'>Mobile browsers usage</div><span class='handler e-icons burg-icon'></span>"/>
                    <PanelDirective sizeX={3} sizeY={2} row={1} col={0} content={lineTemplate} header="<div class='header'>Sales increase percentage</div><span class='handler e-icons burg-icon'></span>"/>
                </PanelsDirective>
            </DashboardLayoutComponent>
        </div>);
}
export default App;
import {
    AccumulationChartComponent, AccumulationSeriesCollectionDirective,
    AccumulationSeriesDirective, AccumulationTooltip, Category, ChartComponent, ColumnSeries, DataLabel, Inject, Legend, LineSeries,
    SeriesCollectionDirective, SeriesDirective, Tooltip
  } from "@syncfusion/ej2-react-charts";
import {  DashboardLayoutComponent, PanelDirective , PanelsDirective } from '@syncfusion/ej2-react-layouts';
import * as React from 'react';

function App() {
    const cellSpacing: number[] = [10, 10];

    // Template for line Chart 
    function lineTemplate(): JSX.Element {
         const lineData: object[] = [
            { x: 2013, y: 28 }, { x: 2014, y: 25 },{ x: 2015, y: 26 }, { x: 2016, y: 27 },
            { x: 2017, y: 32 }, { x: 2018, y: 35 },
         ];

        return(
            <div className="template" >
            <ChartComponent style={{ "height":"162px" }}>
                <Inject services={[LineSeries]} />
                    <SeriesCollectionDirective>
                        <SeriesDirective dataSource={lineData} xName='x' yName='y' type='Line'/>
                    </SeriesCollectionDirective>
            </ChartComponent>
            </div>
        );
     }

    // Template for Pie Chart
    function pieTemplate(): JSX.Element {
        const pieData: object[] = [
            { x: 'TypeScript', y: 13, text: 'TS 13%' }, 
            { x: 'React', y: 12.5, text: 'Reat 12.5%' },
            { x: 'MVC', y: 12, text: 'MVC 12%' },
            { x: 'Core', y: 12.5, text: 'Core 12.5%' },
            { x: 'Vue', y: 10, text: 'Vue 10%' },
            { x: 'Angular', y: 40, text: 'Angular 40%' }
        ];
        return(
            <div className="template" >
            <AccumulationChartComponent style={{"height":"162px"}} tooltip={{enable: true}}>
                <Inject services={[ AccumulationTooltip]} />
                <AccumulationSeriesCollectionDirective>
                    <AccumulationSeriesDirective dataSource={pieData} xName='x' yName='y' innerRadius="40%" />
                </AccumulationSeriesCollectionDirective>
            </AccumulationChartComponent>
            </div>
        );
    }

    // Template for Pie Chart 1
    function pieTemplate1(): JSX.Element {
        const pieData: object[] = [
            { 'x': 'Chrome', y: 37, text: '37%' },
            { 'x': 'UC Browser', y: 17, text: '17%' },
            { 'x': 'iPhone', y: 19, text: '19%' },
            { 'x': 'Others', y: 4, text: '4%' },
            { 'x': 'Opera', y: 11, text: '11%' },
            { 'x': 'Android', y: 12, text: '12%' }
        ];
        const dataLabel: object = { visible: true, position: 'Inside', name: 'text', font: { fontWeight: '600' }};
        const enableAnimation: boolean = false;
        return(
            <div className="template" >
                <AccumulationChartComponent style={{"height":"162px"}} enableAnimation={ enableAnimation } legendSettings= {{ visible: false }}
                    tooltip={{enable: true, format: '${point.x} : <b>${point.y}%</b>'}}>
                    <Inject services={[ AccumulationTooltip]} />
                    <AccumulationSeriesCollectionDirective>
                        <AccumulationSeriesDirective dataSource={pieData} dataLabel={dataLabel} xName='x' yName='y' radius="70%" name = 'Browser'/>
                    </AccumulationSeriesCollectionDirective>
                </AccumulationChartComponent>
            </div>
        );
    }

    function columnTemplate(): JSX.Element {
        const chartData: any[] = [
          { month: 'Jan', sales: 35 }, { month: 'Feb', sales: 28 },
          { month: 'Mar', sales: 34 }, { month: 'Apr', sales: 32 },
          { month: 'May', sales: 40 }, { month: 'Jun', sales: 32 },
          { month: 'Jul', sales: 35 }, { month: 'Aug', sales: 55 },
          { month: 'Sep', sales: 38 }, { month: 'Oct', sales: 30 },
          { month: 'Nov', sales: 25 }, { month: 'Dec', sales: 32 }
        ];
        return(
            <div className="template" >
                <ChartComponent style={{"height":"162px"}} primaryXAxis={{ valueType: 'Category'  }}>
                    <Inject services={[ColumnSeries, Legend, Tooltip, Category, DataLabel]} />
                        <SeriesCollectionDirective>
                            <SeriesDirective dataSource={chartData} xName='month' yName='sales' type='Column' />
                        </SeriesCollectionDirective>
                </ChartComponent>
            </div>
        );
     }
    return (
        <div id='container'>
           <DashboardLayoutComponent id="dashboard_default" draggableHandle='.e-panel-header' columns={6} cellSpacing={cellSpacing} allowResizing={true}>
                <PanelsDirective>
                    <PanelDirective sizeX={3} sizeY={2} row={0} col={0} content={pieTemplate as any} header="<div class='header'>Product usage ratio</div><span class='handler e-icons burg-icon'></span>" />
                    <PanelDirective sizeX={3} sizeY={2} row={0} col={3} content={columnTemplate as any} header="<div class='header'>Last year Sales Comparison</div><span class='handler e-icons burg-icon'></span>" />
                    <PanelDirective sizeX={3} sizeY={2} row={0} col={3} content={pieTemplate1 as any} header="<div class='header'>Mobile browsers usage</div><span class='handler e-icons burg-icon'></span>"/>
                    <PanelDirective sizeX={3} sizeY={2} row={1} col={0} content={lineTemplate as any} header="<div class='header'>Sales increase percentage</div><span class='handler e-icons burg-icon'></span>"/>
                </PanelsDirective>
            </DashboardLayoutComponent>
        </div>
    );
}
export default App;
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App  from './App';

ReactDOM.render(<App />, document.getElementById('root'));

Disable dragging of panels

By default, the dragging of panels is enabled in Dashboard Layout. It can also be disabled with the help of allowDragging API. Setting allowDragging to false disables the dragging functionality in Dashboard Layout.

The following sample demonstrates Dashboard Layout with dragging support disabled.

import { DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import * as React from 'react';
function App() {
    const cellSpacing = [10, 10];
    let allowDragging = false;
    let panels = [
        { 'sizeX': 1, 'sizeY': 1, 'row': 0, 'col': 0, content: '<div class="content">0</div>' },
        { 'sizeX': 3, 'sizeY': 2, 'row': 0, 'col': 1, content: '<div class="content">1</div>' },
        { 'sizeX': 1, 'sizeY': 3, 'row': 0, 'col': 4, content: '<div class="content">2</div>' },
        { 'sizeX': 1, 'sizeY': 1, 'row': 1, 'col': 0, content: '<div class="content">3</div>' },
        { 'sizeX': 2, 'sizeY': 1, 'row': 2, 'col': 0, content: '<div class="content">4</div>' },
        { 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 2, content: '<div class="content">5</div>' },
        { 'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 3, content: '<div class="content">6</div>' }
    ];
    return (<div>
          <div className="container">
          <DashboardLayoutComponent id='defaultLayout' cellSpacing={cellSpacing} panels={panels} columns={5} allowDragging={allowDragging}/>
          </div>
      </div>);
}
export default App;
import {  DashboardLayoutComponent } from '@syncfusion/ej2-react-layouts';
import * as React from 'react';

function App() {
  const cellSpacing: number[] = [10, 10];
  let allowDragging: boolean = false;
  let panels: any =  [
  {'sizeX': 1, 'sizeY': 1, 'row': 0, 'col': 0, content:'<div class="content">0</div>'},
  {'sizeX': 3, 'sizeY': 2, 'row': 0, 'col': 1, content:'<div class="content">1</div>'},
  {'sizeX': 1, 'sizeY': 3, 'row': 0, 'col': 4, content:'<div class="content">2</div>'},
  {'sizeX': 1, 'sizeY': 1, 'row': 1, 'col': 0, content:'<div class="content">3</div>'},
  {'sizeX': 2, 'sizeY': 1, 'row': 2, 'col': 0, content:'<div class="content">4</div>'},
  {'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 2, content:'<div class="content">5</div>'},
  {'sizeX': 1, 'sizeY': 1, 'row': 2, 'col': 3, content:'<div class="content">6</div>'}
  ];

  return (
      <div>
          <div className="container">
          <DashboardLayoutComponent id='defaultLayout' cellSpacing={cellSpacing}  panels={panels} columns={5}
          allowDragging={allowDragging} />
          </div>
      </div>
  );
}
export default App;
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App  from './App';

ReactDOM.render(<App />, document.getElementById('root'));

You can refer to our React Dashboard Layout feature tour page for its groundbreaking feature representations. You can also explore our React Dashboard Layout example to knows how to present and manipulate data.