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.
@@ -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
- ...$Exact<RGLProps>,
46
+ type Props<Breakpoint: string = string> = {|
47
+ ...React.ElementConfig<typeof ReactGridLayout>,
47
48
 
48
49
  // Responsive config
49
- breakpoint: Breakpoint,
50
- breakpoints: { [key: Breakpoint]: number },
50
+ breakpoint?: ?Breakpoint,
51
+ breakpoints: Breakpoints<Breakpoint>,
51
52
  cols: { [key: Breakpoint]: number },
52
- layouts: { [key: Breakpoint]: Layout },
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: { lg: null, md: null, sm: null, xs: null, xxs: null },
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(this.props);
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(nextProps: Props<*>) {
223
- const { breakpoints, cols, layouts, compactType } = nextProps;
229
+ onWidthChange(prevProps: Props<*>) {
230
+ const { breakpoints, cols, layouts, compactType } = this.props;
224
231
  const newBreakpoint =
225
- nextProps.breakpoint ||
226
- getBreakpointFromWidth(nextProps.breakpoints, nextProps.width);
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
- this.props.breakpoints !== breakpoints ||
235
- this.props.cols !== cols
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 layouts))
239
- layouts[lastBreakpoint] = cloneLayout(this.state.layout);
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
- layouts,
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
- nextProps.children,
262
+ this.props.children,
255
263
  newCols,
256
264
  compactType
257
265
  );
258
266
 
259
267
  // Store the new layout.
260
- layouts[newBreakpoint] = layout;
268
+ newLayouts[newBreakpoint] = layout;
261
269
 
262
270
  // callbacks
263
- this.props.onLayoutChange(layout, layouts);
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(nextProps.margin, newBreakpoint);
281
+ const margin = getIndentationValue(this.props.margin, newBreakpoint);
274
282
  const containerPadding = getIndentationValue(
275
- nextProps.containerPadding,
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
- nextProps.width,
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 WPProps = {
6
+ type WPDefaultProps = {|
7
+ measureBeforeMount: boolean
8
+ |};
9
+
10
+ // eslint-disable-next-line no-unused-vars
11
+ type WPProps = {|
8
12
  className?: string,
9
- measureBeforeMount: boolean,
10
- style?: Object
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
- Props,
22
- ComposedProps: { ...Props, ...WPProps }
23
- >(
24
- ComposedComponent: ReactComponentType<Props>
25
- ): ReactComponentType<ComposedProps> {
26
- return class WidthProvider extends React.Component<ComposedProps, WPState> {
27
- static defaultProps = {
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
+ `);
@@ -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 ResponsiveLayout = {
8
- lg?: Layout,
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
- type Breakpoints = {
18
- lg?: number,
19
- md?: number,
20
- sm?: number,
21
- xs?: number,
22
- xxs?: number
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 = Array<LayoutItem>;
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 = { x: number, y: number, e: Event };
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
- if (l.static) return layout;
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
- return [].concat(layout).sort(function(a, b) {
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 [].concat(layout).sort(function(a, b) {
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
- let layout: Layout = [];
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
- layout = correctBounds(layout, { cols: cols });
634
- layout = compact(layout, compactType, cols);
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)));