react-grid-layout 0.17.1 → 0.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.js +10 -14
- package/.flowconfig +13 -1
- package/.travis.yml +3 -1
- package/CHANGELOG.md +66 -0
- package/README.md +47 -7
- package/__mocks__/react-grid-layout.js +2 -0
- package/build/GridItem.js +125 -211
- package/build/ReactGridLayout.js +142 -220
- package/build/ReactGridLayoutPropTypes.js +129 -0
- package/build/ResponsiveReactGridLayout.js +52 -44
- package/build/calculateUtils.js +177 -0
- package/build/components/WidthProvider.js +21 -11
- package/build/fastRGLPropsEqual.js +5 -0
- package/build/responsiveUtils.js +5 -5
- package/build/utils.js +77 -10
- package/css/styles.css +4 -0
- package/flow-typed/npm/classnames_v2.x.x.js +23 -0
- package/flow-typed/npm/enzyme_v3.x.x.js +143 -0
- package/flow-typed/npm/jest_v24.x.x.js +1182 -0
- package/lib/GridItem.jsx +125 -148
- package/lib/ReactGridLayout.jsx +83 -200
- package/lib/ReactGridLayoutPropTypes.js +179 -0
- package/lib/ResponsiveReactGridLayout.jsx +36 -28
- package/lib/calculateUtils.js +142 -0
- package/lib/components/WidthProvider.jsx +35 -16
- package/lib/fastRGLPropsEqual.js +46 -0
- package/lib/responsiveUtils.js +13 -20
- package/lib/utils.js +62 -10
- package/package.json +44 -29
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import React from "react";
|
|
2
|
+
import * as React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import isEqual from "lodash.isequal";
|
|
5
5
|
|
|
@@ -7,16 +7,17 @@ import {
|
|
|
7
7
|
cloneLayout,
|
|
8
8
|
synchronizeLayoutWithChildren,
|
|
9
9
|
validateLayout,
|
|
10
|
-
noop
|
|
10
|
+
noop,
|
|
11
|
+
type Layout
|
|
11
12
|
} from "./utils";
|
|
12
13
|
import {
|
|
13
14
|
getBreakpointFromWidth,
|
|
14
15
|
getColsFromBreakpoint,
|
|
15
|
-
findOrGenerateResponsiveLayout
|
|
16
|
+
findOrGenerateResponsiveLayout,
|
|
17
|
+
type ResponsiveLayout,
|
|
18
|
+
type Breakpoints,
|
|
16
19
|
} from "./responsiveUtils";
|
|
17
20
|
import ReactGridLayout from "./ReactGridLayout";
|
|
18
|
-
import type { Props as RGLProps } from "./ReactGridLayout";
|
|
19
|
-
import type { Layout } from "./utils";
|
|
20
21
|
|
|
21
22
|
const type = obj => Object.prototype.toString.call(obj);
|
|
22
23
|
|
|
@@ -42,14 +43,14 @@ type State = {
|
|
|
42
43
|
layouts?: { [key: string]: Layout }
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
type Props<Breakpoint: string = string> = {
|
|
46
|
-
|
|
46
|
+
type Props<Breakpoint: string = string> = {|
|
|
47
|
+
...React.ElementConfig<typeof ReactGridLayout>,
|
|
47
48
|
|
|
48
49
|
// Responsive config
|
|
49
|
-
breakpoint
|
|
50
|
-
breakpoints:
|
|
50
|
+
breakpoint?: ?Breakpoint,
|
|
51
|
+
breakpoints: Breakpoints<Breakpoint>,
|
|
51
52
|
cols: { [key: Breakpoint]: number },
|
|
52
|
-
layouts:
|
|
53
|
+
layouts: ResponsiveLayout<Breakpoint>,
|
|
53
54
|
width: number,
|
|
54
55
|
margin: { [key: Breakpoint]: [number, number] } | [number, number],
|
|
55
56
|
containerPadding: { [key: Breakpoint]: [number, number] } | [number, number],
|
|
@@ -63,7 +64,7 @@ type Props<Breakpoint: string = string> = {
|
|
|
63
64
|
cols: number,
|
|
64
65
|
containerPadding: [number, number] | null
|
|
65
66
|
) => void
|
|
66
|
-
};
|
|
67
|
+
|};
|
|
67
68
|
|
|
68
69
|
export default class ResponsiveReactGridLayout extends React.Component<
|
|
69
70
|
Props<>,
|
|
@@ -141,7 +142,13 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
141
142
|
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
|
|
142
143
|
layouts: {},
|
|
143
144
|
margin: [10, 10],
|
|
144
|
-
containerPadding: {
|
|
145
|
+
containerPadding: {
|
|
146
|
+
lg: [0, 0],
|
|
147
|
+
md: [0, 0],
|
|
148
|
+
sm: [0, 0],
|
|
149
|
+
xs: [0, 0],
|
|
150
|
+
xxs: [0, 0]
|
|
151
|
+
},
|
|
145
152
|
onBreakpointChange: noop,
|
|
146
153
|
onLayoutChange: noop,
|
|
147
154
|
onWidthChange: noop
|
|
@@ -203,7 +210,7 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
203
210
|
!isEqual(this.props.breakpoints, prevProps.breakpoints) ||
|
|
204
211
|
!isEqual(this.props.cols, prevProps.cols)
|
|
205
212
|
) {
|
|
206
|
-
this.onWidthChange(
|
|
213
|
+
this.onWidthChange(prevProps);
|
|
207
214
|
}
|
|
208
215
|
}
|
|
209
216
|
|
|
@@ -219,28 +226,29 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
219
226
|
* When the width changes work through breakpoints and reset state with the new width & breakpoint.
|
|
220
227
|
* Width changes are necessary to figure out the widget widths.
|
|
221
228
|
*/
|
|
222
|
-
onWidthChange(
|
|
223
|
-
const { breakpoints, cols, layouts, compactType } =
|
|
229
|
+
onWidthChange(prevProps: Props<*>) {
|
|
230
|
+
const { breakpoints, cols, layouts, compactType } = this.props;
|
|
224
231
|
const newBreakpoint =
|
|
225
|
-
|
|
226
|
-
getBreakpointFromWidth(
|
|
232
|
+
this.props.breakpoint ||
|
|
233
|
+
getBreakpointFromWidth(this.props.breakpoints, this.props.width);
|
|
227
234
|
|
|
228
235
|
const lastBreakpoint = this.state.breakpoint;
|
|
229
236
|
const newCols: number = getColsFromBreakpoint(newBreakpoint, cols);
|
|
237
|
+
const newLayouts = { ...layouts };
|
|
230
238
|
|
|
231
239
|
// Breakpoint change
|
|
232
240
|
if (
|
|
233
241
|
lastBreakpoint !== newBreakpoint ||
|
|
234
|
-
|
|
235
|
-
|
|
242
|
+
prevProps.breakpoints !== breakpoints ||
|
|
243
|
+
prevProps.cols !== cols
|
|
236
244
|
) {
|
|
237
245
|
// Preserve the current layout if the current breakpoint is not present in the next layouts.
|
|
238
|
-
if (!(lastBreakpoint in
|
|
239
|
-
|
|
246
|
+
if (!(lastBreakpoint in newLayouts))
|
|
247
|
+
newLayouts[lastBreakpoint] = cloneLayout(this.state.layout);
|
|
240
248
|
|
|
241
249
|
// Find or generate a new layout.
|
|
242
250
|
let layout = findOrGenerateResponsiveLayout(
|
|
243
|
-
|
|
251
|
+
newLayouts,
|
|
244
252
|
breakpoints,
|
|
245
253
|
newBreakpoint,
|
|
246
254
|
lastBreakpoint,
|
|
@@ -251,16 +259,16 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
251
259
|
// This adds missing items.
|
|
252
260
|
layout = synchronizeLayoutWithChildren(
|
|
253
261
|
layout,
|
|
254
|
-
|
|
262
|
+
this.props.children,
|
|
255
263
|
newCols,
|
|
256
264
|
compactType
|
|
257
265
|
);
|
|
258
266
|
|
|
259
267
|
// Store the new layout.
|
|
260
|
-
|
|
268
|
+
newLayouts[newBreakpoint] = layout;
|
|
261
269
|
|
|
262
270
|
// callbacks
|
|
263
|
-
this.props.onLayoutChange(layout,
|
|
271
|
+
this.props.onLayoutChange(layout, newLayouts);
|
|
264
272
|
this.props.onBreakpointChange(newBreakpoint, newCols);
|
|
265
273
|
|
|
266
274
|
this.setState({
|
|
@@ -270,15 +278,15 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
270
278
|
});
|
|
271
279
|
}
|
|
272
280
|
|
|
273
|
-
const margin = getIndentationValue(
|
|
281
|
+
const margin = getIndentationValue(this.props.margin, newBreakpoint);
|
|
274
282
|
const containerPadding = getIndentationValue(
|
|
275
|
-
|
|
283
|
+
this.props.containerPadding,
|
|
276
284
|
newBreakpoint
|
|
277
285
|
);
|
|
278
286
|
|
|
279
287
|
//call onWidthChange on every change of width, not only on breakpoint changes
|
|
280
288
|
this.props.onWidthChange(
|
|
281
|
-
|
|
289
|
+
this.props.width,
|
|
282
290
|
margin,
|
|
283
291
|
newCols,
|
|
284
292
|
containerPadding
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type { Position } from "./utils";
|
|
3
|
+
|
|
4
|
+
export type PositionParams = {
|
|
5
|
+
margin: [number, number],
|
|
6
|
+
containerPadding: [number, number],
|
|
7
|
+
containerWidth: number,
|
|
8
|
+
cols: number,
|
|
9
|
+
rowHeight: number,
|
|
10
|
+
maxRows: number
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Helper for generating column width
|
|
14
|
+
export function calcGridColWidth(positionParams: PositionParams): number {
|
|
15
|
+
const { margin, containerPadding, containerWidth, cols } = positionParams;
|
|
16
|
+
return (
|
|
17
|
+
(containerWidth - margin[0] * (cols - 1) - containerPadding[0] * 2) / cols
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Return position on the page given an x, y, w, h.
|
|
23
|
+
* left, top, width, height are all in pixels.
|
|
24
|
+
* @param {PositionParams} positionParams Parameters of grid needed for coordinates calculations.
|
|
25
|
+
* @param {Number} x X coordinate in grid units.
|
|
26
|
+
* @param {Number} y Y coordinate in grid units.
|
|
27
|
+
* @param {Number} w W coordinate in grid units.
|
|
28
|
+
* @param {Number} h H coordinate in grid units.
|
|
29
|
+
* @return {Position} Object containing coords.
|
|
30
|
+
*/
|
|
31
|
+
export function calcGridItemPosition(
|
|
32
|
+
positionParams: PositionParams,
|
|
33
|
+
x: number,
|
|
34
|
+
y: number,
|
|
35
|
+
w: number,
|
|
36
|
+
h: number,
|
|
37
|
+
state: ?Object
|
|
38
|
+
): Position {
|
|
39
|
+
const { margin, containerPadding, rowHeight } = positionParams;
|
|
40
|
+
const colWidth = calcGridColWidth(positionParams);
|
|
41
|
+
const out = {};
|
|
42
|
+
|
|
43
|
+
// If resizing, use the exact width and height as returned from resizing callbacks.
|
|
44
|
+
if (state && state.resizing) {
|
|
45
|
+
out.width = Math.round(state.resizing.width);
|
|
46
|
+
out.height = Math.round(state.resizing.height);
|
|
47
|
+
}
|
|
48
|
+
// Otherwise, calculate from grid units.
|
|
49
|
+
else {
|
|
50
|
+
// 0 * Infinity === NaN, which causes problems with resize constraints;
|
|
51
|
+
// Fix this if it occurs.
|
|
52
|
+
// Note we do it here rather than later because Math.round(Infinity) causes deopt
|
|
53
|
+
out.width =
|
|
54
|
+
w === Infinity
|
|
55
|
+
? w
|
|
56
|
+
: Math.round(colWidth * w + Math.max(0, w - 1) * margin[0]);
|
|
57
|
+
out.height =
|
|
58
|
+
h === Infinity
|
|
59
|
+
? h
|
|
60
|
+
: Math.round(rowHeight * h + Math.max(0, h - 1) * margin[1]);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// If dragging, use the exact width and height as returned from dragging callbacks.
|
|
64
|
+
if (state && state.dragging) {
|
|
65
|
+
out.top = Math.round(state.dragging.top);
|
|
66
|
+
out.left = Math.round(state.dragging.left);
|
|
67
|
+
}
|
|
68
|
+
// Otherwise, calculate from grid units.
|
|
69
|
+
else {
|
|
70
|
+
out.top = Math.round((rowHeight + margin[1]) * y + containerPadding[1]);
|
|
71
|
+
out.left = Math.round((colWidth + margin[0]) * x + containerPadding[0]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Translate x and y coordinates from pixels to grid units.
|
|
79
|
+
* @param {PositionParams} positionParams Parameters of grid needed for coordinates calculations.
|
|
80
|
+
* @param {Number} top Top position (relative to parent) in pixels.
|
|
81
|
+
* @param {Number} left Left position (relative to parent) in pixels.
|
|
82
|
+
* @param {Number} w W coordinate in grid units.
|
|
83
|
+
* @param {Number} h H coordinate in grid units.
|
|
84
|
+
* @return {Object} x and y in grid units.
|
|
85
|
+
*/
|
|
86
|
+
export function calcXY(
|
|
87
|
+
positionParams: PositionParams,
|
|
88
|
+
top: number,
|
|
89
|
+
left: number,
|
|
90
|
+
w: number,
|
|
91
|
+
h: number
|
|
92
|
+
): { x: number, y: number } {
|
|
93
|
+
const { margin, cols, rowHeight, maxRows } = positionParams;
|
|
94
|
+
const colWidth = calcGridColWidth(positionParams);
|
|
95
|
+
|
|
96
|
+
// left = colWidth * x + margin * (x + 1)
|
|
97
|
+
// l = cx + m(x+1)
|
|
98
|
+
// l = cx + mx + m
|
|
99
|
+
// l - m = cx + mx
|
|
100
|
+
// l - m = x(c + m)
|
|
101
|
+
// (l - m) / (c + m) = x
|
|
102
|
+
// x = (left - margin) / (coldWidth + margin)
|
|
103
|
+
let x = Math.round((left - margin[0]) / (colWidth + margin[0]));
|
|
104
|
+
let y = Math.round((top - margin[1]) / (rowHeight + margin[1]));
|
|
105
|
+
|
|
106
|
+
// Capping
|
|
107
|
+
x = Math.max(Math.min(x, cols - w), 0);
|
|
108
|
+
y = Math.max(Math.min(y, maxRows - h), 0);
|
|
109
|
+
|
|
110
|
+
return { x, y };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Given a height and width in pixel values, calculate grid units.
|
|
115
|
+
* @param {PositionParams} positionParams Parameters of grid needed for coordinates calcluations.
|
|
116
|
+
* @param {Number} height Height in pixels.
|
|
117
|
+
* @param {Number} width Width in pixels.
|
|
118
|
+
* @param {Number} x X coordinate in grid units.
|
|
119
|
+
* @param {Number} y Y coordinate in grid units.
|
|
120
|
+
* @return {Object} w, h as grid units.
|
|
121
|
+
*/
|
|
122
|
+
export function calcWH(
|
|
123
|
+
positionParams: PositionParams,
|
|
124
|
+
width: number,
|
|
125
|
+
height: number,
|
|
126
|
+
x: number,
|
|
127
|
+
y: number
|
|
128
|
+
): { w: number, h: number } {
|
|
129
|
+
const { margin, maxRows, cols, rowHeight } = positionParams;
|
|
130
|
+
const colWidth = calcGridColWidth(positionParams);
|
|
131
|
+
|
|
132
|
+
// width = colWidth * w - (margin * (w - 1))
|
|
133
|
+
// ...
|
|
134
|
+
// w = (width + margin) / (colWidth + margin)
|
|
135
|
+
let w = Math.round((width + margin[0]) / (colWidth + margin[0]));
|
|
136
|
+
let h = Math.round((height + margin[1]) / (rowHeight + margin[1]));
|
|
137
|
+
|
|
138
|
+
// Capping
|
|
139
|
+
w = Math.max(Math.min(w, cols - x), 0);
|
|
140
|
+
h = Math.max(Math.min(h, maxRows - y), 0);
|
|
141
|
+
return { w, h };
|
|
142
|
+
}
|
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import React from "react";
|
|
2
|
+
import * as React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import ReactDOM from "react-dom";
|
|
5
|
-
import type { ComponentType as ReactComponentType } from "react";
|
|
6
5
|
|
|
7
|
-
type
|
|
6
|
+
type WPDefaultProps = {|
|
|
7
|
+
measureBeforeMount: boolean
|
|
8
|
+
|};
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line no-unused-vars
|
|
11
|
+
type WPProps = {|
|
|
8
12
|
className?: string,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
13
|
+
style?: Object,
|
|
14
|
+
...WPDefaultProps
|
|
15
|
+
|};
|
|
12
16
|
|
|
13
|
-
type WPState = {
|
|
17
|
+
type WPState = {|
|
|
14
18
|
width: number
|
|
15
|
-
};
|
|
19
|
+
|};
|
|
16
20
|
|
|
17
21
|
/*
|
|
18
22
|
* A simple HOC that provides facility for listening to container resizes.
|
|
23
|
+
*
|
|
24
|
+
* The Flow type is pretty janky here. I can't just spread `WPProps` into this returned object - I wish I could - but it triggers
|
|
25
|
+
* a flow bug of some sort that causes it to stop typechecking.
|
|
19
26
|
*/
|
|
20
|
-
export default function WidthProvider<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
export default function WidthProvider<Config>(
|
|
28
|
+
ComposedComponent: React.AbstractComponent<Config>
|
|
29
|
+
): React.AbstractComponent<{|
|
|
30
|
+
...Config,
|
|
31
|
+
measureBeforeMount?: boolean,
|
|
32
|
+
className?: string,
|
|
33
|
+
style?: Object,
|
|
34
|
+
width?: number
|
|
35
|
+
|}> {
|
|
36
|
+
return class WidthProvider extends React.Component<
|
|
37
|
+
{|
|
|
38
|
+
...Config,
|
|
39
|
+
measureBeforeMount?: boolean,
|
|
40
|
+
className?: string,
|
|
41
|
+
style?: Object,
|
|
42
|
+
width?: number
|
|
43
|
+
|},
|
|
44
|
+
WPState
|
|
45
|
+
> {
|
|
46
|
+
static defaultProps: WPDefaultProps = {
|
|
28
47
|
measureBeforeMount: false
|
|
29
48
|
};
|
|
30
49
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// @preval
|
|
2
|
+
|
|
3
|
+
require("@babel/register");
|
|
4
|
+
|
|
5
|
+
// Fast way to compare RGL props in shouldComponentUpdate.
|
|
6
|
+
// Generates the fastest possible comparison of the type:
|
|
7
|
+
// function (a, b) { return a.className === b.className && a.style === b.style && ... }
|
|
8
|
+
// This avoids enumerating keys, avoids us keeping our own key list, and can be very easily optimized.
|
|
9
|
+
|
|
10
|
+
const PropTypes = require("prop-types");
|
|
11
|
+
const propTypes = require("./ReactGridLayoutPropTypes").default;
|
|
12
|
+
const keys = Object.keys(propTypes);
|
|
13
|
+
|
|
14
|
+
// Remove 'children' key as we don't want to compare it
|
|
15
|
+
keys.splice(keys.indexOf("children"), 1);
|
|
16
|
+
|
|
17
|
+
// Returns a code string indicating what to do here.
|
|
18
|
+
// In most cases we want to do a simple equality comparison,
|
|
19
|
+
// but we have some arrays and tuples and objects we want
|
|
20
|
+
// to do a shallow comparison on.
|
|
21
|
+
function getEqualType(key) {
|
|
22
|
+
if (
|
|
23
|
+
[
|
|
24
|
+
PropTypes.number,
|
|
25
|
+
PropTypes.bool,
|
|
26
|
+
PropTypes.string,
|
|
27
|
+
PropTypes.func
|
|
28
|
+
].includes(propTypes[key])
|
|
29
|
+
) {
|
|
30
|
+
return `(a.${key} === b.${key})`;
|
|
31
|
+
}
|
|
32
|
+
return `isEqualImpl(a.${key}, b.${key})`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Exports a function that compares a and b. `isEqualImpl` is a required
|
|
36
|
+
// third prop, as we can't otherwise access it.
|
|
37
|
+
module.exports = () =>
|
|
38
|
+
eval(`
|
|
39
|
+
function fastRGLPropsEqual(a, b, isEqualImpl) {
|
|
40
|
+
if (a === b) return true;
|
|
41
|
+
return (
|
|
42
|
+
${keys.map(getEqualType).join(" && ")}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
fastRGLPropsEqual;
|
|
46
|
+
`);
|
package/lib/responsiveUtils.js
CHANGED
|
@@ -4,22 +4,15 @@ import { cloneLayout, compact, correctBounds } from "./utils";
|
|
|
4
4
|
|
|
5
5
|
import type { CompactType, Layout } from "./utils";
|
|
6
6
|
|
|
7
|
-
export type
|
|
8
|
-
|
|
9
|
-
md?: Layout,
|
|
10
|
-
sm?: Layout,
|
|
11
|
-
xs?: Layout,
|
|
12
|
-
xxs?: Layout
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type Breakpoint = string;
|
|
7
|
+
export type Breakpoint = string;
|
|
8
|
+
export type DefaultBreakpoints = 'lg' | 'md' | 'sm' | 'xs' | 'xxs';
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
// + indicates read-only
|
|
11
|
+
export type ResponsiveLayout<T: Breakpoint> = {
|
|
12
|
+
+[breakpoint: T]: Layout
|
|
13
|
+
};
|
|
14
|
+
export type Breakpoints<T: Breakpoint> = {
|
|
15
|
+
+[breakpoint: T]: number
|
|
23
16
|
};
|
|
24
17
|
|
|
25
18
|
/**
|
|
@@ -30,7 +23,7 @@ type Breakpoints = {
|
|
|
30
23
|
* @return {String} Highest breakpoint that is less than width.
|
|
31
24
|
*/
|
|
32
25
|
export function getBreakpointFromWidth(
|
|
33
|
-
breakpoints: Breakpoints
|
|
26
|
+
breakpoints: Breakpoints<Breakpoint>,
|
|
34
27
|
width: number
|
|
35
28
|
): Breakpoint {
|
|
36
29
|
const sorted = sortBreakpoints(breakpoints);
|
|
@@ -50,7 +43,7 @@ export function getBreakpointFromWidth(
|
|
|
50
43
|
*/
|
|
51
44
|
export function getColsFromBreakpoint(
|
|
52
45
|
breakpoint: Breakpoint,
|
|
53
|
-
cols: Breakpoints
|
|
46
|
+
cols: Breakpoints<Breakpoint>
|
|
54
47
|
): number {
|
|
55
48
|
if (!cols[breakpoint]) {
|
|
56
49
|
throw new Error(
|
|
@@ -77,8 +70,8 @@ export function getColsFromBreakpoint(
|
|
|
77
70
|
* @return {Array} New layout.
|
|
78
71
|
*/
|
|
79
72
|
export function findOrGenerateResponsiveLayout(
|
|
80
|
-
layouts: ResponsiveLayout
|
|
81
|
-
breakpoints: Breakpoints
|
|
73
|
+
layouts: ResponsiveLayout<Breakpoint>,
|
|
74
|
+
breakpoints: Breakpoints<Breakpoint>,
|
|
82
75
|
breakpoint: Breakpoint,
|
|
83
76
|
lastBreakpoint: Breakpoint,
|
|
84
77
|
cols: number,
|
|
@@ -110,7 +103,7 @@ export function findOrGenerateResponsiveLayout(
|
|
|
110
103
|
* @param {Object} breakpoints Key/value pair of breakpoint names to widths.
|
|
111
104
|
* @return {Array} Sorted breakpoints.
|
|
112
105
|
*/
|
|
113
|
-
export function sortBreakpoints(breakpoints: Breakpoints): Array<Breakpoint> {
|
|
106
|
+
export function sortBreakpoints(breakpoints: Breakpoints<Breakpoint>): Array<Breakpoint> {
|
|
114
107
|
const keys: Array<string> = Object.keys(breakpoints);
|
|
115
108
|
return keys.sort(function(a, b) {
|
|
116
109
|
return breakpoints[a] - breakpoints[b];
|
package/lib/utils.js
CHANGED
|
@@ -20,7 +20,7 @@ export type LayoutItem = {
|
|
|
20
20
|
isDraggable?: ?boolean,
|
|
21
21
|
isResizable?: ?boolean
|
|
22
22
|
};
|
|
23
|
-
export type Layout =
|
|
23
|
+
export type Layout = $ReadOnlyArray<LayoutItem>;
|
|
24
24
|
export type Position = {
|
|
25
25
|
left: number,
|
|
26
26
|
top: number,
|
|
@@ -38,7 +38,7 @@ export type ReactDraggableCallbackData = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export type PartialPosition = { left: number, top: number };
|
|
41
|
-
export type DroppingPosition = {
|
|
41
|
+
export type DroppingPosition = { left: number, top: number, e: Event };
|
|
42
42
|
export type Size = { width: number, height: number };
|
|
43
43
|
export type GridDragEvent = {
|
|
44
44
|
e: Event,
|
|
@@ -128,6 +128,27 @@ export function childrenEqual(a: ReactChildren, b: ReactChildren): boolean {
|
|
|
128
128
|
);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* See `fastRGLPropsEqual.js`.
|
|
133
|
+
* We want this to run as fast as possible - it is called often - and to be
|
|
134
|
+
* resilient to new props that we add. So rather than call lodash.isEqual,
|
|
135
|
+
* which isn't suited to comparing props very well, we use this specialized
|
|
136
|
+
* function in conjunction with preval to generate the fastest possible comparison
|
|
137
|
+
* function, tuned for exactly our props.
|
|
138
|
+
*/
|
|
139
|
+
type FastRGLPropsEqual = (Object, Object, Function) => boolean;
|
|
140
|
+
export const fastRGLPropsEqual: FastRGLPropsEqual = require("./fastRGLPropsEqual");
|
|
141
|
+
|
|
142
|
+
// Like the above, but a lot simpler.
|
|
143
|
+
export function fastPositionEqual(a: Position, b: Position) {
|
|
144
|
+
return (
|
|
145
|
+
a.left === b.left &&
|
|
146
|
+
a.top === b.top &&
|
|
147
|
+
a.width === b.width &&
|
|
148
|
+
a.height === b.height
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
131
152
|
/**
|
|
132
153
|
* Given two layoutitems, check if they collide.
|
|
133
154
|
*/
|
|
@@ -144,6 +165,8 @@ export function collides(l1: LayoutItem, l2: LayoutItem): boolean {
|
|
|
144
165
|
* Given a layout, compact it. This involves going down each y coordinate and removing gaps
|
|
145
166
|
* between items.
|
|
146
167
|
*
|
|
168
|
+
* Does not modify layout items (clones). Creates a new layout array.
|
|
169
|
+
*
|
|
147
170
|
* @param {Array} layout Layout.
|
|
148
171
|
* @param {Boolean} verticalCompact Whether or not to compact the layout
|
|
149
172
|
* vertically.
|
|
@@ -226,6 +249,9 @@ function resolveCompactionCollision(
|
|
|
226
249
|
|
|
227
250
|
/**
|
|
228
251
|
* Compact an item in the layout.
|
|
252
|
+
*
|
|
253
|
+
* Modifies item.
|
|
254
|
+
*
|
|
229
255
|
*/
|
|
230
256
|
export function compactItem(
|
|
231
257
|
compareWith: Layout,
|
|
@@ -273,6 +299,8 @@ export function compactItem(
|
|
|
273
299
|
/**
|
|
274
300
|
* Given a layout, make sure all elements fit within its bounds.
|
|
275
301
|
*
|
|
302
|
+
* Modifies layout items.
|
|
303
|
+
*
|
|
276
304
|
* @param {Array} layout Layout array.
|
|
277
305
|
* @param {Number} bounds Number of columns.
|
|
278
306
|
*/
|
|
@@ -351,6 +379,8 @@ export function getStatics(layout: Layout): Array<LayoutItem> {
|
|
|
351
379
|
/**
|
|
352
380
|
* Move an element. Responsible for doing cascading movements of other elements.
|
|
353
381
|
*
|
|
382
|
+
* Modifies layout items.
|
|
383
|
+
*
|
|
354
384
|
* @param {Array} layout Full layout to modify.
|
|
355
385
|
* @param {LayoutItem} l element to move.
|
|
356
386
|
* @param {Number} [x] X position in grid units.
|
|
@@ -366,7 +396,9 @@ export function moveElement(
|
|
|
366
396
|
compactType: CompactType,
|
|
367
397
|
cols: number
|
|
368
398
|
): Layout {
|
|
369
|
-
|
|
399
|
+
// If this is static and not explicitly enabled as draggable,
|
|
400
|
+
// no move is possible, so we can short-circuit this immediately.
|
|
401
|
+
if (l.static && l.isDraggable !== true) return layout;
|
|
370
402
|
|
|
371
403
|
// Short-circuit if nothing to do.
|
|
372
404
|
if (l.y === y && l.x === x) return layout;
|
|
@@ -393,6 +425,7 @@ export function moveElement(
|
|
|
393
425
|
: compactType === "horizontal" && typeof x === "number"
|
|
394
426
|
? oldX >= x
|
|
395
427
|
: false;
|
|
428
|
+
// $FlowIgnore acceptable modification of read-only array as it was recently cloned
|
|
396
429
|
if (movingUp) sorted = sorted.reverse();
|
|
397
430
|
const collisions = getAllCollisions(sorted, l);
|
|
398
431
|
|
|
@@ -556,8 +589,14 @@ export function sortLayoutItems(
|
|
|
556
589
|
else return sortLayoutItemsByRowCol(layout);
|
|
557
590
|
}
|
|
558
591
|
|
|
592
|
+
/**
|
|
593
|
+
* Sort layout items by row ascending and column ascending.
|
|
594
|
+
*
|
|
595
|
+
* Does not modify Layout.
|
|
596
|
+
*/
|
|
559
597
|
export function sortLayoutItemsByRowCol(layout: Layout): Layout {
|
|
560
|
-
|
|
598
|
+
// Slice to clone array as sort modifies
|
|
599
|
+
return layout.slice(0).sort(function(a, b) {
|
|
561
600
|
if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
|
|
562
601
|
return 1;
|
|
563
602
|
} else if (a.y === b.y && a.x === b.x) {
|
|
@@ -568,8 +607,13 @@ export function sortLayoutItemsByRowCol(layout: Layout): Layout {
|
|
|
568
607
|
});
|
|
569
608
|
}
|
|
570
609
|
|
|
610
|
+
/**
|
|
611
|
+
* Sort layout items by column ascending then row ascending.
|
|
612
|
+
*
|
|
613
|
+
* Does not modify Layout.
|
|
614
|
+
*/
|
|
571
615
|
export function sortLayoutItemsByColRow(layout: Layout): Layout {
|
|
572
|
-
return
|
|
616
|
+
return layout.slice(0).sort(function(a, b) {
|
|
573
617
|
if (a.x > b.x || (a.x === b.x && a.y > b.y)) {
|
|
574
618
|
return 1;
|
|
575
619
|
}
|
|
@@ -581,6 +625,8 @@ export function sortLayoutItemsByColRow(layout: Layout): Layout {
|
|
|
581
625
|
* Generate a layout using the initialLayout and children as a template.
|
|
582
626
|
* Missing entries will be added, extraneous ones will be truncated.
|
|
583
627
|
*
|
|
628
|
+
* Does not modify initialLayout.
|
|
629
|
+
*
|
|
584
630
|
* @param {Array} initialLayout Layout passed in through props.
|
|
585
631
|
* @param {String} breakpoint Current responsive breakpoint.
|
|
586
632
|
* @param {?String} compact Compaction option.
|
|
@@ -595,7 +641,7 @@ export function synchronizeLayoutWithChildren(
|
|
|
595
641
|
initialLayout = initialLayout || [];
|
|
596
642
|
|
|
597
643
|
// Generate one layout item per child.
|
|
598
|
-
|
|
644
|
+
const layout: LayoutItem[] = [];
|
|
599
645
|
React.Children.forEach(children, (child: ReactElement<any>, i: number) => {
|
|
600
646
|
// Don't overwrite if it already exists.
|
|
601
647
|
const exists = getLayoutItem(initialLayout, String(child.key));
|
|
@@ -630,10 +676,8 @@ export function synchronizeLayoutWithChildren(
|
|
|
630
676
|
});
|
|
631
677
|
|
|
632
678
|
// Correct the layout.
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
return layout;
|
|
679
|
+
const correctedLayout = correctBounds(layout, { cols: cols });
|
|
680
|
+
return compact(correctedLayout, compactType, cols);
|
|
637
681
|
}
|
|
638
682
|
|
|
639
683
|
/**
|
|
@@ -682,6 +726,14 @@ export function validateLayout(
|
|
|
682
726
|
}
|
|
683
727
|
}
|
|
684
728
|
|
|
729
|
+
// Legacy support for verticalCompact: false
|
|
730
|
+
export function compactType(
|
|
731
|
+
props: ?{ verticalCompact: boolean, compactType: CompactType }
|
|
732
|
+
): CompactType {
|
|
733
|
+
const { verticalCompact, compactType } = props || {};
|
|
734
|
+
return verticalCompact === false ? null : compactType;
|
|
735
|
+
}
|
|
736
|
+
|
|
685
737
|
// Flow can't really figure this out, so we just use Object
|
|
686
738
|
export function autoBindHandlers(el: Object, fns: Array<string>): void {
|
|
687
739
|
fns.forEach(key => (el[key] = el[key].bind(el)));
|