react-grid-layout 1.4.0 → 1.4.2

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
@@ -3,7 +3,13 @@ import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { DraggableCore } from "react-draggable";
5
5
  import { Resizable } from "react-resizable";
6
- import { fastPositionEqual, perc, setTopLeft, setTransform } from "./utils";
6
+ import {
7
+ fastPositionEqual,
8
+ perc,
9
+ resizeItemInDirection,
10
+ setTopLeft,
11
+ setTransform
12
+ } from "./utils";
7
13
  import {
8
14
  calcGridItemPosition,
9
15
  calcGridItemWHPx,
@@ -24,15 +30,12 @@ import type {
24
30
  GridDragEvent,
25
31
  GridResizeEvent,
26
32
  DroppingPosition,
27
- Position
33
+ Position,
34
+ ResizeHandleAxis
28
35
  } from "./utils";
29
36
 
30
37
  import type { PositionParams } from "./calculateUtils";
31
- import type {
32
- ResizeHandleAxis,
33
- ResizeHandle,
34
- ReactRef
35
- } from "./ReactGridLayoutPropTypes";
38
+ import type { ResizeHandle, ReactRef } from "./ReactGridLayoutPropTypes";
36
39
 
37
40
  type PartialPosition = { top: number, left: number };
38
41
  type GridItemCallback<Data: GridDragEvent | GridResizeEvent> = (
@@ -41,16 +44,18 @@ type GridItemCallback<Data: GridDragEvent | GridResizeEvent> = (
41
44
  h: number,
42
45
  Data
43
46
  ) => void;
47
+
48
+ type ResizeCallbackData = {
49
+ node: HTMLElement,
50
+ size: Position,
51
+ handle: ResizeHandleAxis
52
+ };
53
+
44
54
  type GridItemResizeCallback = (
45
55
  e: Event,
46
56
  data: ResizeCallbackData,
47
57
  position: Position
48
58
  ) => void;
49
- type ResizeCallbackData = {
50
- node: HTMLElement,
51
- size: Position,
52
- handle: string
53
- };
54
59
 
55
60
  type State = {
56
61
  resizing: ?{ top: number, left: number, width: number, height: number },
@@ -370,8 +375,8 @@ export default class GridItem extends React.Component<Props, State> {
370
375
  * Utility function to setup callback handler definitions for
371
376
  * similarily structured resize events.
372
377
  */
373
- curryResizeHandler(position: Position, handler: function): function {
374
- return (e: Event, data: ResizeCallbackData): function =>
378
+ curryResizeHandler(position: Position, handler: Function): Function {
379
+ return (e: Event, data: ResizeCallbackData): Function =>
375
380
  handler(e, data, position);
376
381
  }
377
382
 
@@ -388,7 +393,6 @@ export default class GridItem extends React.Component<Props, State> {
388
393
  ): ReactElement<any> {
389
394
  const {
390
395
  cols,
391
- x,
392
396
  minW,
393
397
  minH,
394
398
  maxW,
@@ -566,144 +570,51 @@ export default class GridItem extends React.Component<Props, State> {
566
570
  this.onResizeHandler(e, callbackData, position, "onResize");
567
571
 
568
572
  /**
569
- * Wrapper around drag events to provide more useful data.
570
- * All drag events call the function with the given handler name,
571
- * with the signature (index, x, y).
573
+ * Wrapper around resize events to provide more useful data.
572
574
  */
573
575
  onResizeHandler(
574
576
  e: Event,
575
- { node, size, handle }: ResizeCallbackData,
576
- position: Position,
577
+ { node, size, handle }: ResizeCallbackData, // 'size' is updated position
578
+ position: Position, // existing position
577
579
  handlerName: string
578
580
  ): void {
579
581
  const handler = this.props[handlerName];
580
582
  if (!handler) return;
581
- const { x, y, i, maxH, minH } = this.props;
582
- let { minW, maxW } = this.props;
583
+ const { x, y, i, maxH, minH, containerWidth } = this.props;
584
+ const { minW, maxW } = this.props;
585
+
586
+ // Clamping of dimensions based on resize direction
587
+ let updatedSize = size;
588
+ if (node) {
589
+ updatedSize = resizeItemInDirection(
590
+ handle,
591
+ position,
592
+ size,
593
+ containerWidth
594
+ );
595
+ this.setState({
596
+ resizing: handlerName === "onResizeStop" ? null : updatedSize
597
+ });
598
+ }
583
599
 
584
- // Get new XY
600
+ // Get new XY based on pixel size
585
601
  let { w, h } = calcWH(
586
602
  this.getPositionParams(),
587
- size.width,
588
- size.height,
603
+ updatedSize.width,
604
+ updatedSize.height,
589
605
  x,
590
606
  y,
591
607
  handle
592
608
  );
593
609
 
610
+ // Min/max capping.
594
611
  // minW should be at least 1 (TODO propTypes validation?)
595
- minW = Math.max(minW, 1);
596
-
597
- // Min/max capping
598
- w = clamp(w, minW, maxW);
612
+ w = clamp(w, Math.max(minW, 1), maxW);
599
613
  h = clamp(h, minH, maxH);
600
614
 
601
- let updatedSize = size;
602
- if (node) {
603
- const currentLeft = position.left;
604
- const currentTop = position.top;
605
- const currentWidth = position.width;
606
- const currentHeight = position.height;
607
-
608
- const resizeNorth = ({ left, height, width }) => {
609
- const top = currentTop - (height - currentHeight);
610
-
611
- return {
612
- top,
613
- left,
614
- width,
615
- height: this.constrainHeight(top, currentHeight, height),
616
- top: this.constrainTop(top)
617
- };
618
- };
619
-
620
- const resizeEast = ({ top, left, height, width }) => ({
621
- top,
622
- height,
623
- width: this.constrainWidth(currentLeft, currentWidth, width),
624
- left: this.constrainLeft(left)
625
- });
626
-
627
- const resizeWest = ({ top, height, width }) => {
628
- const left = currentLeft - (width - currentWidth);
629
-
630
- return {
631
- height,
632
- width:
633
- left <= 0
634
- ? currentWidth
635
- : this.constrainWidth(currentLeft, currentWidth, width),
636
- top: this.constrainTop(top),
637
- left: this.constrainLeft(left)
638
- };
639
- };
640
-
641
- const resizeSouth = ({ top, left, height, width }) => ({
642
- width,
643
- left,
644
- height: this.constrainHeight(top, currentHeight, height),
645
- top: this.constrainTop(top)
646
- });
647
-
648
- const resizeNorthEast = (...args) => resizeNorth(resizeEast(...args));
649
- const resizeNorthWest = (...args) => resizeNorth(resizeWest(...args));
650
- const resizeSouthEast = (...args) => resizeSouth(resizeEast(...args));
651
- const resizeSouthWest = (...args) => resizeSouth(resizeWest(...args));
652
-
653
- const ordinalResizeHandlerMap = {
654
- n: resizeNorth,
655
- ne: resizeNorthEast,
656
- e: resizeEast,
657
- se: resizeSouthEast,
658
- s: resizeSouth,
659
- sw: resizeSouthWest,
660
- w: resizeWest,
661
- nw: resizeNorthWest
662
- };
663
-
664
- const resizeHandler = ordinalResizeHandlerMap[handle];
665
- updatedSize = resizeHandler
666
- ? resizeHandler({
667
- top: currentTop,
668
- left: currentLeft,
669
- height: currentHeight,
670
- width: currentWidth,
671
- ...size
672
- })
673
- : size;
674
- this.setState({
675
- resizing: handlerName === "onResizeStop" ? null : updatedSize
676
- });
677
- }
678
-
679
615
  handler.call(this, i, w, h, { e, node, size: updatedSize, handle });
680
616
  }
681
617
 
682
- /**
683
- * Helper functions to constrain dimensions of a GridItem
684
- */
685
- constrainWidth(left: number, currentWidth: number, newWidth: number): number {
686
- return left + newWidth > this.props.containerWidth
687
- ? currentWidth
688
- : newWidth;
689
- }
690
-
691
- constrainHeight(
692
- top: number,
693
- currentHeight: number,
694
- newHeight: number
695
- ): number {
696
- return top <= 0 ? currentHeight : newHeight;
697
- }
698
-
699
- constrainLeft(left: number): number {
700
- return Math.max(0, left);
701
- }
702
-
703
- constrainTop(top: number): number {
704
- return Math.max(0, top);
705
- }
706
-
707
618
  render(): ReactNode {
708
619
  const {
709
620
  x,
@@ -405,55 +405,65 @@ export default class ReactGridLayout extends React.Component<Props, State> {
405
405
  let { layout } = this.state;
406
406
  const { cols, preventCollision, allowOverlap } = this.props;
407
407
 
408
+ let shouldMoveItem = false;
409
+ let finalLayout;
410
+ let x;
411
+ let y;
412
+
408
413
  const [newLayout, l] = withLayoutItem(layout, i, l => {
414
+ let hasCollisions;
415
+ x = l.x;
416
+ y = l.y;
417
+ if (["sw", "w", "nw", "n", "ne"].indexOf(handle) !== -1) {
418
+ if (["sw", "nw", "w"].indexOf(handle) !== -1) {
419
+ x = l.x + (l.w - w);
420
+ w = l.x !== x && x < 0 ? l.w : w;
421
+ x = x < 0 ? 0 : x;
422
+ }
423
+
424
+ if (["ne", "n", "nw"].indexOf(handle) !== -1) {
425
+ y = l.y + (l.h - h);
426
+ h = l.y !== y && y < 0 ? l.h : h;
427
+ y = y < 0 ? 0 : y;
428
+ }
429
+
430
+ shouldMoveItem = true;
431
+ }
432
+
409
433
  // Something like quad tree should be used
410
434
  // to find collisions faster
411
- let hasCollisions;
412
435
  if (preventCollision && !allowOverlap) {
413
- const collisions = getAllCollisions(layout, { ...l, w, h }).filter(
414
- layoutItem => layoutItem.i !== l.i
415
- );
436
+ const collisions = getAllCollisions(layout, {
437
+ ...l,
438
+ w,
439
+ h,
440
+ x,
441
+ y
442
+ }).filter(layoutItem => layoutItem.i !== l.i);
416
443
  hasCollisions = collisions.length > 0;
417
444
 
418
445
  // If we're colliding, we need adjust the placeholder.
419
446
  if (hasCollisions) {
420
- // adjust w && h to maximum allowed space
421
- let leastX = Infinity,
422
- leastY = Infinity;
423
- collisions.forEach(layoutItem => {
424
- if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x);
425
- if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y);
426
- });
427
-
428
- if (Number.isFinite(leastX)) l.w = leastX - l.x;
429
- if (Number.isFinite(leastY)) l.h = leastY - l.y;
447
+ // Reset layoutItem dimensions if there were collisions
448
+ y = l.y;
449
+ h = l.h;
450
+ x = l.x;
451
+ w = l.w;
452
+ shouldMoveItem = false;
430
453
  }
431
454
  }
432
455
 
456
+ l.w = w;
457
+ l.h = h;
458
+
433
459
  return l;
434
460
  });
435
461
 
436
462
  // Shouldn't ever happen, but typechecking makes it necessary
437
463
  if (!l) return;
438
464
 
439
- let finalLayout;
440
- if (["sw", "w", "nw", "n", "ne"].indexOf(handle) !== -1) {
441
- let x = l.x;
442
- let y = l.y;
443
- if (["sw", "nw", "w"].indexOf(handle) !== -1) {
444
- x = l.x + (l.w - w);
445
- w = l.x !== x && x < 0 ? l.w : w;
446
- x = x < 0 ? 0 : x;
447
- }
448
-
449
- if (["ne", "n", "nw"].indexOf(handle) !== -1) {
450
- y = l.y + (l.h - h);
451
- h = l.y !== y && y < 0 ? l.h : h;
452
- y = y < 0 ? 0 : y;
453
- }
454
-
455
- l.w = w;
456
- l.h = h;
465
+ finalLayout = newLayout;
466
+ if (shouldMoveItem) {
457
467
  // Move the element to the new position.
458
468
  const isUserAction = true;
459
469
  finalLayout = moveElement(
@@ -467,10 +477,6 @@ export default class ReactGridLayout extends React.Component<Props, State> {
467
477
  cols,
468
478
  allowOverlap
469
479
  );
470
- } else {
471
- l.w = w;
472
- l.h = h;
473
- finalLayout = newLayout;
474
480
  }
475
481
 
476
482
  // Create placeholder element (display only)
@@ -11,7 +11,8 @@ import type {
11
11
  EventCallback,
12
12
  CompactType,
13
13
  Layout,
14
- LayoutItem
14
+ LayoutItem,
15
+ ResizeHandleAxis
15
16
  } from "./utils";
16
17
 
17
18
  // util
@@ -19,15 +20,6 @@ export type ReactRef<T: HTMLElement> = {|
19
20
  +current: T | null
20
21
  |};
21
22
 
22
- export type ResizeHandleAxis =
23
- | "s"
24
- | "w"
25
- | "e"
26
- | "n"
27
- | "sw"
28
- | "nw"
29
- | "se"
30
- | "ne";
31
23
  export type ResizeHandle =
32
24
  | ReactElement<any>
33
25
  | ((
package/lib/utils.js CHANGED
@@ -5,6 +5,17 @@ import type {
5
5
  ChildrenArray as ReactChildrenArray,
6
6
  Element as ReactElement
7
7
  } from "react";
8
+
9
+ export type ResizeHandleAxis =
10
+ | "s"
11
+ | "w"
12
+ | "e"
13
+ | "n"
14
+ | "sw"
15
+ | "nw"
16
+ | "se"
17
+ | "ne";
18
+
8
19
  export type LayoutItem = {
9
20
  w: number,
10
21
  h: number,
@@ -19,7 +30,7 @@ export type LayoutItem = {
19
30
  static?: boolean,
20
31
  isDraggable?: ?boolean,
21
32
  isResizable?: ?boolean,
22
- resizeHandles?: Array<"s" | "w" | "e" | "n" | "sw" | "nw" | "se" | "ne">,
33
+ resizeHandles?: Array<ResizeHandleAxis>,
23
34
  isBounded?: ?boolean
24
35
  };
25
36
  export type Layout = $ReadOnlyArray<LayoutItem>;
@@ -661,6 +672,126 @@ export function perc(num: number): string {
661
672
  return num * 100 + "%";
662
673
  }
663
674
 
675
+ /**
676
+ * Helper functions to constrain dimensions of a GridItem
677
+ */
678
+ const constrainWidth = (
679
+ left: number,
680
+ currentWidth: number,
681
+ newWidth: number,
682
+ containerWidth: number
683
+ ) => {
684
+ return left + newWidth > containerWidth ? currentWidth : newWidth;
685
+ };
686
+
687
+ const constrainHeight = (
688
+ top: number,
689
+ currentHeight: number,
690
+ newHeight: number
691
+ ) => {
692
+ return top < 0 ? currentHeight : newHeight;
693
+ };
694
+
695
+ const constrainLeft = (left: number) => Math.max(0, left);
696
+
697
+ const constrainTop = (top: number) => Math.max(0, top);
698
+
699
+ const resizeNorth = (currentSize, { left, height, width }, _containerWidth) => {
700
+ const top = currentSize.top - (height - currentSize.height);
701
+
702
+ return {
703
+ left,
704
+ width,
705
+ height: constrainHeight(top, currentSize.height, height),
706
+ top: constrainTop(top)
707
+ };
708
+ };
709
+
710
+ const resizeEast = (
711
+ currentSize,
712
+ { top, left, height, width },
713
+ containerWidth
714
+ ) => ({
715
+ top,
716
+ height,
717
+ width: constrainWidth(
718
+ currentSize.left,
719
+ currentSize.width,
720
+ width,
721
+ containerWidth
722
+ ),
723
+ left: constrainLeft(left)
724
+ });
725
+
726
+ const resizeWest = (currentSize, { top, height, width }, containerWidth) => {
727
+ const left = currentSize.left - (width - currentSize.width);
728
+
729
+ return {
730
+ height,
731
+ width:
732
+ left < 0
733
+ ? currentSize.width
734
+ : constrainWidth(
735
+ currentSize.left,
736
+ currentSize.width,
737
+ width,
738
+ containerWidth
739
+ ),
740
+ top: constrainTop(top),
741
+ left: constrainLeft(left)
742
+ };
743
+ };
744
+
745
+ const resizeSouth = (
746
+ currentSize,
747
+ { top, left, height, width },
748
+ containerWidth
749
+ ) => ({
750
+ width,
751
+ left,
752
+ height: constrainHeight(top, currentSize.height, height),
753
+ top: constrainTop(top)
754
+ });
755
+
756
+ const resizeNorthEast = (...args) =>
757
+ resizeNorth(args[0], resizeEast(...args), args[2]);
758
+ const resizeNorthWest = (...args) =>
759
+ resizeNorth(args[0], resizeWest(...args), args[2]);
760
+ const resizeSouthEast = (...args) =>
761
+ resizeSouth(args[0], resizeEast(...args), args[2]);
762
+ const resizeSouthWest = (...args) =>
763
+ resizeSouth(args[0], resizeWest(...args), args[2]);
764
+
765
+ const ordinalResizeHandlerMap = {
766
+ n: resizeNorth,
767
+ ne: resizeNorthEast,
768
+ e: resizeEast,
769
+ se: resizeSouthEast,
770
+ s: resizeSouth,
771
+ sw: resizeSouthWest,
772
+ w: resizeWest,
773
+ nw: resizeNorthWest
774
+ };
775
+
776
+ /**
777
+ * Helper for clamping width and position when resizing an item.
778
+ */
779
+ export function resizeItemInDirection(
780
+ direction: ResizeHandleAxis,
781
+ currentSize: Position,
782
+ newSize: Position,
783
+ containerWidth: number
784
+ ): Position {
785
+ const ordinalHandler = ordinalResizeHandlerMap[direction];
786
+ // Shouldn't be possible given types; that said, don't fail hard
787
+ if (!ordinalHandler) return newSize;
788
+ return ordinalHandler(
789
+ currentSize,
790
+ { ...currentSize, ...newSize },
791
+ containerWidth
792
+ );
793
+ }
794
+
664
795
  export function setTransform({ top, left, width, height }: Position): Object {
665
796
  // Replace unitless items with px
666
797
  const translate = `translate(${left}px,${top}px)`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grid-layout",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "A draggable and resizable grid layout with responsive breakpoints, for React.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -38,12 +38,15 @@
38
38
  "homepage": "https://github.com/STRML/react-grid-layout",
39
39
  "dependencies": {
40
40
  "clsx": "^2.0.0",
41
- "fast-equals": "^5.0.1",
41
+ "fast-equals": "^4.0.3",
42
42
  "prop-types": "^15.8.1",
43
43
  "react-draggable": "^4.4.5",
44
44
  "react-resizable": "^3.0.5",
45
45
  "resize-observer-polyfill": "^1.5.1"
46
46
  },
47
+ "_dependencyNotes": {
48
+ "fast-equals": "Bug in CRA5 causes fast-equals@5 to fail to import due to .cjs file. See https://github.com/react-grid-layout/react-grid-layout/issues/1904"
49
+ },
47
50
  "devDependencies": {
48
51
  "@babel/cli": "^7.22.15",
49
52
  "@babel/core": "^7.22.17",
@@ -119,9 +122,11 @@
119
122
  }
120
123
  },
121
124
  "lint-staged": {
122
- "*.{js,jsx,css}": [
123
- "eslint --ext .js,.jsx --fix",
124
- "yarn fmt"
125
+ "*.{js,jsx}": [
126
+ "eslint --ext .js,.jsx --fix"
127
+ ],
128
+ "*": [
129
+ "prettier --ignore-unknown --write"
125
130
  ]
126
131
  }
127
132
  }