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/lib/GridItem.jsx CHANGED
@@ -4,7 +4,8 @@ import ReactDOM from "react-dom";
4
4
  import PropTypes from "prop-types";
5
5
  import { DraggableCore } from "react-draggable";
6
6
  import { Resizable } from "react-resizable";
7
- import { perc, setTopLeft, setTransform } from "./utils";
7
+ import { fastPositionEqual, perc, setTopLeft, setTransform } from "./utils";
8
+ import { calcGridItemPosition, calcXY, calcWH } from "./calculateUtils";
8
9
  import classNames from "classnames";
9
10
  import type { Element as ReactElement, Node as ReactNode } from "react";
10
11
 
@@ -16,6 +17,8 @@ import type {
16
17
  Position
17
18
  } from "./utils";
18
19
 
20
+ import type { PositionParams } from "./calculateUtils";
21
+
19
22
  type PartialPosition = { top: number, left: number };
20
23
  type GridItemCallback<Data: GridDragEvent | GridResizeEvent> = (
21
24
  i: string,
@@ -151,8 +154,8 @@ export default class GridItem extends React.Component<Props, State> {
151
154
  // Current position of a dropping element
152
155
  droppingPosition: PropTypes.shape({
153
156
  e: PropTypes.object.isRequired,
154
- x: PropTypes.number.isRequired,
155
- y: PropTypes.number.isRequired
157
+ left: PropTypes.number.isRequired,
158
+ top: PropTypes.number.isRequired
156
159
  })
157
160
  };
158
161
 
@@ -175,19 +178,53 @@ export default class GridItem extends React.Component<Props, State> {
175
178
 
176
179
  currentNode: HTMLElement;
177
180
 
181
+ shouldComponentUpdate(nextProps: Props, nextState: State) {
182
+ // We can't deeply compare children. If the developer memoizes them, we can
183
+ // use this optimization.
184
+ if (this.props.children !== nextProps.children) return true;
185
+ if (this.props.droppingPosition !== nextProps.droppingPosition) return true;
186
+ // TODO memoize these calculations so they don't take so long?
187
+ const oldPosition = calcGridItemPosition(
188
+ this.getPositionParams(this.props),
189
+ this.props.x,
190
+ this.props.y,
191
+ this.props.w,
192
+ this.props.h,
193
+ this.state
194
+ );
195
+ const newPosition = calcGridItemPosition(
196
+ this.getPositionParams(nextProps),
197
+ nextProps.x,
198
+ nextProps.y,
199
+ nextProps.w,
200
+ nextProps.h,
201
+ nextState
202
+ );
203
+ return (
204
+ !fastPositionEqual(oldPosition, newPosition) ||
205
+ this.props.useCSSTransforms !== nextProps.useCSSTransforms
206
+ );
207
+ }
208
+
209
+ componentDidMount() {
210
+ this.moveDroppingItem({});
211
+ }
212
+
178
213
  componentDidUpdate(prevProps: Props) {
179
- if (this.props.droppingPosition && prevProps.droppingPosition) {
180
- this.moveDroppingItem(prevProps);
181
- }
214
+ this.moveDroppingItem(prevProps);
182
215
  }
183
216
 
217
+ // When a droppingPosition is present, this means we should fire a move event, as if we had moved
218
+ // this element by `x, y` pixels.
184
219
  moveDroppingItem(prevProps: Props) {
185
220
  const { droppingPosition } = this.props;
186
- const { dragging } = this.state;
221
+ if (!droppingPosition) return;
187
222
 
188
- if (!droppingPosition || !prevProps.droppingPosition) {
189
- return;
190
- }
223
+ const prevDroppingPosition = prevProps.droppingPosition || {
224
+ left: 0,
225
+ top: 0
226
+ };
227
+ const { dragging } = this.state;
191
228
 
192
229
  if (!this.currentNode) {
193
230
  // eslint-disable-next-line react/no-find-dom-node
@@ -195,18 +232,18 @@ export default class GridItem extends React.Component<Props, State> {
195
232
  }
196
233
 
197
234
  const shouldDrag =
198
- (dragging && droppingPosition.x !== prevProps.droppingPosition.x) ||
199
- droppingPosition.y !== prevProps.droppingPosition.y;
235
+ (dragging && droppingPosition.left !== prevDroppingPosition.left) ||
236
+ droppingPosition.top !== prevDroppingPosition.top;
200
237
 
201
238
  if (!dragging) {
202
239
  this.onDragStart(droppingPosition.e, {
203
240
  node: this.currentNode,
204
- deltaX: droppingPosition.x,
205
- deltaY: droppingPosition.y
241
+ deltaX: droppingPosition.left,
242
+ deltaY: droppingPosition.top
206
243
  });
207
244
  } else if (shouldDrag) {
208
- const deltaX = droppingPosition.x - dragging.left;
209
- const deltaY = droppingPosition.y - dragging.top;
245
+ const deltaX = droppingPosition.left - dragging.left;
246
+ const deltaY = droppingPosition.top - dragging.top;
210
247
 
211
248
  this.onDrag(droppingPosition.e, {
212
249
  node: this.currentNode,
@@ -216,121 +253,15 @@ export default class GridItem extends React.Component<Props, State> {
216
253
  }
217
254
  }
218
255
 
219
- // Helper for generating column width
220
- calcColWidth(): number {
221
- const { margin, containerPadding, containerWidth, cols } = this.props;
222
- return (
223
- (containerWidth - margin[0] * (cols - 1) - containerPadding[0] * 2) / cols
224
- );
225
- }
226
-
227
- /**
228
- * Return position on the page given an x, y, w, h.
229
- * left, top, width, height are all in pixels.
230
- * @param {Number} x X coordinate in grid units.
231
- * @param {Number} y Y coordinate in grid units.
232
- * @param {Number} w W coordinate in grid units.
233
- * @param {Number} h H coordinate in grid units.
234
- * @return {Object} Object containing coords.
235
- */
236
- calcPosition(
237
- x: number,
238
- y: number,
239
- w: number,
240
- h: number,
241
- state: ?Object
242
- ): Position {
243
- const { margin, containerPadding, rowHeight } = this.props;
244
- const colWidth = this.calcColWidth();
245
- const out = {};
246
-
247
- // If resizing, use the exact width and height as returned from resizing callbacks.
248
- if (state && state.resizing) {
249
- out.width = Math.round(state.resizing.width);
250
- out.height = Math.round(state.resizing.height);
251
- }
252
- // Otherwise, calculate from grid units.
253
- else {
254
- // 0 * Infinity === NaN, which causes problems with resize constraints;
255
- // Fix this if it occurs.
256
- // Note we do it here rather than later because Math.round(Infinity) causes deopt
257
- out.width =
258
- w === Infinity
259
- ? w
260
- : Math.round(colWidth * w + Math.max(0, w - 1) * margin[0]);
261
- out.height =
262
- h === Infinity
263
- ? h
264
- : Math.round(rowHeight * h + Math.max(0, h - 1) * margin[1]);
265
- }
266
-
267
- // If dragging, use the exact width and height as returned from dragging callbacks.
268
- if (state && state.dragging) {
269
- out.top = Math.round(state.dragging.top);
270
- out.left = Math.round(state.dragging.left);
271
- }
272
- // Otherwise, calculate from grid units.
273
- else {
274
- out.top = Math.round((rowHeight + margin[1]) * y + containerPadding[1]);
275
- out.left = Math.round((colWidth + margin[0]) * x + containerPadding[0]);
276
- }
277
-
278
- return out;
279
- }
280
-
281
- /**
282
- * Translate x and y coordinates from pixels to grid units.
283
- * @param {Number} top Top position (relative to parent) in pixels.
284
- * @param {Number} left Left position (relative to parent) in pixels.
285
- * @return {Object} x and y in grid units.
286
- */
287
- calcXY(top: number, left: number): { x: number, y: number } {
288
- const { margin, cols, rowHeight, w, h, maxRows } = this.props;
289
- const colWidth = this.calcColWidth();
290
-
291
- // left = colWidth * x + margin * (x + 1)
292
- // l = cx + m(x+1)
293
- // l = cx + mx + m
294
- // l - m = cx + mx
295
- // l - m = x(c + m)
296
- // (l - m) / (c + m) = x
297
- // x = (left - margin) / (coldWidth + margin)
298
- let x = Math.round((left - margin[0]) / (colWidth + margin[0]));
299
- let y = Math.round((top - margin[1]) / (rowHeight + margin[1]));
300
-
301
- // Capping
302
- x = Math.max(Math.min(x, cols - w), 0);
303
- y = Math.max(Math.min(y, maxRows - h), 0);
304
-
305
- return { x, y };
306
- }
307
-
308
- /**
309
- * Given a height and width in pixel values, calculate grid units.
310
- * @param {Number} height Height in pixels.
311
- * @param {Number} width Width in pixels.
312
- * @return {Object} w, h as grid units.
313
- */
314
- calcWH({
315
- height,
316
- width
317
- }: {
318
- height: number,
319
- width: number
320
- }): { w: number, h: number } {
321
- const { margin, maxRows, cols, rowHeight, x, y } = this.props;
322
- const colWidth = this.calcColWidth();
323
-
324
- // width = colWidth * w - (margin * (w - 1))
325
- // ...
326
- // w = (width + margin) / (colWidth + margin)
327
- let w = Math.round((width + margin[0]) / (colWidth + margin[0]));
328
- let h = Math.round((height + margin[1]) / (rowHeight + margin[1]));
329
-
330
- // Capping
331
- w = Math.max(Math.min(w, cols - x), 0);
332
- h = Math.max(Math.min(h, maxRows - y), 0);
333
- return { w, h };
256
+ getPositionParams(props: Props = this.props): PositionParams {
257
+ return {
258
+ cols: props.cols,
259
+ containerPadding: props.containerPadding,
260
+ containerWidth: props.containerWidth,
261
+ margin: props.margin,
262
+ maxRows: props.maxRows,
263
+ rowHeight: props.rowHeight
264
+ };
334
265
  }
335
266
 
336
267
  /**
@@ -369,9 +300,13 @@ export default class GridItem extends React.Component<Props, State> {
369
300
  * @param {Element} child Child element.
370
301
  * @return {Element} Child wrapped in Draggable.
371
302
  */
372
- mixinDraggable(child: ReactElement<any>): ReactElement<any> {
303
+ mixinDraggable(
304
+ child: ReactElement<any>,
305
+ isDraggable: boolean
306
+ ): ReactElement<any> {
373
307
  return (
374
308
  <DraggableCore
309
+ disabled={!isDraggable}
375
310
  onStart={this.onDragStart}
376
311
  onDrag={this.onDrag}
377
312
  onStop={this.onDragStop}
@@ -395,16 +330,19 @@ export default class GridItem extends React.Component<Props, State> {
395
330
  */
396
331
  mixinResizable(
397
332
  child: ReactElement<any>,
398
- position: Position
333
+ position: Position,
334
+ isResizable: boolean
399
335
  ): ReactElement<any> {
400
- const { cols, x, minW, minH, maxW, maxH } = this.props;
336
+ const { cols, x, minW, minH, maxW, maxH, transformScale } = this.props;
337
+ const positionParams = this.getPositionParams();
401
338
 
402
339
  // This is the max possible width - doesn't go to infinity because of the width of the window
403
- const maxWidth = this.calcPosition(0, 0, cols - x, 0).width;
340
+ const maxWidth = calcGridItemPosition(positionParams, 0, 0, cols - x, 0)
341
+ .width;
404
342
 
405
343
  // Calculate min/max constraints using our min & maxes
406
- const mins = this.calcPosition(0, 0, minW, minH);
407
- const maxes = this.calcPosition(0, 0, maxW, maxH);
344
+ const mins = calcGridItemPosition(positionParams, 0, 0, minW, minH);
345
+ const maxes = calcGridItemPosition(positionParams, 0, 0, maxW, maxH);
408
346
  const minConstraints = [mins.width, mins.height];
409
347
  const maxConstraints = [
410
348
  Math.min(maxes.width, maxWidth),
@@ -412,6 +350,10 @@ export default class GridItem extends React.Component<Props, State> {
412
350
  ];
413
351
  return (
414
352
  <Resizable
353
+ draggableOpts={{
354
+ disabled: !isResizable
355
+ }}
356
+ className={isResizable ? undefined : "react-resizable-hide"}
415
357
  width={position.width}
416
358
  height={position.height}
417
359
  minConstraints={minConstraints}
@@ -419,6 +361,7 @@ export default class GridItem extends React.Component<Props, State> {
419
361
  onResizeStop={this.onResizeStop}
420
362
  onResizeStart={this.onResizeStart}
421
363
  onResize={this.onResize}
364
+ transformScale={transformScale}
422
365
  >
423
366
  {child}
424
367
  </Resizable>
@@ -448,7 +391,13 @@ export default class GridItem extends React.Component<Props, State> {
448
391
  newPosition.top = cTop - pTop + offsetParent.scrollTop;
449
392
  this.setState({ dragging: newPosition });
450
393
 
451
- const { x, y } = this.calcXY(newPosition.top, newPosition.left);
394
+ const { x, y } = calcXY(
395
+ this.getPositionParams(),
396
+ newPosition.top,
397
+ newPosition.left,
398
+ this.props.w,
399
+ this.props.h
400
+ );
452
401
 
453
402
  return (
454
403
  this.props.onDragStart &&
@@ -466,7 +415,10 @@ export default class GridItem extends React.Component<Props, State> {
466
415
  * @param {Object} callbackData an object with node, delta and position information
467
416
  */
468
417
  onDrag = (e: Event, { node, deltaX, deltaY }: ReactDraggableCallbackData) => {
469
- if (!this.props.onDrag) return;
418
+ const { onDrag, transformScale } = this.props;
419
+ if (!onDrag) return;
420
+ deltaX /= transformScale;
421
+ deltaY /= transformScale;
470
422
 
471
423
  const newPosition: PartialPosition = { top: 0, left: 0 };
472
424
 
@@ -476,11 +428,17 @@ export default class GridItem extends React.Component<Props, State> {
476
428
  newPosition.top = this.state.dragging.top + deltaY;
477
429
  this.setState({ dragging: newPosition });
478
430
 
479
- const { x, y } = this.calcXY(newPosition.top, newPosition.left);
431
+ const { x, y } = calcXY(
432
+ this.getPositionParams(),
433
+ newPosition.top,
434
+ newPosition.left,
435
+ this.props.w,
436
+ this.props.h
437
+ );
480
438
 
481
439
  return (
482
- this.props.onDrag &&
483
- this.props.onDrag.call(this, this.props.i, x, y, {
440
+ onDrag &&
441
+ onDrag.call(this, this.props.i, x, y, {
484
442
  e,
485
443
  node,
486
444
  newPosition
@@ -504,7 +462,13 @@ export default class GridItem extends React.Component<Props, State> {
504
462
  newPosition.top = this.state.dragging.top;
505
463
  this.setState({ dragging: null });
506
464
 
507
- const { x, y } = this.calcXY(newPosition.top, newPosition.left);
465
+ const { x, y } = calcXY(
466
+ this.getPositionParams(),
467
+ newPosition.top,
468
+ newPosition.left,
469
+ this.props.w,
470
+ this.props.h
471
+ );
508
472
 
509
473
  return (
510
474
  this.props.onDragStop &&
@@ -567,10 +531,16 @@ export default class GridItem extends React.Component<Props, State> {
567
531
  ) {
568
532
  const handler = this.props[handlerName];
569
533
  if (!handler) return;
570
- const { cols, x, i, maxW, minW, maxH, minH } = this.props;
534
+ const { cols, x, y, i, maxW, minW, maxH, minH } = this.props;
571
535
 
572
536
  // Get new XY
573
- let { w, h } = this.calcWH(size);
537
+ let { w, h } = calcWH(
538
+ this.getPositionParams(),
539
+ size.width,
540
+ size.height,
541
+ x,
542
+ y
543
+ );
574
544
 
575
545
  // Cap w at numCols
576
546
  w = Math.min(w, cols - x);
@@ -598,7 +568,14 @@ export default class GridItem extends React.Component<Props, State> {
598
568
  useCSSTransforms
599
569
  } = this.props;
600
570
 
601
- const pos = this.calcPosition(x, y, w, h, this.state);
571
+ const pos = calcGridItemPosition(
572
+ this.getPositionParams(),
573
+ x,
574
+ y,
575
+ w,
576
+ h,
577
+ this.state
578
+ );
602
579
  const child = React.Children.only(this.props.children);
603
580
 
604
581
  // Create the child element. We clone the existing element but modify its className and style.
@@ -625,10 +602,10 @@ export default class GridItem extends React.Component<Props, State> {
625
602
  });
626
603
 
627
604
  // Resizable support. This is usually on but the user can toggle it off.
628
- if (isResizable) newChild = this.mixinResizable(newChild, pos);
605
+ newChild = this.mixinResizable(newChild, pos, isResizable);
629
606
 
630
607
  // Draggable support. This is always on, except for with placeholders.
631
- if (isDraggable) newChild = this.mixinDraggable(newChild);
608
+ newChild = this.mixinDraggable(newChild, isDraggable);
632
609
 
633
610
  return newChild;
634
611
  }