207
app/components/FloatGrid/Row.js
Executable file
207
app/components/FloatGrid/Row.js
Executable file
@@ -0,0 +1,207 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
WidthProvider,
|
||||
Responsive
|
||||
} from 'react-grid-layout';
|
||||
import { Row as BSRow } from 'reactstrap';
|
||||
|
||||
import { FloatGridContext } from './floatGridContext';
|
||||
|
||||
const ResponsiveGrid = WidthProvider(Responsive);
|
||||
const responsiveBreakpoints = {
|
||||
xl: 1139, lg: 959, md: 719, sm: 539, xs: 0
|
||||
//xl: Number.MAX_VALUE, lg: 1199, md: 991, sm: 767, xs: 576
|
||||
};
|
||||
const breakPointSteps = _.keys(responsiveBreakpoints);
|
||||
const TOTAL_ROW = 12;
|
||||
|
||||
const simplifyChildrenArray = (reactChildren) => _.map(reactChildren, child => (
|
||||
{ ...child, key: child.key.replace(/\.\$/g, '') }
|
||||
));
|
||||
|
||||
export class Row extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
columns: PropTypes.object,
|
||||
onLayoutChange: PropTypes.func,
|
||||
rowHeight: PropTypes.number,
|
||||
gridSize: PropTypes.object
|
||||
};
|
||||
|
||||
static contextType = FloatGridContext;
|
||||
|
||||
_lastLayouts = { };
|
||||
state = {
|
||||
trueColSizes: { },
|
||||
activeLayout: 'xl'
|
||||
}
|
||||
initialDebounceTimeout = false;
|
||||
|
||||
componentDidUpdate(nextProps) {
|
||||
if (!_.isEqual(nextProps.gridSize, this.props.gridSize)) {
|
||||
if (!_.isEmpty(this._lastLayouts)) {
|
||||
this._updateTrueColSizes(this._lastLayouts[this.state.activeLayout]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, rowHeight, onLayoutChange, ...otherProps } = this.props;
|
||||
const { trueColSizes } = this.state;
|
||||
|
||||
if (this.context.active) {
|
||||
const layouts = this._lastLayouts = this._calculateLayouts(children);
|
||||
const adjustedChildren = simplifyChildrenArray(
|
||||
React.Children.map(children, (child) =>
|
||||
React.cloneElement(child, { trueSize: trueColSizes[child.props.i] })));
|
||||
|
||||
return (
|
||||
<ResponsiveGrid
|
||||
cols={{ xl: 12, lg: 12, md: 12, sm: 12, xs: 12 }}
|
||||
breakpoints={ responsiveBreakpoints }
|
||||
layouts={ layouts }
|
||||
padding={ [ 0, 0 ] }
|
||||
margin={ [ 0, 0 ] }
|
||||
rowHeight={ rowHeight }
|
||||
onLayoutChange={(currentLayout, allLayouts) => {
|
||||
// Notify the parent
|
||||
onLayoutChange(this._transformForChangeHandler(allLayouts));
|
||||
// Recalculate true sizes
|
||||
this._updateTrueColSizes(currentLayout);
|
||||
|
||||
clearTimeout(this.initialDebounceTimeout);
|
||||
this.initialDebounceTimeout = setTimeout(() => {
|
||||
this.context.setGridReady();
|
||||
}, 0);
|
||||
}}
|
||||
onBreakpointChange={(activeLayout) => {
|
||||
this.setState({ activeLayout });
|
||||
}}
|
||||
onResize={
|
||||
(layout, oldItem, newItem) => {
|
||||
this.setState({
|
||||
trueColSizes: {
|
||||
...trueColSizes,
|
||||
[newItem.i]: this.context.gridUnitsToPx(newItem.w, newItem.h)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
{ ...otherProps }
|
||||
>
|
||||
{ adjustedChildren }
|
||||
</ResponsiveGrid>
|
||||
);
|
||||
} else {
|
||||
const adjustedChildren = React.Children.map(children, (child) =>
|
||||
React.cloneElement(child, { active: false }));
|
||||
|
||||
return (
|
||||
<BSRow>
|
||||
{ adjustedChildren }
|
||||
</BSRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_updateTrueColSizes = (layout) => {
|
||||
const { trueColSizes } = this.state;
|
||||
for (let child of layout) {
|
||||
trueColSizes[child.i] = this.context.gridUnitsToPx(child.w, child.h);
|
||||
}
|
||||
this.setState({ trueColSizes });
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nearest breakpoint relative to the one provided in the
|
||||
* first param. For example - when the `definition` param contains
|
||||
* such bps - { md: 6, xs: 8 }, for `breakpoint` - xl/md will return 6
|
||||
*/
|
||||
_findClosestBreakpoint = (breakpoint, definition) => {
|
||||
let found = 12;
|
||||
for (let bp of _.drop(breakPointSteps, _.indexOf(breakPointSteps, breakpoint))) {
|
||||
if (!_.isUndefined(definition[bp])) {
|
||||
found = definition[bp];
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
_calculateLayouts = (children) => {
|
||||
let output = { };
|
||||
const childrenArray = React.Children.toArray(children);
|
||||
for (let breakPoint of breakPointSteps) {
|
||||
let rowChildren = [];
|
||||
let rowCounter = 0;
|
||||
let y = 0;
|
||||
for (let child of childrenArray) {
|
||||
let bpData = { };
|
||||
// Save the props for current child and breakpoint
|
||||
const config = _.pick(child.props, [
|
||||
'i',
|
||||
'h', 'minH', 'maxH',
|
||||
'minW', 'maxW',
|
||||
breakPoint, `${breakPoint}MinW`, `${breakPoint}MaxW`,
|
||||
'moved', 'static', 'isResizable', 'isDraggable'
|
||||
]);
|
||||
// Calculate the needed definition
|
||||
bpData = Object.assign(bpData, {
|
||||
...config,
|
||||
// Add default heights when none provided
|
||||
...{
|
||||
// Set the x to the calculated value or take from the
|
||||
// props if defined for controlled type
|
||||
x: _.isUndefined(child.props[`${breakPoint}X`]) ?
|
||||
rowCounter : child.props[`${breakPoint}X`],
|
||||
h: child.props[`${breakPoint}H`] || config.h || 1,
|
||||
minH: config.minH || (config.h || 1),
|
||||
maxH: config.maxH || (config.h || 1),
|
||||
},
|
||||
w: config[breakPoint] ||
|
||||
this._findClosestBreakpoint(breakPoint, child.props),
|
||||
// Set the y to the calculated value or take from the
|
||||
// props if defined for controlled type
|
||||
y: _.isUndefined(child.props[`${breakPoint}Y`]) ?
|
||||
y : child.props[`${breakPoint}Y`]
|
||||
});
|
||||
rowChildren = [...rowChildren, bpData];
|
||||
rowCounter += bpData.w;
|
||||
if (rowCounter + bpData.x > TOTAL_ROW) {
|
||||
// Increase by the largest found height
|
||||
y += _.max(_.map(rowChildren, 'h'));
|
||||
rowCounter = 0;
|
||||
rowChildren = [];
|
||||
}
|
||||
output = {
|
||||
...output,
|
||||
[breakPoint]: [...(output[breakPoint] || []), bpData]
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the calculated layout to a structure
|
||||
* which is provided by `layouts` props
|
||||
*/
|
||||
_transformForChangeHandler = (layouts) => {
|
||||
let output = { };
|
||||
for (let breakPoint of breakPointSteps) {
|
||||
const bpLayout = layouts[breakPoint];
|
||||
for (let element of bpLayout) {
|
||||
output[element.i] = {
|
||||
...(output[element.i]),
|
||||
...(element),
|
||||
[breakPoint]: element.w,
|
||||
[`${breakPoint}X`]: element.x,
|
||||
[`${breakPoint}Y`]: element.y,
|
||||
[`${breakPoint}H`]: element.h
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user