react-grid-layout 0.16.4 → 0.17.1
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 +25 -0
- package/.browserslistrc +3 -0
- package/.flowconfig +1 -1
- package/.prettierignore +2 -0
- package/.prettierrc +18 -0
- package/.travis.yml +2 -2
- package/CHANGELOG.md +69 -0
- package/README.md +99 -52
- package/build/GridItem.js +596 -414
- package/build/ReactGridLayout.js +648 -423
- package/build/ResponsiveReactGridLayout.js +270 -151
- package/build/components/WidthProvider.js +96 -53
- package/build/responsiveUtils.js +56 -12
- package/build/utils.js +376 -183
- package/css/styles.css +4 -0
- package/index.js +5 -5
- package/index.js.flow +5 -7
- package/interfaces/classnames.js +1 -0
- package/{build/GridItem.js.flow → lib/GridItem.jsx} +230 -96
- package/{build/ReactGridLayout.js.flow → lib/ReactGridLayout.jsx} +220 -35
- package/{build/ResponsiveReactGridLayout.js.flow → lib/ResponsiveReactGridLayout.jsx} +73 -20
- package/{build/components/WidthProvider.js.flow → lib/components/WidthProvider.jsx} +1 -1
- package/{build/responsiveUtils.js.flow → lib/responsiveUtils.js} +0 -0
- package/{build/utils.js.flow → lib/utils.js} +49 -37
- package/package.json +37 -43
- package/.babelrc +0 -12
- package/.jshintignore +0 -1
- package/.jshintrc +0 -10
|
@@ -28,7 +28,9 @@ import type {
|
|
|
28
28
|
CompactType,
|
|
29
29
|
GridResizeEvent,
|
|
30
30
|
GridDragEvent,
|
|
31
|
+
DragOverEvent,
|
|
31
32
|
Layout,
|
|
33
|
+
DroppingPosition,
|
|
32
34
|
LayoutItem
|
|
33
35
|
} from "./utils";
|
|
34
36
|
|
|
@@ -38,7 +40,13 @@ type State = {
|
|
|
38
40
|
mounted: boolean,
|
|
39
41
|
oldDragItem: ?LayoutItem,
|
|
40
42
|
oldLayout: ?Layout,
|
|
41
|
-
oldResizeItem: ?LayoutItem
|
|
43
|
+
oldResizeItem: ?LayoutItem,
|
|
44
|
+
droppingDOMNode: ?ReactElement<any>,
|
|
45
|
+
droppingPosition?: DroppingPosition,
|
|
46
|
+
// Mirrored props
|
|
47
|
+
children: ReactChildrenArray<ReactElement<any>>,
|
|
48
|
+
compactType?: CompactType,
|
|
49
|
+
propsLayout?: Layout
|
|
42
50
|
};
|
|
43
51
|
|
|
44
52
|
export type Props = {
|
|
@@ -50,7 +58,7 @@ export type Props = {
|
|
|
50
58
|
draggableCancel: string,
|
|
51
59
|
draggableHandle: string,
|
|
52
60
|
verticalCompact: boolean,
|
|
53
|
-
compactType:
|
|
61
|
+
compactType: CompactType,
|
|
54
62
|
layout: Layout,
|
|
55
63
|
margin: [number, number],
|
|
56
64
|
containerPadding: [number, number] | null,
|
|
@@ -58,8 +66,11 @@ export type Props = {
|
|
|
58
66
|
maxRows: number,
|
|
59
67
|
isDraggable: boolean,
|
|
60
68
|
isResizable: boolean,
|
|
69
|
+
isDroppable: boolean,
|
|
61
70
|
preventCollision: boolean,
|
|
62
71
|
useCSSTransforms: boolean,
|
|
72
|
+
transformScale: number,
|
|
73
|
+
droppingItem: $Shape<LayoutItem>,
|
|
63
74
|
|
|
64
75
|
// Callbacks
|
|
65
76
|
onLayoutChange: Layout => void,
|
|
@@ -69,10 +80,31 @@ export type Props = {
|
|
|
69
80
|
onResize: EventCallback,
|
|
70
81
|
onResizeStart: EventCallback,
|
|
71
82
|
onResizeStop: EventCallback,
|
|
83
|
+
onDrop: (itemPosition: {
|
|
84
|
+
x: number,
|
|
85
|
+
y: number,
|
|
86
|
+
w: number,
|
|
87
|
+
h: number
|
|
88
|
+
}) => void,
|
|
72
89
|
children: ReactChildrenArray<ReactElement<any>>
|
|
73
90
|
};
|
|
74
91
|
// End Types
|
|
75
92
|
|
|
93
|
+
const compactType = (props: Props): CompactType => {
|
|
94
|
+
const { verticalCompact, compactType } = props || {};
|
|
95
|
+
|
|
96
|
+
return verticalCompact === false ? null : compactType;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const layoutClassName = "react-grid-layout";
|
|
100
|
+
let isFirefox = false;
|
|
101
|
+
// Try...catch will protect from navigator not existing (e.g. node) or a bad implementation of navigator
|
|
102
|
+
try {
|
|
103
|
+
isFirefox = navigator.userAgent.toLowerCase().includes("firefox");
|
|
104
|
+
} catch (e) {
|
|
105
|
+
/* Ignore */
|
|
106
|
+
}
|
|
107
|
+
|
|
76
108
|
/**
|
|
77
109
|
* A reactive, fluid grid layout with draggable, resizable components.
|
|
78
110
|
*/
|
|
@@ -154,6 +186,10 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
154
186
|
preventCollision: PropTypes.bool,
|
|
155
187
|
// Use CSS transforms instead of top/left
|
|
156
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,
|
|
157
193
|
|
|
158
194
|
//
|
|
159
195
|
// Callbacks
|
|
@@ -175,11 +211,19 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
175
211
|
onResize: PropTypes.func,
|
|
176
212
|
// Calls when resize is complete.
|
|
177
213
|
onResizeStop: PropTypes.func,
|
|
214
|
+
// Calls when some element is dropped.
|
|
215
|
+
onDrop: PropTypes.func,
|
|
178
216
|
|
|
179
217
|
//
|
|
180
218
|
// Other validations
|
|
181
219
|
//
|
|
182
220
|
|
|
221
|
+
droppingItem: PropTypes.shape({
|
|
222
|
+
i: PropTypes.string.isRequired,
|
|
223
|
+
w: PropTypes.number.isRequired,
|
|
224
|
+
h: PropTypes.number.isRequired
|
|
225
|
+
}),
|
|
226
|
+
|
|
183
227
|
// Children must not have duplicate keys.
|
|
184
228
|
children: function(props: Props, propName: string) {
|
|
185
229
|
var children = props[propName];
|
|
@@ -213,17 +257,25 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
213
257
|
margin: [10, 10],
|
|
214
258
|
isDraggable: true,
|
|
215
259
|
isResizable: true,
|
|
260
|
+
isDroppable: false,
|
|
216
261
|
useCSSTransforms: true,
|
|
262
|
+
transformScale: 1,
|
|
217
263
|
verticalCompact: true,
|
|
218
264
|
compactType: "vertical",
|
|
219
265
|
preventCollision: false,
|
|
266
|
+
droppingItem: {
|
|
267
|
+
i: "__dropping-elem__",
|
|
268
|
+
h: 1,
|
|
269
|
+
w: 1
|
|
270
|
+
},
|
|
220
271
|
onLayoutChange: noop,
|
|
221
272
|
onDragStart: noop,
|
|
222
273
|
onDrag: noop,
|
|
223
274
|
onDragStop: noop,
|
|
224
275
|
onResizeStart: noop,
|
|
225
276
|
onResize: noop,
|
|
226
|
-
onResizeStop: noop
|
|
277
|
+
onResizeStop: noop,
|
|
278
|
+
onDrop: noop
|
|
227
279
|
};
|
|
228
280
|
|
|
229
281
|
state: State = {
|
|
@@ -233,14 +285,18 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
233
285
|
this.props.children,
|
|
234
286
|
this.props.cols,
|
|
235
287
|
// Legacy support for verticalCompact: false
|
|
236
|
-
this.
|
|
288
|
+
compactType(this.props)
|
|
237
289
|
),
|
|
238
290
|
mounted: false,
|
|
239
291
|
oldDragItem: null,
|
|
240
292
|
oldLayout: null,
|
|
241
|
-
oldResizeItem: null
|
|
293
|
+
oldResizeItem: null,
|
|
294
|
+
droppingDOMNode: null,
|
|
295
|
+
children: []
|
|
242
296
|
};
|
|
243
297
|
|
|
298
|
+
dragEnterCounter = 0;
|
|
299
|
+
|
|
244
300
|
constructor(props: Props, context: any): void {
|
|
245
301
|
super(props, context);
|
|
246
302
|
autoBindHandlers(this, [
|
|
@@ -260,20 +316,25 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
260
316
|
this.onLayoutMaybeChanged(this.state.layout, this.props.layout);
|
|
261
317
|
}
|
|
262
318
|
|
|
263
|
-
|
|
319
|
+
static getDerivedStateFromProps(nextProps: Props, prevState: State) {
|
|
264
320
|
let newLayoutBase;
|
|
321
|
+
|
|
322
|
+
if (prevState.activeDrag) {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
|
|
265
326
|
// Legacy support for compactType
|
|
266
327
|
// Allow parent to set layout directly.
|
|
267
328
|
if (
|
|
268
|
-
!isEqual(nextProps.layout,
|
|
269
|
-
nextProps.compactType !==
|
|
329
|
+
!isEqual(nextProps.layout, prevState.propsLayout) ||
|
|
330
|
+
nextProps.compactType !== prevState.compactType
|
|
270
331
|
) {
|
|
271
332
|
newLayoutBase = nextProps.layout;
|
|
272
|
-
} else if (!childrenEqual(
|
|
333
|
+
} else if (!childrenEqual(nextProps.children, prevState.children)) {
|
|
273
334
|
// If children change, also regenerate the layout. Use our state
|
|
274
335
|
// as the base in case because it may be more up to date than
|
|
275
336
|
// what is in props.
|
|
276
|
-
newLayoutBase =
|
|
337
|
+
newLayoutBase = prevState.layout;
|
|
277
338
|
}
|
|
278
339
|
|
|
279
340
|
// We need to regenerate the layout.
|
|
@@ -282,10 +343,27 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
282
343
|
newLayoutBase,
|
|
283
344
|
nextProps.children,
|
|
284
345
|
nextProps.cols,
|
|
285
|
-
|
|
346
|
+
compactType(nextProps)
|
|
286
347
|
);
|
|
287
|
-
|
|
288
|
-
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
layout: newLayout,
|
|
351
|
+
// We need to save these props to state for using
|
|
352
|
+
// getDerivedStateFromProps instead of componentDidMount (in which we would get extra rerender)
|
|
353
|
+
compactType: nextProps.compactType,
|
|
354
|
+
children: nextProps.children,
|
|
355
|
+
propsLayout: nextProps.layout
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
componentDidUpdate(prevProps: Props, prevState: State) {
|
|
363
|
+
if (!this.state.activeDrag) {
|
|
364
|
+
const newLayout = this.state.layout;
|
|
365
|
+
const oldLayout = prevState.layout;
|
|
366
|
+
|
|
289
367
|
this.onLayoutMaybeChanged(newLayout, oldLayout);
|
|
290
368
|
}
|
|
291
369
|
}
|
|
@@ -308,11 +386,6 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
308
386
|
);
|
|
309
387
|
}
|
|
310
388
|
|
|
311
|
-
compactType(props: ?Object): CompactType {
|
|
312
|
-
if (!props) props = this.props;
|
|
313
|
-
return props.verticalCompact === false ? null : props.compactType;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
389
|
/**
|
|
317
390
|
* When dragging starts
|
|
318
391
|
* @param {String} i Id of the child
|
|
@@ -368,14 +441,14 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
368
441
|
y,
|
|
369
442
|
isUserAction,
|
|
370
443
|
this.props.preventCollision,
|
|
371
|
-
this.
|
|
444
|
+
compactType(this.props),
|
|
372
445
|
cols
|
|
373
446
|
);
|
|
374
447
|
|
|
375
448
|
this.props.onDrag(layout, oldDragItem, l, placeholder, e, node);
|
|
376
449
|
|
|
377
450
|
this.setState({
|
|
378
|
-
layout: compact(layout, this.
|
|
451
|
+
layout: compact(layout, compactType(this.props), cols),
|
|
379
452
|
activeDrag: placeholder
|
|
380
453
|
});
|
|
381
454
|
}
|
|
@@ -404,14 +477,15 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
404
477
|
y,
|
|
405
478
|
isUserAction,
|
|
406
479
|
preventCollision,
|
|
407
|
-
this.
|
|
480
|
+
compactType(this.props),
|
|
408
481
|
cols
|
|
409
482
|
);
|
|
410
|
-
|
|
411
|
-
|
|
483
|
+
if (this.state.activeDrag) {
|
|
484
|
+
this.props.onDragStop(layout, oldDragItem, l, null, e, node);
|
|
485
|
+
}
|
|
412
486
|
|
|
413
487
|
// Set state
|
|
414
|
-
const newLayout = compact(layout, this.
|
|
488
|
+
const newLayout = compact(layout, compactType(this.props), cols);
|
|
415
489
|
const { oldLayout } = this.state;
|
|
416
490
|
this.setState({
|
|
417
491
|
activeDrag: null,
|
|
@@ -425,6 +499,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
425
499
|
|
|
426
500
|
onLayoutMaybeChanged(newLayout: Layout, oldLayout: ?Layout) {
|
|
427
501
|
if (!oldLayout) oldLayout = this.state.layout;
|
|
502
|
+
|
|
428
503
|
if (!isEqual(oldLayout, newLayout)) {
|
|
429
504
|
this.props.onLayoutChange(newLayout);
|
|
430
505
|
}
|
|
@@ -453,14 +528,17 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
453
528
|
// to find collisions faster
|
|
454
529
|
let hasCollisions;
|
|
455
530
|
if (preventCollision) {
|
|
456
|
-
const collisions = getAllCollisions(layout, { ...l, w, h }).filter(
|
|
531
|
+
const collisions = getAllCollisions(layout, { ...l, w, h }).filter(
|
|
532
|
+
layoutItem => layoutItem.i !== l.i
|
|
533
|
+
);
|
|
457
534
|
hasCollisions = collisions.length > 0;
|
|
458
535
|
|
|
459
536
|
// If we're colliding, we need adjust the placeholder.
|
|
460
537
|
if (hasCollisions) {
|
|
461
538
|
// adjust w && h to maximum allowed space
|
|
462
|
-
let leastX = Infinity,
|
|
463
|
-
|
|
539
|
+
let leastX = Infinity,
|
|
540
|
+
leastY = Infinity;
|
|
541
|
+
collisions.forEach(layoutItem => {
|
|
464
542
|
if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x);
|
|
465
543
|
if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y);
|
|
466
544
|
});
|
|
@@ -490,7 +568,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
490
568
|
|
|
491
569
|
// Re-compact the layout and set the drag placeholder.
|
|
492
570
|
this.setState({
|
|
493
|
-
layout: compact(layout, this.
|
|
571
|
+
layout: compact(layout, compactType(this.props), cols),
|
|
494
572
|
activeDrag: placeholder
|
|
495
573
|
});
|
|
496
574
|
}
|
|
@@ -503,7 +581,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
503
581
|
this.props.onResizeStop(layout, oldResizeItem, l, null, e, node);
|
|
504
582
|
|
|
505
583
|
// Set state
|
|
506
|
-
const newLayout = compact(layout, this.
|
|
584
|
+
const newLayout = compact(layout, compactType(this.props), cols);
|
|
507
585
|
const { oldLayout } = this.state;
|
|
508
586
|
this.setState({
|
|
509
587
|
activeDrag: null,
|
|
@@ -529,7 +607,8 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
529
607
|
containerPadding,
|
|
530
608
|
rowHeight,
|
|
531
609
|
maxRows,
|
|
532
|
-
useCSSTransforms
|
|
610
|
+
useCSSTransforms,
|
|
611
|
+
transformScale
|
|
533
612
|
} = this.props;
|
|
534
613
|
|
|
535
614
|
// {...this.state.activeDrag} is pretty slow, actually
|
|
@@ -550,6 +629,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
550
629
|
isDraggable={false}
|
|
551
630
|
isResizable={false}
|
|
552
631
|
useCSSTransforms={useCSSTransforms}
|
|
632
|
+
transformScale={transformScale}
|
|
553
633
|
>
|
|
554
634
|
<div />
|
|
555
635
|
</GridItem>
|
|
@@ -561,7 +641,10 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
561
641
|
* @param {Element} child React element.
|
|
562
642
|
* @return {Element} Element wrapped in draggable and properly placed.
|
|
563
643
|
*/
|
|
564
|
-
processGridItem(
|
|
644
|
+
processGridItem(
|
|
645
|
+
child: ReactElement<any>,
|
|
646
|
+
isDroppingItem?: boolean
|
|
647
|
+
): ?ReactElement<any> {
|
|
565
648
|
if (!child || !child.key) return;
|
|
566
649
|
const l = getLayoutItem(this.state.layout, String(child.key));
|
|
567
650
|
if (!l) return null;
|
|
@@ -575,10 +658,11 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
575
658
|
isDraggable,
|
|
576
659
|
isResizable,
|
|
577
660
|
useCSSTransforms,
|
|
661
|
+
transformScale,
|
|
578
662
|
draggableCancel,
|
|
579
663
|
draggableHandle
|
|
580
664
|
} = this.props;
|
|
581
|
-
const { mounted } = this.state;
|
|
665
|
+
const { mounted, droppingPosition } = this.state;
|
|
582
666
|
|
|
583
667
|
// Parse 'static'. Any properties defined directly on the grid item will take precedence.
|
|
584
668
|
const draggable = Boolean(
|
|
@@ -608,6 +692,7 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
608
692
|
isResizable={resizable}
|
|
609
693
|
useCSSTransforms={useCSSTransforms && mounted}
|
|
610
694
|
usePercentages={!mounted}
|
|
695
|
+
transformScale={transformScale}
|
|
611
696
|
w={l.w}
|
|
612
697
|
h={l.h}
|
|
613
698
|
x={l.x}
|
|
@@ -618,26 +703,126 @@ export default class ReactGridLayout extends React.Component<Props, State> {
|
|
|
618
703
|
maxH={l.maxH}
|
|
619
704
|
maxW={l.maxW}
|
|
620
705
|
static={l.static}
|
|
706
|
+
droppingPosition={isDroppingItem ? droppingPosition : undefined}
|
|
621
707
|
>
|
|
622
708
|
{child}
|
|
623
709
|
</GridItem>
|
|
624
710
|
);
|
|
625
711
|
}
|
|
626
712
|
|
|
713
|
+
onDragOver = (e: DragOverEvent) => {
|
|
714
|
+
// we should ignore events from layout's children in Firefox
|
|
715
|
+
// to avoid unpredictable jumping of a dropping placeholder
|
|
716
|
+
if (
|
|
717
|
+
isFirefox &&
|
|
718
|
+
!e.nativeEvent.target.className.includes(layoutClassName)
|
|
719
|
+
) {
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const { droppingItem } = this.props;
|
|
724
|
+
const { layout } = this.state;
|
|
725
|
+
const { layerX, layerY } = e.nativeEvent;
|
|
726
|
+
const droppingPosition = { x: layerX, y: layerY, e };
|
|
727
|
+
|
|
728
|
+
if (!this.state.droppingDOMNode) {
|
|
729
|
+
this.setState({
|
|
730
|
+
droppingDOMNode: <div key={droppingItem.i} />,
|
|
731
|
+
droppingPosition,
|
|
732
|
+
layout: [
|
|
733
|
+
...layout,
|
|
734
|
+
{
|
|
735
|
+
...droppingItem,
|
|
736
|
+
x: 0,
|
|
737
|
+
y: 0,
|
|
738
|
+
static: false,
|
|
739
|
+
isDraggable: true
|
|
740
|
+
}
|
|
741
|
+
]
|
|
742
|
+
});
|
|
743
|
+
} 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 });
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
e.stopPropagation();
|
|
751
|
+
e.preventDefault();
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
removeDroppingPlaceholder = () => {
|
|
755
|
+
const { droppingItem, cols } = this.props;
|
|
756
|
+
const { layout } = this.state;
|
|
757
|
+
|
|
758
|
+
const newLayout = compact(
|
|
759
|
+
layout.filter(l => l.i !== droppingItem.i),
|
|
760
|
+
compactType(this.props),
|
|
761
|
+
cols
|
|
762
|
+
);
|
|
763
|
+
|
|
764
|
+
this.setState({
|
|
765
|
+
layout: newLayout,
|
|
766
|
+
droppingDOMNode: null,
|
|
767
|
+
activeDrag: null,
|
|
768
|
+
droppingPosition: undefined
|
|
769
|
+
});
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
onDragLeave = () => {
|
|
773
|
+
this.dragEnterCounter--;
|
|
774
|
+
|
|
775
|
+
// onDragLeave can be triggered on each layout's child.
|
|
776
|
+
// But we know that count of dragEnter and dragLeave events
|
|
777
|
+
// will be balanced after leaving the layout's container
|
|
778
|
+
// so we can increase and decrease count of dragEnter and
|
|
779
|
+
// when it'll be equal to 0 we'll remove the placeholder
|
|
780
|
+
if (this.dragEnterCounter === 0) {
|
|
781
|
+
this.removeDroppingPlaceholder();
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
onDragEnter = () => {
|
|
786
|
+
this.dragEnterCounter++;
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
onDrop = () => {
|
|
790
|
+
const { droppingItem } = this.props;
|
|
791
|
+
const { layout } = this.state;
|
|
792
|
+
const { x, y, w, h } = layout.find(l => l.i === droppingItem.i) || {};
|
|
793
|
+
|
|
794
|
+
// reset gragEnter counter on drop
|
|
795
|
+
this.dragEnterCounter = 0;
|
|
796
|
+
|
|
797
|
+
this.removeDroppingPlaceholder();
|
|
798
|
+
|
|
799
|
+
this.props.onDrop({ x, y, w, h });
|
|
800
|
+
};
|
|
801
|
+
|
|
627
802
|
render() {
|
|
628
|
-
const { className, style } = this.props;
|
|
803
|
+
const { className, style, isDroppable } = this.props;
|
|
629
804
|
|
|
630
|
-
const mergedClassName = classNames(
|
|
805
|
+
const mergedClassName = classNames(layoutClassName, className);
|
|
631
806
|
const mergedStyle = {
|
|
632
807
|
height: this.containerHeight(),
|
|
633
808
|
...style
|
|
634
809
|
};
|
|
635
810
|
|
|
636
811
|
return (
|
|
637
|
-
<div
|
|
812
|
+
<div
|
|
813
|
+
className={mergedClassName}
|
|
814
|
+
style={mergedStyle}
|
|
815
|
+
onDrop={isDroppable ? this.onDrop : noop}
|
|
816
|
+
onDragLeave={isDroppable ? this.onDragLeave : noop}
|
|
817
|
+
onDragEnter={isDroppable ? this.onDragEnter : noop}
|
|
818
|
+
onDragOver={isDroppable ? this.onDragOver : noop}
|
|
819
|
+
>
|
|
638
820
|
{React.Children.map(this.props.children, child =>
|
|
639
821
|
this.processGridItem(child)
|
|
640
822
|
)}
|
|
823
|
+
{isDroppable &&
|
|
824
|
+
this.state.droppingDOMNode &&
|
|
825
|
+
this.processGridItem(this.state.droppingDOMNode, true)}
|
|
641
826
|
{this.placeholder()}
|
|
642
827
|
</div>
|
|
643
828
|
);
|
|
@@ -20,10 +20,26 @@ import type { Layout } from "./utils";
|
|
|
20
20
|
|
|
21
21
|
const type = obj => Object.prototype.toString.call(obj);
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Get a value of margin or containerPadding.
|
|
25
|
+
*
|
|
26
|
+
* @param {Array | Object} param Margin | containerPadding, e.g. [10, 10] | {lg: [10, 10], ...}.
|
|
27
|
+
* @param {String} breakpoint Breakpoint: lg, md, sm, xs and etc.
|
|
28
|
+
* @return {Array}
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
function getIndentationValue(
|
|
32
|
+
param: { [key: string]: [number, number] } | [number, number],
|
|
33
|
+
breakpoint: string
|
|
34
|
+
) {
|
|
35
|
+
return Array.isArray(param) ? param : param[breakpoint];
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
type State = {
|
|
24
39
|
layout: Layout,
|
|
25
40
|
breakpoint: string,
|
|
26
|
-
cols: number
|
|
41
|
+
cols: number,
|
|
42
|
+
layouts?: { [key: string]: Layout }
|
|
27
43
|
};
|
|
28
44
|
|
|
29
45
|
type Props<Breakpoint: string = string> = {
|
|
@@ -35,6 +51,8 @@ type Props<Breakpoint: string = string> = {
|
|
|
35
51
|
cols: { [key: Breakpoint]: number },
|
|
36
52
|
layouts: { [key: Breakpoint]: Layout },
|
|
37
53
|
width: number,
|
|
54
|
+
margin: { [key: Breakpoint]: [number, number] } | [number, number],
|
|
55
|
+
containerPadding: { [key: Breakpoint]: [number, number] } | [number, number],
|
|
38
56
|
|
|
39
57
|
// Callbacks
|
|
40
58
|
onBreakpointChange: (Breakpoint, cols: number) => void,
|
|
@@ -68,6 +86,18 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
68
86
|
// # of cols. This is a breakpoint -> cols map
|
|
69
87
|
cols: PropTypes.object,
|
|
70
88
|
|
|
89
|
+
// # of margin. This is a breakpoint -> margin map
|
|
90
|
+
// e.g. { lg: [5, 5], md: [10, 10], sm: [15, 15] }
|
|
91
|
+
// Margin between items [x, y] in px
|
|
92
|
+
// e.g. [10, 10]
|
|
93
|
+
margin: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
|
94
|
+
|
|
95
|
+
// # of containerPadding. This is a breakpoint -> containerPadding map
|
|
96
|
+
// e.g. { lg: [5, 5], md: [10, 10], sm: [15, 15] }
|
|
97
|
+
// Padding inside the container [x, y] in px
|
|
98
|
+
// e.g. [10, 10]
|
|
99
|
+
containerPadding: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
|
100
|
+
|
|
71
101
|
// layouts is an object mapping breakpoints to layouts.
|
|
72
102
|
// e.g. {lg: Layout, md: Layout, ...}
|
|
73
103
|
layouts(props: Props<>, propName: string) {
|
|
@@ -110,6 +140,8 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
110
140
|
breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 },
|
|
111
141
|
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
|
|
112
142
|
layouts: {},
|
|
143
|
+
margin: [10, 10],
|
|
144
|
+
containerPadding: { lg: null, md: null, sm: null, xs: null, xxs: null },
|
|
113
145
|
onBreakpointChange: noop,
|
|
114
146
|
onLayoutChange: noop,
|
|
115
147
|
onWidthChange: noop
|
|
@@ -142,18 +174,10 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
142
174
|
};
|
|
143
175
|
}
|
|
144
176
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
nextProps.width != this.props.width ||
|
|
149
|
-
nextProps.breakpoint !== this.props.breakpoint ||
|
|
150
|
-
!isEqual(nextProps.breakpoints, this.props.breakpoints) ||
|
|
151
|
-
!isEqual(nextProps.cols, this.props.cols)
|
|
152
|
-
) {
|
|
153
|
-
this.onWidthChange(nextProps);
|
|
154
|
-
} else if (!isEqual(nextProps.layouts, this.props.layouts)) {
|
|
177
|
+
static getDerivedStateFromProps(nextProps: Props<*>, prevState: State) {
|
|
178
|
+
if (!isEqual(nextProps.layouts, prevState.layouts)) {
|
|
155
179
|
// Allow parent to set layouts directly.
|
|
156
|
-
const { breakpoint, cols } =
|
|
180
|
+
const { breakpoint, cols } = prevState;
|
|
157
181
|
|
|
158
182
|
// Since we're setting an entirely new layout object, we must generate a new responsive layout
|
|
159
183
|
// if one does not exist.
|
|
@@ -165,7 +189,21 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
165
189
|
cols,
|
|
166
190
|
nextProps.compactType
|
|
167
191
|
);
|
|
168
|
-
|
|
192
|
+
return { layout: newLayout, layouts: nextProps.layouts };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
componentDidUpdate(prevProps: Props<*>) {
|
|
199
|
+
// Allow parent to set width or breakpoint directly.
|
|
200
|
+
if (
|
|
201
|
+
this.props.width != prevProps.width ||
|
|
202
|
+
this.props.breakpoint !== prevProps.breakpoint ||
|
|
203
|
+
!isEqual(this.props.breakpoints, prevProps.breakpoints) ||
|
|
204
|
+
!isEqual(this.props.cols, prevProps.cols)
|
|
205
|
+
) {
|
|
206
|
+
this.onWidthChange(this.props);
|
|
169
207
|
}
|
|
170
208
|
}
|
|
171
209
|
|
|
@@ -188,6 +226,7 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
188
226
|
getBreakpointFromWidth(nextProps.breakpoints, nextProps.width);
|
|
189
227
|
|
|
190
228
|
const lastBreakpoint = this.state.breakpoint;
|
|
229
|
+
const newCols: number = getColsFromBreakpoint(newBreakpoint, cols);
|
|
191
230
|
|
|
192
231
|
// Breakpoint change
|
|
193
232
|
if (
|
|
@@ -200,7 +239,6 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
200
239
|
layouts[lastBreakpoint] = cloneLayout(this.state.layout);
|
|
201
240
|
|
|
202
241
|
// Find or generate a new layout.
|
|
203
|
-
const newCols: number = getColsFromBreakpoint(newBreakpoint, cols);
|
|
204
242
|
let layout = findOrGenerateResponsiveLayout(
|
|
205
243
|
layouts,
|
|
206
244
|
breakpoints,
|
|
@@ -224,12 +262,6 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
224
262
|
// callbacks
|
|
225
263
|
this.props.onLayoutChange(layout, layouts);
|
|
226
264
|
this.props.onBreakpointChange(newBreakpoint, newCols);
|
|
227
|
-
this.props.onWidthChange(
|
|
228
|
-
nextProps.width,
|
|
229
|
-
nextProps.margin,
|
|
230
|
-
newCols,
|
|
231
|
-
nextProps.containerPadding
|
|
232
|
-
);
|
|
233
265
|
|
|
234
266
|
this.setState({
|
|
235
267
|
breakpoint: newBreakpoint,
|
|
@@ -237,6 +269,20 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
237
269
|
cols: newCols
|
|
238
270
|
});
|
|
239
271
|
}
|
|
272
|
+
|
|
273
|
+
const margin = getIndentationValue(nextProps.margin, newBreakpoint);
|
|
274
|
+
const containerPadding = getIndentationValue(
|
|
275
|
+
nextProps.containerPadding,
|
|
276
|
+
newBreakpoint
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
//call onWidthChange on every change of width, not only on breakpoint changes
|
|
280
|
+
this.props.onWidthChange(
|
|
281
|
+
nextProps.width,
|
|
282
|
+
margin,
|
|
283
|
+
newCols,
|
|
284
|
+
containerPadding
|
|
285
|
+
);
|
|
240
286
|
}
|
|
241
287
|
|
|
242
288
|
render() {
|
|
@@ -246,6 +292,8 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
246
292
|
breakpoints,
|
|
247
293
|
cols,
|
|
248
294
|
layouts,
|
|
295
|
+
margin,
|
|
296
|
+
containerPadding,
|
|
249
297
|
onBreakpointChange,
|
|
250
298
|
onLayoutChange,
|
|
251
299
|
onWidthChange,
|
|
@@ -256,6 +304,11 @@ export default class ResponsiveReactGridLayout extends React.Component<
|
|
|
256
304
|
return (
|
|
257
305
|
<ReactGridLayout
|
|
258
306
|
{...other}
|
|
307
|
+
margin={getIndentationValue(margin, this.state.breakpoint)}
|
|
308
|
+
containerPadding={getIndentationValue(
|
|
309
|
+
containerPadding,
|
|
310
|
+
this.state.breakpoint
|
|
311
|
+
)}
|
|
259
312
|
onLayoutChange={this.onLayoutChange}
|
|
260
313
|
layout={this.state.layout}
|
|
261
314
|
cols={this.state.cols}
|
|
@@ -57,7 +57,7 @@ export default function WidthProvider<
|
|
|
57
57
|
|
|
58
58
|
onWindowResize = () => {
|
|
59
59
|
if (!this.mounted) return;
|
|
60
|
-
// eslint-disable-next-line
|
|
60
|
+
// eslint-disable-next-line react/no-find-dom-node
|
|
61
61
|
const node = ReactDOM.findDOMNode(this); // Flow casts this to Text | Element
|
|
62
62
|
if (node instanceof HTMLElement)
|
|
63
63
|
this.setState({ width: node.offsetWidth });
|
|
File without changes
|