Preview: http://dashboards.webkom.co/react/airframe
This commit is contained in:
Tomasz Owczarczyk
2019-08-15 00:54:44 +02:00
parent f975443095
commit 37092d1d6c
626 changed files with 56691 additions and 0 deletions

89
app/components/FloatGrid/Col.js Executable file
View File

@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import classNames from 'classnames';
import { Col as BootstrapCol } from 'reactstrap';
// Twice Smaller than Bootstrap breakpoints
const breakPoints = [
{ id: 'xl', min: 600 },
{ id: 'lg', min: 496, max: 600 },
{ id: 'md', min: 384, max: 496 },
{ id: 'sm', min: 288, max: 384 },
{ id: 'xs', max: 288 }
];
const getCurrentbreakPoint = (width, breakPoints) => {
let output = 'xl';
for (let bp of breakPoints) {
if (
(_.isUndefined(bp.min) || bp.min <= width) &&
(_.isUndefined(bp.max) || bp.max > width)
) {
output = bp.id;
}
}
return output;
};
export class Col extends React.Component {
static propTypes = {
active: PropTypes.bool,
lg: PropTypes.number,
md: PropTypes.number,
sm: PropTypes.number,
xs: PropTypes.number,
xl: PropTypes.number,
xlH: PropTypes.number,
lgH: PropTypes.number,
mdH: PropTypes.number,
smH: PropTypes.number,
xsH: PropTypes.number,
xlX: PropTypes.number,
lgX: PropTypes.number,
mdX: PropTypes.number,
smX: PropTypes.number,
xsX: PropTypes.number,
xlY: PropTypes.number,
lgY: PropTypes.number,
mdY: PropTypes.number,
smY: PropTypes.number,
xsY: PropTypes.number,
trueSize: PropTypes.object,
children: PropTypes.node,
className: PropTypes.string
}
static defaultProps = {
active: true
}
render() {
const { active, children, className, trueSize } = this.props;
const bsColumnProps = _.pick(this.props, ['xl', 'lg', 'md', 'sm', 'xs']);
const otherProps = _.omit(this.props, [..._.keys(Col.propTypes),
'minW', 'maxW', 'minH', 'maxH', 'moved', 'static', 'isDraggable', 'isResizable']);
const floatColBpId = trueSize ? getCurrentbreakPoint(trueSize.wPx, breakPoints) : 'xl';
const floatColClasses = classNames(className, 'float-col',
'float-column', `float-column--size-${floatColBpId}`);
return active ? (
<div { ...otherProps } className={ floatColClasses }>
{ children }
</div>
) : (
<BootstrapCol
{ ...(_.extend(bsColumnProps, otherProps)) }
className={ classNames(className, 'pb-3') }
>
{ children }
</BootstrapCol>
);
}
}

122
app/components/FloatGrid/Grid.js Executable file
View File

@@ -0,0 +1,122 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Container } from 'reactstrap';
import { FloatGridContext } from './floatGridContext';
import './../../styles/components/float-grid.scss';
export class Grid extends React.Component {
static propTypes = {
active: PropTypes.bool,
children: PropTypes.node,
fluid: PropTypes.bool,
rowHeight: PropTypes.number,
className: PropTypes.string
}
static defaultProps = {
active: true,
fluid: false,
rowHeight: 100
}
state = {
gridSize: { w: 0, h: 0 },
gridReady: false,
}
_gridRef = React.createRef();
_resizeDebounceTimeout = 0;
componentDidMount() {
this.setState({
gridSize: {
w: this._gridRef.current.clientWidth,
h: this._gridRef.current.clientHeight
}
});
if (typeof window !== 'undefined') {
window.addEventListener('resize', this._resizeHandler);
}
}
componentDidUpdate(nextProps) {
// HACK
if (
typeof window !== 'undefined' &&
nextProps.fluid !== this.props.fluid
) {
window.dispatchEvent(new Event('resize'));
}
}
componentWillUnmount() {
if (typeof window !== 'undefined') {
window.removeEventListener('resize', this._resizeHandler);
}
}
render() {
const { active, children, fluid, className, rowHeight, ...otherProps } = this.props;
const { gridSize } = this.state;
const modifiedChildren = React.Children.map(children, child => (
React.cloneElement(child, {
...otherProps,
active,
gridSize
})
));
const floatWrapClasses = classNames({
['float-grid-parent__static']: !fluid
}, className, 'float-grid-parent');
return(
<FloatGridContext.Provider
value={{
gridUnitsToPx: (w, h) => {
return {
wPx: w / 12 * gridSize.w,
hPx: h * rowHeight
}
},
active,
gridReady: this.state.gridReady,
setGridReady: () => { this.setState({ gridReady: true }) }
}}
>
{
active ? (
<div
className={ floatWrapClasses }
ref={ this._gridRef }
>
{ modifiedChildren }
</div>
) : (
<div ref={ this._gridRef }>
<Container fluid={ fluid } className={ className } { ...otherProps }>
{ modifiedChildren }
</Container>
</div>
)
}
</FloatGridContext.Provider>
);
}
_resizeHandler = () => {
clearInterval(this._resizeDebounceTimeout);
this._resizeDebounceTimeout = setTimeout(() => {
this.setState({
gridSize: {
w: this._gridRef.current.clientWidth,
h: this._gridRef.current.clientHeight
}
});
}, 1000 / 60 * 10); //Every 10 frames debounce
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FloatGridContext } from './floatGridContext';
export const Ready = ({ children }) => (
<FloatGridContext.Consumer>
{
({ gridReady }) => gridReady ? children : null
}
</FloatGridContext.Consumer>
);
Ready.propTypes = {
children: PropTypes.node
}

207
app/components/FloatGrid/Row.js Executable file
View 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;
}
}

View File

@@ -0,0 +1,3 @@
import React from 'react';
export const FloatGridContext = React.createContext();

View File

@@ -0,0 +1,16 @@
import { Col } from './Col';
import { Row } from './Row';
import { Grid } from './Grid';
import { Ready } from './Ready';
Grid.Col = Col;
Grid.Row = Row;
Grid.Ready = Ready;
export const applyColumn = (columnId, layouts) => ({
...layouts[columnId],
i: columnId,
key: columnId
});
export default Grid;