Skip to content

Instantly share code, notes, and snippets.

@DWboutin
Created April 27, 2020 15:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DWboutin/03e60c46ec8b31a83d26fdef76ec7d10 to your computer and use it in GitHub Desktop.
Save DWboutin/03e60c46ec8b31a83d26fdef76ec7d10 to your computer and use it in GitHub Desktop.
React-DND
import React from 'react';
import { connect } from 'react-redux';
import { Responsive, WidthProvider } from 'react-grid-layout';
import { changeLayout, changeCurrentBreakpoint, changeWidgetOptions } from '../../actions/dashboard-actions';
import { fetch as fetchLayouts, create as createLayout } from '../../actions/layouts-actions';
import { fetch as fetchChartTypes } from '../../actions/chart-types-actions';
import {
fetch as fetchWidgets,
create as createWidget,
update as updateWidget,
updateMany as updateManyWidgets,
remove as removeWidget
} from '../../actions/widgets-actions';
import { toggleDigitalControl, historyFetch} from '../../actions/devices-actions';
import DropZone from '../../wrapper/DropZone';
import WidgetCard from '../reusables/WidgetCard';
import WidgetDigitalControl from '../reusables/WidgetDigitalControl';
import WidgetDigitalSensor from '../reusables/WidgetDigitalSensor';
import WidgetAnalogSensor from '../reusables/WidgetAnalogSensor';
import AnalogSensorForm from '../reusables/WidgetOptionsForm/AnalogSensorForm';
import DigitalSensorForm from '../reusables/WidgetOptionsForm/DigitalSensorForm';
import DigitalControlForm from '../reusables/WidgetOptionsForm/DigitalControlForm';
import StyledDialog from '../reusables/StyledDialog';
import SocketsHandler from '../../utils/socketsHandler';
import { createWidgetKey, getWidgetLayouts } from '../../utils/helpers';
import '../../assets/scss/components/section/WidgetGrid.scss';
const ResponsiveReactGridLayout = WidthProvider(Responsive);
class WidgetGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
compactType: 'vertical',
mounted: true,
dialogIsOpen: false,
dialogWidgetProps: null,
layouts: null,
isModified: false,
deviceHistoryFetch: [],
};
this.socketHandler = null;
this.deviceHistoryIdsFetching = []; // to store device id to fetch history only one time
this.handleDropItem = this.handleDropItem.bind(this);
this.onBreakpointChange = this.onBreakpointChange.bind(this);
this.onLayoutChange = this.onLayoutChange.bind(this);
this.getWidgetsKeysLengthForKey = this.getWidgetsKeysLengthForKey.bind(this);
this.handleDialogOpen = this.handleDialogOpen.bind(this);
this.handleDialogClose = this.handleDialogClose.bind(this);
this.handleClearDialogWidgetProps = this.handleClearDialogWidgetProps.bind(this);
this.handleWidgetOptionsFormSubmit = this.handleWidgetOptionsFormSubmit.bind(this);
this.onLayoutModification = this.onLayoutModification.bind(this);
this.handleDigitalControlToggle = this.handleDigitalControlToggle.bind(this);
this.handleWidgetRemove = this.handleWidgetRemove.bind(this);
this.handleDeviceHistoryFetch = this.handleDeviceHistoryFetch.bind(this);
}
componentDidMount() {
const { getLayouts, createLayout, getWidgets, getChartTypes } = this.props;
this.socketHandler = new SocketsHandler();
this.socketHandler.listen();
getLayouts().then((layoutList) => {
if (layoutList.length <= 0) {
createLayout({
name: 'default',
}).then(getLayouts());
}
});
getWidgets();
getChartTypes();
function iframeResize() {
// What's the page height?
var height = document.body.scrollHeight;
parent.postMessage(height, "*");
}
iframeResize();
}
onBreakpointChange(breakpoint) {
const { changeCurrentBreakpoint } = this.props;
changeCurrentBreakpoint(breakpoint);
}
onLayoutChange(layout, layouts) {
const { changeLayout } = this.props;
this.setState({
layouts,
});
changeLayout(layouts);
if (this.state.isModified) {
this.updateWidgetLayouts();
}
}
onLayoutModification() {
this.setState({
isModified: true,
});
}
updateWidgetLayouts() {
const { updateManyWidgets, getWidgets } = this.props;
const objectsLayouts = {};
const layouts = this.state.layouts;
Object.keys(layouts).forEach((size) => {
layouts[size].forEach((layout) => {
const widgetKey = (typeof layout.i !== 'string') ? 'widget-id-' + layout.i : layout.i;
const currentObj = objectsLayouts[widgetKey];
if (typeof currentObj === 'undefined') {
objectsLayouts[widgetKey] = {};
}
layout.i = widgetKey;
objectsLayouts[widgetKey][size] = layout;
});
});
updateManyWidgets(objectsLayouts).then(() => {
this.setState({
isModified: false,
});
getWidgets();
});
}
getWidgetsKeysLengthForKey(key) {
const { widgets } = this.props;
const keys = [];
Object.keys(widgets).forEach((widgetKey) => {
if (widgetKey.indexOf(key) !== -1) {
keys.push(widgetKey);
}
});
return keys.length;
}
handleDropItem(droppedProps) {
const { createWidget, activeLayout, layoutsList, getWidgets, currentBreakpoint } = this.props;
const widgetLayout = {};
widgetLayout[currentBreakpoint] = {
...droppedProps.widget.grid,
i: `widget-id-${droppedProps.widget.id}`,
};
createWidget(droppedProps.widget, layoutsList[activeLayout].id, widgetLayout)
.then(() => {
// refresh widget list
getWidgets()
});
}
handleDialogOpen(widgetProps) {
this.setState({
dialogIsOpen: true,
dialogWidgetProps: widgetProps,
});
}
handleDialogClose() {
this.setState({
dialogIsOpen: false,
});
}
handleClearDialogWidgetProps() {
this.setState({
dialogWidgetProps: null,
});
}
handleWidgetOptionsFormSubmit(values) {
const { updateWidget, getWidgets } = this.props;
const widgetId = values.widget_id;
delete values.widget_id;
updateWidget(widgetId, values)
.then(() => getWidgets())
.then(() => {
this.handleDialogClose();
});
}
handleWidgetRemove() {
const { deleteWidget, getWidgets } = this.props;
const { dialogWidgetProps } = this.state;
deleteWidget(dialogWidgetProps.id)
.then(() => getWidgets())
.then(() => {
this.handleDialogClose();
});
}
handleDigitalControlToggle(objectId) {
const { toggleDigitalControlDevice } = this.props;
toggleDigitalControlDevice(objectId);
}
handleDeviceHistoryFetch(objectId, dateStart, dateEnd) {
const { getDeviceHistory, devicesHistory } = this.props;
if (this.deviceHistoryIdsFetching.indexOf(objectId) === -1 && Object.keys(devicesHistory).indexOf(objectId) === -1) {
this.deviceHistoryIdsFetching.push(objectId);
return getDeviceHistory(objectId, dateStart, dateEnd);
} else {
return false;
}
}
render() {
const { widgets, layouts, cols, breakpoints, rowHeight, currentBreakpoint, devices, devicesHistory } = this.props;
const { dialogIsOpen, dialogWidgetProps } = this.state;
return (
<DropZone id="widget-grid" accepts={['device']} onDrop={this.handleDropItem}>
<ResponsiveReactGridLayout
id="widget-grid"
breakpoints={breakpoints}
layouts={layouts}
cols={cols}
rowHeight={rowHeight}
onBreakpointChange={this.onBreakpointChange}
onLayoutChange={this.onLayoutChange}
measureBeforeMount
useCSSTransforms={this.state.mounted}
compactType={this.state.compactType}
preventCollision={!this.state.compactType}
onDragStop={this.onLayoutModification}
onResizeStop={this.onLayoutModification}
>
{widgets.map(widget => (
<div key={`widget-id-${widget.id}`} data-grid={widget.layout[currentBreakpoint]}>
{(devices[widget.object_id] && devices[widget.object_id].is_digital_control) && (
<WidgetDigitalControl
{...widget}
device={devices[widget.object_id]}
deviceHistory={devicesHistory[widget.object_id]}
socketHandler={this.socketHandler}
widgetKey={widget['random-generated-hash-id']}
handleDialogOpen={this.handleDialogOpen}
handleDigitalControlToggle={this.handleDigitalControlToggle}
handleDeviceHistoryFetch={this.handleDeviceHistoryFetch}
/>
)}
{(devices[widget.object_id] && devices[widget.object_id].is_digital_sensor) && (
<WidgetDigitalSensor
{...widget}
device={devices[widget.object_id]}
deviceHistory={devicesHistory[widget.object_id]}
socketHandler={this.socketHandler}
widgetKey={widget['random-generated-hash-id']}
handleDialogOpen={this.handleDialogOpen}
handleDeviceHistoryFetch={this.handleDeviceHistoryFetch}
/>
)}
{(devices[widget.object_id] && devices[widget.object_id].is_analog_sensor) && (
<WidgetAnalogSensor
{...widget}
device={devices[widget.object_id]}
deviceHistory={devicesHistory[widget.object_id]}
socketHandler={this.socketHandler}
widgetKey={widget['random-generated-hash-id']}
handleDialogOpen={this.handleDialogOpen}
handleDeviceHistoryFetch={this.handleDeviceHistoryFetch}
/>
)}
</div>
))}
</ResponsiveReactGridLayout>
<StyledDialog
isOpen={(dialogIsOpen && dialogWidgetProps !== null)}
iconClasses="far fa-cog"
title={(dialogWidgetProps !== null) ? dialogWidgetProps.name : ''}
handleDialogClose={this.handleDialogClose}
handleClearDialogWidgetProps={this.handleClearDialogWidgetProps}
color={(dialogWidgetProps !== null && dialogWidgetProps.color) ? dialogWidgetProps.color : ''}
>
{(dialogIsOpen && dialogWidgetProps !== null && dialogWidgetProps.device.is_digital_control) && (
<DigitalControlForm
widgetProps={dialogWidgetProps}
onSubmit={this.handleWidgetOptionsFormSubmit}
handleDialogClose={this.handleDialogClose}
onWidgetRemove={this.handleWidgetRemove}
/>
)}
{(dialogIsOpen && dialogWidgetProps !== null && dialogWidgetProps.device.is_digital_sensor) && (
<DigitalSensorForm
widgetProps={dialogWidgetProps}
onSubmit={this.handleWidgetOptionsFormSubmit}
handleDialogClose={this.handleDialogClose}
onWidgetRemove={this.handleWidgetRemove}
/>
)}
{(dialogIsOpen && dialogWidgetProps !== null && dialogWidgetProps.device.is_analog_sensor) && (
<AnalogSensorForm
widgetProps={dialogWidgetProps}
onSubmit={this.handleWidgetOptionsFormSubmit}
handleDialogClose={this.handleDialogClose}
onWidgetRemove={this.handleWidgetRemove}
/>
)}
</StyledDialog>
</DropZone>
);
}
}
WidgetGrid.propTypes = {};
WidgetGrid.defaultProps = {
className: 'layout',
rowHeight: 30,
cols: {
xlg: 12,
lg: 12,
md: 10,
sm: 6,
xs: 4,
},
breakpoints: {
xlg: 1920,
lg: 1280,
md: 960,
sm: 600,
xs: 0,
},
initialLayout: [],
};
export const mapStateToProps = (state, props) => ({
locale: state.application.locale,
widgets: state.widgets.list,
layouts: state.layouts.layout,
devices: state.devices.list,
devicesHistory: state.devices.history,
activeLayout: state.layouts.activeLayout,
layoutsList: state.layouts.list,
currentBreakpoint: state.dashboard.currentBreakpoint,
});
export const mapDispatchToProps = dispatch => ({
getChartTypes: () => dispatch(fetchChartTypes()),
getLayouts: () => dispatch(fetchLayouts()),
getDeviceHistory: (objectId, dateStart, dateEnd) => dispatch(historyFetch(objectId, dateStart, dateEnd)),
createLayout: (layout) => dispatch(createLayout(layout)),
getWidgets: (widgetKey, widget) => dispatch(fetchWidgets()),
addWidget: (widgetKey, widget) => dispatch(addWidget(widgetKey, widget)),
createWidget: (widget, layoutId, widgetLayout) => dispatch(createWidget(widget, layoutId, widgetLayout)),
updateWidget: (widgetId, widgetData) => dispatch(updateWidget(widgetId, widgetData)),
deleteWidget: (widgetId) => dispatch(removeWidget(widgetId)),
changeCurrentBreakpoint: (breakpoint) => dispatch(changeCurrentBreakpoint(breakpoint)),
changeLayout: (layouts) => dispatch(changeLayout(layouts)),
updateManyWidgets: (widgets) => dispatch(updateManyWidgets(widgets)),
toggleDigitalControlDevice: (objectId) => dispatch(toggleDigitalControl(objectId))
});
export default connect(mapStateToProps, mapDispatchToProps)(WidgetGrid);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment