react-grid-layout 1.2.0 → 1.2.4

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.
@@ -8,13 +8,15 @@ import {
8
8
  synchronizeLayoutWithChildren,
9
9
  validateLayout,
10
10
  noop,
11
- type Layout
11
+ type Layout,
12
+ type Pick
12
13
  } from "./utils";
13
14
  import {
14
15
  getBreakpointFromWidth,
15
16
  getColsFromBreakpoint,
16
17
  findOrGenerateResponsiveLayout,
17
18
  type ResponsiveLayout,
19
+ type OnLayoutChangeCallback,
18
20
  type Breakpoints
19
21
  } from "./responsiveUtils";
20
22
  import ReactGridLayout from "./ReactGridLayout";
@@ -28,13 +30,13 @@ const type = obj => Object.prototype.toString.call(obj);
28
30
  * @param {String} breakpoint Breakpoint: lg, md, sm, xs and etc.
29
31
  * @return {Array}
30
32
  */
31
-
32
33
  function getIndentationValue<T: ?[number, number]>(
33
34
  param: { [key: string]: T } | T,
34
35
  breakpoint: string
35
36
  ): T {
36
- // $FlowIssue doesn't seem to understand this
37
+ // $FlowIgnore TODO fix this typedef
37
38
  if (param == null) return null;
39
+ // $FlowIgnore TODO fix this typedef
38
40
  return Array.isArray(param) ? param : param[breakpoint];
39
41
  }
40
42
 
@@ -42,7 +44,7 @@ type State = {
42
44
  layout: Layout,
43
45
  breakpoint: string,
44
46
  cols: number,
45
- layouts?: { [key: string]: Layout }
47
+ layouts?: ResponsiveLayout<string>
46
48
  };
47
49
 
48
50
  type Props<Breakpoint: string = string> = {|
@@ -60,7 +62,7 @@ type Props<Breakpoint: string = string> = {|
60
62
 
61
63
  // Callbacks
62
64
  onBreakpointChange: (Breakpoint, cols: number) => void,
63
- onLayoutChange: (Layout, { [key: Breakpoint]: Layout }) => void,
65
+ onLayoutChange: OnLayoutChangeCallback,
64
66
  onWidthChange: (
65
67
  containerWidth: number,
66
68
  margin: [number, number],
@@ -69,6 +71,20 @@ type Props<Breakpoint: string = string> = {|
69
71
  ) => void
70
72
  |};
71
73
 
74
+ type DefaultProps = Pick<
75
+ Props<>,
76
+ {|
77
+ breakpoints: 0,
78
+ cols: 0,
79
+ containerPadding: 0,
80
+ layouts: 0,
81
+ margin: 0,
82
+ onBreakpointChange: 0,
83
+ onLayoutChange: 0,
84
+ onWidthChange: 0
85
+ |}
86
+ >;
87
+
72
88
  export default class ResponsiveReactGridLayout extends React.Component<
73
89
  Props<>,
74
90
  State
@@ -140,18 +156,18 @@ export default class ResponsiveReactGridLayout extends React.Component<
140
156
  onWidthChange: PropTypes.func
141
157
  };
142
158
 
143
- static defaultProps = {
159
+ static defaultProps: DefaultProps = {
144
160
  breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 },
145
161
  cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
162
+ containerPadding: { lg: null, md: null, sm: null, xs: null, xxs: null },
146
163
  layouts: {},
147
164
  margin: [10, 10],
148
- containerPadding: { lg: null, md: null, sm: null, xs: null, xxs: null },
149
165
  onBreakpointChange: noop,
150
166
  onLayoutChange: noop,
151
167
  onWidthChange: noop
152
168
  };
153
169
 
154
- state = this.generateInitialState();
170
+ state: State = this.generateInitialState();
155
171
 
156
172
  generateInitialState(): State {
157
173
  const { width, breakpoints, layouts, cols } = this.props;
@@ -178,7 +194,10 @@ export default class ResponsiveReactGridLayout extends React.Component<
178
194
  };
179
195
  }
