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,6 +1,6 @@
1
1
  // @flow
2
2
  import React from "react";
3
- import PropTypes from "prop-types";
3
+
4
4
  import isEqual from "lodash.isequal";
5
5
  import classNames from "classnames";
6
6
  import {
@@ -12,11 +12,16 @@ import {
12
12
  getLayoutItem,
13
13
  moveElement,
14
14
  synchronizeLayoutWithChildren,
15
- validateLayout,
16
15
  getAllCollisions,
17
- noop
16
+ compactType,
17
+ noop,
18
+ fastRGLPropsEqual
18
19
  } from "./utils";
20
+
21
+ import { calcXY } from "./calculateUtils";
22
+
19
23
  import GridItem from "./GridItem";
24
+ import ReactGridLayoutPropTypes from "./ReactGridLayoutPropTypes";
20
25
  import type {
21
26
  ChildrenArray as ReactChildrenArray,
22
27
  Element as ReactElement
@@ -24,7 +29,6 @@ import type {
24
29
 
25
30
  // Types
26
31
  import type {
27
- EventCallback,
28
32
  CompactType,
29
33
  GridResizeEvent,
30
34
  GridDragEvent,
@@ -34,6 +38,8 @@ import type {
34
38
  LayoutItem
35
39
  } from "./utils";
36
40
 
41
+ import type { PositionParams } from "./calculateUtils";
42
+
37
43
  type State = {
38
44
  activeDrag: ?LayoutItem,
39
45
  layout: Layout,
@@ -49,58 +55,15 @@ type State = {
49
55
  propsLayout?: Layout
50
56
  };
51
57
 
52
- export type Props = {
53
- className: string,
54
- style: Object,
55
- width: number,
56
- autoSize: boolean,
57
- cols: number,
58
- draggableCancel: string,
59
- draggableHandle: string,
60
- verticalCompact: boolean,
61
- compactType: CompactType,
62
- layout: Layout,
63
- margin: [number, number],
64
- containerPadding: [number, number] | null,
65
- rowHeight: number,
66
- maxRows: number,
67
- isDraggable: boolean,
68
- isResizable: boolean,
69
- isDroppable: boolean,
70
- preventCollision: boolean,
71
- useCSSTransforms: boolean,
72
- transformScale: number,
73
- droppingItem: $Shape<LayoutItem>,
74
-
75
- // Callbacks
76
- onLayoutChange: Layout => void,
77
- onDrag: EventCallback,
78
- onDragStart: EventCallback,
79
- onDragStop: EventCallback,
80
- onResize: EventCallback,
81
- onResizeStart: EventCallback,
82
- onResizeStop: EventCallback,
83
- onDrop: (itemPosition: {
84
- x: number,
85
- y: number,
86
- w: number,
87
- h: number
88
- }) => void,
89
- children: ReactChildrenArray<ReactElement<any>>
90
- };
91
- // End Types
92
-
93
- const compactType = (props: Props): CompactType => {
94
- const { verticalCompact, compactType } = props || {};
58
+ import type { Props } from "./ReactGridLayoutPropTypes";
95
59
 
96
- return verticalCompact === false ? null : compactType;
97
- };
60
+ // End Types
98
61
 
99
62
  const layoutClassName = "react-grid-layout";
100
63
  let isFirefox = false;
101
64
  // Try...catch will protect from navigator not existing (e.g. node) or a bad implementation of navigator
102
65
  try {
103
- isFirefox = navigator.userAgent.toLowerCase().includes("firefox");
66
+ isFirefox = /firefox/i.test(navigator.userAgent);
104
67
  } catch (e) {
105
68
  /* Ignore */
106
69
  }
@@ -113,135 +76,8 @@ export default class ReactGridLayout extends React.Component<Props, State> {
113
76
  // TODO publish internal ReactClass displayName transform
114
77
  static displayName = "ReactGridLayout";
115
78
 
116
- static propTypes = {
117
- //
118
- // Basic props
119
- //
120
- className: PropTypes.string,
121
- style: PropTypes.object,
122
-
123
- // This can be set explicitly. If it is not set, it will automatically
124
- // be set to the container width. Note that resizes will *not* cause this to adjust.
125
- // If you need that behavior, use WidthProvider.
126
- width: PropTypes.number,
127
-
128
- // If true, the container height swells and contracts to fit contents
129
- autoSize: PropTypes.bool,
130
- // # of cols.
131
- cols: PropTypes.number,
132
-
133
- // A selector that will not be draggable.
134
- draggableCancel: PropTypes.string,
135
- // A selector for the draggable handler
136
- draggableHandle: PropTypes.string,
137
-
138
- // Deprecated
139
- verticalCompact: function(props: Props) {
140
- if (
141
- props.verticalCompact === false &&
142
- process.env.NODE_ENV !== "production"
143
- ) {
144
- console.warn(
145
- // eslint-disable-line no-console
146
- "`verticalCompact` on <ReactGridLayout> is deprecated and will be removed soon. " +
147
- 'Use `compactType`: "horizontal" | "vertical" | null.'
148
- );
149
- }
150
- },
151
- // Choose vertical or hotizontal compaction
152
- compactType: PropTypes.oneOf(["vertical", "horizontal"]),
153
-
154
- // layout is an array of object with the format:
155
- // {x: Number, y: Number, w: Number, h: Number, i: String}
156
- layout: function(props: Props) {
157
- var layout = props.layout;
158
- // I hope you're setting the data-grid property on the grid items
159
- if (layout === undefined) return;
160
- validateLayout(layout, "layout");
161
- },
162
-
163
- //
164
- // Grid Dimensions
165
- //
166
-
167
- // Margin between items [x, y] in px
168
- margin: PropTypes.arrayOf(PropTypes.number),
169
- // Padding inside the container [x, y] in px
170
- containerPadding: PropTypes.arrayOf(PropTypes.number),
171
- // Rows have a static height, but you can change this based on breakpoints if you like
172
- rowHeight: PropTypes.number,
173
- // Default Infinity, but you can specify a max here if you like.
174
- // Note that this isn't fully fleshed out and won't error if you specify a layout that
175
- // extends beyond the row capacity. It will, however, not allow users to drag/resize
176
- // an item past the barrier. They can push items beyond the barrier, though.
177
- // Intentionally not documented for this reason.
178
- maxRows: PropTypes.number,
179
-
180
- //
181
- // Flags
182
- //
183
- isDraggable: PropTypes.bool,
184
- isResizable: PropTypes.bool,
185
- // If true, grid items won't change position when being dragged over.
186
- preventCollision: PropTypes.bool,
187
- // Use CSS transforms instead of top/left
188
- useCSSTransforms: PropTypes.bool,
189
- // parent layout transform scale
190
- transformScale: PropTypes.number,
191
- // If true, an external element can trigger onDrop callback with a specific grid position as a parameter
192
- isDroppable: PropTypes.bool,
193
-
194
- //
195
- // Callbacks
196
- //
197
-
198
- // Callback so you can save the layout. Calls after each drag & resize stops.
199
- onLayoutChange: PropTypes.func,
200
-
201
- // Calls when drag starts. Callback is of the signature (layout, oldItem, newItem, placeholder, e, ?node).
202
- // All callbacks below have the same signature. 'start' and 'stop' callbacks omit the 'placeholder'.
203
- onDragStart: PropTypes.func,
204
- // Calls on each drag movement.
205
- onDrag: PropTypes.func,
206
- // Calls when drag is complete.
207
- onDragStop: PropTypes.func,
208
- //Calls when resize starts.
209
- onResizeStart: PropTypes.func,
210
- // Calls when resize movement happens.
211
- onResize: PropTypes.func,
212
- // Calls when resize is complete.
213
- onResizeStop: PropTypes.func,
214
- // Calls when some element is dropped.
215
- onDrop: PropTypes.func,
216
-
217
- //
218
- // Other validations
219
- //
220
-
221
- droppingItem: PropTypes.shape({
222
- i: PropTypes.string.isRequired,
223
- w: PropTypes.number.isRequired,
224
- h: PropTypes.number.isRequired
225
- }),
226
-
227
- // Children must not have duplicate keys.
228
- children: function(props: Props, propName: string) {
229
- var children = props[propName];
230
-
231
- // Check children keys for duplicates. Throw if found.
232
- var keys = {};
233
- React.Children.forEach(children, function(child) {
234
- if (keys[child.key]) {
235
- throw new Error(
236
- 'Duplicate child key "' +
237
- child.key +
238
- '" found! This will cause problems in ReactGridLayout.'
239
- );
240
- }
241
- keys[child.key] = true;
242
- });
243
- }
244
- };
79
+ // Refactored to another module to make way for preval
80
+ static propTypes = ReactGridLayoutPropTypes;
245
81
 
246
82
  static defaultProps = {
247
83
  autoSize: true,
@@ -359,6 +195,18 @@ export default class ReactGridLayout extends React.Component<Props, State> {
359
195
  return null;
360
196
  }
361
197
 
198
+ shouldComponentUpdate(nextProps: Props, nextState: State) {
199
+ return (
200
+ // NOTE: this is almost always unequal. Therefore the only way to get better performance
201
+ // from SCU is if the user intentionally memoizes children. If they do, and they can
202
+ // handle changes properly, performance will increase.
203
+ this.props.children !== nextProps.children ||
204
+ !fastRGLPropsEqual(this.props, nextProps, isEqual) ||
205
+ this.state.activeDrag !== nextState.activeDrag ||
206
+ this.state.droppingPosition !== nextState.droppingPosition
207
+ );
208
+ }
209
+
362
210
  componentDidUpdate(prevProps: Props, prevState: State) {
363
211
  if (!this.state.activeDrag) {
364
212
  const newLayout = this.state.layout;
@@ -462,6 +310,8 @@ export default class ReactGridLayout extends React.Component<Props, State> {
462
310
  * @param {Element} node The current dragging DOM element
463
311
  */
464
312
  onDragStop(i: string, x: number, y: number, { e, node }: GridDragEvent) {
313
+ if (!this.state.activeDrag) return;
314
+
465
315
  const { oldDragItem } = this.state;
466
316
  let { layout } = this.state;
467
317
  const { cols, preventCollision } = this.props;
@@ -480,9 +330,8 @@ export default class ReactGridLayout extends React.Component<Props, State> {
480
330
  compactType(this.props),
481
331
  cols
482
332
  );
483
- if (this.state.activeDrag) {
484
- this.props.onDragStop(layout, oldDragItem, l, null, e, node);
485
- }
333
+
334
+ this.props.onDragStop(layout, oldDragItem, l, null, e, node);
486
335
 
487
336
  // Set state
488
337
  const newLayout = compact(layout, compactType(this.props), cols);
@@ -664,13 +513,17 @@ export default class ReactGridLayout extends React.Component<Props, State> {
664
513
  } = this.props;
665
514
  const { mounted, droppingPosition } = this.state;
666
515
 
667
- // Parse 'static'. Any properties defined directly on the grid item will take precedence.
668
- const draggable = Boolean(
669
- !l.static && isDraggable && (l.isDraggable || l.isDraggable == null)
670
- );
671
- const resizable = Boolean(
672
- !l.static && isResizable && (l.isResizable || l.isResizable == null)
673
- );
516
+ // Determine user manipulations possible.
517
+ // If an item is static, it can't be manipulated by default.
518
+ // Any properties defined directly on the grid item will take precedence.
519
+ const draggable =
520
+ typeof l.isDraggable === "boolean"
521
+ ? l.isDraggable
522
+ : !l.static && isDraggable;
523
+ const resizable =
524
+ typeof l.isResizable === "boolean"
525
+ ? l.isResizable
526
+ : !l.static && isResizable;
674
527
 
675
528
  return (
676
529
  <GridItem
@@ -710,22 +563,51 @@ export default class ReactGridLayout extends React.Component<Props, State> {
710
563
  );
711
564
  }
712
565
 
566
+ // Called while dragging an element. Part of browser native drag/drop API.
567
+ // Native event target might be the layout itself, or an element within the layout.
713
568
  onDragOver = (e: DragOverEvent) => {
714
569
  // we should ignore events from layout's children in Firefox
715
570
  // to avoid unpredictable jumping of a dropping placeholder
571
+ // FIXME remove this hack
716
572
  if (
717
573
  isFirefox &&
718
- !e.nativeEvent.target.className.includes(layoutClassName)
574
+ e.nativeEvent.target.className.indexOf(layoutClassName) === -1
719
575
  ) {
720
576
  return false;
721
577
  }
722
578
 
723
- const { droppingItem } = this.props;
579
+ const {
580
+ droppingItem,
581
+ margin,
582
+ cols,
583
+ rowHeight,
584
+ maxRows,
585
+ width,
586
+ containerPadding
587
+ } = this.props;
724
588
  const { layout } = this.state;
589
+ // This is relative to the DOM element that this event fired for.
725
590
  const { layerX, layerY } = e.nativeEvent;
726
- const droppingPosition = { x: layerX, y: layerY, e };
591
+ const droppingPosition = { left: layerX, top: layerY, e };
727
592
 
728
593
  if (!this.state.droppingDOMNode) {
594
+ const positionParams: PositionParams = {
595
+ cols,
596
+ margin,
597
+ maxRows,
598
+ rowHeight,
599
+ containerWidth: width,
600
+ containerPadding: containerPadding || margin
601
+ };
602
+
603
+ const calculatedPosition = calcXY(
604
+ positionParams,
605
+ layerY,
606
+ layerX,
607
+ droppingItem.w,
608
+ droppingItem.h
609
+ );
610
+
729
611
  this.setState({
730
612
  droppingDOMNode: <div key={droppingItem.i} />,
731
613
  droppingPosition,
@@ -733,18 +615,19 @@ export default class ReactGridLayout extends React.Component<Props, State> {
733
615
  ...layout,
734
616
  {
735
617
  ...droppingItem,
736
- x: 0,
737
- y: 0,
618
+ x: calculatedPosition.x,
619
+ y: calculatedPosition.y,
738
620
  static: false,
739
621
  isDraggable: true
740
622
  }
741
623
  ]
742
624
  });
743
625
  } else if (this.state.droppingPosition) {
744
- const shouldUpdatePosition =
745
- this.state.droppingPosition.x != layerX ||
746
- this.state.droppingPosition.y != layerY;
747
- shouldUpdatePosition && this.setState({ droppingPosition });
626
+ const { left, top } = this.state.droppingPosition;
627
+ const shouldUpdatePosition = left != layerX || top != layerY;
628
+ if (shouldUpdatePosition) {
629
+ this.setState({ droppingPosition });
630
+ }
748
631
  }
749
632
 
750
633
  e.stopPropagation();
@@ -786,7 +669,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
786
669
  this.dragEnterCounter++;
787
670
  };
788
671
 
789
- onDrop = () => {
672
+ onDrop = (e: Event) => {
790
673
  const { droppingItem } = this.props;
791
674
  const { layout } = this.state;
792
675
  const { x, y, w, h } = layout.find(l => l.i === droppingItem.i) || {};
@@ -796,7 +679,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
796
679
 
797
680
  this.removeDroppingPlaceholder();
798
681
 
799
- this.props.onDrop({ x, y, w, h });
682
+ this.props.onDrop({ x, y, w, h, e });
800
683
  };
801
684
 
802
685
  render() {
@@ -0,0 +1,179 @@
1
+ // @flow
2
+ import PropTypes from "prop-types";
3
+ import React from "react";
4
+ import type {
5
+ ChildrenArray as ReactChildrenArray,
6
+ Element as ReactElement
7
+ } from "react";
8
+ import type { EventCallback, CompactType, Layout, LayoutItem } from "./utils";
9
+
10
+ export type Props = {|
11
+ className: string,
12
+ style: Object,
13
+ width: number,
14
+ autoSize: boolean,
15
+ cols: number,
16
+ draggableCancel: string,
17
+ draggableHandle: string,
18
+ verticalCompact: boolean,
19
+ compactType: CompactType,
20
+ layout: Layout,
21
+ margin: [number, number],
22
+ containerPadding: [number, number] | null,
23
+ rowHeight: number,
24
+ maxRows: number,
25
+ isDraggable: boolean,
26
+ isResizable: boolean,
27
+ isDroppable: boolean,
28
+ preventCollision: boolean,
29
+ useCSSTransforms: boolean,
30
+ transformScale: number,
31
+ droppingItem: $Shape<LayoutItem>,
32
+
33
+ // Callbacks
34
+ onLayoutChange: Layout => void,
35
+ onDrag: EventCallback,
36
+ onDragStart: EventCallback,
37
+ onDragStop: EventCallback,
38
+ onResize: EventCallback,
39
+ onResizeStart: EventCallback,
40
+ onResizeStop: EventCallback,
41
+ onDrop: (itemPosition: {
42
+ x: number,
43
+ y: number,
44
+ w: number,
45
+ h: number,
46
+ e: Event
47
+ }) => void,
48
+ children: ReactChildrenArray<ReactElement<any>>
49
+ |};
50
+
51
+ export default {
52
+ //
53
+ // Basic props
54
+ //
55
+ className: PropTypes.string,
56
+ style: PropTypes.object,
57
+
58
+ // This can be set explicitly. If it is not set, it will automatically
59
+ // be set to the container width. Note that resizes will *not* cause this to adjust.
60
+ // If you need that behavior, use WidthProvider.
61
+ width: PropTypes.number,
62
+
63
+ // If true, the container height swells and contracts to fit contents
64
+ autoSize: PropTypes.bool,
65
+ // # of cols.
66
+ cols: PropTypes.number,
67
+
68
+ // A selector that will not be draggable.
69
+ draggableCancel: PropTypes.string,
70
+ // A selector for the draggable handler
71
+ draggableHandle: PropTypes.string,
72
+
73
+ // Deprecated
74
+ verticalCompact: function(props: Props) {
75
+ if (
76
+ props.verticalCompact === false &&
77
+ process.env.NODE_ENV !== "production"
78
+ ) {
79
+ console.warn(
80
+ // eslint-disable-line no-console
81
+ "`verticalCompact` on <ReactGridLayout> is deprecated and will be removed soon. " +
82
+ 'Use `compactType`: "horizontal" | "vertical" | null.'
83
+ );
84
+ }
85
+ },
86
+ // Choose vertical or hotizontal compaction
87
+ compactType: PropTypes.oneOf(["vertical", "horizontal"]),
88
+
89
+ // layout is an array of object with the format:
90
+ // {x: Number, y: Number, w: Number, h: Number, i: String}
91
+ layout: function(props: Props) {
92
+ var layout = props.layout;
93
+ // I hope you're setting the data-grid property on the grid items
94
+ if (layout === undefined) return;
95
+ require("./utils").validateLayout(layout, "layout");
96
+ },
97
+
98
+ //
99
+ // Grid Dimensions
100
+ //
101
+
102
+ // Margin between items [x, y] in px
103
+ margin: PropTypes.arrayOf(PropTypes.number),
104
+ // Padding inside the container [x, y] in px
105
+ containerPadding: PropTypes.arrayOf(PropTypes.number),
106
+ // Rows have a static height, but you can change this based on breakpoints if you like
107
+ rowHeight: PropTypes.number,
108
+ // Default Infinity, but you can specify a max here if you like.
109
+ // Note that this isn't fully fleshed out and won't error if you specify a layout that
110
+ // extends beyond the row capacity. It will, however, not allow users to drag/resize
111
+ // an item past the barrier. They can push items beyond the barrier, though.
112
+ // Intentionally not documented for this reason.
113
+ maxRows: PropTypes.number,
114
+
115
+ //
116
+ // Flags
117
+ //
118
+ isDraggable: PropTypes.bool,
119
+ isResizable: PropTypes.bool,
120
+ // If true, grid items won't change position when being dragged over.
121
+ preventCollision: PropTypes.bool,
122
+ // Use CSS transforms instead of top/left
123
+ useCSSTransforms: PropTypes.bool,
124
+ // parent layout transform scale
125
+ transformScale: PropTypes.number,
126
+ // If true, an external element can trigger onDrop callback with a specific grid position as a parameter
127
+ isDroppable: PropTypes.bool,
128
+
129
+ //
130
+ // Callbacks
131
+ //
132
+
133
+ // Callback so you can save the layout. Calls after each drag & resize stops.
134
+ onLayoutChange: PropTypes.func,
135
+
136
+ // Calls when drag starts. Callback is of the signature (layout, oldItem, newItem, placeholder, e, ?node).
137
+ // All callbacks below have the same signature. 'start' and 'stop' callbacks omit the 'placeholder'.
138
+ onDragStart: PropTypes.func,
139
+ // Calls on each drag movement.
140
+ onDrag: PropTypes.func,
141
+ // Calls when drag is complete.
142
+ onDragStop: PropTypes.func,
143
+ //Calls when resize starts.
144
+ onResizeStart: PropTypes.func,
145
+ // Calls when resize movement happens.
146
+ onResize: PropTypes.func,
147
+ // Calls when resize is complete.
148
+ onResizeStop: PropTypes.func,
149
+ // Calls when some element is dropped.
150
+ onDrop: PropTypes.func,
151
+
152
+ //
153
+ // Other validations
154
+ //
155
+
156
+ droppingItem: PropTypes.shape({
157
+ i: PropTypes.string.isRequired,
158
+ w: PropTypes.number.isRequired,
159
+ h: PropTypes.number.isRequired
160
+ }),
161
+
162
+ // Children must not have duplicate keys.
163
+ children: function(props: Props, propName: string) {
164
+ var children = props[propName];
165
+
166
+ // Check children keys for duplicates. Throw if found.
167
+ var keys = {};
168
+ React.Children.forEach(children, function(child) {
169
+ if (keys[child.key]) {
170
+ throw new Error(
171
+ 'Duplicate child key "' +
172
+ child.key +
173
+ '" found! This will cause problems in ReactGridLayout.'
174
+ );
175
+ }
176
+ keys[child.key] = true;
177
+ });
178
+ }
179
+ };