180
196
 
181
- static getDerivedStateFromProps(nextProps: Props<*>, prevState: State) {
197
+ static getDerivedStateFromProps(
198
+ nextProps: Props<*>,
199
+ prevState: State
200
+ ): ?$Shape<State> {
182
201
  if (!isEqual(nextProps.layouts, prevState.layouts)) {
183
202
  // Allow parent to set layouts directly.
184
203
  const { breakpoint, cols } = prevState;
@@ -212,7 +231,7 @@ export default class ResponsiveReactGridLayout extends React.Component<
212
231
  }
213
232
 
214
233
  // wrap layouts so we do not need to pass layouts to child
215
- onLayoutChange = (layout: Layout) => {
234
+ onLayoutChange: Layout => void = (layout: Layout) => {
216
235
  this.props.onLayoutChange(layout, {
217
236
  ...this.props.layouts,
218
237
  [this.state.breakpoint]: layout
@@ -290,7 +309,7 @@ export default class ResponsiveReactGridLayout extends React.Component<
290
309
  );
291
310
  }
292
311
 
293
- render() {
312
+ render(): React.Element<typeof ReactGridLayout> {
294
313
  /* eslint-disable no-unused-vars */
295
314
  const {
296
315
  breakpoint,
@@ -309,6 +328,7 @@ export default class ResponsiveReactGridLayout extends React.Component<
309
328
  return (
310
329
  <ReactGridLayout
311
330
  {...other}
331
+ // $FlowIgnore should allow nullable here due to DefaultProps
312
332
  margin={getIndentationValue(margin, this.state.breakpoint)}
313
333
  containerPadding={getIndentationValue(
314
334
  containerPadding,
@@ -26,7 +26,7 @@ export function calcGridItemWHPx(
26
26
  gridUnits: number,
27
27
  colOrRowSize: number,
28
28
  marginPx: number
29
- ) {
29
+ ): number {
30
30
  // 0 * Infinity === NaN, which causes problems with resize contraints
31
31
  if (!Number.isFinite(gridUnits)) return gridUnits;
32
32
  return Math.round(
@@ -148,6 +148,10 @@ export function calcWH(
148
148
  }
149
149
 
150
150
  // Similar to _.clamp
151
- export function clamp(num: number, lowerBound: number, upperBound: number) {
151
+ export function clamp(
152
+ num: number,
153
+ lowerBound: number,
154
+ upperBound: number
155
+ ): number {
152
156
  return Math.max(Math.min(num, upperBound), lowerBound);
153
157
  }
@@ -1,7 +1,8 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
3
  import PropTypes from "prop-types";
4
- import ReactDOM from "react-dom";
4
+ import classNames from "classnames";
5
+ import type { ReactRef } from "../ReactGridLayoutPropTypes";
5
6
 
6
7
  type WPDefaultProps = {|
7
8
  measureBeforeMount: boolean
@@ -18,29 +19,27 @@ type WPState = {|
18
19
  width: number
19
20
  |};
20
21
 
22
+ type ComposedProps<Config> = {|
23
+ ...Config,
24
+ measureBeforeMount?: boolean,
25
+ className?: string,
26
+ style?: Object,
27
+ width?: number
28
+ |};
29
+
30
+ const layoutClassName = "react-grid-layout";
31
+
21
32
  /*
22
33
  * A simple HOC that provides facility for listening to container resizes.
23
34
  *
24
35
  * 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
36
  * a flow bug of some sort that causes it to stop typechecking.
26
37
  */
27
- export default function WidthProvider<Config>(
38
+ export default function WidthProvideRGL<Config>(
28
39
  ComposedComponent: React.AbstractComponent<Config>
29
- ): React.AbstractComponent<{|
30
- ...Config,
31
- measureBeforeMount?: boolean,
32
- className?: string,
33
- style?: Object,
34
- width?: number
35
- |}> {
40
+ ): React.AbstractComponent<ComposedProps<Config>> {
36
41
  return class WidthProvider extends React.Component<
37
- {|
38
- ...Config,
39
- measureBeforeMount?: boolean,
40
- className?: string,
41
- style?: Object,
42
- width?: number
43
- |},
42
+ ComposedProps<Config>,
44
43
  WPState
45
44
  > {
46
45
  static defaultProps: WPDefaultProps = {
@@ -53,15 +52,15 @@ export default function WidthProvider<Config>(
53
52
  measureBeforeMount: PropTypes.bool
54
53
  };
55
54
 
56
- state = {
55
+ state: WPState = {
57
56
  width: 1280
58
57
  };
59
58
 
59
+ elementRef: ReactRef<HTMLDivElement> = React.createRef();
60
60
  mounted: boolean = false;
61
61
 
62
62
  componentDidMount() {
63
63
  this.mounted = true;
64
-
65
64
  window.addEventListener("resize", this.onWindowResize);
66
65
  // Call to properly set the breakpoint and resize the elements.
67
66
  // Note that if you're doing a full-width element, this can get a little wonky if a scrollbar
@@ -76,21 +75,34 @@ export default function WidthProvider<Config>(
76
75
 
77
76
  onWindowResize = () => {
78
77
  if (!this.mounted) return;
79
- // eslint-disable-next-line react/no-find-dom-node
80
- const node = ReactDOM.findDOMNode(this); // Flow casts this to Text | Element
81
- if (node instanceof HTMLElement)
78
+ const node = this.elementRef.current; // Flow casts this to Text | Element
79
+ // fix: grid position error when node or parentNode display is none by window resize
80
+ // #924 #1084
81
+ if (node instanceof HTMLElement && node.offsetWidth) {
82
82
  this.setState({ width: node.offsetWidth });
83
+ }
83
84
  };
84
85
 
85
86
  render() {
86
87
  const { measureBeforeMount, ...rest } = this.props;
87
88
  if (measureBeforeMount && !this.mounted) {
88
89
  return (
89
- <div className={this.props.className} style={this.props.style} />
90
+ <div
91
+ className={classNames(this.props.className, layoutClassName)}
92
+ style={this.props.style}
93
+ // $FlowIgnore ref types
94
+ ref={this.elementRef}
95
+ />
90
96
  );
91
97
  }
92
98
 
93
- return <ComposedComponent {...rest} {...this.state} />;
99
+ return (
100
+ <ComposedComponent
101
+ innerRef={this.elementRef}
102
+ {...rest}
103
+ {...this.state}
104
+ />
105
+ );
94
106
  }
95
107
  };
96
108
  }
@@ -15,6 +15,11 @@ export type Breakpoints<T: Breakpoint> = {
15
15
  +[breakpoint: T]: number
16
16
  };
17
17
 
18
+ export type OnLayoutChangeCallback = (
19
+ Layout,
20
+ { [key: Breakpoint]: Layout }
21
+ ) => void;
22
+
18
23
  /**
19
24
  * Given a width, find the highest breakpoint that matches is valid for it (width > breakpoint).
20
25
  *
package/lib/utils.js CHANGED
@@ -19,7 +19,7 @@ export type LayoutItem = {
19
19
  static?: boolean,
20
20
  isDraggable?: ?boolean,
21
21
  isResizable?: ?boolean,
22
- resizeHandles?: Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'>,
22
+ resizeHandles?: Array<"s" | "w" | "e" | "n" | "sw" | "nw" | "se" | "ne">,
23
23
  isBounded?: ?boolean
24
24
  };
25
25
  export type Layout = $ReadOnlyArray<LayoutItem>;
@@ -52,12 +52,15 @@ export type DragOverEvent = MouseEvent & {
52
52
  nativeEvent: {
53
53
  layerX: number,
54
54
  layerY: number,
55
- target: {
56
- className: String
57
- }
55
+ ...Event
58
56
  }
59
57
  };
60
58
 
59
+ // Helpful port from TS
60
+ export type Pick<FromType, Properties: { [string]: 0 }> = $Exact<
61
+ $ObjMapi<Properties, <K, V>(k: K, v: V) => $ElementType<FromType, K>>
62
+ >;
63
+
61
64
  type REl = ReactElement<any>;
62
65
  export type ReactChildren = ReactChildrenArray<REl>;
63
66
 
@@ -99,6 +102,35 @@ export function cloneLayout(layout: Layout): Layout {
99
102
  return newLayout;
100
103
  }
101
104
 
105
+ // Modify a layoutItem inside a layout. Returns a new Layout,
106
+ // does not mutate. Carries over all other LayoutItems unmodified.
107
+ export function modifyLayout(layout: Layout, layoutItem: LayoutItem): Layout {
108
+ const newLayout = Array(layout.length);
109
+ for (let i = 0, len = layout.length; i < len; i++) {
110
+ if (layoutItem.i === layout[i].i) {
111
+ newLayout[i] = layoutItem;
112
+ } else {
113
+ newLayout[i] = layout[i];
114
+ }
115
+ }
116
+ return newLayout;
117
+ }
118
+
119
+ // Function to be called to modify a layout item.
120
+ // Does defensive clones to ensure the layout is not modified.
121
+ export function withLayoutItem(
122
+ layout: Layout,
123
+ itemKey: string,
124
+ cb: LayoutItem => LayoutItem
125
+ ): [Layout, ?LayoutItem] {
126
+ let item = getLayoutItem(layout, itemKey);
127
+ if (!item) return [layout, null];
128
+ item = cb(cloneLayoutItem(item)); // defensive clone then modify
129
+ // FIXME could do this faster if we already knew the index
130
+ layout = modifyLayout(layout, item);
131
+ return [layout, item];
132
+ }
133
+
102
134
  // Fast path to cloning, since this is monomorphic
103
135
  export function cloneLayoutItem(layoutItem: LayoutItem): LayoutItem {
104
136
  return {
@@ -144,7 +176,7 @@ type FastRGLPropsEqual = (Object, Object, Function) => boolean;
144
176
  export const fastRGLPropsEqual: FastRGLPropsEqual = require("./fastRGLPropsEqual");
145
177
 
146
178
  // Like the above, but a lot simpler.
147
- export function fastPositionEqual(a: Position, b: Position) {
179
+ export function fastPositionEqual(a: Position, b: Position): boolean {
148
180
  return (
149
181
  a.left === b.left &&
150
182
  a.top === b.top &&
@@ -276,7 +308,6 @@ export function compactItem(
276
308
  l.y--;
277
309
  }
278
310
  } else if (compactH) {
279
- l.y = Math.min(bottom(compareWith), l.y);
280
311
  // Move the element left as far as it can go without colliding.
281
312
  while (l.x > 0 && !getFirstCollision(compareWith, l)) {
282
313
  l.x--;
@@ -665,9 +696,11 @@ export function synchronizeLayoutWithChildren(
665
696
  if (!isProduction) {
666
697
  validateLayout([g], "ReactGridLayout.children");
667
698
  }
699
+ // FIXME clone not really necessary here
668
700
  layout[i] = cloneLayoutItem({ ...g, i: child.key });
669
701
  } else {
670
702
  // Nothing provided: ensure this is added to the bottom
703
+ // FIXME clone not really necessary here
671
704
  layout[i] = cloneLayoutItem({
672
705
  w: 1,
673
706
  h: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grid-layout",
3
- "version": "1.2.0",
3
+ "version": "1.2.4",
4
4
  "description": "A draggable and resizable grid layout with responsive breakpoints, for React.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -42,45 +42,49 @@
42
42
  "react-resizable": "^1.10.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@babel/cli": "^7.11.6",
46
- "@babel/core": "^7.11.6",
47
- "@babel/plugin-proposal-class-properties": "^7.10.4",
48
- "@babel/plugin-transform-flow-comments": "^7.10.4",
49
- "@babel/preset-env": "^7.11.5",
50
- "@babel/preset-flow": "^7.10.4",
51
- "@babel/preset-react": "^7.10.4",
52
- "@babel/register": "^7.11.5",
45
+ "@babel/cli": "^7.13.0",
46
+ "@babel/core": "^7.13.8",
47
+ "@babel/plugin-proposal-class-properties": "^7.13.0",
48
+ "@babel/plugin-transform-flow-comments": "^7.12.13",
49
+ "@babel/preset-env": "^7.13.8",
50
+ "@babel/preset-flow": "^7.12.13",
51
+ "@babel/preset-react": "^7.12.13",
52
+ "@babel/register": "^7.13.8",
53
53
  "babel-eslint": "^10.1.0",
54
- "babel-jest": "^26.3.0",
55
- "babel-loader": "^8.1.0",
54
+ "babel-jest": "^26.6.3",
55
+ "babel-loader": "^8.2.2",
56
56
  "babel-plugin-preval": "^5.0.0",
57
- "css-loader": "^4.2.2",
58
- "ejs": "^3.1.5",
57
+ "css-loader": "^5.1.1",
58
+ "ejs": "^3.1.6",
59
59
  "enzyme": "^3.11.0",
60
- "enzyme-adapter-react-16": "^1.15.4",
61
- "enzyme-to-json": "^3.5.0",
62
- "eslint": "^7.8.1",
63
- "eslint-plugin-flowtype": "^5.2.0",
60
+ "enzyme-adapter-react-16": "^1.15.6",
61
+ "enzyme-to-json": "^3.6.1",
62
+ "eslint": "^7.21.0",
63
+ "eslint-plugin-flowtype": "^5.3.1",
64
64
  "eslint-plugin-mocha": "^8.0.0",
65
- "eslint-plugin-react": "^7.20.6",
66
- "exports-loader": "^1.1.0",
67
- "flow-bin": "^0.133.0",
68
- "husky": "^4.2.5",
69
- "imports-loader": "^1.1.0",
70
- "jest": "^26.4.2",
71
- "lint-staged": "^10.3.0",
72
- "lodash": "^4.17.20",
65
+ "eslint-plugin-react": "^7.22.0",
66
+ "exports-loader": "^2.0.0",
67
+ "flow-bin": "^0.145.0",
68
+ "husky": "^5.1.2",
69
+ "imports-loader": "^2.0.0",
70
+ "jest": "^26.6.3",
71
+ "lint-staged": "^10.5.4",
72
+ "lodash": "^4.17.21",
73
73
  "opener": "^1.5.2",
74
- "prettier": "^2.1.1",
74
+ "prettier": "^2.2.1",
75
75
  "react": "^16.13.1",
76
76
  "react-dom": "^16.13.1",
77
- "react-hot-loader": "^4.12.21",
77
+ "react-hot-loader": "^4.13.0",
78
78
  "react-transform-hmr": "^1.0.2",
79
- "style-loader": "^1.2.1",
79
+ "style-loader": "^2.0.0",
80
80
  "timsort": "^0.3.0",
81
- "webpack": "^4.44.1",
82
- "webpack-cli": "^3.3.12",
83
- "webpack-dev-server": "^3.11.0"
81
+ "webpack": "^5.24.2",
82
+ "webpack-cli": "^4.5.0",
83
+ "webpack-dev-server": "^3.11.2"
84
+ },
85
+ "peerDependencies": {
86
+ "react": ">= 16.3.0",
87
+ "react-dom": ">= 16.3.0"
84
88
  },
85
89
  "publishConfig": {
86
90
  "registry": "https://registry.npmjs.org"
@@ -99,13 +103,7 @@
99
103
  },
100
104
  "lint-staged": {
101
105
  "*.{js,jsx,css}": [
102
- "prettier --write",
103
- "git add"
106
+ "prettier --write"
104
107
  ]
105
- },
106
- "husky": {
107
- "hooks": {
108
- "pre-commit": "lint-staged && make lint && make test"
109
- }
110
108
  }
111
109
  }